Peg Jump Puzzle
% Form of board, x for full, o for vacant spaces. Starting is
% [ [o], [x, x], [x, x, x], [x, x, x, x] ]
% Legal jumps along a line.
linjmp([x, x, o | T], [o, o, x | T]).
linjmp([o, x, x | T], [x, o, o | T]).
linjmp([H|T1], [H|T2]) :- linjmp(T1,T2).
% Rotate the board
rotate([ [A], [B, C], [D, E, F], [G, H, I, J], [K, L, M, N, O]],
[ [K], [L, G], [M, H, D], [N, I, E, B], [O, J, F, C, A]]).
% A jump on some line.
horizjmp([A|T],[B|T]) :- linjmp(A,B).
horizjmp([H|T1],[H|T2]) :- horizjmp(T1,T2).
% One legal jump.
jump(B,A) :- horizjmp(B,A).
jump(B,A) :- rotate(B,BR), horizjmp(BR,BRJ), rotate(A,BRJ).
jump(B,A) :- rotate(BR,B), horizjmp(BR,BRJ), rotate(BRJ,A).
% Series of legal boards.
series(From, To, [From, To]) :- jump(From, To).
series(From, To, [From, By | Rest])
:- jump(From, By),
series(By, To, [By | Rest]).
% Print a series of boards. This puts one board per line and looks a lot
% nicer than the jumble that appears when the system simply beltches out
% a list of boards. The write_ln predicate is a built-in which always
% succeeds (is always satisfied), but prints as a side-effect. Therefore
% print_series(Z) will succeed with any list, and the members of the list
% will be printed, one per line, as a side-effect of that success.
print_series_r([]) :-
write_ln('*******************************************************').
print_series_r([X|Y]) :- write_ln(X), print_series_r(Y).
print_series(Z) :-
write_ln('\n*******************************************************'),
print_series_r(Z).
% A solution.
solution(L) :- series([[o], [x, x], [x, x, x], [x, x, x, x], [x, x, x, x, x]],
[[x], [o, o], [o, o, o], [o, o, o, o], [o, o, o, o, o]], L).
% Find a print the first solution.
solve :- solution(X), print_series(X).
% Find all the solutions.
solveall :- solve, fail.
% This finds each solution with stepping.
solvestep(Z) :- Z = next, solution(X), print_series(X).
Most of the code is concerned with defining a legal move. First, we define
horizontal moves.
The two legal changes (left and right jumps) are expressed
in the two basis rules for linjmp:
linjmp([x, x, o | T], [o, o, x | T]).
linjmp([o, x, x | T], [x, o, o | T]).
These define the horizontal jump, including the requirement that the
pegs to the right are unchanged (the contents of T).
The left part of the row is kept the same by the recursive part of
linjmp:
linjmp([H|T1], [H|T2]) :- linjmp(T1,T2).
The left part of the row is filled with H) on each side.
Now, the basis case of horizjmp
horizjmp([A|T],[B|T]) :- linjmp(A,B).
makes sure the rows after
the change are the same, and the
recursive case of horizjmp
horizjmp([H|T1],[H|T2]) :- horizjmp(T1,T2).
makes sure the rows before
are all unchanged.
Once the horizontal jumps are taken care of, the other jumps are done by
using rotation to turn them into horizontal jumps. For instance,
the move:
is handled as:
The rotate rule handles rotating the board (either way),
and jump puts it all together to define all legal jumps.