:- dynamic	players/1, mynumber/1, 
		asker/1, askingP/1, askingW/1, askingR/1,
		hold/2, not_hold/2, showed_nothing/2, showed/2,
		possP/2, possW/2, possR/2,
		gameover/1, out/1.



/* Beginning of game and setting up initial knowledge. */

play( Players, MyNumber, MyCards ) :-
						/* Check players.  3-6 may play. */
		Players > 2, MyNumber > 0, Players < 7, MyNumber < 7,
						/* Establish initial game knowledge. */
		setup(Players, MyNumber, MyCards ),
						/* Start playing game. Player 1 goes first. */
		taketurns(1, Players).



setup( Players, MyNumber, MyCards ) :-

		assert(players(Players)),	/* Important information for entire game. */
		assert(mynumber(MyNumber)),
		i_hold(MyNumber, MyCards),
		allCards(All), i_not_hold(MyNumber, MyCards, All).



i_hold(Me, [NextCard | Rest]) :-

		not(Rest = []),
		eliminate(NextCard, Me),
		i_hold(Me, Rest).

i_hold(Me, [LastCard]) :-

		eliminate(LastCard, Me).



i_not_hold(Me, [NextCard | Rest], NotMine) :-

		append( S_all, [NextCard | E_all], NotMine ),
		append( S_all, E_all, Really_NotMine),
		( (not(Rest = []),
		   i_not_hold(Me, Rest, Really_NotMine)
		  );

		  (Rest = [],
		   doesnt_have(Me, Really_NotMine)
		  )
		).





/* General game planner. Game is divided into turns. */
taketurns(_Active, _Total) :-

		gameover(X),		/* Check end condition. */
					/* Success here means game is over and X won. */
		write('Player '), write(X), write(' won.').

taketurns(Active, Total) :-

		out(Active),		/* Make sure this player is still in the game. */
					/* Success here means skip this person's turn. */
		write('Player '), write(Active), write(' is out.'), nl,
		Next is (Active mod Total)+1,
		taketurns(Next, Total).

taketurns(Active, Total) :-

		not_in_room(Active),	/* A player must be in a room to ask. */
		Next is (Active mod Total)+1,
		taketurns(Next, Total).

taketurns(Active, Total) :-

		not(out(Active)),	/* Double check. */
		assert(asker(Active)),
		Next is (Active mod Total)+1,
					/* Following 3 actions define normal a turn. */
		question_time(Active),
		askingP(P), askingW(W), askingR(R),
		answer_time(Next, Active, [P, W, R]),
		guess_time(Active),
					/* Clean up situation information before next turn. */
		retract(askingP(P)), retract(askingW(W)), retract(askingR(R)),
		retract(asker(Active)),
		taketurns(Next, Total).




not_in_room(X) :-				/* checks if X is in a room & so can ask */

		write('Can player '), write(X), write(' ask a question?'), nl,
		write('(yes/no)'), read(no), nl.
/* if 'no', not_in_room succeeds, and question_time in taketurns is never called */


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */





/* Question phase. */
question_time(Asker) :-

		mynumber(Asker),		/* Its my turn to ask. */
		i_ask.

question_time(Asker) :-

		not(mynumber(Asker)), 		/* Its another player's turn to ask */
		opp_ask(Asker).



opp_ask(Asker) :-

		write('What does player '), write(Asker), write(' ask?'), nl,
		write('[player, weapon, room]: '), read(Q), nl,
		check_question(Q, Asker),			/* Avoid crashing. */
		[P, W, R] = Q,
		assert(askingP(P)), assert(askingW(W)), assert(askingR(R)).



check_question(Q, _Player) :-

		[P, W, R] = Q, person(P), weapon(W), room(R).

check_question(Q, Player) :-

		write(Q), write(' is not a valid question!'), nl,
		write('Please enter "[person, weapon, room]."'), nl,
		opp_ask(Player).



i_ask :-

	/*	reason(true),		*/
						/* Get next possiblity for each card type. */
		possP([P | _PossP], _I), possW([W | _PossW], _J), possR([R | _PossR], _K),
		append( [P, W, R], [], Q ), write('My question is:  '), write(Q), nl,
		assert(askingP(P)), assert(askingW(W)), assert(askingR(R)).




/* Answer phase. An answer always involves showing cards. */
answer_time(Answerer, Asker, Q) :-

		(Answerer = Asker),				/* Nobody had a card. */
		assert(showed_nothing(Asker, Q)).

answer_time(Answerer, Asker, Q) :-

		not(mynumber(Answerer)),
		can_opp_ans(Answerer, Asker, Q).

answer_time(Answerer, Asker, Q) :-

		mynumber(Answerer),
		i_ans(Answerer, Asker, Q).



can_opp_ans(Ans, Ask, Q) :-

		write('Does player '), write(Ans), write(' have one of those cards?'), nl,
		write('(yes/no) '), read(YesNo), nl,
		opp_ans(Ans, Ask, Q, YesNo).


opp_ans(Ans, Ask, _Q, yes) :-		/* Ans has at least one of the cards in Q. */

		mynumber(Ask),
		show_me(Ans, Ask).

opp_ans(_Ans, Ask, _Q, yes) :-

		not(mynumber(Ask)).	/*,might_have(Ans, Q)*/
				/* not sure what happens when one opp shows another a card. */

opp_ans(Ans, Ask, Q, no) :-		/* Ans has none of the cards in Q. */

		doesnt_have(Ans, Q),
		players(N), NextAns is (Ans mod N)+1,
		answer_time(NextAns, Ask, Q).

opp_ans(Ans, Ask, Q, _YesNo) :-

		nl, write('Please respond yes or no!'), nl,		/* Avoid crashing. */
		can_opp_ans(Ans, Ask, Q).



i_ans(Me, Ask, Q) :-

		append([P, W, R], [], Q),

			/* It's strategic to show a player a card they've already seen. */
		( ( (showed(Ask, P), Show = P);

		    (showed(Ask, W), Show = W);

		    (showed(Ask, R), Show = R);

		    (( (hold(Me, P), Show = P);

		       (hold(Me, W), Show = W);

		       (hold(Me, R), Show = R) ),
		       assert(showed(Ask, Show))
		    )

		  ),
		  i_show(Ask, Show)
		);

		( write('I do not have any of those cards.'), nl, nl,
		  players(N), NextAns is (Me mod N)+1,
		  answer_time(NextAns, Ask, Q)
		).




/* Show part of answer phase: information is collected. */
show_me(Ans, Ask) :-

		write('What card does player '), write(Ans), write(' show me?'), nl,
		read(Card), nl,
		( ( (person(Card); weapon(Card); room(Card)),
		    eliminate(Card, Ans)
		  );

			/* If that player did not show a legal card, ask him/her again. */
		  ( write('That is not a card!'), nl,
		    askingP(P), askingW(W), askingR(R), can_opp_ans(Ans, Ask, [P,W,R])
		  )
		).		


i_show(P, C) :-

		write('Show player '), write(P), write(' this card:  '), write(C), nl, nl.




/* Guess phase. */
guess_time(Player) :-

		not(mynumber(Player)),
		does_opp_guess(Player).

guess_time(Player) :-

		mynumber(Player), 
		my_guess(Player).



my_guess(Me) :-
					/* Computer only guesses when it is sure. */
		possP([P], 1), possW([W], 1), possR([R], 1),
		try_win(Me, [P, W, R]).

my_guess(_Me) :-

		write('I have no guess.'), nl.



does_opp_guess(Player) :-

		write('Does player '), write(Player), write(' guess?'), nl,
		write('(yes/no)'), read(YesNo), nl,
		opp_guess(Player, YesNo).


opp_guess(Player, no) :-

		not(showed_nothing(Player, _Q)).

opp_guess(Player, no) :-
				/* If no one else had a card in Q and Player does not */
				/* guess Q, then Player has some of the cards in Q. */
		showed_nothing(Player, Q),	/* might_have(Player, Q) */
		retract(showed_nothing(Player, Q)).

opp_guess(Player, yes) :-

		write('What does player '), write(Player), write(' guess?'), nl,
		write('[person, weapon, room]:  '), read(Guess), nl,
		try_win(Player, Guess).

opp_guess(Player, _YesNo) :-

		nl, write('Please respond yes or no!'), nl,		/* Avoid crashing. */
		does_opp_guess(Player).



try_win(P, G) :-

		write('Is '), write(G), write(' correct?'), nl,
		write('(yes/no)'), read(YesNo), nl,
		( ( YesNo = yes, assert(gameover(P)) );

		  ( YesNo = no, assert(out(P)), write('Does this give new knowledge?') );

		  ( nl, write('Please respond yes or no!'), nl, try_win(P, G) )
		).





/* Reasoning part. */
doesnt_have(P, [Next | Rest]) :-

		assert(not_hold(P, Next)),
		( (not(Rest = []), doesnt_have(P, Rest));

		  Rest = []
		).


/* If player P holds card X, then it is no longer a possibility. */
eliminate(X, P) :-

		( ((person(X), eliminateP(X));

		   (weapon(X), eliminateW(X));

		   (room(X), eliminateR(X))
		  ),
		  assert(hold(P, X))
		);

	/* In case there was a mistake in the initial setup. */
	/* i.e. the cards the computer was dealt were entered incorrectly. */
	/* Other times eliminate() is called, X is already known to be a legal card. */

		( mynumber(P),
		  write('OOPS!  I cannot have '), write(X), write('. It is not a card.'), nl,
		  write('Start over.')
		).


eliminateP(X) :-

		possP(PossList, I),
		( not(member(X, PossList));		/* Card X is already eliminated. */

		  ( append(Start_PL, [ X|Rest_PL ], PossList),
		    retract(possP(PossList, I)), J is I - 1,
		    append(Start_PL, Rest_PL, New_PossList),
		    assert(possP(New_PossList, J))
		  )
		).


eliminateW(X) :-

		possW(PossList, I),
		( not(member(X, PossList));		/* Card X is already eliminated. */

		  ( append(Start_PL, [ X|Rest_PL ], PossList),
		    retract(possW(PossList, I)), J is I - 1,
		    append(Start_PL, Rest_PL, New_PossList),
		    assert(possW(New_PossList, J))
		  )
		).

eliminateR(X) :-

		possR(PossList, I),
		( not(member(X, PossList));		/* Card X is already eliminated. */

		  ( append(Start_PL, [ X|Rest_PL ], PossList),
		    retract(possR(PossList, I)), J is I - 1,
		    append(Start_PL, Rest_PL, New_PossList),
		    assert(possR(New_PossList, J))
		  )
		).




		   


/* List of all cards. */
/*allCards([colM, missS, mrsW, mrG, profP, mrsP,*/
/*	rope, pipe, candle, gun, wrench, knife,*/
/*	conservatory, hall, lounge, ball, kitchen, study, billiard, library, dining]).*/

/* Card descriptions. */
person(missS). person(colM). person(mrsW). person(mrG). person(mrsP).  person(profP).

weapon(rope). weapon(wrench). weapon(candle). weapon(pipe). weapon(gun). weapon(knife).

room(lounge). room(dining). room(kitchen).  room(ball). room(conservatory). room(billiard). room(library). room(study). room(hall). 

/* at beginning, anything could possibly be part of the solution */
/*possP([ colM, missS, mrsW, mrG, profP, mrsP ], 6).*/
/*possW([ rope, pipe, candle, gun, wrench, knife ], 6).*/
/*possR([ conservatory, hall, lounge, ball, kitchen, study, billiard, library, dining ], 9).*/

possP([colM, missS, mrG], 3).
possW([rope, gun, wrench], 3).
possR([hall, lounge, study, ball], 4).


allCards([colM, missS, 
	rope, gun,
	hall, lounge, study]).
