3

I have a program due in the morning using Prolog.

I am implementing Towers of Hanoi.

What I need help with is each time that it prints Move disc number # from _ to _. I need to increment a counter saying Move X: "String" String being the previous statement of what to move.

At the end of the program I need to print the total number of moves taken to accomplish the puzzle. I currently have my program setup recursively bottom-up.

Adam Wenger
  • 17,100
  • 6
  • 52
  • 63
Michael Stramel
  • 1,337
  • 1
  • 16
  • 18

3 Answers3

1

Using this variant, one can call hanoi(3,left,middle,right,Moves-NMoves), and a list of moves will be instantied to Moves, and the number of moves taken will be instantiated to NMoves. One can easily write a predicate to format each list member into Input/Output.

Notice how difference lists are utilized here to avoid the expensive use of append/3. The length/2 call in the hanoi/5 predicate acts as a sort of proof that the resulting list of moves is the correct size.

hanoi(N, Src, Aux, Dest, Moves-NMoves) :-
  NMoves is 2^N - 1,
  length(Moves, NMoves),
  move(N, Src, Aux, Dest, Moves-_).


move(1, Src, _, Dest, [Src->Dest|Rest]-Rest) :- !.
move(2, Src, Aux, Dest, [Src->Aux,Src->Dest,Aux->Dest|Rest]-Rest) :- !.
move(N, Src, Aux, Dest, Moves-Rest) :-
  N0 is N-1,
  move(N0, Src, Dest, Aux, Moves-M1),
  move(1, Src, Aux, Dest, M1-M2),
  move(N0, Aux, Src, Dest, M2-Rest).

A more readable approach could involve the use of DCG to hide the plumbing:

hanoi(N, Src, Aux, Dest, Moves-NMoves) :-
  NMoves is 2^N - 1,
  length(Moves, NMoves),
  phrase(move(N, Src, Aux, Dest), Moves).


move(1, Src, _, Dest) --> !,
  [Src->Dest].

move(2, Src, Aux, Dest) --> !,
  [Src->Aux,Src->Dest,Aux->Dest].

move(N, Src, Aux, Dest) -->
  { succ(N0, N) },
  move(N0, Src, Dest, Aux),
  move(1, Src, Aux, Dest),
  move(N0, Aux, Src, Dest).
eazar001
  • 1,572
  • 2
  • 16
  • 29
1

One common problem is keeping IO mixed with the calculation. It is better to isolate IO by having a wrapper predicate with an argument that becomes instantiated with the result.

Consider this (code is meant to demonstrate the addition of a counter, not solve the problem):

% one move
move(1,X,Y,_,_,1) :-                           
    write('Move top disk from '), 
    write(X), write(' to '), write(Y), nl.

move(N,X,Y,Z,Count,Counter) :- 
    N>1, 
    M is N-1,
    Countplus is Count*2,        % binary recursion tree
    move(M,X,Z,Y,Countplus,C1), 
    move(1,X,Y,_,Countplus,C2),
    move(M,Z,Y,X,Countplus,C3),
    Counter is C1+C2+C3.         % sum of moves

towers(N,X,Y,Z) :-
    Count is N*N-1,
    move(N,X,Y,Z,Count,Counter),                       
    % you can now do whatever you want with Counter, e.g. print it:
    write('The number of steps required with '),
    write(N), write(' disks is '), write(Count), write('.').

Even if the above code is not related to what you have, the same procedure applies: add arguments to transfer the Counter and use recursion to increment it.

Alternatively, there are global variables but are usually frowned upon as they are ease to misuse and write procedural programs.

Thanos Tintinidis
  • 5,828
  • 1
  • 20
  • 31
  • 1
    format/2 is also useful to combine successive write/1 calls, for example: format("The number of steps required with ~w disks is ~w.", [N,Count]). – mat Nov 16 '11 at 09:02
1

instead of writting a do-all predicate, break it in do-one-thing predicates:

  1. write a predicate that gets the number of discs and returns a list of moves that solve the problem (i.e. [1->2, 3->1, ...])
  2. the standard length/2 predicate already allows you to count the number of moves on the list.
  3. write a predicate to print the list of moves in an user friendly format.
  4. write a predicate to call the previous predicates in order.
salva
  • 9,943
  • 4
  • 29
  • 57