%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%
%%%     8-puzzle animation -- using VT100 character graphics
%%%
%%%
%%%             

puzzle(P) :- solve(P,S), 
             animate(P,S),
             message.

animate(P,S) :- initialize(P),
                cursor(1,2), write(S), 
                cursor(1,22), write('Hit ENTER to step solver.'),
                get0(_X),
                play_back(S).

:- dynamic location/3.   %%% So that location of a tile 
                         %%%  can be retracted/asserted.
                         %%% Location(s) asserted and retracted
                         %%%  by puzzle animator below

initialize(A/B/C/D/E/F/H/I/J) :-
  cls,
  retractall(location(_,_,_)),    
  assert(location(A,20,5)),  
  assert(location(B,30,5)),  
  assert(location(C,40,5)),  
  assert(location(F,40,10)), 
  assert(location(J,40,15)), 
  assert(location(I,30,15)), 
  assert(location(H,20,15)), 
  assert(location(D,20,10)),
  assert(location(E,30,10)), draw_all. 

draw_all :- draw(1), draw(2), draw(3), draw(4),
            draw(5), draw(6), draw(7), draw(8).

   %%% play_back([left,right,up,...]).
play_back([M|R]) :- call(M), get0(_X), play_back(R).
play_back([]) :- cursor(1,24).  %%% Put cursor out of the way

message :- nl,nl,
   write('    ********************************************'), nl, 
   write('    *  Enter 8-puzzle goals in the form ...    *'), nl,
   write('    *     ?-  puzzle(0/8/1/2/4/3/7/6/5).       *'), nl,
   write('    *   Enter goal ''message'' to reread this.   *'), nl,
   write('    ********************************************'), nl, nl.


cursor(X,Y) :- put(27), put(91), %%% ESC [
               write(Y),
               put(59),           %%%   ;
               write(X),
               put(72).           %%%   M

   %%% clear the screen, quickly
cls :-  put(27), put("["), put("2"), put("J").

   %%% video attributes -- bold and blink not working
plain         :- put(27), put("["), put("0"), put("m").
reverse_video :- put(27), put("["), put("7"), put("m").


   %%% Tile objects, character map(s)
   %%% Each tile should be drawn using the character map,
   %%%   drawn at 'location', which is asserted and retracted
   %%%   by 'playback'.
character_map(N, [ [' ',' ',' ',' ',' ',' ',' '],
                   [' ',' ',' ', N ,' ',' ',' '],
                   [' ',' ',' ',' ',' ',' ',' '] ]).


   %%% move empty tile (spot) to the left
left :- retract(location(0,X0,Y0)),
        Xnew is X0 - 10,
        location(Tile,Xnew,Y0),
        assert(location(0,Xnew,Y0)),
        right(Tile),right(Tile),right(Tile),
        right(Tile),right(Tile),
        right(Tile),right(Tile),right(Tile),
        right(Tile),right(Tile).

up :- retract(location(0,X0,Y0)),
      Ynew is Y0 - 5,
      location(Tile,X0,Ynew),
      assert(location(0,X0,Ynew)),
      down(Tile),down(Tile),down(Tile),down(Tile),down(Tile).

right :- retract(location(0,X0,Y0)),
         Xnew is X0 + 10,
         location(Tile,Xnew,Y0),
         assert(location(0,Xnew,Y0)),
         left(Tile),left(Tile),left(Tile),left(Tile),left(Tile),
         left(Tile),left(Tile),left(Tile),left(Tile),left(Tile).

down :- retract(location(0,X0,Y0)),
        Ynew is Y0 + 5,
        location(Tile,X0,Ynew),
        assert(location(0,X0,Ynew)),
        up(Tile),up(Tile),up(Tile),up(Tile),up(Tile).


draw(Obj) :- reverse_video, character_map(Obj,M),
             location(Obj,X,Y),
             draw(X,Y,M), plain.

 %%% hide tile
hide(Obj) :- character_map(Obj,M),
             location(Obj,X,Y),
             hide(X,Y,M).

hide(_,_,[]).
hide(X,Y,[R|G]) :- hide_row(X,Y,R),
                   Y1 is Y + 1,
                   hide(X,Y1,G).

hide_row(_,_,[]).
hide_row(X,Y,[_|R]) :- cursor(X,Y),
                       write(' '),
                       X1 is X + 1,
                       hide_row(X1,Y,R).

   %%% draw tile
draw(_,_,[]).
draw(X,Y,[R|G]) :- draw_row(X,Y,R),
                   Y1 is Y + 1,
                   draw(X,Y1,G).

draw_row(_,_,[]).
draw_row(X,Y,[P|R]) :- cursor(X,Y),
                       write(P),
                       X1 is X + 1,
                       draw_row(X1,Y,R).

   %%% Move an Object up
up(Obj)    :- hide(Obj),
              retract(location(Obj,X,Y)),
              Y1 is Y - 1,
              assert(location(Obj,X,Y1)), 
              draw(Obj).

down(Obj)  :- hide(Obj),
              retract(location(Obj,X,Y)),
              Y1 is Y + 1,
              assert(location(Obj,X,Y1)),
              draw(Obj).

left(Obj)  :- hide(Obj),
              retract(location(Obj,X,Y)),
              X1 is X - 1,
              assert(location(Obj,X1,Y)),
              draw(Obj).

right(Obj) :- hide(Obj),
              retract(location(Obj,X,Y)),
              X1 is X + 1,
              assert(location(Obj,X1,Y)),
              draw(Obj).

:- message.


