2
\$\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\$
0

2 Answers 2

3
\$\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\$
1
  • \$\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
    Commented Oct 13, 2014 at 14:42
1
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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