1

I have a test coming up on Prolog and I can't really grasp the basic ideas of it. I kind of understand some of the examples I have been going through, but I can't sit down and know off hand how to solve a specific problem.

Our professor gave us some examples and I was wondering if someone could walk me through how to do it so I have some sort of idea how to approach things like this.

The example came from our book:

Donna, Danny, David, and Doreen were seated at a table. 
The men sat across from each other, as did the women. 
They each ordered a different drink and main course.

Facts:

Doreen sat beside the person that ordered steak
The chicken came with a coke
The person with lasagna sat across from the person with milk
David never drinks coffee
Donna only drinks water
Danny could not afford to order steak

I tried something where each person had a list associated with them and you would populate in the facts, but I don't think this is the correct approach. Could someone walk me through this? Thanks!

EDIT:

This is the code I ended up with, it finishes most of the puzzle, it leaves one of the entrees and one of the drinks out, but that should be fixable:

  sat_across([X,_,Y,_], X, Y).
  sat_across([_,X,_,Y], X, Y).

  sat_beside(T, X, Y) :-  % this is tricky
      nth1(N,T,X), nth1(M,T,Y),
      (N =:= M+1 ; N =:= M-1 ; N == 1, M == 4 ; N == 4, M == 1).
      not_connected(T, Place) :- \+ member(Place, T).
  connected(T, Place) :- member(Place, T).

  solve(T) :- T = [_,_,_,_],
      sat_across(T, (danny,_,_), (david,_,_)),
      sat_across(T, (donna,_,_), (doreen,_,_)),
      sat_beside(T, (doreen,_,_), (_,_,steak)),
      connected(T, (_,coke,chicken)),
      sat_across(T, (_,_,lasagna), (_,milk,_)),
      not_connected(T, (david,coffee,_)),
      connected(T, (donna,water,_)),
      not_connected(T, (danny,_,steak)).
false
  • 10,264
  • 13
  • 101
  • 209
William Hoskins
  • 305
  • 5
  • 17

3 Answers3

0

You should recognize that there are eight variables in this problem: the drinks and main courses for each person, so DonnaDrink, DannyFood, etc.

Your program should contrain these eight variables with = and \= according to the facts given, perhaps using extra predicates such as gender.

(This is actually a simple variant of the zebra puzzle, but without all the ordering.)

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • The main thing I was having an issue with is figuring out how to represent who was beside who. We know who is across, but beside isn't defined explicitly. Is this not important in this case? – William Hoskins Oct 02 '12 at 15:29
0

Use pattern matching, with each position carrying an attribute triple: Name,Drink,Food.

Then we have a [N,S,W,E] table (abusing of contract Bridge convention) and we must apply all available constraints: I think could be done this way...

sat_across([X,_,Y,_], X, Y).
sat_across([_,X,_,Y], X, Y).
sat_across([X,_,Y,_], Y, X).
sat_across([_,X,_,Y], Y, X).

sat_beside(T, X, Y) :-  % this is tricky
    nth1(N,T,X), nth1(M,T,Y),
    (N =:= M+1 ; N =:= M-1 ; N == 1, M == 4 ; N == 4, M == 1).

cant_afford(T, Place) :- \+ member(Place, T).

solve(T) :- T = [N,S,W,E],
      sat_across(T, (danny,_,_), (david,_,_)),
      sat_across(T, (donna,_,_), (doreen,_,_)),
      sat_beside(T, (doreen,_,_), (_,_,steak)),
      ....
      cant_afford(T, (danny,_,steak)),
      ....
CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • Thanks for the response! Can you explain what is going on in the sat_beside method? I understand the sat_across, but I don't really know what is going on in sat_beside. – William Hoskins Oct 02 '12 at 16:28
  • nth1/3 associate an index to list'element, thus the 'beside relation' can translate to adjacent indexes. sat_across would be more compact in this way, but less clear, I think. – CapelliC Oct 02 '12 at 16:29
  • Okay, that makes sense, thanks! The only other thing I don't know how to represent in this manner is the facts like "Danny couldn't afford steak". I feel if I knew prolog better I would know, but I am not sure. Can I just do something along the lines of "sat_across(T, (danny,_,not(steak)),(_,_,_))"? I feel like this is a really bad way to do it, but I am not sure – William Hoskins Oct 02 '12 at 16:40
  • Oh, okay, I got the answer now, the cant_afford method got me there! Thanks! – William Hoskins Oct 02 '12 at 16:54
0

Sorry if I'm necro'ing this thread, but I wasn't really satisfied by the answers (they seem inelegant / incomplete).

This is a variant on the Zebra problem. The only real question is "What position is X in," where X can be a person, an entree, or a drink. Thus, your domain is { 1, 2, 3, 4 }, where each number represents a seat.

Then add your constraints, make sure that each item is in a unique position relative to other items of its type (that is, a person can only be in 1 seat, etc), and set prolog loose.

Oh, and don't forget to put your negative constraints last. Here's what I ended up with:

%  Organize the seating, so:
%  1  2
%  3  4
%  This means that "across" is always (n+2)%4
%  and "beside" is (n+1)%2

pos(1). pos(2). pos(3). pos(4).

beside(A,B) :- pos(A), B is (A+1) mod 2.
across(A,B) :- pos(A), B is (A+2) mod 4.

zebra(Donna,Danny,David,Doreen,
     Steak,Lasagna,Pizza,Chicken,
     Coke,Milk,Coffee,Water) :-
  % Our 4 given positive constraints
  beside(Doreen,Steak),
  Chicken=Coke,
  across(Lasagna,Milk),
  Donna=Water,
  % The men and women sat across from each other
  across(David,Danny),
  across(Doreen,Donna),
  % Ensure that each item is only in 1 position
  uniq_pos(Steak,Lasagna,Pizza,Chicken),
  uniq_pos(Coke,Milk,Coffee,Water),
  uniq_pos(Donna,Danny,David,Doreen),
  % The remaining two negative constraints.
  \+ David=Coffee,
  \+ Danny=Steak.

% Ensures that all 4 items are in unique positions.
uniq_pos(A,B,C,D) :-
  pos(A), pos(B), pos(C), pos(D),
  \+ A=B, \+ A=C, \+ A=D,
          \+ B=C, \+ B=D,
                  \+ C=D.

Seems to work. Prolog tells me that:
David, in seat 1, got steak and milk,
Doreen, in seat 2, got chicken and coke,
Danny, in seat 3, got lasagna and coffee, and
Donna, in seat 4, got pizza and water.

Hope this helps someone else who's trying to understand this problem.

  • Consider using CLP(FD) constraints instead of this primitive arithmetic. You then need not worry about the order in which you state the constraints. Your program will also become shorter: For example you can use `[A,B,C,D] ins 1..4` in YAP and SWI with their `library(clpfd)`instead of your multiple `pos/1` goals (which, by the way, you should consider replacing by a single call to `maplist/2` like `maplist(pos, [A,B,C,D])` if your Prolog does not support CLP(FD)). Your program will also likely become more efficient. If you do not use CLP(FD), at least consider using `dif/2` for disequalities. – mat Oct 03 '13 at 15:01