:- dynamic	players/1, mynumber/1, 
		asker/1, askingP/1, askingW/1, askingR/1,
		hold/2, not_hold/2, not_held/3,
		showed_nothing/2, showed/2, guess/1,
		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)),
		allCards(All), i_not_hold(MyNumber, MyCards, All),
		i_hold(MyNumber, MyCards),
		Next is (MyNumber mod Players)+1,
		other_lists(Players, Next, MyNumber).
/* When setup() is complete, many facts have been asserted:			*/
/*		hold() fact for computer player					*/
/*		hold() facts with empty lists for all other players		*/
/*		not_held() facts for all cards that the computer doesn't hold	*/



i_not_hold(Me, [LastCard], NotMine) :-

		append( Start_NM, [LastCard | End_NM], NotMine ),
		append( Start_NM, End_NM, Really_NotMine),
	/*	assert( not_hold(Me, Really_NotMine) ),		*/	/* Not sure if this matters. */
		make_not_held_lists(Really_NotMine, Me).

i_not_hold(Me, [NextCard | Rest], NotMine) :-

		append( Start_NM, [NextCard | End_NM], NotMine ),
		append( Start_NM, End_NM, Really_NotMine),
		i_not_hold(Me, Rest, Really_NotMine).



/* Set up not_held(C,L,N) facts. They reveal 1 correct card when N=# of players. */
make_not_held_lists([Last_NM], Me) :-

		assert(not_held(Last_NM, [Me], 1)).

make_not_held_lists([Next_NM | Rest], Me) :-

		assert(not_held(Next_NM, [Me], 1)),
		make_not_held_lists(Rest, Me).



i_hold(Me, [LastCard]) :-

		eliminate(LastCard, Me), !.

i_hold(Me, [NextCard | Rest]) :-

		eliminate(NextCard, Me),
		i_hold(Me, Rest).



/* Set up facts regarding other players' cards. */
other_lists(_Total, Me, Me).		/* All players' lists have been asserted. */

other_lists(Total, X, Me) :-

		assert(hold(X, [])),
	/*	assert(not_hold(X, [])),	*/	/* Not sure if these will matter. */
		Next is (X mod Total)+1,
		other_lists(Total, Next, Me).






/* 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 player'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. */
					/* Success here means this player can't 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([P, W, R], _Player) :-

		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_and_strategy,		*/
/* Eventually, there should be a separate planning system to handle reasoning and advanced strategy. */
/* Reasoning would include sorting through lots of partial knowledge to find new definite facts to */
/* narrow the possX() lists.  The action narrowed() would be one part of that reasoner.  */

		players(Total), narrowed(Total),
						/* Get next possiblity for each card type. */
		possP([P | _PossP], _I), possW([W | _PossW], _J), possR([R | _PossR], _K),
		write('My question is:  '), write([P, W, R]), nl,
		assert(askingP(P)), assert(askingW(W)), assert(askingR(R)).





/* Answer phase. An answer always involves showing cards. */
answer_time(Asker, Asker, Q) :-

		assert(showed_nothing(Asker, Q)).		/* Nobody had a card. */
								/* Not sure if this can give knowledge. */
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).



/* Response part of answer phase: some information is gathered. */
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),		/* I asked the question. */
		show_me(Ans, Ask).

opp_ans(_Ans, Ask, _Q, yes) :-		/* Ans has at least one of the cards in Q. */

		not(mynumber(Ask)).	/* Another opponent asked the question. */
	/*	might_have(Ans, Q)	*/
/* When one opponent shows another a card in response to a question [p,w,r], it becomes known that */
/* the shower holds card p or w or r.  When the reasoning system is further developed, a predicate */
/* like might_have() could be called to assert this partial knowledge that the computer would later */
/* use to infer definite knowledge. Currently the reasoning system can't handle this. */

opp_ans(Ans, Ask, Q, no) :-		/* Ans has none of the cards in Q. */

		doesnt_have(Ans, Q),	/* Info. */
		players(N), NextAns is (Ans mod N)+1,
		answer_time(NextAns, Ask, Q).

opp_ans(Ans, Ask, Q, _YesNo) :-		/* Avoid crashing. */

		nl, write('Please respond yes or no!'), nl,
		can_opp_ans(Ans, Ask, Q).



i_ans(Me, Ask, [P, W, R]) :-

			/* First pick a card. */
			/* 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)
		    )
		  )

		),
			/* Then show that card to the opponent. */
		i_show(Ask, Show).

i_ans(Me, Ask, Q) :-

		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: some information is collected. */
show_me(Ans, _Me) :-

		write('What card does player '), write(Ans), write(' show me?'), nl,
		read(Card), nl,
		(person(Card); weapon(Card); room(Card)),	/* Make sure that card is legal. */
		eliminate(Card, Ans).

show_me(Ans, Me) :-
			/* 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, Me, [P,W,R]).		



i_show(P, C) :-

		write('Show player '), write(P), write(' this card:  '), write(C), nl, nl.





/* Guess phase: guessing right means you win; guessing wrong means you're out of the game. */
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) :-

		showed_nothing(Player, Q),
		retract(showed_nothing(Player, Q)).
	/*	might_have(Player, Q) 		*/
/* If no one else had a card in Q and Player does not guess Q, then Player must have */
/* some of the cards in Q. Again, the current system can't handle this partial information. */

opp_guess(Player, yes) :-

		write('What does player '), write(Player), write(' guess?'), nl,
		write('[person, weapon, room]:  '), read(Guess), nl,
		assert(guess(Guess)),
		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,
		won(P, YesNo).


won(P, yes) :-

		assert(gameover(P)).		/* Player P won so the game will end. */

won(P, no) :-

		assert(out(P)).			/* Player P was wrong so s/he can't ask questions anymore. */

won(P, _YesNo) :-

		nl, write('Please respond yes or no!'), nl,
		guess(G), try_win(P, G).





/* Reasoning part. */
doesnt_have(P, [Last]) :-

		not_held(Last, List, N),	/* I still don't know where Last is. */
		not(member(P, List)),		/* I didn't already know P doesn't hold Last. */
						/* If these conditions are met, change what I */
						/* know about where Last is not. */
		append(P, List, NewList),
		M is N+1,
		retract(not_held(Last, List, N)),
		assert(not_held(Last, NewList, M)).

doesnt_have(_P, [Last]).			/* I already know P doesn't have Last. */

doesnt_have(P, [Next | Rest]) :-

		not_held(Next, List, N),	/* I still don't know where Next is. */
		not(member(P, List)),		/* I didn't already know P doesn't hold Next. */
						/* If these conditions are met, change what I */
						/* know about where Next is not. */
		append(P, List, NewList),
		M is N+1,
		retract(not_held(Next, List, N)),
		assert(not_held(Next, NewList, M)),
		doesnt_have(P, Rest).

doesnt_have(P, [_Next | Rest]) :-		/* I already know P doesn't have Next, but */

		doesnt_have(P, Rest).		/* there are still cards to check out. */



/* If player P holds card X, then it is no longer a possibility. */
eliminate(X, P) :-

		person(X),
		eliminateP(X),
		change_lists(X, P).

eliminate(X, P) :-

		weapon(X),
		eliminateW(X),
		change_lists(X, P).

eliminate(X, P) :-

		room(X),
		eliminateR(X),
		change_lists(X, P).

eliminate(X, P) :-

		mynumber(P),
		write('OOPS!  I cannot have '), write(X), write('. It is not a card.'), nl,
		write('Start over.'),
		halt.
/* The second eliminate() is called only if 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. */


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))
		  )
		).



change_lists(C, P) :-

		not_held(C, NotHolders, N),	/* Condition: holder of card C is unknown. */
						/* Action: change knowledge of P's cards. */
		hold(P, HeldCards),
		append(C, HeldCards, New_HC),
		retract(hold(P, HeldCards)),
		assert(hold(P, New_HC)),
						/* This partial knowledge is not relevant */
						/* now because of new definite knowledge. */ 
		retract(not_held(C, NotHolders, N)).

change_lists(_C, _P).		/* Don't change anything if holder of C is already known. */



narrowed(Total) :-

		not_held(Card, List, Total),	/* Condition: no players hold Card. */
		thats_it(Card),
		retract(not_held(Card, List, Total)),
		narrowed(Total), !.

narrowed(_Total).		/* Stop when there are no more full not_held() facts. */


thats_it(C) :-

		person(C),
		retract(possP(_Old, _N)),
		assert(possP([C], 1)).

thats_it(C) :-

		weapon(C),
		retract(possW(_Old, _N)),
		assert(possW([C], 1)).

thats_it(C) :-

		room(C),
		retract(possR(_Old, _N)),
		assert(possR([C], 1)).




/* System's knowledge base. Facts necessary to play game */

/* 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). 


/* Lists of cards that could still be in the solution. */
/* Before the game begins, anything is possible. */
/*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, mrG,
	rope, gun, wrench,
	hall, lounge, study, ball]).
