blob: 06dd9ca139e3c1b07ccc7b8d98d08860b3178639 [file] [log] [blame]
% File : knight.pl
% Authors: Naoyuki Tamura (tamura@kobe-u.ac.jp)
% Mutsunori Banbara (banbara@kobe-u.ac.jp)
% Updated: 26 May 2008
% Purpose: Knight's Tour (posed by Brook Taylor, 18C)
% Note : Each position (I,J) on the board N*N
% is indexed by (N+2)*(I-1)+J-1
/*
| ?- main.
Finds a Knight's Tour (N>=8 takes a long time...)
N = 8.
1 12 9 6 3 14 17 20
10 7 2 13 18 21 4 15
31 28 11 8 5 16 19 22
64 25 32 29 36 23 48 45
33 30 27 24 49 46 37 58
26 63 52 35 40 57 44 47
53 34 61 50 55 42 59 38
62 51 54 41 60 39 56 43
yes
*/
:- dynamic n/1, corner/1, next_K/1.
main :-
write('Finds a Knight''s Tour (N>=8 takes a long time...)'), nl,
write('N = '),
flush_output,
read(N),
statistics(runtime, _),
knight_tour(N),
statistics(runtime, [_,T]),
nl,
write('CPU time = '), write(T), write(' msec'), nl,
!.
knight_tour(N) :-
knight_init(N),
gen_res(L),
solve(1, 1, L),
write_board(L).
knight_init(N) :-
retractall(n(_)),
retractall(corner(_)),
retractall(next_K(_)),
assertz(n(N)).
% gen_res/1 creates resources k/2 as a list, and asserts corner/1 and next_K/1.
% - k(K, _) for K = (N+2)*(I-1)+J-1, I = 1..N, J = 1..N
% - corner(K) for K = (N+2)*(I-1)+J-1, I = 1,N, J = 1,N
% - next_K(DK) for DK = (N+2)*DI+DJ, (DI,DJ) = (+-2,+-1),(+-1,+-2)
gen_res(L) :- gen_res(1, 1, L).
gen_res(I, _, []) :- clause(n(N), _), I > N, !,
calc_K(1, 1, C1),
calc_K(1, N, C2),
calc_K(N, 1, C3),
calc_K(N, N, C4),
calc_DK(-2, -1, DK1),
calc_DK(-2, 1, DK2),
calc_DK(-1, -2, DK3),
calc_DK(-1, 2, DK4),
calc_DK( 1, -2, DK5),
calc_DK( 1, 2, DK6),
calc_DK( 2, -1, DK7),
calc_DK( 2, 1, DK8),
assertz(corner(C1)),
assertz(corner(C2)),
assertz(corner(C3)),
assertz(corner(C4)),
assertz(next_K(DK1)),
assertz(next_K(DK2)),
assertz(next_K(DK3)),
assertz(next_K(DK4)),
assertz(next_K(DK5)),
assertz(next_K(DK6)),
assertz(next_K(DK7)),
assertz(next_K(DK8)).
gen_res(I, J, L) :- clause(n(N), _), J > N, !,
I1 is I + 1,
gen_res(I1, 1, L).
gen_res(I, J, [k(K, _)|L]) :-
clause(n(N), _),
I =< N, J =< N,
calc_K(I, J, K),
J1 is J + 1,
gen_res(I, J1, L).
% solve(+I, +J, +R) finds knight tour starting from the position (I,J).
% R is a list of k(Index, Step).
solve(I, J, R) :-
clause(n(N), _),
Step = 1,
calc_K(I, J, K),
del_res(k(K,Step), R, R0),
solve(Step, N, K, R0).
solve(Step, N, _, []) :-
Step >= N*N,
!.
solve(Step, N, K, R) :-
Step < N*N,
check(Step, N, R),
Step1 is Step + 1,
next(K, K1, R),
del_res(k(K1,Step1), R, R0),
solve(Step1, N, K1, R0).
del_res(X, [X|Xs], Xs).
del_res(X, [Y|Ys], [Y|Zs]) :- del_res(X, Ys, Zs).
check(Step, N, R) :-
(Step mod 4 =:= 0, Step < N*N-1
-> \+ isolate_one(_, R)
; true),
!.
isolate_one(K, R) :-
del_res(k(K, _), R, _),
\+ (next0(K, K1), del_res(k(K1, _), R, _)).
% if current is a corner, it's ok
next(K, K1, _) :-
clause(corner(K), _),
!,
next0(K, K1).
% if the next can be a not-visitied corner, it should be selected
next(K, K1, R) :-
next0(K, K1),
clause(corner(K1), _),
not_visited(K1, R),
!.
% otherwise
next(K, K1, _) :- next0(K, K1).
next0(K, K1) :- clause(next_K(DK), _), K1 is K + DK.
not_visited(K, R) :- \+ \+ del_res(k(K, _), R, _).
calc_K(I, J, K) :- clause(n(N), _), K is (N+2)*(I-1)+(J-1).
calc_DK(DI, DJ, DK) :- clause(n(N), _), DK is (N+2)*DI+DJ.
% write_board(L) writes step numbers S for each I = 1..N, J = 1..N
write_board(L) :-
clause(n(N), _),
for(I, 1, N),
nl,
for(J, 1, N),
calc_K(I, J, K),
member(k(K, Step), L),
write_number(Step),
fail.
write_board(_) :- nl.
for(M, M, N) :- M =< N.
for(I, M, N) :- M =< N, M1 is M + 1, for(I, M1, N).
write_number(C) :- C < 10, !, write(' '), write(C).
write_number(C) :- write(' '), write(C).
member(X, [X|_]).
member(X, [_|Xs]) :- member(X, Xs).
%%%
knight_tour_applet(N, Ans) :-
knight_init(N),
gen_res(L),
solve(1, 1, L),
get_step(L, Ans).
get_step(L, A) :-
clause(n(N), _),
get_step(1, 1, N, L, A).
get_step(I, _, N, _, []) :-
I > N,
!.
get_step(I, J, N, L, A) :- J > N, !,
I1 is I+1,
get_step(I1, 1, N, L, A).
get_step(I, J, N, L, [Step|A]) :-
I =< N, J =< N,
calc_K(I, J, K),
member(k(K,Step), L),
!,
J1 is J+1,
get_step(I, J1, N, L, A).