I'm pretty proud of the following, but would welcome any comments from experts:

%% Provide with a Goal and it will iterate towards it.

guess(Goal, Res) :-
    guessIterate(Goal, [], Res).

guessIterate(Goal, Guesses, Res) :-
    check_against_guesses(Guess, Guesses),
    score(Guess, Goal, ThisScore),
        %% if completely correct,...
        ThisScore == [4, 0]
        Res = Guess
        write(Guess), write(' '), write(ThisScore),nl,
        NewGuesses = [guess(Guess, ThisScore) | Guesses],
        guessIterate(Goal, NewGuesses, Res)

If this Guess is correct, it will pedict the right results for previous guesses
check_against_guesses(_, []).
check_against_guesses(Guess, [guess(Code, Score) | TGuesses]) :-
    score(Code, Guess, Score),
    check_against_guesses(Guess, TGuesses).

makeGuess(Guess) :-
    Colours = [red, blue, green, yellow, orange, purple, pink],
    member(G1, Colours),
    member(G2, Colours),
    member(G3, Colours),
    member(G4, Colours),
    Guess = [G1, G2, G3, G4].   

Score a guess, [#Black, #White], by first looking for exact matches - black - and then
look for inexaxct matches - white.
It is not possible for a guess of [...red, red...] to match twice with a
single 'red' in the Goal

score(Guess, Goal, Res) :-
    pass1(Guess, Goal, [], Pass1Res),
    pass2(Guess, Pass1Res, Pass2Res),
    countUp(Pass2Res, [0,0], Res).

pass1(_, [], Acc, AccRev) :-
    reverse(Acc, AccRev).

pass1([HGuess | TGuess], [HGoal | TGoal], Acc, Res) :-
        HGuess == HGoal
        AccNew = [black  | Acc]
        AccNew = [HGoal | Acc]
    pass1(TGuess, TGoal, AccNew, Res).

%% check each Guess element with Goal and change Goal element to 'white' if there is a match
pass2([], Goal, GoalRev) :-
    reverse(Goal, GoalRev).

pass2([HGuess | TGuess], Goal, Res) :-
    pass2Helper(HGuess, Goal, [], ModifiedGoal),
    pass2(TGuess, ModifiedGoal, Res).

pass2Helper(_, [], Acc, AccRev) :-
    reverse(Acc, AccRev).

pass2Helper(Guess, [HGoal | TGoal], Acc, Res) :-
        Guess == HGoal
        reverse(TGoal, TGoalRev),
        %% Res = [white | TGoalRev]
        append(TGoalRev, [white | Acc], Res)
        AccNew = [HGoal | Acc],
        pass2Helper(Guess, TGoal, AccNew, Res)

countUp([], Acc, Acc).
countUp([HRes|TRes], [AccB, AccW|_], Res) :-
        HRes == black
        AccBNew is AccB + 1,
        countUp(TRes, [AccBNew, AccW], Res)
            HRes == white
            AccWNew is AccW + 1,
            countUp(TRes, [AccB, AccWNew], Res)
            countUp(TRes, [AccB, AccW], Res)

Executive summary:

  • please_use_very_readable_names insteadOfUnreadableOneslikeInJava.

  • Use higher-order predicates.

  • Always consider using DCGs when describing lists. pass1 and pass2 can be simplified a lot with DCGS.

  • Use format/2 instead of multiple write/1 calls.


Higher order predicate maplist/2, replacing 4 almost identical member/2 calls:

make_guess(Guess) :-
    length(Guess, 4),
    Colours = [red, blue, green, yellow, orange, purple, pink],
    maplist(list_member(Colours), Guess).

list_member(Ls, M) :- member(M, Ls).

Sample query and its result:

?- make_guess(Gs).
Gs = [red, red, red, red] ; 
Gs = [red, red, red, blue] ;
Gs = [red, red, red, green].

Further example, for counting the number if white elements with the higher-order predicate include/3:

list_num_white(Ls, N) :-
    include(=(white), Ls, Ws),
    length(Ws, N).

Sample query and its result:

?- list_num_white([white,black,white], N).
N = 2

Exercise: Generalize this by parametrizing the element, so that you can use list_element_count(Ls, black, N) and list_element_count(Ls, white, N).

Example for format/2: Instead of:

write(Guess), write(' '), write(ThisScore),nl

you can write:

format("~w ~w\n", [Guess,ThisScore])
  • \$\begingroup\$ Thanks! I love the higher-order suggestions, and will work further on the tutorials to understand how DCGs could help me. \$\endgroup\$ – Simon H Oct 13 '14 at 14:42

Please note that there is an "error" especially in the score predicate. For example, by launching the command: *lscore([red, blue, green], [red, red, blue], S), the result is S = [1,2] instead of [1,1]. And there are many of these cases.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.