Escape from Zurg in Prolog
Motivation
There is a paper that asks for a solution to the following problem:
Buzz, Woody, Rex, and Hamm have to escape from Zurg. They
merely have to cross one last bridge before they are free.
However, the bridge is fragile and can hold at most two of them
at the same time. Moreover, to cross the bridge a flashlight is
needed to avoid traps and broken parts. The problem is that our
friends have only one flashlight with one battery that lasts
for only 60 minutes. The toys need different times to cross the
bridge (in either direction):
Toy |
Time |
|
Buzz |
5 |
Woody |
10 |
Rex |
20 |
Hamm |
25 |
Since there can be only two toys on the bridge at the same
time, they cannot cross the bridge all at once. Since they need
the flashlight to cross the bridge, whenever two have crossed
the bridge, somebody has to go back and bring the flashlight to
those toys on the other side that still have to cross the
bridge. The problem now is: In which order can the four toys
cross the bridge in time (that is, within 60 minutes) to be
saved from Zurg?
The approach taken by the paper follows a long tradition: First, a
convoluted Prolog solution is presented, and this is then compared
with a more elegant solution in a different language.
The point of
this page is to show you that there is also an
elegant Prolog solution for this task.
Prolog solution
To represent the state of the world in this task, we
use a term of the form state(T,Ls,Rs), where:
- T is an integer that denotes the time taken so far
- Ls is a list of toys that are currently on
the left side
- Rs is a list of toys that are currently on
the right side
Without loss of generality, let us suppose that all toys are
initially on the left side, and need to cross the
bridge to get to the right side. Clearly, two
important states are hence:
- the initial state, which we can represent
as state(0,[buzz,woody,rex,hamm],[]) in our representation
- the desired final state, where all toys are on the
right side, or equivalently, none of the toys are on the left
side, and which we can hence represent
as state(_,[],_)
We are looking for a sequence of state transitions
or moves that take us from the initial state to the desired
final state.
We distinguish between two kinds of moves:
- left_to_right(Toy1,Toy2) means that Toy1 and Toy2 move from left to right
- right_to_left(Toy) means that Toy moves
from right to left
It follows from the task description that no other kinds of
moves are necessary.
An important precondition for each such move is that we stay
within the time limit of 60 minutes.
For convenience, we are using a
Prolog Definite Clause
Grammar (DCG) to describe a sequence of moves.
In addition, we are using declarative
integer arithmetic to reason about integer expressions.
Using these features, here is a Prolog solution for this puzzle:
toy_time(buzz, 5).
toy_time(woody, 10).
toy_time(rex, 20).
toy_time(hamm, 25).
moves(Ms) :- phrase(moves(state(0,[buzz,woody,rex,hamm],[])), Ms).
moves(state(T0,Ls0,Rs0)) -->
{ select(Toy1, Ls0, Ls1), select(Toy2, Ls1, Ls2),
Toy1 @< Toy2,
toy_time(Toy1, Time1), toy_time(Toy2, Time2),
T1 #= T0 + max(Time1,Time2), T1 #=< 60 },
[left_to_right(Toy1,Toy2)],
moves_(state(T1,Ls2,[Toy1,Toy2|Rs0])).
moves_(state(_,[],_)) --> [].
moves_(state(T0,Ls0,Rs0)) -->
{ select(Toy, Rs0, Rs1),
toy_time(Toy, Time),
T1 #= T0 + Time, T1 #=< 60 },
[right_to_left(Toy)],
moves(state(T1,[Toy|Ls0],Rs1)).
Note that only 4 predicates (instead of 8) are necessary.
Example interaction, using GNU Prolog 1.3.0:
| ?- moves(Ms).
Ms = [left_to_right(buzz,woody),right_to_left(buzz),left_to_right(hamm,rex),right_to_left(woody),left_to_right(buzz,woody)] ? ;
Ms = [left_to_right(buzz,woody),right_to_left(woody),left_to_right(hamm,rex),right_to_left(buzz),left_to_right(buzz,woody)] ? ;
no
Source file: zurg.pl
Further reading
Prolog is frequently compared with other languages. Such
comparisons are often heavily biased against Prolog by misusing or
neglecting some of its features. See Prolog compared with
LISP for an example from 1982. Its abstract ends
with:
Nevertheless, the results are interesting enough so as to make
us consider them worth divulging, especially because they seem
to contradict the notion, repeatedly argued for by Prolog
advocates, that performances of the two languages are similar in
relation to speed.
In this concrete case, Richard O'Keefe has published a response
as Prolog
compared with LISP? in 1983. The abstract ends with:
In this article, I point out which features of Prolog have been
misused and give guidelines for their proper use. I also compare
a new Prolog and LISP program performing a similar task, and
find that the execution times are comparable. This is in accord
with earlier results.
More about Prolog: The Power of Prolog
In particular:
Main page