Create a simple version of the board game:   Mastermind.

It must be possible to:

   choose the number of colors will be used in the game (2 - 20)
   choose the color code length (4 - 10)
   choose the maximum number of guesses the player has (7 - 20)
   choose whether or not colors may be repeated in the code


The (computer program) game should display all the player guesses and the results of that guess.

Display (just an idea):



 Feature 
 Graphic Version 
 Text Version


 Player guess

 Colored circles

 Alphabet letters


Correct color & position

Black circle

X


Correct color

White circle

O


None

Gray circle

 -




A text version example:        1.   ADEF   -   XXO- 

Translates to:

the first guess;

the four colors (ADEF);

result:

 two correct colors and spot, 
 one correct color/wrong spot, one color isn't in the code.
Happy coding!



Related tasks

   Bulls and cows
   Bulls and cows/Player
   Guess the number
   Guess the number/With Feedback


mastermind :- mastermind(7, 4, 8, no_duplicates).
 
mastermind(Colours, Length, Guesses, Duplicates) :-
	between(2, 20, Colours),
	between(4, 10, Length),
	between(7, 20, Guesses),
	member(Duplicates, [allow_duplicates, no_duplicates]),
 
	create_board(Colours, Length, Duplicates, Board),
	intro(Colours, Length, Duplicates),
	play(board(Board, Length, Colours, Guesses), [], 0), !.
 
intro(Colours, Length, Duplicates) :-
	format('Guess the code!~n'),
	format('There are ~p character types, and ~p letters to guess~n',
	    [Colours, Length]),
	Duplicates = allow_duplicates
	-> format('Duplicates are allowed~n~n')
	; format('Duplicates are not allowed~n~n').
 
/* Create the combination to be guessed */
create_board(Colours, Length, Duplicates, Board) :-
	length(Board, Length),
	valid_char_list(Colours, CharSet),
	repeat,
	maplist(random_alpha(CharSet), Board),
	check_for_duplicates(Board, Duplicates).
 
check_for_duplicates(_, allow_dupicates).
check_for_duplicates(Board, no_duplicates) :- is_set(Board).
 
/* Main loop - get the player guess and print out status */
play(board(Board,_,_,MaxGuesses), _, MaxGuesses) :-
	write('Sorry, You failed to guess in time...!\nThe code was : '),
	maplist(write, Board),
	nl.
play(BoardData, PrevGuesses, GuessNum) :-
	BoardData = board(_, Length, Colours, MaxGuesses),
	GuessNum < MaxGuesses,
	ReportedGuess is GuessNum + 1,
	format('Guess #~p of #~p: ', [ReportedGuess, MaxGuesses]),
	get_player_guess(Length, Colours, Guess),
	evaluate_and_print_result(BoardData, PrevGuesses, ReportedGuess, Guess).
 
evaluate_and_print_result(board(Board,_,_,_), _, _,Board) :-
	format('Well done! You Guessed Correctly.~n').
evaluate_and_print_result(BoardData, PrevGuesses, NextGuessNum, Guess) :-
	BoardData = board(Board, _, _, _),
	dif(Board, Guess),
	match_guess_to_board(Board, Guess, Diffs),
	append(PrevGuesses, [guess(NextGuessNum, Guess, Diffs)], Guesses),
	maplist(print_guess, Guesses),
	play(BoardData, Guesses, NextGuessNum).
 
/* Get the player guess and validate that it matches the rules */
get_player_guess(Length, Colours, Guess) :-
	repeat,
	read_line_to_string(user_input, Line),
	string_chars(Line, Guess),
 
	% validate the correct number of items have been entered
	length(Guess, Length),
 
	% validate that all the characters are valid for the number of colours
	valid_char_list(Colours, ValidCharSet),
	subset(Guess, ValidCharSet).
 
/* Predicates to figure out how many places are correct */
match_guess_to_board(Board, Guess, Matches) :-
	maplist(guess_differences(Board), Board, Guess, Differences),
	sort(0, @>=, Differences, Matches).
 
% Same position, same type
guess_differences(_Board, B, B, 'X').
% Same type, different position
guess_differences(Board, B, G, 'O') :- dif(B, G), member(G, Board).
% Type not on board
guess_differences(Board, B, G, '-') :- dif(B, G), \+ member(G, Board).
 
/* Print out the current progress */
print_guess(guess(NextGuessNumber, Guess, Differences))	:-
	format('~w: ', NextGuessNumber),
	maplist(format('~w '), Guess),
	format(' : '),
	maplist(format('~w '), Differences),
	nl.
 
/* Utils */
alpha_chars([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t]).
 
valid_char_list(Colours, CharSet) :-
	alpha_chars(AllChars),
	truncate_list(AllChars, Colours, CharSet).
 
random_alpha(AllChars, RandomMember) :- random_member(RandomMember, AllChars).
 
truncate_list(_, 0, []).
truncate_list([A|T], N, [A|R]) :-
	N > 0,
	N1 is N - 1,
	truncate_list(T, N1, R).