1
\$\begingroup\$

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) :-
    makeGuess(Guess),
    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)
        )
    ).
\$\endgroup\$
2
\$\begingroup\$

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.

Examples:

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])
\$\endgroup\$
  • \$\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

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.