The Prolog solution for Hanoi usually looks something like this. The solution writes the exits to the screen when it meets them and does not collect the moves in the list:
move_one(P1, P2) :- format("Move disk from ~k to ~k", [P1, P2]), nl. move(1, P1, P2, _) :- move_one(P1, P2). move(N, P1, P2, P3) :- N > 1, N1 is N - 1, move(N1, P1, P3, P2), move(1, P1, P2, P3), move(N1, P3, P2, P1). hanoi(N) :- move(N, left, center, right).
This can be changed to collect the moves in the list instead, adding the list argument everywhere and using append/3 :
move(0, _, _, _, []). move(N, P1, P2, P3, Moves) :- N > 0, N1 is N - 1, move(N1, P1, P3, P2, M1), append(M1, [P1-to-P2], M2), move(N1, P3, P2, P1, M3), append(M2, M3, Moves). hanoi(N, Moves) :- move(N, left, center, right, Moves).
We were able to make the basic example simpler without write . append/3 does the job, but it's a bit awkward. In addition, is/2 , in particular, makes it non-relational.
Using DCG and CLP (FD), append/3 can be eliminated and made more relational. Here I would call the initial "naive" approach, and this is also more readable:
hanoi_dcg(N, Moves) :- N in 0..1000, phrase(move(N, left, center, right), Moves). move(0, _, _, _) --> []. move(N, P1, P2, P3) --> { N
This leads to:
| ?- hanoi_dcg(3, Moves). Moves = [left-to-center,left-to-right,center-to-right,left-to-center,right-to-left,right-to-center,left-to-center] ? a no | ?- hanoi_dcg(N, [left-to-center,left-to-right,center-to-right,left-to-center,right-to-left,right-to-center,left-to-center]). N = 3 ? ; (205 ms) no | ?-
Although it is relational, it has several problems:
- Useless selection points in both directions
- Problems with termination, if not limited to something like
N in 0..1000
I feel that there is a way around these two questions, but it hasn’t worked yet. (I'm sure if some smarter Prologers than me, like @mat, @false or @repeat, look, they will have a good answer right away.)