% Peg solitaire puzzle
% Brute force, no tabling
% Can make about 9 moves

/*
Board consists of list peg(X,Y) and hole(X,Y)
enumerated in row-major order (2,0), (3,0), ..., (3,6), (4,6)
with blocked spaces missing
*/

% test(N) -- print the number of possible N moves
test(N) :-
	g_assign(count, 0),
	solve(N),
	g_inc(count),				% not affected by backtracking
	fail.
test(N) :-
	g_read(count, K),
	format("At ~p, ~p solutions\n", [N,K]).

% solve(N) -- make N moves from initial state
solve(N) :-
	init(State0),
	moves(N, State0).

% moves(N, State) -- make N moves from State.
moves(0, _).
moves(N, State1) :-
	N > 0,
	move1(State1, State2),
	N1 is N-1,
	moves(N1, State2).

% move1(State1, State2) -- make 1 move from State1 to reach State 2
move1([peg(X,Y),peg(X1,Y),hole(X2,Y)|State],
      [hole(X,Y),hole(X1,Y),peg(X2,Y)|State]).
move1([peg(X,Y)|State], [hole(X,Y)|State2]) :-
	Y1 is Y+1, move_up2(X, Y1, State, State2).
move1([hole(X,Y),peg(X1,Y),peg(X2,Y)|State],
      [peg(X,Y),hole(X1,Y),hole(X2,Y)|State]).
move1([hole(X,Y)|State], [peg(X,Y)|State2]) :-
	Y1 is Y+1, move_down2(X, Y1, State, State2).
move1([Place|State], [Place|State2]) :-
	move1(State, State2).

% move_up2(X, Y+1, State1, State2)
% move up from (X,Y) to (X,Y+2) in State1 to reach State2
% State1 is tail after (X,Y)
move_up2(X, Y1, [peg(X,Y1)|State], [hole(X,Y1)|State2]) :- !,
	Y2 is Y1+1,
	move_up3(X, Y2, State, State2).
move_up2(X, Y1, [hole(X,Y1)|_], _) :- !, fail.
move_up2(X, Y1, [Place|State], [Place|State2]) :-
	move_up2(X, Y1, State, State2).

% move_up3(X, Y+2, State1, State2)
% move up from (X,Y) to (X,Y+2) in State1 to reach State2
% State1 is tail after (X,Y+1)
move_up3(X, Y2, [hole(X,Y2)|State], [peg(X,Y2)|State]) :- !.
move_up3(X, Y2, [peg(X,Y2)|_], _) :- !, fail.
move_up3(X, Y2, [Place|State], [Place|State2]) :-
	move_up3(X, Y2, State, State2).

% move_down2(X, Y+1, State1, State2)
% move down from (X,Y+2) to (X,Y) in State1 to reach State2
% State1 is tail after (X,Y)
move_down2(X, Y1, [peg(X,Y1)|State], [hole(X,Y1)|State2]) :- !,
	Y2 is Y1+1,
	move_down3(X, Y2, State, State2).
move_down2(X, Y1, [hole(X,Y1)|_], _) :- !, fail.
move_down2(X, Y1, [Place|State], [Place|State2]) :-
	move_down2(X, Y1, State, State2).

% move_down3(X, Y+2, State1, State2)
% move down from (X,Y+2) to (X,Y) in State1 to reach State2
% State1 is tail after (X,Y+1)
move_down3(X, Y2, [peg(X,Y2)|State], [hole(X,Y2)|State]) :- !.
move_down3(X, Y2, [hole(X,Y2)|_], _) :- !, fail.
move_down3(X, Y2, [Place|State], [Place|State2]) :-
	move_down3(X, Y2, State, State2).

% init(-State) -- return initial state of 33-hole solitaire
% ordered in row-major order
init([                  peg(2,0),peg(3,0),peg(4,0),
                        peg(2,1),peg(3,1),peg(4,1),
      peg(0,2),peg(1,2),peg(2,2),peg(3,2),peg(4,2),peg(5,2),peg(6,2),
      peg(0,3),peg(1,3),peg(2,3),hole(3,3),peg(4,3),peg(5,3),peg(6,3),
      peg(0,4),peg(1,4),peg(2,4),peg(3,4),peg(4,4),peg(5,4),peg(6,4),
                        peg(2,5),peg(3,5),peg(4,5),
                        peg(2,6),peg(3,6),peg(4,6)]).
