/********************************************************************
    Constraint-based Graphical Programming in B-Prolog
    %
    solve eight-puzzle problems
*********************************************************************/
go:-
    cgWindow(Win,"eightPuzzle"),Win^topMargin#=30, Win^leftMargin #= 20,
    handleWindowClosing(Win),
    puzzle(Os,Win),
    cgSame(Os,window,Win),
    cgShow(Os).

handleWindowClosing(Win),{windowClosing(Win)} => cgClose(Win).

puzzle(Comps,Win):-
    initState(S,Ssquares),
    finalState(G,Gsquares),
    % place the boards
    cgLabel(Lcurr,"Current"),
    cgLabel(Lgoal,"Goal"),
    cgRectangle([Box1,Box2]),
    cgTable([[Lcurr,Lgoal],[Box1,Box2]],2,2),
    cgInside(Ssquares,Box1), Box1^width #= 200, Box1^height #= 200,
    cgInside(Gsquares,Box2), Box2^width #= 200, Box2^height #= 200,
    %
    handleAction(S,G,1,3),
    cgSame([Lcurr,Lgoal,Box1,Box2],window,Win),
    cgPack([Lcurr,Lgoal,Box1,Box2]),
    append([Lcurr,Lgoal|Ssquares],Gsquares,Comps).

initState(State,Buttons):-
    State=state(board(row(B1,B3,B0),
		      row(B8,B2,B4),
		      row(B7,B6,B5)),
		1,3),
    Buttons=[B0,B1,B2,B3,B4,B5,B6,B7,B8],
    cgButton(Buttons,[" ","1","2","3","4","5","6","7","8"]),
    B0^width #= 40,
    cgSame(Buttons,fontSize,36),
    cgGrid([[B1,B3,B0],
	    [B8,B2,B4],
	    [B7,B6,B5]],2,2).

finalState(State,Buttons):-
    State=state(board(row(B1,B2,B3),
		      row(B8,B0,B4),
		      row(B7,B6,B5)),
		2,2),
    Buttons=[B0,B1,B2,B3,B4,B5,B6,B7,B8],
    cgButton(Buttons,[" ","1","2","3","4","5","6","7","8"]),
    B0^width #= 40,
    cgSame(Buttons,fontSize,36),
    cgGrid([[B1,B2,B3],
	    [B8,B0,B4],
	    [B7,B6,B5]],2,2).

handleAction(State,Goal,X0,Y0):-
    State=state(board(row(B11,B12,B13),
		  row(B21,B22,B23),
		  row(B31,B32,B33)),
	    X0,Y0),
    handleAction(B11,State,Goal,1,1),
    handleAction(B12,State,Goal,1,2),
    handleAction(B13,State,Goal,1,3),
    handleAction(B21,State,Goal,2,1),
    handleAction(B22,State,Goal,2,2),
    handleAction(B23,State,Goal,2,3),
    handleAction(B31,State,Goal,3,1),
    handleAction(B32,State,Goal,3,2),
    handleAction(B33,State,Goal,3,3).

handleAction(B,State,Goal,X,Y),{actionPerformed(B)} =>
    move(B,State,Goal,X,Y).

move(_B,Curr,Goal,_X,_Y):-
    Curr=state(Cboard,X0,Y0),
    Goal=state(Gboard,X0,Y0),
    Cboard=board(row(C1,C2,C3),
		 row(C4,C5,C6),
		 row(C7,C8,C9)),
    Gboard=board(row(G1,G2,G3),
		 row(G4,G5,G6),
		 row(G7,G8,G9)),
    match([C1,C2,C3,C4,C5,C6,C7,C8,C9],[G1,G2,G3,G4,G5,G6,G7,G8,G9]),!.
move(B,Curr,_Goal,X,Y):-
    Curr=state(Board,X0,Y0),
    neighbors(X0,Y0,X,Y),!,
    %update texts on buttons
    cgGetText(B,Text),
    cgSetText(B," "),
    arg(X0,Board,Row0),
    arg(Y0,Row0,B0),
    cgSetText(B0,Text),
    % (X,Y) becomes the empty square
    setarg(2,Curr,X),
    setarg(3,Curr,Y),
    cgShow([B0,B]).
move(_B,_Curr,_Goal,_X,_Y).

match([],[]).
match([C|Cs],[G|Gs]):-
    cgGetText(C,Text),
    cgGetText(G,Text),
    match(Cs,Gs).

neighbors(X0,Y0,X,Y):-
    X0=:=X,
    (Y=:=Y0+1;Y=:=Y0-1).
neighbors(X0,Y0,X,Y):-
    Y0=:=Y,
    (X=:=X0+1;X=:=X0-1).

