%-------------------------------------------------------------------------% % % Author : Erwan Jahier % File : mastermind.m % %-------------------------------------------------------------------------% :- module mastermind. :- interface. :- import_module io, list. :- type color ---> blue ; white ; black ; yellow ; gray ; red ; brown ; orange ; hole. :- type mastermind ---> mastermind(color, color, color, color, color). :- type store ---> store(mastermind, int, int). :- type database == list(store). :- pred main(io__state::di, io__state::uo) is det. %-------------------------------------------------------------------------% :- implementation. :- import_module mastermind_generator, mastermind_checker, display, list, int, require, std_util. main --> ask_user_for_a_mastermind(Mastermind), solve_mastermind(Mastermind). %-------------------------------------------------------------------------% :- pred solve_mastermind(mastermind, io__state, io__state). :- mode solve_mastermind(in, di, uo) is det. solve_mastermind(Code) --> { generate_all_possible_guess(ListofGuesses) }, { solve_mastermind_step(Code, 0, NumberOfGuesses, [], Database, ListofGuesses, _) }, display_mastermind(Code, Database), % Display the results when tcl/tk display is broken. write_string("The solution was found with "), write(NumberOfGuesses), write_string(" guesses.\n"), write_string("The successive guesses were :\n"), print_database(Database), nl. %-------------------------------------------------------------------------% :- pred solve_mastermind_step( mastermind::in, % Code to break int::in, % Guess counter int::out, % New guess counter database::in, % List of previous attemps database::out, % New list of attemps list(mastermind)::in, % List of untried attemps list(mastermind)::out % New list of untried attemps ) is det. solve_mastermind_step(Code, GuessNumberIn, GuessNumberOut, DatabaseIn, DatabaseOut, ListofGuessesIn, ListofGuessesOut) :- ( propose_a_guess(DatabaseIn, ListofGuessesIn, ListofGuesses2, Guess), check_mastermind(Guess, Code, Bulls, Cows), ( Bulls = 5 -> DatabaseOut = [ store(Guess, Bulls, Cows) | DatabaseIn], GuessNumberOut is GuessNumberIn + 1, ListofGuessesOut = ListofGuesses2 ; solve_mastermind_step(Code, GuessNumberIn + 1, GuessNumberOut, [ store(Guess, Bulls, Cows) | DatabaseIn], DatabaseOut, ListofGuesses2, ListofGuessesOut ) ) ; % Should never occur error(" in solve_mastermind_step/6: I have tried all the possible solutions and I was not able to break that code !\n") ). %-------------------------------------------------------------------------% :- pred is_guess_sensible(mastermind, database). :- mode is_guess_sensible(in, in) is semidet. is_guess_sensible(_, []). is_guess_sensible(Guess, [ store(OldGuess, Bulls, Cows) | DatabaseTail]) :- % The guess is sensible if it produces the same number of cows and % bulls with the previous attemps. check_mastermind(OldGuess, Guess, Bulls, Cows), is_guess_sensible(Guess, DatabaseTail). :- pred propose_a_guess(database, list(mastermind), list(mastermind), mastermind). :- mode propose_a_guess(in, in, out, out) is semidet. propose_a_guess(Database, ListofGuessesIn, ListofGuessesOut, ProposedGuess) :- ListofGuessesIn = [ Guess | TailGuesses ], ( is_guess_sensible(Guess, Database) -> ListofGuessesOut = TailGuesses, ProposedGuess = Guess ; propose_a_guess(Database, TailGuesses, ListofGuessesOut, ProposedGuess) ). :- pred generate_all_possible_guess(list(mastermind)). :- mode generate_all_possible_guess(out) is det. generate_all_possible_guess(ListOfGuesses) :- solutions(generate_a_guess, ListOfGuesses). :- pred generate_a_guess(mastermind). :- mode generate_a_guess(out) is multi. generate_a_guess(Guess) :- Guess = mastermind(C1, C2, C3, C4, C5), select_color(C1), select_color(C2), select_color(C3), select_color(C4), select_color(C5). :- pred select_color(color). :- mode select_color(out) is multi. select_color(blue). select_color(white). select_color(black). select_color(yellow). select_color(gray). select_color(red). select_color(brown). select_color(orange). %-------------------------------------------------------------------------% :- pred print_database(database, io__state, io__state). :- mode print_database(in, di, uo) is det. print_database(Database) --> { reverse(Database, DatabaseReversed) }, print_database2(DatabaseReversed, 1), nl. :- pred print_database2(database, int, io__state, io__state). :- mode print_database2(in, in, di, uo) is det. print_database2([], _) --> nl. print_database2([store(mastermind(C1, C2, C3, C4, C5), Bulls, Cows)|Tail], N) --> write_string(" "), write(N), write_string(": "), write(C1), write_string(" "), write(C2), write_string(" "), write(C3), write_string(" "), write(C4), write_string(" "), write(C5), write_string(" ("), write(Bulls), write_string(" Bulls and "), write(Cows), write_string(" Cows).\n"), print_database2(Tail, N + 1). %-------------------------------------------------------------------------% % % Author : Erwan Jahier % File : mastermind_checker.m % % % This module implements the check_mastermind/4 predicate. % This predicate takes in input a Code and a Guess and outputs the number of % Bulls (colors that appears in identical position in the Guess and in the Code) % and Cows (colors that appears in the Guess and in the code but at wrong % positions). :- module mastermind_checker. :- interface. :- import_module mastermind, int. :- pred check_mastermind(mastermind, mastermind, int, int). :- mode check_mastermind(in, in, out, out) is det. :- implementation. :- import_module int, list, mastermind. %-------------------------------------------------------------------------% check_mastermind(Guess, Code, Bulls, Cows) :- count_bulls(Guess, Code, Bulls, Guess2, Code2), % Code2 and Guess2 is the same as Code and Guess except that % we have removed the well placed whatsits. count_cows(Guess2, Code2, Cows). :- pred count_bulls(mastermind, mastermind, int, mastermind, mastermind). :- mode count_bulls(in, in, out, out, out) is det. count_bulls(Guess, Code, Bulls, Guess2, Code2) :- Guess = mastermind(G1, G2, G3, G4, G5), Code = mastermind(Color1, Color2, Color3, Color4, Color5), Guess2 = mastermind( remove_bulls(G1, Color1), remove_bulls(G2, Color2), remove_bulls(G3, Color3), remove_bulls(G4, Color4), remove_bulls(G5, Color5) ), Code2 = mastermind( remove_bulls(Color1, G1), remove_bulls(Color2, G2), remove_bulls(Color3, G3), remove_bulls(Color4, G4), remove_bulls(Color5, G5) ), count_hole(Guess2, Bulls). :- func remove_bulls(color, color) = color. :- mode remove_bulls(in, in) = out is det. remove_bulls(G, Color) = Out :- % We remove the well placed whatsits ( (G = Color) -> Out = hole ; Out = G ). :- pred count_hole(mastermind, int). :- mode count_hole(in, out) is det. count_hole(mastermind(C1, C2, C3, C4, C5), N) :- Counter0 = 0, count_hole2(C1, Counter0, Counter1), count_hole2(C2, Counter1, Counter2), count_hole2(C3, Counter2, Counter3), count_hole2(C4, Counter3, Counter4), count_hole2(C5, Counter4, N). :- pred count_hole2(color, int, int). :- mode count_hole2(in, in, out) is det. count_hole2(Color, Cin, Cout) :- ( (Color = hole) -> Cout is Cin + 1 ; Cout = Cin ). :- pred count_cows(mastermind, mastermind, int). :- mode count_cows(in, in, out) is det. count_cows(Guess, Code, Cows) :- % Outputs the number of cows in the Guess. Guess = mastermind(G1, G2, G3, G4, G5), Code = mastermind(Color1, Color2, Color3, Color4, Color5), List1 = [G1, G2, G3, G4, G5], remove_cows_from_list(Color1, List1, List2), remove_cows_from_list(Color2, List2, List3), remove_cows_from_list(Color3, List3, List4), remove_cows_from_list(Color4, List4, List5), remove_cows_from_list(Color5, List5, List6), list__length(List6, M), Cows is 5 - M. :- pred remove_cows_from_list(color, list(color), list(color)). :- mode remove_cows_from_list(in, in, out) is det. remove_cows_from_list(_, [], []). remove_cows_from_list(C, [X | Xs], ListOut) :- ( C \= hole, C = X -> ListOut = Xs ; remove_cows_from_list(C, Xs, List), ListOut = [C | List] % Bug ! % ListOut = [X | List] % Correct code. ). %-------------------------------------------------------------------------% %-------------------------------------------------------------------------% % % Author : Erwan Jahier % File : mastermind_generator.m % % This module provides mastermind generators: % - ask_user_for_a_mastermind/1 % - generate_random_mastermind/3. % % The first one will prompt the user to enter a mastermind problem. The second % one will pseudo-randomly generate mastermind problems. :- module mastermind_generator. :- interface. :- import_module mastermind, io, random. :- pred ask_user_for_a_mastermind( mastermind::out, io__state::di, io__state::uo)is det. :- pred generate_random_mastermind( mastermind::out, random__supply::mdi, random__supply::muo) is det. :- pred convert_string_into_color(string, color). :- mode convert_string_into_color(in, out) is semidet. :- mode convert_string_into_color(out, in) is semidet. :- implementation. :- import_module mastermind, random, term, term_io, io, list, int. %-------------------------------------------------------------------------% ask_user_for_a_mastermind(Mastermind) --> write_string("Enter a mastermind problem :\n"), term_io__read_term(ReadTerm), ( { convert_readterm_into_mastermind(ReadTerm, Mastermind1) } -> { Mastermind = Mastermind1 } ; write_string("\nYou should write something like:\n"), write_string("mastermind(blue, white, black, yellow, gray)."), write_string("\nPossible colors are: "), write_string("blue, white, black, yellow, gray, red, brown, "), write_string("orange.\n"), ask_user_for_a_mastermind(Mastermind) ). :- pred convert_readterm_into_mastermind(read_term, mastermind). :- mode convert_readterm_into_mastermind(in, out) is semidet. convert_readterm_into_mastermind(ReadTerm, Mastermind) :- ReadTerm = term(_Varset, Term), Term = functor( atom("mastermind"), [ functor(atom(ColorStr1), _, _), functor(atom(ColorStr2), _, _), functor(atom(ColorStr3), _, _), functor(atom(ColorStr4), _, _), functor(atom(ColorStr5), _, _) ], context("<standard input>", _) ), convert_string_into_color(ColorStr1, Color1), convert_string_into_color(ColorStr2, Color2), convert_string_into_color(ColorStr3, Color3), convert_string_into_color(ColorStr4, Color4), convert_string_into_color(ColorStr5, Color5), Mastermind = mastermind(Color1, Color2, Color3, Color4, Color5). % :- pred convert_string_into_color(string, color). % :- mode convert_string_into_color(in, out) is semidet. % :- mode convert_string_into_color(out, in) is semidet. convert_string_into_color("red", red). convert_string_into_color("blue", blue). convert_string_into_color("white", white). convert_string_into_color("yellow", yellow). convert_string_into_color("gray", gray). convert_string_into_color("brown", brown). convert_string_into_color("orange", orange). convert_string_into_color("black", black). %-------------------------------------------------------------------------% % random__init(0, RS) % generate_random_mastermind(Mastermind, RS, RSout) % ... generate_random_mastermind(mastermind(C1, C2, C3, C4, C5), RSin, RSout) :- generate_one_color(C1, RSin, RS1), generate_one_color(C2, RS1, RS2), generate_one_color(C3, RS2, RS3), generate_one_color(C4, RS3, RS4), generate_one_color(C5, RS4, RSout). :- pred generate_one_color(color, random__supply, random__supply). :- mode generate_one_color(out, mdi, muo) is det. generate_one_color(Color, RSin, RSout) :- random__random(Num, RSin, RS1), random__randmax(RandMax, RS1, RSout), NNum is Num * 8, ( NNum < RandMax -> Color = blue ; NNum < 2*RandMax -> Color = white ; NNum < 3*RandMax -> Color = black ; NNum < 4*RandMax -> Color = yellow ; NNum < 5*RandMax -> Color = gray ; NNum < 6*RandMax -> Color = red ; Color = brown ). %-------------------------------------------------------------------------% % % Author : Erwan Jahier % File : display.m % % Display a mastermind board via a tcl/tk script :- module display. :- interface. :- import_module io, mastermind. :- pred display_mastermind(mastermind, database, io__state, io__state). :- mode display_mastermind(in, in, di, uo) is det. %-------------------------------------------------------------------------% :- implementation. :- import_module mastermind, mastermind_generator, mastermind_checker, list, int, require, std_util, string. display_mastermind(Code, Database) --> { generate_tcltk_command(Code, Database, TclTkCmd) }, io__call_system(TclTkCmd, _Res). :- pred generate_tcltk_command(mastermind, database, string). :- mode generate_tcltk_command(in, in, out) is det. generate_tcltk_command(Code, Database, TclTkCmd) :- Code = mastermind(C1, C2, C3, C4, C5), CodeList = [C1, C2, C3, C4, C5], list__length(Database, Card), int_to_string(Card, CardStr), transform_list_into_command(CodeList, CodeCmd2), wrap_with(CodeCmd2, " \" ", " \" ", CodeCmd), transform_database_into_command(Database, DatabaseCmd), append("display_mastermind ", CodeCmd, A1), append(A1, DatabaseCmd, A2), append(A2, CardStr, TclTkCmd). :- pred transform_database_into_command(database, string). :- mode transform_database_into_command(in, out) is det. transform_database_into_command(Database, DatabaseCmd) :- transform_database_into_command2(Database, SubmitionCmd, ResponseCmd), wrap_with(SubmitionCmd, " \" ", " \" ", SubmitionCmdNew), wrap_with(ResponseCmd, " \" ", " \" ", ResponseCmdNew), append(SubmitionCmdNew, ResponseCmdNew, DatabaseCmd). :- pred transform_database_into_command2(database, string, string). :- mode transform_database_into_command2(in, out, out) is det. transform_database_into_command2(Database, SubmitionList, ResponseList) :- ( Database = [], SubmitionList = " ", ResponseList = " " ; Database = [Store | Tail], Store = store(mastermind(C1, C2, C3, C4, C5), Bulls, Cows), List = [C1, C2, C3, C4, C5], transform_list_into_command(List, Submition), wrap_with(Submition, " { ", " } ", Submition2), generate_response_command(Bulls, Cows, Response), transform_database_into_command2(Tail, SubmitionTail, ResponseTail), append(Submition2, SubmitionTail, SubmitionList), append(Response, ResponseTail, ResponseList) ). :- pred generate_response_command(int, int, string). :- mode generate_response_command(in, in, out) is det. generate_response_command(Bulls, Cows, Response) :- generate_response(Bulls, " black ", BullsCmd), generate_response(Cows, " white ", CowsCmd), append(BullsCmd, CowsCmd, BullsCowsCmd), wrap_with(BullsCowsCmd, " { ", " } ", Response). :- pred generate_response(int, string, string). :- mode generate_response(in, in, out) is det. generate_response(N, Color, Cmd) :- ( N = 0 -> Cmd = " " ; % N > 0, NN is N - 1, generate_response(NN, Color, Cmd2), append(Color, Cmd2, Cmd) ). :- pred transform_list_into_command(list(color), string). :- mode transform_list_into_command(in, out) is det. transform_list_into_command(List , Cmd) :- ( List = [], Cmd = "" ; List = [C | Tail], transform_list_into_command(Tail, TailCmd), ( convert_string_into_color(CStr, C) -> append(CStr, " ", A1), append(A1, TailCmd, Cmd) ; Cmd = TailCmd ) ). :- pred wrap_with(string, string, string, string). :- mode wrap_with(in, in, in, out) is det. wrap_with(StringIn, Before, After, StringOut) :- append(Before, StringIn, A1), append(A1, After, StringOut).