Knight's tour: Difference between revisions
Content added Content deleted
Line 6,782: | Line 6,782: | ||
=={{header|ObjectIcon}}== |
=={{header|ObjectIcon}}== |
||
{{trans|ATS}} |
|||
This program can print multiple solutions by applying Warnsdorff’s heuristic, by trying in turn each ‘Warnsdorff move’. |
|||
<lang objecticon># -*- ObjectIcon -*- |
<lang objecticon># -*- ObjectIcon -*- |
||
Line 6,789: | Line 6,789: | ||
# |
# |
||
# Using Warnsdorff’s heuristic, find multiple solutions. |
# Using Warnsdorff’s heuristic, find multiple solutions. |
||
# |
|||
# Based on my ATS/Postiats program. |
|||
# |
|||
# The main difference from the ATS is this program uses a |
|||
# co-expression pair to make a generator of solutions, whereas the ATS |
|||
# simply prints solutions where they are found. |
|||
# |
|||
# Usage: ./knights_tour [START_POSITION [MAX_TOURS [closed]]] |
|||
# Examples: |
|||
# ./knights_tour (prints one tour starting from a1) |
|||
# ./knights_tour c5 |
|||
# ./knights_tour c5 2000 |
|||
# ./knights_tour c5 2000 closed |
|||
# |
# |
||
Line 6,799: | Line 6,812: | ||
local f_out |
local f_out |
||
local tours |
local tours |
||
local make_tour |
|||
local tour_board |
local tour_board |
||
local n_tour |
local n_tour |
||
local starting_position |
local starting_position |
||
local |
local i, j |
||
local max_tours |
local max_tours |
||
local closed_only |
|||
starting_position := |
starting_position := \algebraic_notation_to_i_j(args[1]) | [1, 1] |
||
i := starting_position[1] |
|||
(\algebraic_notation_to_i_j(args[1]) | |
|||
j := starting_position[2] |
|||
usage_error()) |
|||
i_start := starting_position[1] |
|||
j_start := starting_position[2] |
|||
max_tours := integer(args[2]) | 1 |
max_tours := integer(args[2]) | 1 |
||
closed_only := if \args[3] === "closed" then &yes else &no |
|||
f_out := FileStream.stdout |
f_out := FileStream.stdout |
||
tours := KnightsTours() |
tours := KnightsTours() |
||
make_tour := create tours.generate(i_start, j_start) |
|||
n_tour := 0 |
n_tour := 0 |
||
if n_tour < max_tours then |
|||
every tour_board := tours.generate(i, j, closed_only) do { |
|||
n_tour +:= 1 |
|||
n_tour +:= 1 |
|||
write("Tour number ", n_tour, ":") |
|||
f_out.write(tour_board. |
f_out.write(tour_board.make_moves_display()) |
||
f_out.write() |
f_out.write(tour_board.make_board_display()) |
||
f_out.write() |
|||
} |
|||
if max_tours <= n_tour then |
|||
break |
|||
} |
|||
end |
end |
||
procedure usage_error() |
procedure usage_error() |
||
write("Usage: ", &progname, " POSITION [MAX_TOURS=1]") |
write("Usage: ", &progname, " POSITION [MAX_TOURS=1 [closed]]") |
||
write("Examples: ") |
write("Examples: ") |
||
write(" ", &progname, " c5 2") |
|||
write(" ", &progname, " a1") |
write(" ", &progname, " a1") |
||
write(" ", &progname, " c5 2") |
|||
write(" ", &progname, " e3 200 closed") |
|||
exit(0) |
exit(0) |
||
end |
end |
||
Line 6,889: | Line 6,904: | ||
public try(i, j, value) |
public try(i, j, value) |
||
# Backtracking assignment. Though we use it for ordinary |
|||
# assignment. |
|||
# |
|||
# The board is stored in column-major order. |
# The board is stored in column-major order. |
||
suspend board[i + (n_ranks * (j - 1))] <- value |
suspend board[i + (n_ranks * (j - 1))] <- value |
||
Line 6,898: | Line 6,916: | ||
s := "" |
s := "" |
||
every i := n_ranks to 1 by -1 do |
every i := n_ranks to 1 by -1 do |
||
{ |
|||
s ||:= " " |
s ||:= " " |
||
every j := 1 to n_files do |
every j := 1 to n_files do |
||
Line 6,922: | Line 6,941: | ||
local i, j |
local i, j |
||
local s |
local s |
||
local first_position, last_position |
|||
positions := list(n_squares) |
positions := list(n_squares) |
||
Line 6,929: | Line 6,949: | ||
s := "" |
s := "" |
||
every j := 1 to n_squares - 1 do |
every j := 1 to n_squares - 1 do |
||
{ |
|||
s ||:= positions[j].make_display() |
s ||:= positions[j].make_display() |
||
s ||:= (if j % n_files = 0 then " ->\n" else " -> ") |
s ||:= (if j % n_files = 0 then " ->\n" else " -> ") |
||
} |
} |
||
s ||:= positions[n_squares].make_display() |
s ||:= positions[n_squares].make_display() |
||
first_position := find_nth_position(1) |
|||
last_position := find_nth_position(n_squares) |
|||
if knight_positions_are_attacking(first_position.i, |
|||
first_position.j, |
|||
last_position.i, |
|||
last_position.j) then |
|||
s ||:= " -> cycle" |
|||
return s |
return s |
||
end |
|||
public find_nth_position(n) |
|||
local i, j |
|||
local position |
|||
position := &null |
|||
i := 1 |
|||
while /position & i <= n_ranks do |
|||
{ |
|||
j := 1 |
|||
while /position & j <= n_files do |
|||
{ |
|||
if square(i, j) = n then |
|||
position := Move(i, j) |
|||
j +:= 1 |
|||
} |
|||
i +:= 1 |
|||
} |
|||
return position |
|||
end |
end |
||
Line 6,946: | Line 6,996: | ||
private board |
private board |
||
public new(num_ranks, num_files) |
public new(num_ranks, num_files, i, j, closed_only) |
||
board := Chessboard(num_ranks, num_files) |
board := Chessboard(num_ranks, num_files) |
||
n_ranks := board.n_ranks |
n_ranks := board.n_ranks |
||
Line 6,954: | Line 7,004: | ||
end |
end |
||
public generate(i, j, |
public generate(i, j, closed_only) |
||
# i,j = starting position. |
|||
local move |
|||
local consumer |
|||
local explorer |
|||
local tour_board |
local tour_board |
||
# Simple coroutines. The consumer receives complete tours (each in |
|||
# Generate boards recursively. |
|||
# the form of a Chessboard) from the explorer. |
|||
consumer := ¤t |
|||
explorer := create explore(consumer, i, j, 1, |
|||
closed_only, i, j) |
|||
while tour_board := @explorer do |
|||
suspend tour_board |
|||
end |
|||
private explore(consumer, i, j, n_position, |
|||
closed_only, i_start, j_start) |
|||
# i,j = starting position. |
|||
board.try(i, j, n_position) |
board.try(i, j, n_position) |
||
explore_inner(consumer, i, j, n_position, |
|||
closed_only, i_start, j_start) |
|||
board.try(i, j, &null) |
|||
end |
|||
private explore_inner(consumer, i, j, n_position, |
|||
closed_only, i_start, j_start) |
|||
local moves, mv |
|||
if n_squares - n_position = 1 then { |
if n_squares - n_position = 1 then { |
||
# Is the last move possible? If so, make it and output the |
|||
# board. (Only zero or one of the moves can be non-null.) |
|||
moves := possible_moves(i, j) |
|||
every try_last_move(consumer, moves[1 to 8], |
|||
closed_only, i_start, j_start) |
|||
} |
|||
} else { |
} else { |
||
moves := next_moves(i, j, n_position) |
|||
every mv := !moves do |
|||
if \mv then |
|||
n_position + 1) do |
|||
explore(consumer, mv.i, mv.j, n_position + 1, |
|||
suspend tour_board.copy() |
|||
closed_only, i_start, j_start) |
|||
} |
} |
||
end |
|||
private try_last_move(consumer, move, closed_only, i_start, j_start) |
|||
if \move then |
|||
if (/closed_only | |
|||
knight_positions_are_attacking(move.i, move.j, |
|||
i_start, j_start)) then |
|||
{ |
|||
board.try(move.i, move.j, n_squares) |
|||
(board.copy())@consumer |
|||
board.try(move.i, move.j, &null) |
|||
} |
|||
end |
end |
||
private next_moves(i, j, n_position) |
private next_moves(i, j, n_position) |
||
local |
local moves |
||
local |
local w_list, w |
||
local |
local k |
||
local n |
|||
moves := possible_moves(i, j) |
|||
# |
|||
w_list := list(8) |
|||
# Prune and sort the moves according to Warnsdorff’s heuristic, |
|||
every k := 1 to 8 do |
|||
# keeping only moves that have the minimum number of legal |
|||
w_list[k] := count_following_moves(moves[k], n_position) |
|||
# following moves. |
|||
w := pick_w(w_list) |
|||
# |
|||
if w = 0 then |
|||
# There may be more than one such move, from a given position. For |
|||
# A dead end. |
|||
# reproducibility of the results, order them stably. |
|||
moves := list(8, &null) |
|||
# |
|||
else |
|||
# w is least positive number of following moves. Nullify any |
|||
# move that has either zero following moves (it is a dead end) |
|||
# or more than w following moves (it violates Warnsdorff’s |
|||
# heuristic). |
|||
every k := 1 to 8 do |
|||
if w_list[k] ~= w then |
|||
moves[k] := &null |
|||
return moves |
|||
end |
|||
private count_following_moves(move, n_position) |
|||
good_moves := [] |
|||
local w |
|||
local following_moves |
|||
every move := possible_moves(i, j) do { |
|||
board.try(move.i, move.j, n_position) |
|||
w := 0 |
|||
if \move then { |
|||
board.try(move.i, move.j, n_position + 1) |
|||
following_moves := possible_moves(move.i, move.j) |
|||
if n ~= 0 then { |
|||
every ( \following_moves[1 to 8] & w +:= 1 ) |
|||
good_moves := [move] |
|||
n_following := [n] |
|||
} else if n = n_following[1] then { |
|||
put(good_moves, move) |
|||
put(n_following, n) |
|||
} |
|||
} |
|||
board.try(move.i, move.j, &null) |
board.try(move.i, move.j, &null) |
||
} |
} |
||
return w |
|||
every suspend !good_moves |
|||
end |
|||
private pick_w(w_list) |
|||
local w |
|||
w := 0 |
|||
every w := next_pick (w, w_list[1 to 8]) |
|||
return w |
|||
end |
|||
private next_pick(u, v) |
|||
local w |
|||
if v = 0 then |
|||
w := u |
|||
else if u = 0 then |
|||
w := v |
|||
else |
|||
w := min (u, v) |
|||
return w |
|||
end |
end |
||
private possible_moves(i, j) |
private possible_moves(i, j) |
||
local |
local move1, move2, move3, move4 |
||
local |
local move5, move6, move7, move8 |
||
move1 := try_move(i + 1, j + 2) |
|||
static strides_list |
|||
move2 := try_move(i + 2, j + 1) |
|||
initial strides_list := |
|||
move3 := try_move(i + 1, j - 2) |
|||
move4 := try_move(i + 2, j - 1) |
|||
move5 := try_move(i - 1, j + 2) |
|||
move6 := try_move(i - 2, j + 1) |
|||
move7 := try_move(i - 1, j - 2) |
|||
move8 := try_move(i - 2, j - 1) |
|||
return [move1, move2, move3, move4, |
|||
move5, move6, move7, move8] |
|||
end |
|||
private try_move(i1, j1) |
|||
every stride := !strides_list do { |
|||
i1 |
return (1 <= i1 <= n_ranks & |
||
1 <= j1 <= n_files & |
|||
/board.square(i1, j1) & |
|||
Move(i1, j1)) | &null |
|||
if /board.square(i1, j1) then |
|||
suspend Move(i1, j1) |
|||
} |
|||
end |
end |
||
end |
end |
||
procedure knight_positions_are_attacking(i1, j1, i2, j2) |
|||
local i_diff, j_diff |
|||
i_diff := abs(i1 - i2) |
|||
j_diff := abs(j1 - j2) |
|||
return (((i_diff = 2 & j_diff = 1) | |
|||
(i_diff = 1 & j_diff = 2)) & &yes) | fail |
|||
end</lang> |
|||
{{out}} |
{{out}} |
||
$ ./knights_tour |
$ ./knights_tour c5 2 closed |
||
<pre>Tour number 1: |
<pre>Tour number 1: |
||
c5 -> a6 -> b8 -> d7 -> f8 -> h7 -> g5 -> h3 -> |
|||
g1 -> e2 -> c1 -> a2 -> b4 -> d3 -> e1 -> g2 -> |
|||
h4 -> g6 -> h8 -> f7 -> d8 -> b7 -> a5 -> b3 -> |
|||
a1 -> c2 -> a3 -> b1 -> d2 -> f3 -> h2 -> f1 -> |
|||
g3 -> h1 -> f2 -> e4 -> c3 -> a4 -> b2 -> d1 -> |
|||
e3 -> g4 -> h6 -> g8 -> f6 -> h5 -> f4 -> d5 -> |
|||
e7 -> c8 -> a7 -> c6 -> e5 -> c4 -> b6 -> a8 -> |
|||
c7 -> e8 -> d6 -> b5 -> d4 -> f5 -> g7 -> e6 -> cycle |
|||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
8 | |
8 | 56 | 3 | 50 | 21 | 58 | 5 | 44 | 19 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
7 | |
7 | 51 | 22 | 57 | 4 | 49 | 20 | 63 | 6 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
6 | |
6 | 2 | 55 | 52 | 59 | 64 | 45 | 18 | 43 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
5 | |
5 | 23 | 60 | 1 | 48 | 53 | 62 | 7 | 46 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
4 | |
4 | 38 | 13 | 54 | 61 | 36 | 47 | 42 | 17 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
3 | |
3 | 27 | 24 | 37 | 14 | 41 | 30 | 33 | 8 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
2 | |
2 | 12 | 39 | 26 | 29 | 10 | 35 | 16 | 31 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
1 | |
1 | 25 | 28 | 11 | 40 | 15 | 32 | 9 | 34 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
a b c d e f g h |
a b c d e f g h |
||
Tour number 2: |
Tour number 2: |
||
c5 -> a6 -> b8 -> d7 -> f8 -> h7 -> g5 -> h3 -> |
|||
g1 -> e2 -> c1 -> a2 -> b4 -> d3 -> e1 -> g2 -> |
|||
h4 -> g6 -> h8 -> f7 -> d8 -> b7 -> a5 -> b3 -> |
|||
a1 -> c2 -> a3 -> b1 -> d2 -> f3 -> h2 -> f1 -> |
|||
g3 -> h1 -> f2 -> e4 -> c3 -> a4 -> b2 -> d1 -> |
|||
e3 -> g4 -> h6 -> g8 -> f6 -> h5 -> f4 -> d5 -> |
|||
e7 -> c8 -> a7 -> c6 -> e5 -> c4 -> b6 -> a8 -> |
|||
c7 -> b5 -> d6 -> e8 -> g7 -> f5 -> d4 -> e6 -> cycle |
|||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
8 | |
8 | 56 | 3 | 50 | 21 | 60 | 5 | 44 | 19 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
7 | |
7 | 51 | 22 | 57 | 4 | 49 | 20 | 61 | 6 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
6 | |
6 | 2 | 55 | 52 | 59 | 64 | 45 | 18 | 43 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
5 | |
5 | 23 | 58 | 1 | 48 | 53 | 62 | 7 | 46 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
4 | |
4 | 38 | 13 | 54 | 63 | 36 | 47 | 42 | 17 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
3 | |
3 | 27 | 24 | 37 | 14 | 41 | 30 | 33 | 8 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
2 | |
2 | 12 | 39 | 26 | 29 | 10 | 35 | 16 | 31 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
1 | |
1 | 25 | 28 | 11 | 40 | 15 | 32 | 9 | 34 | |
||
+----+----+----+----+----+----+----+----+ |
+----+----+----+----+----+----+----+----+ |
||
a b c d e f g h |
a b c d e f g h |