A route(A,B)
holds if B
is one of the openings reachable on entering through A
.
route(a,g).
route(a,b).
route(g,l).
route(g,f).
route(l,s).
route(b,c).
route(b,h).
route(c,d).
route(h,o).
route(d,i).
route(d,j).
route(i,p).
route(p,q).
route(j,r).
route(r,u).
travel(A,A).
travel(A,C) :- route(A,B), travel(B,C).
start(a).
finish(u).
solve :- start(A), finish(B), travel(A,B).
?- solve.
Prolog says that there is a path from a to u.
We can attach a log to remember the travel route.
travellog(A,A,[]).
travellog(A,C,[A-B|Steps]) :-
route(A,B), travellog(B,C,Steps).
solve(L) :- start(A), finish(B), travellog(A,B,L).
?- solve(L).
a-b-c-d-v-q-p-i-v-q-p-i-v-....
route(q,v).
route(v,d).
?- solve.
goes into an infinite loop.
travelsafe(A,A,_).
travelsafe(A,C,Visited) :-
route(A,B),
not(member(B,Visited)),
travelsafe(B,C,[B|Visited]).
solve2 :- start(A), finish(B), travelsafe(A,B,[A]).
?- solve2.
Exercise: Implement solve2
with a log.
Maze is quite straight-forward to map. Other problems not so much.
We need to represent the number of missionaries and cannibals on each bank, and where the boat is.
start(3-3-0-0-l).
finish(0-0-3-3-_).
A state is safe if no missionary gets eaten.
A missionary gets eaten if there is at least one missonary on a bank and the number of cannibals on that bank outnumber them.
safe(0-_-M2-C2-_) :- M2 >= C2.
safe(M1-C1-0-_-_) :- M1 >= C1.
safe(M1-C1-M2-C2-_) :- M1 >= C1, M2 >= C2.
In order to define a transition, we need all possible ways we can take a step. The boat can carry at most 2 people and at least one person.
carry(2,0).
carry(1,1).
carry(0,2).
carry(1,0).
carry(0,1).
A predicate step(A,B)
is defined if there is a carry/2
that moves the state from A
to B
.
State M1-C1-M2-C2-l
means M1
missionaries and C1
cannibals on the left bank, M2
missionaries and C2
cannibals on the right bank, and the boat is besides the left bank.
State M3-C3-M4-C4-r
means M3
missionaries and C3
cannibals on the left bank, M4
missionaries and C4
cannibals on the right bank, and the boat is besides the right bank.
A predicate step(A,B)
is defined if there is a carry/2
that moves the state from A
to B
.
step(M1-C1-M2-C2-l,M3-C3-M4-C4-r) :-
carry(X,Y),
M1 >= X, M3 is M1 - X, M4 is M2+X,
C1 >= Y, C3 is C1 - Y, C4 is C2+Y.
step(M1-C1-M2-C2-r,M3-C3-M4-C4-l) :-
carry(X,Y),
M2 >= X, M4 is M2 - X, M3 is M1+X,
C2 >= Y, C4 is C2 - Y, C3 is C1+Y.
We need to define the game as a series of steps, where each step is safe and we do not visit the same steps again.
We use Visited
list to track visited states and maintain a log of steps.
travel(A,A,_,[]).
travel(A,C,Visited,[B|Steps]) :-
step(A,B), safe(B), not(member(B,Visited)), travel(B,C,[A,B|Visited],Steps).
Solution to the same is a series steps that go from initial to final state.
solve3(L) :- start(A), finish(B), travel(A,B,[],L).
?- solve3(L) {1}.