% Towers of hanoi, originally by Sergio Greco, modified by Wolfgang Faber.
%
% dlv -FB -N=n..322 
% E.g. for 5: dlv -FB -N=54322 


ostack(X) :- #maxint = 1 + X.

%-------- discs

ostack_div10_mod10_nosteps(S,D,M,2) :- ostack(S), S = S2 + M, M < 10, S2 = D * 10, #int(D). %Safety 
ostack_div10_mod10_nosteps(S,D,M,N) :- ostack_div10_mod10_nosteps(_,S,_,N1), S > 0, S = S2 + M, M < 10, S2 = D * 10, N = N1 * 2, #int(D), #int(M), #int(N). %Safety

disc(D) :- ostack_div10_mod10_nosteps(_,_,D,_), D > 0.

%-------- stacks

stack_div10_mod10(S,0,S) :- disc(S).
stack_div10_mod10(S,S1,D) :- stack_div10_mod10(S1,_,D1), disc(D), 
                             D < D1, S2 = S1 * 10, S = S2 + D, #int(S). %oracolo

stack(S) :- stack_div10_mod10(S,_,_).

mstack_div10_mod10(S,S1,D) :- stack(S1), disc(D), S2 = S1 * 10, S = S2 + D.

%--------- div10 and mod10 for stacks

div10(S,D) :- stack_div10_mod10(S,D,_).
mod10(S,M) :- stack_div10_mod10(S,_,M).

%--------- number of steps: 2^{number of discs} - 1

number(N) :- ostack_div10_mod10_nosteps(_,0,_,N1), N1 = N + 1.

next(I,I1) :- I1 = I+1, number(J), I < J, #int(I). %oracolo

%------ Initial state: All discs on the first stack.

q(0,G,0,0) :- ostack(G).

%------ Put a disc from the first to the second stack.

m(I1,S1,S2,L3) :-  q(I,L1,L2,L3),
                   stack_div10_mod10(L1,S1,D),
                   stack_div10_mod10(S2,L2,D),
                   next(I,I1).

%------ Put a disc from the first to the third stack.

m(I1,S1,L2,S3) :-  q(I,L1,L2,L3),
                   stack_div10_mod10(L1,S1,D),
                   stack_div10_mod10(S3,L3,D),
                   next(I,I1).

%------ Put a disc from the second to the first stack.

m(I1,S1,S2,L3) :-  q(I,L1,L2,L3),
                   stack_div10_mod10(L2,S2,D),
                   stack_div10_mod10(S1,L1,D),
                   next(I,I1).

%------ Put a disc from the second to the third stack.

m(I1,L1,S2,S3) :-  q(I,L1,L2,L3),
                   stack_div10_mod10(L2,S2,D),
                   stack_div10_mod10(S3,L3,D),
                   next(I,I1).

%------ Put a disc from the third to the first stack.

m(I1,S1,L2,S3) :-  q(I,L1,L2,L3),
                   stack_div10_mod10(L3,S3,D),
                   stack_div10_mod10(S1,L1,D),
                   next(I,I1).

%------ Put a disc from the third to the second stack.

m(I1,L1,S2,S3) :-  q(I,L1,L2,L3),
                   stack_div10_mod10(L3,S3,D),
                   stack_div10_mod10(S2,L2,D),
                   next(I,I1).

%------ Guess next config.

q(I,A1,A2,A3) v noq(I,A1,A2,A3) :- m(I,A1,A2,A3).

%------ One configuration per step

:- q(I,L1,L2,L3), q(J,L1,L2,L3), I != J.
:- q(I,L1,L2,L3), q(I,M1,M2,M3), L1 != M1.
:- q(I,L1,L2,L3), q(I,M1,M2,M3), L2 != M2.
:- q(I,L1,L2,L3), q(I,M1,M2,M3), L3 != M3.
:- m(I,L1,L2,L3), not have_q(I). 

have_q(I) :- q(I,_,_,_).

% ------ Query: 

goal :- number(J), q(J,0,0,G), ostack(G).
goal?

