I spent a long time on this wondering why it doesn't solve to a single solution, without looking at the linked picture or your code, and didn't realise there were given values for all the highscores. :-|
Your code last[thompson].
should probably be last(thompson).
to mean "thompson is a last name", but from there it's hard to get anything useful from it without building a list of all valid last names. And from there you may as well put the valid last names in a list.
I've chosen a compound term bowler(Firstname, Lastname, Highscore, Points)
as the output I want to see, so I've made a list of Bowlers
with five of those in it. Then added the clue information to solve for the values in a brute-force search style which works like this:
% Mr. Smith’s high score was three pins higher than that of the bowler with 30 points.
member(bowler(_, smith, Hsmith, _), Bowlers),
member(bowler(_, _, H_30, 30), Bowlers),
Hsmith is H_30 + 3,
says "the solution is only found when there is a member of Bowlers matching smith as their surname and some highscore for smith, and another member with 30 points and score for them, and the two score variables are 3 points apart". In full:
:- use_module(library(dif)).
solve(Bowlers) :-
% bowler(Firstname, Lastname, Highscore, Points).
Bowlers = [ bowler(frank, L1, H1, P1),
bowler(michael, L2, H2, P2),
bowler(steven, L3, H3, P3),
bowler(craig, L4, H4, P4),
bowler(walter, L5, H5, P5) ],
% everyone has last names from the text
permutation([L1, L2, L3, L4, L5], [smith, stewart, williams, thompson, lewis]),
% apparently the scores are evenly distributed one per person, no dupes.
permutation([P1, P2, P3, P4, P5], [28, 30, 32, 34, 36]),
% the valid high scores, one per person, no dupes.
permutation([H1, H2, H3, H4, H5], [264, 288, 276, 273, 285]),
% Frank, whose last name wasn’t Thompson,
% had the highest score (288) but he didn’t win the tournament (didn't score 36 points).
member(bowler(frank, Lfrank, 288, Pfrank), Bowlers),
dif(Lfrank, thompson),
dif(Pfrank, 36),
% Mr. Smith’s high score was three pins higher than that of the bowler with 30 points.
member(bowler(_, smith, Hsmith, _), Bowlers),
member(bowler(_, _, H_30, 30), Bowlers),
Hsmith is H_30 + 3,
% Steven’s last name wasn’t Stewart
member(bowler(steven, Ssteven, _, _), Bowlers),
dif(Ssteven, stewart),
% The winner of the tournament, who had 36 points, had an odd number for a high score.
member(bowler(_, _, Hwin, 36), Bowlers),
1 is Hwin /\ 1,
% Michael finished the tournament with six points less than Mr. Williams.
member(bowler(michael, _, _, Pmichael), Bowlers),
member(bowler(_, williams, _, Pwilliams), Bowlers),
Pmichael is Pwilliams - 6,
% The bowler with a high score of 273 had two points more than Mr. Smith but two points less than Craig.
member(bowler(_, _, 273, P_273), Bowlers),
member(bowler(_, smith, _, Psmith), Bowlers),
member(bowler(craig, _, _, Pcraig), Bowlers),
P_273 is Psmith + 2,
P_273 is Pcraig - 2,
% Mr. Thompson won fewer points than Walter but more than Steven Lewis.
member(bowler(_, thompson, _, Pthompson), Bowlers),
member(bowler(walter, _, _, Pwalter), Bowlers),
member(bowler(steven, lewis, _, Plewis), Bowlers),
Pthompson < Pwalter,
Pthompson > Plewis.
This feels slow, 10 million inferences to solve, and a further 17 million to rule out any other solutions:
?- time(solve(B)).
10,099,024 inferences, 1.550 CPU in 1.550 seconds (100% CPU, 6517307 Lips)
B = [bowler(frank, williams, 288, 34), bowler(michael, smith, 276, 28), bowler(steven, lewis, 273, 30), bowler(craig, thompson, 264, 32), bowler(walter, stewart, 285, 36)]
1.550 seconds cpu time
17,191,884 inferences, 2.613 CPU in 2.613 seconds (100% CPU, 6579160 Lips)
false
It searches from the top of the code down generating one permutation of names, one of points, one of scores, then checks frank
didn't get thompson
as his surname, retraces and tries again until it generates a solution which passes all the checks.
By reordering the clauses to fail faster so it does less work before failing, and adding more hints from the text, the amount of work can be reduced. e.g. from the last clue we know one member is definitely Steven Smith, we know Frank had a score of 288, we can directly write "Mr Smiths score is 3 pins higher than...", to get this version:
:- use_module(library(dif)).
solve(Bowlers) :-
% bowler(Firstname, Lastname, Highscore, Points).
Bowlers = [ bowler(frank, L1, 288, P1),
bowler(michael, L2, H2, P2),
bowler(steven, lewis, H3, P3),
bowler(craig, L4, H4, P4),
bowler(walter, L5, H5, P5) ],
dif(L1, thompson), % Frank, whose last name wasn’t Thompson
dif(P1, 36), % didn’t win (didn't score 36 points).
dif(L5, thompson), % Walter isn't Mr Thompson because they scored differently.
dif(L4, smith), % Craig isn't Mr Smith because they scored differently.
dif(L2, williams), % Michael isn't Mr Williams because they scored differently.
% everyone has last names from the text
permutation([L1, L2, L4, L5], [smith, stewart, williams, thompson]),
% scores are evenly distributed one per person, no dupes.
permutation([P1, P2, P3, P4, P5], [28, 30, 32, 34, 36]),
% Michael finished the tournament with six points less than Mr. Williams.
member(bowler(_, williams, _, Pwilliams), Bowlers),
P2 is Pwilliams - 6,
% the valid high scores, one per person, no dupes.
permutation([H2, H3, H4, H5], [264, 276, 273, 285]),
% The winner of the tournament, who had 36 points, had an odd number for a high score.
member(bowler(_, _, Hwin, 36), Bowlers),
1 is rem(Hwin, 2),
% Mr. Smith’s high score was three pins higher than that of the bowler with 30 points.
member(bowler(_, _, H_30, 30), Bowlers),
Hsmith is H_30 + 3,
member(bowler(_, smith, Hsmith, Psmith), Bowlers),
% The bowler with a high score of 273 had two points more than Mr. Smith but two points less than Craig.
P_273 is Psmith + 2,
member(bowler(_, _, 273, P_273), Bowlers),
Pcraig is P_273 + 2,
member(bowler(craig, _, _, Pcraig), Bowlers),
% Mr. Thompson won fewer points than Walter but more than Steven Lewis.
member(bowler(_, thompson, _, Pthompson), Bowlers),
member(bowler(walter, _, _, Pwalter), Bowlers),
Pthompson < Pwalter,
member(bowler(steven, lewis, _, Plewis), Bowlers),
Pthompson > Plewis.
This version does:
?- time(solve(B)).
34,664 inferences, 0.007 CPU in 0.007 seconds (100% CPU, 5150062 Lips)
B = [bowler(frank, williams, 288, 34), bowler(michael, smith, 276, 28), bowler(steven, lewis, 273, 30), bowler(craig, thompson, 264, 32), bowler(walter, stewart, 285, 36)]
26,321 inferences, 0.011 CPU in 0.011 seconds (100% CPU, 2397565 Lips)
false
Almost 300x faster.