Bear with me, I know this is obviously super simple. I just can't seem to wrap my head around prolog. I have this logic problem:
At the recent festival, the 100 metres heats were closely monitored.
Each contestant had to run in two races so that the average place could be determined..
Only one runner finished in the same place in both races.
Alan was never last. Charles always beat Darren. Brian had at least one
first place. Alan finished third in at least one of the races. Both Darren
and Charles had a second place. What were the two results?
Answer: Race 1: Brian, Charles, Alan, Darren. Race 2: Charles, Darren, Alan,
Brian.
This is what I have come up with so far, but I can't figure out the "at least" and other more complex conditions.
place(one).
place(two).
place(three).
place(four).
/* Place (constants): one, two, three, four.
Names (variables): Alan_1, Brian_1, Charles_1, Darren_1
Names (variables): Alan_2, Brian_2, Charles_2, Darren_2
*/
higher(one,two).
higher(one,three).
higher(one,four).
higher(two,three).
higher(two,four).
higher(three,four).
is_higher(X,Y):- higher(X,Y).
is_higher(X,Y):- higher(X,Z), is_higher(Z,Y).
is_different(S,T,U,V,W,X,Y,Z):- W\=X,W\=Y,W\=Z,X\=Y,X\=Z,Y\=Z,W\=S,W\=T,
W\=U,W\=V,S\=T,T\=U,U\=V,S\=U,S\=V,T\=V,
T\=W,T\=X,T\=Y,T\=Z,U\=W,U\=X,U\=Y,U\=Z,
V\=X,V\=Y,V\=Z.
solution(Alan_1, Brian_1, Charles_1, Darren_1, Alan_2, Brian_2, Charles_2,
Darren_2):-
place(Alan_1), place(Brian_1), place(Charles_1), place(Darren_1),
place(Alan_2), place(Brian_2), place(Charles_2), place(Darren_2),
Alan_1\=four, Alan_2\=four, higher(Charles_1, Darren_1), higher(Charles_2,
Darren_2),
is_different(Alan_1, Brian_1, Charles_1, Darren_1, Alan_2, Brian_2,
Charles_2, Darren_2).
/* Query */
% solution(Alan_1, Brian_1, Charles_1, Darren_1, Alan_2, Brian_2, Charles_2,
Darren_2).
Any help or words of wisdom would be super appreciated.
I solved with SWI + CLPFD.
For representing "at least",
I used #\/ .
:-use_module(library(clpfd)).
solve(RaceResult):-
RaceResult = [Alan1,Alan2,Chales1,Chales2,Brian1,Brian2,Darren1,Darren2],
RaceResult ins 1..4,
all_different([Alan1,Chales1,Brian1,Darren1]),
all_different([Alan2,Chales2,Brian2,Darren2]),
% Only one runner finished in the same place in both races.
(Alan1 #= Alan2) #<==> AlanSame,
(Chales1 #= Chales2) #<==> ChalesSame,
(Brian1 #= Brian2) #<==> BrianSame,
(Darren1 #= Darren2) #<==> DarrenSame,
sum([AlanSame,ChalesSame,BrianSame,DarrenSame], #=, 1),
% Alan was never last.
Alan1 #\= 4,
Alan2 #\= 4,
% Alan finished third in at least one of the races.
(Alan1 #= 3 ) #\/ (Alan2 #= 3),
% Charles always beat Darren.
Chales1 #< Darren1,
Chales2 #< Darren2,
% Brian had at least one first place.
(Brian1 #= 1 ) #\/ (Brian2 #= 1),
% Both Darren and Charles had a second place.
(Darren1 #= 2) #\/ (Darren2 #= 2),
(Chales1 #= 2) #\/ (Chales2 #= 2),
labeling([ffc],RaceResult).
?- solve([Alan1,Alan2,Chales1,Chales2,Brian1,Brian2,Darren1,Darren2]).
Alan1 = Alan2, Alan2 = 3,
Chales1 = Brian2, Brian2 = 1,
Chales2 = Darren1, Darren1 = 2,
Brian1 = Darren2, Darren2 = 4 ;
Alan1 = Alan2, Alan2 = 3,
Chales1 = Darren2, Darren2 = 2,
Chales2 = Brian1, Brian1 = 1,
Brian2 = Darren1, Darren1 = 4.
Maybe there is another solution?
Race1:
Chales
Darren
Alan
Brian
Race2:
Brian
Chales
Alan
Darren
... Sorry just replacing race1 and race2.
I did it this way:
puzzle :-
Races = [[_,_,_,_],[_,_,_,_]],
Races = [R1,R2],
(first(brian,R1);first(brian,R2);(first(brian,R1),first(brian,R2))),
(third(alan,R1);third(alan,R2);(third(alan,R1),third(alan,R2))),
((second(darren,R1),second(charles,R2));(second(darren,R2),second(charles,R1))),
member(brian,R1),
member(charles,R1),
member(brian,R2),
member(charles,R2),
member(alan,R1),
member(darren,R1),
member(alan,R2),
member(darren,R2),
before(charles,darren,R1),
before(charles,darren,R2),
never_last(alan,Races),
only_one_same(Races),
write(Races),nl.
zip_equal([],[],[]).
zip_equal([R|R1s],[R|R2s],[R|Rs]) :- !, zip_equal(R1s,R2s,Rs).
zip_equal([_|R1s],[_|R2s],Rs) :- !, zip_equal(R1s,R2s,Rs).
never_last(X,[[A1,B1,C1,_],[A2,B2,C2,_]]) :- member(X,[A1,B1,C1]), member(X,[A2,B2,C2]).
before(X,Y,[X|Rs]) :- !, member(Y,Rs).
before(X,Y,[_|Rs]) :- before(X,Y,Rs).
first(X,[X,_,_,_]).
second(X,[_,X,_,_]).
third(X,[_,_,X,_]).
only_one_same([R1s,R2s]) :- zip_equal(R1s,R2s,[_]).
Related
As a beginner, I am trying to solve a matrix problem, in which the solution is either the summation or the multiplication of the distinct digit in the matrix. For example,[[14,_,_,_],[15,_,_,_],[28,_,1,_]]. The predicate should generate the solution according to the matrix. However, I bumped into a problem that my combined predicate failed, but each of them succeeded independently.
I broke the problem down to summation and multiplication. Therefore, using clpfd library, and some predicates, for summation:
removeHead([Head|Tail],Tail).
get_head_element([],_).
get_head_element([Head|Rest],Head).
solve_sum(Row,RestRow):-
get_head_element(Row, Goal),
removeHead(Row,RestRow),
RestRow ins 1..9,
all_distinct(RestRow),
sum(RestRow, #=, Goal).
For multiplication:
multiply(List,Result):-
multiply(List,1,Result).
multiply([Element|RestList],PrevResult,Result):-
NextResult #= PrevResult * Element,
multiply(RestList,NextResult, Result).
multiply([], Result, Result).
solve_multiply(Row,RestRow):-
get_head_element(Row, Goal),
removeHead(Row,RestRow),
RestRow ins 1..9,
multiply(RestRow,Goal),
all_distinct(RestRow).
Both solve_sum and solve_multiply works for a single row, but here I combine these two predicates to:
solve_row_sum_or_multiply([],_).
solve_row_sum_or_multiply([HeadRow|Matrix],Solution):-
maplist(all_distinct,Matrix),
get_head_element(HeadRow,Goal),
( Goal >= 25
-> solve_multiply(HeadRow,Solution),
write(Solution)
; ( solve_sum(HeadRow,Solution),
write(Solution)
; solve_multiply(HeadRow,Solution),
write(Solution))
),solve_row_sum_or_multiply(Matrix,Solution).
When I use the solve_row_sum_or_multiply([[14,_,_,_],[15,_,_,_],[28,_,_,_]],X), it will give me the possible combinations. However, when solve_row_sum_or_multiply([[14,_,_,_],[15,_,_,_],[28,_,1,_]],X), it gave me false, but there's a solution possible there, such as [[14,7,2,1],[15,3,7,5],[28,4,1,7]].
I am wondering what could be wrong. Any help is highly appreciated!
EDIT
The problem is inside the combined predicate, and it's better to write the predicate which the answer suggests instead of a nested if-then-else predicate.
Let's review this code.
intro code
:- use_module(library(clpfd)).
% ---
% Make sure you see all the information in a list printed at the
% toplevel, i.e. tell the toplevel to not elide large lists (print
% them in full, don't use '...')
% ---
my_print :-
Depth=100,
Options=[quoted(true), portray(true), max_depth(Depth), attributes(portray)],
set_prolog_flag(answer_write_options,Options),
set_prolog_flag(debugger_write_options,Options).
:- my_print.
multiply/2
Recode the multiply/2 predicate so that it can be read more easily. Add test code to make sure it actually does what we think it does:
% ---
% Set up constraint: all the elements in List, multiplied
% together, must yield Result. If the List is empty, the
% Result must be 1.
% ---
multiply(Factors,Product):-
multiply_2(Factors,1,Product).
multiply_2([Factor|Factors],PartialProduct,Product):-
PartialProduct2 #= PartialProduct * Factor,
multiply_2(Factors,PartialProduct2,Product).
multiply_2([],Product,Product).
solve_multiply([Product|Factors]):-
Factors ins 1..9,
multiply(Factors,Product),
all_distinct(Factors).
% ---
% Testing
% ---
:- begin_tests(multiply).
test(1) :-
multiply([X,Y,Z],6),
[X,Y,Z] ins 1..9,
X #=< Y, Y #=< Z,
bagof([X,Y,Z],label([X,Y,Z]),Bag),
sort(Bag,BagS),
assertion(BagS == [[1, 1, 6], [1, 2, 3]]).
test(2) :-
multiply([],Result),
assertion(Result == 1).
test(3) :-
multiply([X,Y],3),
[X,Y] ins 1..9,
X #=< Y,
bagof([X,Y],label([X,Y]),Bag),
sort(Bag,BagS),
assertion(BagS == [[1,3]]).
test(4) :-
solve_multiply([6,X,Y,Z]),
bagof([X,Y,Z],label([X,Y,Z]),Bag),
sort(Bag,BagS),
assertion(BagS == [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]).
test(5) :-
solve_multiply([362880,F1,F2,F3,F4,F5,F6,F7,F8,F9]),
F1 #=< F2,
F2 #=< F3,
F3 #=< F4,
F4 #=< F5,
F5 #=< F6,
F6 #=< F7,
F7 #=< F8,
F8 #=< F9,
bagof([F1,F2,F3,F4,F5,F6,F7,F8,F9],label([F1,F2,F3,F4,F5,F6,F7,F8,F9]),Bag),
sort(Bag,BagS),
assertion(BagS == [[1,2,3,4,5,6,7,8,9]]).
test(6,fail) :-
solve_multiply([-1,X,Y,Z]).
:- end_tests(multiply).
solve_sum/1
For summation, after simplifying and re-coding to Prolog's "pattern matching" style instead of (for want of a better word) "Algol calling style" as is used in the original the question (which confused me greatly I have to say).
This looks ok too. Note that the predicate solve_sum/1 is quite different-in-argument-style from multiply/2 It is better - and easier on the reader (and oneself) - if one keeps the same calling conventions.
solve_sum([Sum|Terms]):-
Terms ins 1..9,
all_distinct(Terms),
sum(Terms, #=, Sum).
% ---
% Testing
% ---
:- begin_tests(sum).
test(0) :-
solve_sum([0]).
test(1) :-
solve_sum([6,X,Y,Z]),
X #=< Y, Y #=< Z,
bagof([X,Y,Z],label([X,Y,Z]),Bag),
sort(Bag,BagS),
assertion(BagS == [[1, 2, 3]]).
test(2) :-
solve_sum([9,X,Y,Z]),
X #=< Y, Y #=< Z,
bagof([X,Y,Z],label([X,Y,Z]),Bag),
sort(Bag,BagS),
assertion(BagS == [[1,2,6],[1,3,5],[2,3,4]]).
test(3,fail) :-
solve_sum([1,_X,_Y,_Z]).
:- end_tests(sum).
A printing predicate to get nice results
Prolog makes it easy to construct arbitrary structures "on the fly", one just has to stipulate exactly how the structure shall look.
Our "solution" is:
A list
With each element of the list a "row" (standing for one of the expressions)
And the row a term: row(Result,Op,RowValues) where Result is the left-hand-side of an equation, Op is one of add or mult and RowValues is a list with the values found for the values in the equation.
print_solution([]).
print_solution([Labeling|Labelings]) :-
format("---~n"),
print_rows_of_solution(Labeling),
format("---~n"),
print_solution(Labelings).
print_rows_of_solution([]).
print_rows_of_solution([row(Result,Op,RowValues)|Rows]) :-
print_row(Result,RowValues,Op),
print_rows_of_solution(Rows).
print_row(Result,[RowEntry|RowEntries],Op) :-
format("~q = ~q",[Result,RowEntry]),
print_row_2(RowEntries,Op).
print_row_2([RowEntry|RowEntries],Op) :-
((Op == mult) -> OpChar = '*'
; (Op == add) -> OpChar = '+'
; OpChar = '?'),
format(" ~q ~q",[OpChar,RowEntry]),
print_row_2(RowEntries,Op).
print_row_2([],_) :-
format("~n").
solve_row_sum_or_multiply/2
This has been modified to also collect the operation selected in a second argument (a list).
Note that the "maplist-all-distinct over the matrix" is not done here because that seems wrong.
solve_row_sum_or_multiply([],[]).
solve_row_sum_or_multiply([Row|MoreRows],[mult|Ops]) :-
Row = [X|_Xs],
X >= 25,
solve_multiply(Row), % we now have imposed a product constraint on the current Row
solve_row_sum_or_multiply(MoreRows,Ops).
solve_row_sum_or_multiply([Row|MoreRows],[add|Ops]) :-
Row = [X|_Xs],
X < 25,
solve_sum(Row), % we now have imposed a sum constraint on the current Row
solve_row_sum_or_multiply(MoreRows,Ops).
solve_row_sum_or_multiply([Row|MoreRows],[mult|Ops]) :-
Row = [X|_Xs],
X < 25,
solve_multiply(Row), % alternatively, we now have imposed a product constraint on the current Row
solve_row_sum_or_multiply(MoreRows,Ops).
And finally a "main" predicate
In trial_run/1 we stipulate that all variables except X32 be distinct and that X32 be 1. If X32 were in the "distinct set", none of the variables could take on the value 1 - there is no solution in that case.
We also impose a sorting to get only a set of "canonical" solutions:
We collect all possible solutions for solving and labeling using bagof/3:
trial_run(Solutions) :-
all_distinct([X11,X12,X13,X21,X22,X23,X31,X33]),
X32=1,
X11 #=< X12, X12 #=< X13,
X21 #=< X22, X22 #=< X23,
X31 #=< X33,
% multiple solutions solving x labeling may exist; collect them all
bagof(
[
row(14,Op1,[X11,X12,X13]),
row(15,Op2,[X21,X22,X23]),
row(28,Op3,[X31,X32,X33])
],
(
solve_row_sum_or_multiply( [[14,X11,X12,X13],[15,X21,X22,X23],[28,X31,X32,X33]], [Op1,Op2,Op3] ),
label([X11,X12,X13,X21,X22,X23,X31,X32,X33])
),
Solutions).
Does it work?
Apparently yes, and there is only one solution:
?- trial_run(Solutions),print_solution(Solutions).
---
14 = 2 + 3 + 9
15 = 1 + 6 + 8
28 = 4 * 1 * 7
---
Solutions = [[row(14,add,[2,3,9]),row(15,add,[1,6,8]),row(28,mult,[4,1,7])]].
I want to predicate solve Kakuro.
My kakuro is this
I have to create a predicate using the word solve (solve/1) for the game kakuro. I tried 3-4 codes and all of them have errors. The variables have to be between 1-9(A value is between 1-9) and all the variables have to be different in one rows&columns(24=A+B+C not 24=A+A+C).
First code is:
:- use_module(library(clpfd)).
solve(L):-
L= [A,B,C,E,F,G,J,K,L,N,O,P],
all_different(L),
L ins 1..9,
A #= 24-B-C,
B #= 26-F-J-N,
C #= 15-G-K-O,
E #= 11-F-G,
E #= 17-A,
J #= 22-K-L,
N #= 14-O-P,
L #= 13-P.
Second code:
:- use_module(library(clpfd)).
sum_list([],0).
sum_list([Head|Tail], Sum):-sum_list(Tail, Sum1),Sum is Head+Sum1.
go:-
Vars=[A,B,C,E,F,G,J,K,L,N,O,P],
Vars ins 1..9,
word([A,B,C],24),
word([B,F,J,N],26),
word([C,G,K,O],15),
word([E,F,G],11),
word([A,E],17),
word([J,K,L],22),
word([N,O,P],14),
word([L,P],13),
labeling(Vars),
writeln(Vars).
word(L,Sum):-
sum_list(L,X),
X=:=Sum,
all_different(L).
Third code:
:- use_module(library(clpfd)).
kakuro(L):-
L = [A,B,C,E,F,G,J,K,L,N,O,P],
L ins 1..9,
Z1 = [A,B,C],
all_different(Z1),
A =:= 24-B-C,
Z2 = [B,F,J,N],
all_different(Z2),
B =:=26-F-J-N,
Z3 = [C,G,K,O],
all_different(Z3),
C =:= 15-G-K-O,
Z4 = [E,F,G],
all_different(Z4),
E =:= 11-F-G,
Z5 = [A,E],
all_different(Z5),
E =:= 17-A,
Z6 = [J,K,L],
all_different(Z6),
J =:=22-K-L,
A1 = [N,O,P],
all_different(A1),
N =:= 14-O-P,
A2 = [L,P],
all_different(A2),
L =:=13-P,
labeling([], L).
Also how do I start the solving process? Will it be like: ?-solve(L). ?? Also the L ins 1..9 is not working.
The following is a solution based on DuDa's, but using the sum/3 constraint and delaying labeling to the end. This makes for a more efficient and more declarative solution.
:- use_module(library(clpfd)).
kakuro(Vars):-
Vars=[A,B,C,E,F,G,J,K,L,N,O,P],
Vars ins 1..9,
word([A,B,C],24),
word([B,F,J,N],26),
word([C,G,K,O],15),
word([E,F,G],11),
word([A,E],17),
word([J,K,L],22),
word([N,O,P],14),
word([L,P],13),
labeling([],Vars).
word(L,Sum):-
all_different(L),
sum(L,#=,Sum).
At first before fixing the ins/labeling problem: there are issues with your code #1 and #3. In code #1 you state that all your variables need to have different values. This will not provide a valid solution. In code #1 and #3 you have used the variable L once as container for the variables and once as one element.
I have tested my code with Swish. This is the code:
:- use_module(library(clpfd)).
sum_list([],0).
sum_list([Head|Tail], Sum):-
sum_list(Tail, Sum1),
Sum is Head+Sum1.
kakuro(Vars):-
Vars=[A,B,C,E,F,G,J,K,L,N,O,P],
Vars ins 1..9,
word([A,B,C],24),
word([B,F,J,N],26),
word([C,G,K,O],15),
word([E,F,G],11),
word([A,E],17),
word([J,K,L],22),
word([N,O,P],14),
word([L,P],13),
writeln(Vars).
word(L,Sum):-
labeling([],L),
all_different(L),
sum_list(L,Sum).
This is the (only) output for ?- kakuro(L).:
[9, 8, 7, 8, 2, 1, 9, 5, 8, 7, 2, 5]
At first you need to have the ins/2 predicate running. This should work with SWI prolog. Second you know what to do with the labeling/2 predicate. This one will set values given from their domain (given via ins/2) to the entry. In SWI Prolog the first attribute is a list of options, no need to worry. AFTER labeling your variables you can "test" them and put constraints on them. You can do the labeling once for all variables at the beginning but this is pretty slow because all your progress is lost. So I recommend to do it in the word/2 predicate. If a dead end occurs the backtracking goes back to the word/2 predicate and not to the beginning of the kakuro/1 predicate.
I'm trying to write something like "you have the ball if you previously got the ball, and didn't give it since":
:- use_module(library(clpfd)).
time(T1, has_ball) :-
time(T2, get_ball),
T2 #=< T1,
\+ (time(T3, give_ball),
T2 #< T3, T3 #< T1).
time(0, get_ball).
time(2, give_ball).
This correctly answers direct questions about a specific time T (by providing T, for example with labeling):
?- time(1, has_ball).
true.
?- T in 0..9, label([T]), time(T, has_ball).
T = 0 ;
T = 1 ;
T = 2 ;
false.
But when asked to find all valid times T, I simply get false:
?- time(T, has_ball).
false.
From my understanding, clpfd did some over approximation of the result without labeling, so I would have expected something like "T in inf..sup, time(T, has_ball)." telling me to use labeling. But it is clear that I am wrong, and now I am afraid I might miss solutions in other situations. Can someone help me understand?
EDIT:
Isabelle Newbie's answer made me realize that I actually meant:
time(HasBall, has_ball) :-
time(GetBall, get_ball),
GetBall #=< HasBall,
\+ (time(GiveBall, give_ball),
GetBall #< GiveBall #/\ GiveBall #< HasBall).
Since the idea is that "you still have the ball at time HasBall if you got it at some previous time GetBall, and did NOT give it SINCE". So the time(GiveBall, give_ball) needs to be in the negation. Here replacing \+ with #\ gives a new error ("Domain error: `clpfd_reifiable_expression' expected") which I will investigate.
The short answer is that you shouldn't mix Prolog negation \+ with CLP(FD). CLP(FD) has its own negation operator that works on its constraints, written as #\. So you can write your predicate as:
time(HasBall, has_ball) :-
time(GetBall, get_ball),
GetBall #=< HasBall,
time(GiveBall, give_ball),
#\ (GetBall #< GiveBall #/\ GiveBall #< HasBall).
I renamed your variables because I didn't really understand what was going on. It's a bit clearer now, but shouldn't the negated constraint just be replaced by the positive HasBall #=< GiveBall?
In any case this behaves as I believe you would like it to:
?- time(T, has_ball).
T in 0..2.
?- time(T, has_ball), label([T]).
T = 0 ;
T = 1 ;
T = 2.
?- time(1, has_ball).
true.
?- T in 0..9, label([T]), time(T, has_ball).
T = 0 ;
T = 1 ;
T = 2 ;
false.
To understand a bit more what was going on, we can take your original clause and replace variables by their constant values:
step1(T1) :-
T2 = 0,
T2 #=< T1,
\+ ( T3 = 2, T2 #< T3, T3 #< T1 ).
step2(T1) :-
0 #=< T1,
\+ ( 0 #< 2, 2 #< T1 ).
step3(T1) :-
0 #=< T1,
\+ ( 2 #< T1 ).
So after the last step, your predicate behaves essentially as follows when called with a bound vs. an unbound variable:
?- T1 = 1, 0 #=< T1, \+ (2 #< T1).
T1 = 1.
?- 0 #=< T1, \+ (2 #< T1).
false.
This is because in the first case the last goal is \+ (2 #< 1), which succeeds because 2 #< 1 fails.
But if you don't bind T1, then 2 #< T1 succeeds:
?- 2 #< T1.
T1 in 3..sup.
So its negation \+ (2 #< T1) fails. This goal essentially says "there are no numbers larger than two", which is false. In contrast, the CLP(FD) negation succeeds with the "opposite" constraint:
?- #\ (2 #< T1).
T1 in inf..2.
This almost certainly makes more sense in the context of your program, since it respects the mathematical property that not (A < B) is equivalent to (A >= B):
?- 2 #>= T1.
T1 in inf..2.
EDIT: I missed the fact that maybe no give_ball event has been seen, in which case one would still hold the ball. You can't use #\ to model this the way you tried because #\ applies only to CLP(FD) constraints (specifically, "reifiable" ones) but not to "normal" Prolog goals. You can't mix these levels this way either.
So you need to be more explicit about the two cases that exist: You have not given up the ball if either:
the ball is given up at a certain time, but that time has not arrived yet; or
the ball is not given up at all.
Here is the same in Prolog, separating the places where Prolog negation is applied from where CLP(FD) negation is applied:
has_not_given_up_ball(HasBall) :-
time(GiveBall, give_ball),
\# ( GetBall #< GiveBall #/\ GiveBall #< HasBall ).
has_not_given_up_ball(_HasBall) :-
\+ time(_GiveBall, give_ball).
(Again, I think you should just use HasBall #=< GiveBall instead of the negated constraint.)
And you can then adjust your definition like this:
time(HasBall, has_ball) :-
time(GetBall, get_ball),
GetBall #=< HasBall,
has_not_given_up_ball(HasBall).
If a time(2, give_ball) fact is present, this behaves as before, but with an extra choice point. If I comment out that fact, it models correctly that the ball has not been given up, so one holds it for longer:
?- time(T, has_ball).
T in 0..sup.
?- time(T, has_ball), label([T]).
ERROR: Arguments are not sufficiently instantiated
...
?- time(1, has_ball).
true.
?- T in 0..9, label([T]), time(T, has_ball).
T = 0 ;
T = 1 ;
T = 2 ;
T = 3 ;
T = 4 ;
T = 5 ;
T = 6 ;
T = 7 ;
T = 8 ;
T = 9.
It's only the labeling of a time that is not constrained to a finite domain that errors, as it should.
I am new to prolog and I am trying to solve the following question. I am having difficulty trying to understand the logic to solve the problem. I know its similar to zebra problem but, I am unsure how to approach. Any help would be greatly appreciated.
The answers submitted by five students to a T/F quiz are as follows.
Teresa: T T F T F
Tim: F T T T F
Tania: T F T T F
Tom: F T T F T
Tony: T F T F T
Tania got more answers right than Teresa did.
Tom got more right than Tim.
Tony did not get all the answers right, nor did he get them all wrong.
Write a Prolog program quiz(Answer) that asserts Answer is the list of t and f constants that is the correct answer to the quiz..
If you use SWI-Prolog, you can use library clpfd to solve the puzzle :, I get only one solution (f,f,t,f,t).
You have a solution [A,B,C,D,E].
You initialize the possibles solutions with
[A,B,C,D,E] ins 0..1,
You reify all the answers for teresa for example
teresea([1,1,0,1,0]).
A #= 1 #<==> TA
B #= 1 #<==> TB
.....
you compute the sum of Tis
sum([TA, TB, ...], #= , Steresa),
and later you will have for
Tania got more answers right than Teresa did.
Stania #> Steresa
You get the solution with
label([A,B,C,D,E]).
Hope this helps
small puzzles like this can be solved by generate-and-test
solve(L) :-
% generator
length(L, 5), maplist(tf, L),
% Tania got more answers right than Teresa did.
matches(L, tania, Tania),
matches(L, teresa, Teresa), Tania > Teresa,
...
tf(t).
tf(f).
teresa(t, t, f, t, f).
tim(f, t, t, t, f).
...
Of course, matches(L, tania, Tania) counts correct Tania' answers.
But, I don't find a solution. The only tuple that 'get thru' Tony, it's its exact result. So, this condition
Tony did not get all the answers right
cannot be solved...
edit I had a bug in matches/3. Of course there is a solution.
edit well, the CLP(FD) version can be very compact, while being more general...
teresa(t, t, f, t, f).
...
matches(L, P, N) :-
call(P, A,B,C,D,E),
foldl(eqsum, [A,B,C,D,E], L, 0, N).
eqsum(t,Ls,Acc,N) :- N #= Acc + (° #<==> Ls #= 1).
eqsum(f,Ls,Acc,N) :- N #= Acc + (° #<==> Ls #= 0).
solve(L) :-
length(°L, 5) ins 0..1,
% Tania got more answers right than Teresa did.
matches(L, tania, °) #> matches(L, teresa, °),
% Tom got more right than Tim.
matches(L, tom, °) #> matches(L, tim, °),
% Tony did not get all the answers right, nor did he get them all wrong.
matches(L, tony, °Tony) #> 0, Tony #< 5.
I used my lifter here.
I have been given an exercise to solve the zebra puzzle using a constraint solver of my choice, and I tried it using the Prolog clpfd library.
I am aware that there are other more idiomatic ways to solve this problem in Prolog, but this question is specifically about the clpfd package!
So the specific variation of the puzzle (given that there are many of them) I'm trying to solve is this one:
There are five houses
The Englishman lives in the red house
The Swedish own a dog
The Danish likes to drink tea
The green house is left to the white house
The owner of the green house drinks coffee
The person that smokes Pall Mall owns a bird
Milk is drunk in the middle house
The owner of the yellow house smokes Dunhill
The norwegian lives in the first house
The marlboro smoker lives next to the cat owner
The horse owner lives next to the person who smokes dunhill
The winfield smoker likes to drink beer
The norwegian lives next to the blue house
The german smokes rothmanns
The marlboro smoker has a neighbor who drinks water
I tried to solve it with the following approach:
Each attribute a house can have is modeled as a variable, e.g. "British",
"Dog", "Green", etc. The attributes can take values from 1 to 5, depending on the house
in which they occur, e.g. if the variable "Dog" takes the value 3, the dog lives in the
third house.
This approach makes it easy to model neighbor constraints like this:
def neighbor(X, Y) :-
(X #= Y-1) #\/ (X #= Y+1).
But somehow, the clpfd package does not yield a solution, even though (IMO) the problem is modeled correctly (I used the exact same model with the Choco constraint solver and the result was correct).
Here is the complete code:
:- use_module(library(clpfd)).
neighbor(X, Y) :-
(X #= (Y - 1)) #\/ (X #= (Y + 1)).
solve([British, Swedish, Danish, Norwegian, German], Fish) :-
Nationalities = [British, Swedish, Danish, Norwegian, German],
Colors = [Red, Green, Blue, White, Yellow],
Beverages = [Tea, Coffee, Milk, Beer, Water],
Cigarettes = [PallMall, Marlboro, Dunhill, Winfield, Rothmanns],
Pets = [Dog, Bird, Cat, Horse, Fish],
all_different(Nationalities),
all_different(Colors),
all_different(Beverages),
all_different(Cigarettes),
all_different(Pets),
Nationalities ins 1..5,
Colors ins 1..5,
Beverages ins 1..5,
Cigarettes ins 1..5,
Pets ins 1..5,
British #= Red, % Hint 1
Swedish #= Dog, % Hint 2
Danish #= Tea, % Hint 3
Green #= White - 1 , % Hint 4
Green #= Coffee, % Hint 5,
PallMall #= Bird, % Hint 6
Milk #= 3, % Hint 7
Yellow #= Dunhill, % Hint 8,
Norwegian #= 1, % Hint 9
neighbor(Marlboro, Cat), % Hint 10
neighbor(Horse, Dunhill), % Hint 11
Winfield #= Beer, % Hint 12
neighbor(Norwegian, Blue), % Hint 13
German #= Rothmanns, % Hint 14,
neighbor(Marlboro, Water). % Hint 15
Did I misunderstand a concept within clpfd, or am I simply missing something obvious here? In case it helps, here you can find the same approach implemented using Choco and Scala.
Edit: The reason why I believe that the solver isn't able to solve the problem ist that it never comes up with definite values for the variables, but only with ranges, e.g. "Fish 1..3\/5".
There are several misconceptions here: You state "the clpfd package does not yield a solution", but actually it does yield one:
?- solve(Ls, Fish), label(Ls).
Ls = [3, 5, 2, 1, 4],
Fish in 1\/4,
all_different([5, 3, _G3699, 2, Fish]),
_G3699 in 1\/4,
_G3699+1#=_G3727,
_G3741+1#=_G3699,
_G3727 in 2\/4..5,
2#=_G3727#<==>_G3766,
_G3766 in 0..1,
_G3792#\/_G3766#<==>1,
_G3792 in 0..1,
2#=_G3741#<==>_G3792,
_G3741 in 0\/2..3.
So we know that if there is a solution, then Fish is either 1 or 4. Let's try 1:
?- solve(Ls, Fish), label(Ls), Fish = 1.
false.
No. So let's try 4:
?- solve(Ls, Fish), label(Ls), Fish = 4.
Ls = [3, 5, 2, 1, 4],
Fish = 4.
This works and is a ground solution to the problem. You can get it in a different way for example by including Fish in the variables that are to be labeled:
?- solve(Ls, Fish), label([Fish|Ls]).
Ls = [3, 5, 2, 1, 4],
Fish = 4 ;
false.
The purpose of labeling is exactly to try concrete values for constrained variables, independent of whether there actually is a solution. By coincidence, all_distinct/1 is strong enough to yield a ground solution by itself in this case, but in general this is of course not the case and you must eventually use labeling to obtain an unconditional (i.e., no more pending constraints) answer. Of course you must then in general also label all variables that are of interest to you, not just a subset of them as you did initially. To label a single variable, you can use indomain/1, so appending indomain(Fish) to the first query above would also work. I could not reproduce the instantiation error you mentioned in a further comment, in fact as you see above the most general query solve(X, Y) works with the code you posted. Finally, check this out:
neighbor(X, Y) :- abs(X-Y) #= 1.
running your code in SWI-Prolog, I get
?- solve(X),label(X).
X = [3, 5, 2, 1, 4].
Without label:
?- solve(X).
X = [3, _G3351, _G3354, 1, _G3360],
_G3351 in 4..5,
all_different([_G3351, _G3386, _G3389, 2, _G3395]),
all_different([3, _G3351, _G3354, 1, _G3360]),
_G3386 in 3..5,
all_different([_G3386, _G3444, 1, _G3450, _G3360]),
_G3389 in 1\/3..5,
_G3389+1#=_G3478,
_G3492+1#=_G3389,
_G3395 in 1\/3..5,
_G3478 in 2..6,
_G3444#=_G3478#<==>_G3529,
_G3444 in 2..5,
_G3444#=_G3556#<==>_G3553,
_G3444#=_G3568#<==>_G3565,
_G3444#=_G3492#<==>_G3577,
_G3450 in 2\/5,
all_different([_G3354, 4, 3, _G3450, _G3614]),
_G3360 in 2\/4..5,
_G3354 in 2\/5,
_G3614 in 1..2\/5,
_G3614+1#=_G3556,
_G3568+1#=_G3614,
_G3556 in 2..3\/6,
_G3553 in 0..1,
_G3565#\/_G3553#<==>1,
_G3565 in 0..1,
_G3568 in 0..1\/4,
_G3492 in 0..4,
_G3577 in 0..1,
_G3577#\/_G3529#<==>1,
_G3529 in 0..1.
If I change all_different to all_distinct I get the solution without label:
....
all_distinct(Nationalities),
all_distinct(Colors),
all_distinct(Beverages),
all_distinct(Cigarettes),
all_distinct(Pets),
....
?- solve(X).
X = [3, 5, 2, 1, 4].
As you see, the docs state stronger propagation for all_distinct vs all_different. Running the proposed sample help to understand the difference between those:
?- maplist(in, Vs, [1\/3..4, 1..2\/4, 1..2\/4, 1..3, 1..3, 1..6]), all_distinct(Vs).
false.
?- maplist(in, Vs, [1\/3..4, 1..2\/4, 1..2\/4, 1..3, 1..3, 1..6]), all_different(Vs).
Vs = [_G419, _G422, _G425, _G428, _G431, _G434],
_G419 in 1\/3..4,
all_different([_G419, _G422, _G425, _G428, _G431, _G434]),
_G422 in 1..2\/4,
_G425 in 1..2\/4,
_G428 in 1..3,
_G431 in 1..3,
_G434 in 1..6.