Knight's tour: Difference between revisions

Content added Content deleted
m (→‎{{header|Phix}}: added syntax colouring, marked p2js compatible)
No edit summary
Line 3,397: Line 3,397:


In '''[https://formulae.org/?example=Knight%27s_tour this]''' page you can see the program(s) related to this task and their results.
In '''[https://formulae.org/?example=Knight%27s_tour this]''' page you can see the program(s) related to this task and their results.

=={{header|Fortran}}==
{{works with|Fortran|2008, 2018}}
{{works with|gfortran|11.2.1}}
This code has been placed by its author into the public domain, via the Unlicense. For more information, please refer to <http://unlicense.org/>
<lang fortran>!!!
!!! Find a Knight’s Tour.
!!!
!!! Use Warnsdorff’s heuristic, but write the program so it should not
!!! be able to terminate unsuccessfully.
!!!

module knights_tour
use, intrinsic :: iso_fortran_env, only: output_unit, error_unit

implicit none
private

public :: find_a_knights_tour
public :: notation_is_a_square

integer, parameter :: number_of_ranks = 8
integer, parameter :: number_of_files = 8
integer, parameter :: number_of_squares = number_of_ranks * number_of_files

! ‘Algebraic’ chess notation.
character, parameter :: rank_notation(1:8) = (/ '1', '2', '3', '4', '5', '6', '7', '8' /)
character, parameter :: file_notation(1:8) = (/ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' /)

type :: board_square_t
! Squares are represented by their algebraic notation.
character(2) :: algebraic_notation
contains
procedure, pass :: output => board_square_t_output
procedure, pass :: knight_moves => board_square_t_knight_moves
procedure, pass :: equal => board_square_t_equal
generic :: operator(==) => equal
end type board_square_t

type :: knight_moves_t
integer :: number_of_squares
type(board_square_t) :: squares(1:8)
end type knight_moves_t

type :: path_t
integer :: length
type(board_square_t) :: squares(1:number_of_squares)
contains
procedure, pass :: output => path_t_output
end type path_t

contains

pure function notation_is_a_square (notation) result (bool)
character(*), intent(in) :: notation
logical :: bool

integer :: length
integer :: rank_no
integer :: file_no

length = len_trim (notation)
if (length /= 2) then
bool = .false.
else
rank_no = findloc (rank_notation, notation(2:2), 1)
file_no = findloc (file_notation, notation(1:1), 1)
bool = (1 <= rank_no .and. rank_no <= number_of_ranks) &
& .and. (1 <= file_no .and. file_no <= number_of_files)
end if
end function notation_is_a_square

subroutine path_t_output (path, unit)
!
! Print a path in algebraic notation.
!
class(path_t), intent(in) :: path
integer, intent(in) :: unit

integer :: moves_counter
integer :: i

moves_counter = 1
if (1 <= path%length) then
call path%squares(1)%output(unit)
do i = 2, path%length
if (moves_counter == 8) then
write (unit, '(" ->")', advance = 'yes')
moves_counter = 1
else
write (unit, '(" -> ")', advance = 'no')
moves_counter = moves_counter + 1
end if
call path%squares(i)%output(unit)
end do
end if
write (output_unit, '()')
end subroutine path_t_output

subroutine board_square_t_output (square, unit)
!
! Print a square in algebraic notation.
!
class(board_square_t), intent(in) :: square
integer, intent(in) :: unit

write (unit, '(A2)', advance = 'no') square%algebraic_notation
end subroutine board_square_t_output

elemental function board_square_t_equal (p, q) result (bool)
class(board_square_t), intent(in) :: p, q
logical :: bool

bool = (p%algebraic_notation == q%algebraic_notation)
end function board_square_t_equal

pure function board_square_t_knight_moves (square) result (moves)
!
! Return all possible moves of a knight from a given square.
!
class(board_square_t), intent(in) :: square
type(knight_moves_t) :: moves

integer, parameter :: rank_stride(1:number_of_ranks) = (/ +1, +2, +1, +2, -1, -2, -1, -2 /)
integer, parameter :: file_stride(1:number_of_files) = (/ +2, +1, -2, -1, +2, +1, -2, -1 /)

integer :: rank_no, file_no
integer :: new_rank_no, new_file_no
integer :: i
character(2) :: notation

rank_no = findloc (rank_notation, square%algebraic_notation(2:2), 1)
file_no = findloc (file_notation, square%algebraic_notation(1:1), 1)

moves%number_of_squares = 0
do i = 1, 8
new_rank_no = rank_no + rank_stride(i)
new_file_no = file_no + file_stride(i)
if (1 <= new_rank_no &
& .and. new_rank_no <= number_of_ranks &
& .and. 1 <= new_file_no &
& .and. new_file_no <= number_of_files) then
moves%number_of_squares = moves%number_of_squares + 1
notation(2:2) = rank_notation(new_rank_no)
notation(1:1) = file_notation(new_file_no)
moves%squares(moves%number_of_squares) = board_square_t (notation)
end if
end do
end function board_square_t_knight_moves

pure function unvisited_knight_moves (path) result (moves)
!
! Return moves of a knight from a given square, but only those
! that have not been visited already.
!
class(path_t), intent(in) :: path
type(knight_moves_t) :: moves

type(knight_moves_t) :: all_moves
integer :: i

all_moves = path%squares(path%length)%knight_moves()
moves%number_of_squares = 0
do i = 1, all_moves%number_of_squares
if (all (.not. all_moves%squares(i) == path%squares(1:path%length))) then
moves%number_of_squares = moves%number_of_squares + 1
moves%squares(moves%number_of_squares) = all_moves%squares(i)
end if
end do
end function unvisited_knight_moves

pure function potential_knight_moves (path) result (moves)
!
! Return moves of a knight from a given square, but only those
! that are unvisited, and from which another unvisited move can be
! made.
!
! Sort the returned moves in nondecreasing order of the number of
! possible moves after the first. (This is how we implement
! Warnsdorff’s heuristic.)
!
class(path_t), intent(in) :: path
type(knight_moves_t) :: moves

type(knight_moves_t) :: unvisited_moves
type(knight_moves_t) :: next_moves
type(path_t) :: next_path
type(board_square_t) :: unpruned_squares(1:8)
integer :: warnsdorff_numbers(1:8)
integer :: number_of_unpruned_squares
integer :: least_warnsdorff_number
integer :: i

if (path%length == number_of_squares - 1) then
!
! There is only one square left on the board. Either the knight
! can reach it or it cannot.
!
moves = unvisited_knight_moves (path)
else
!
! Use Warnsdorff’s heuristic: return unvisited moves, but try
! first those with the least number of possible moves following
! it.
!
! If the number of possible moves following is zero, prune the
! move, because it is a dead end.
!
number_of_unpruned_squares = 0
least_warnsdorff_number = 100
unvisited_moves = unvisited_knight_moves (path)
do i = 1, unvisited_moves%number_of_squares
next_path%length = path%length + 1
next_path%squares(1:path%length) = path%squares(1:path%length)
next_path%squares(next_path%length) = unvisited_moves%squares(i)

next_moves = unvisited_knight_moves (next_path)

if (next_moves%number_of_squares /= 0) then
number_of_unpruned_squares = number_of_unpruned_squares + 1
unpruned_squares(number_of_unpruned_squares) = unvisited_moves%squares(i)
warnsdorff_numbers(number_of_unpruned_squares) = next_moves%number_of_squares
least_warnsdorff_number = &
& min (least_warnsdorff_number, next_moves%number_of_squares)
end if
end do

! In-place insertion sort of the unpruned squares.
block
type(board_square_t) :: square
integer :: w_number
integer :: i, j

i = 2
do while (i <= number_of_unpruned_squares)
square = unpruned_squares(i)
w_number = warnsdorff_numbers(i)
j = i - 1
do while (1 <= j .and. w_number < warnsdorff_numbers(j))
unpruned_squares(j + 1) = unpruned_squares(j)
warnsdorff_numbers(j + 1) = warnsdorff_numbers(j)
j = j - 1
end do
unpruned_squares(j + 1) = square
warnsdorff_numbers(j + 1) = w_number
i = i + 1
end do
end block

moves%number_of_squares = number_of_unpruned_squares
moves%squares(1:number_of_unpruned_squares) = &
& unpruned_squares(1:number_of_unpruned_squares)
end if
end function potential_knight_moves

subroutine find_a_knights_tour (starting_square)
!
! Find and print a full knight’s tour.
!
character(2), intent(in) :: starting_square

logical :: solution_found
type(path_t) :: path

path%length = 1
path%squares(1) = board_square_t (starting_square)
path = try_paths (path)
if (path%length /= 0) then
call path%output(output_unit)
else
write (error_unit, '("The program terminated without finding a solution.")')
write (error_unit, '("This is supposed to be impossible for an 8-by-8 board.")')
write (error_unit, '("The program is wrong.")')
error stop
end if

contains

recursive function try_paths (path) result (solution)
!
! Recursively try all possible paths, but using Warnsdorff’s
! heuristic to speed up the search.
!
class(path_t), intent(in) :: path
type(path_t) :: solution

type(path_t) :: new_path
type(knight_moves_t) :: moves
integer :: i

if (path%length == number_of_squares) then
solution = path
else
solution%length = 0

moves = potential_knight_moves (path)

if (moves%number_of_squares /= 0) then
new_path%length = path%length + 1
new_path%squares(1:path%length) = path%squares(1:path%length)

i = 1
do while (solution%length == 0 .and. i <= moves%number_of_squares)
new_path%squares(new_path%length) = moves%squares(i)
solution = try_paths (new_path)
i = i + 1
end do
end if
end if
end function try_paths

end subroutine find_a_knights_tour

end module knights_tour

program knights_tour_main
use, intrinsic :: iso_fortran_env, only: output_unit
use, non_intrinsic :: knights_tour
implicit none

character(200) :: arg
integer :: arg_count
integer :: i

arg_count = command_argument_count ()
do i = 1, arg_count
call get_command_argument (i, arg)
arg = adjustl (arg)
if (1 < i) write (output_unit, '()')
if (notation_is_a_square (arg)) then
call find_a_knights_tour (arg)
else
write (output_unit, '("This is not algebraic notation: ", A)') arg
end if
end do
end program knights_tour_main</lang>

$ ./knights_tour a1 b2 c3
<pre>a1 -> c2 -> a3 -> b1 -> d2 -> f1 -> h2 -> g4 ->
h6 -> g8 -> e7 -> c8 -> a7 -> b5 -> c7 -> a8 ->
b6 -> a4 -> b2 -> d1 -> f2 -> h1 -> g3 -> h5 ->
g7 -> e8 -> f6 -> h7 -> f8 -> d7 -> b8 -> a6 ->
b4 -> a2 -> c3 -> d5 -> e3 -> f5 -> h4 -> g2 ->
e1 -> f3 -> g1 -> h3 -> g5 -> e4 -> d6 -> c4 ->
a5 -> b7 -> d8 -> f7 -> h8 -> g6 -> e5 -> c6 ->
d4 -> e6 -> f4 -> e2 -> c1 -> d3 -> c5 -> b3

b2 -> a4 -> b6 -> a8 -> c7 -> e8 -> g7 -> h5 ->
g3 -> h1 -> f2 -> d1 -> c3 -> a2 -> c1 -> e2 ->
g1 -> h3 -> f4 -> g2 -> h4 -> g6 -> h8 -> f7 ->
d8 -> b7 -> a5 -> b3 -> a1 -> c2 -> e1 -> d3 ->
b4 -> a6 -> b8 -> d7 -> f8 -> h7 -> g5 -> e6 ->
c5 -> e4 -> f6 -> g8 -> h6 -> g4 -> h2 -> f1 ->
d2 -> b1 -> a3 -> c4 -> e5 -> f3 -> d4 -> b5 ->
d6 -> c8 -> a7 -> c6 -> e7 -> f5 -> e3 -> d5

c3 -> a2 -> c1 -> e2 -> g1 -> h3 -> g5 -> h7 ->
f8 -> g6 -> h8 -> f7 -> d8 -> b7 -> a5 -> b3 ->
a1 -> c2 -> a3 -> b1 -> d2 -> f1 -> h2 -> f3 ->
h4 -> g2 -> e1 -> d3 -> f4 -> h5 -> g7 -> e8 ->
f6 -> g8 -> h6 -> g4 -> e5 -> d7 -> b8 -> a6 ->
b4 -> c6 -> a7 -> c8 -> e7 -> d5 -> b6 -> a8 ->
c7 -> e6 -> c5 -> a4 -> b2 -> c4 -> e3 -> d1 ->
f2 -> h1 -> g3 -> e4 -> d6 -> f5 -> d4 -> b5</pre>


=={{header|Go}}==
=={{header|Go}}==