Python/backtracking/knight_tour.py
pre-commit-ci[bot] 716bdeb68b
[pre-commit.ci] pre-commit autoupdate (#11473)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.10 → v0.5.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.10...v0.5.0)
- [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1)

* Fix ruff issues

* Fix ruff issues

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Christian Clauss <cclauss@me.com>
2024-07-02 07:02:29 +02:00

102 lines
2.4 KiB
Python

# Knight Tour Intro: https://www.youtube.com/watch?v=ab_dY3dZFHM
from __future__ import annotations
def get_valid_pos(position: tuple[int, int], n: int) -> list[tuple[int, int]]:
"""
Find all the valid positions a knight can move to from the current position.
>>> get_valid_pos((1, 3), 4)
[(2, 1), (0, 1), (3, 2)]
"""
y, x = position
positions = [
(y + 1, x + 2),
(y - 1, x + 2),
(y + 1, x - 2),
(y - 1, x - 2),
(y + 2, x + 1),
(y + 2, x - 1),
(y - 2, x + 1),
(y - 2, x - 1),
]
permissible_positions = []
for inner_position in positions:
y_test, x_test = inner_position
if 0 <= y_test < n and 0 <= x_test < n:
permissible_positions.append(inner_position)
return permissible_positions
def is_complete(board: list[list[int]]) -> bool:
"""
Check if the board (matrix) has been completely filled with non-zero values.
>>> is_complete([[1]])
True
>>> is_complete([[1, 2], [3, 0]])
False
"""
return not any(elem == 0 for row in board for elem in row)
def open_knight_tour_helper(
board: list[list[int]], pos: tuple[int, int], curr: int
) -> bool:
"""
Helper function to solve knight tour problem.
"""
if is_complete(board):
return True
for position in get_valid_pos(pos, len(board)):
y, x = position
if board[y][x] == 0:
board[y][x] = curr + 1
if open_knight_tour_helper(board, position, curr + 1):
return True
board[y][x] = 0
return False
def open_knight_tour(n: int) -> list[list[int]]:
"""
Find the solution for the knight tour problem for a board of size n. Raises
ValueError if the tour cannot be performed for the given size.
>>> open_knight_tour(1)
[[1]]
>>> open_knight_tour(2)
Traceback (most recent call last):
...
ValueError: Open Knight Tour cannot be performed on a board of size 2
"""
board = [[0 for i in range(n)] for j in range(n)]
for i in range(n):
for j in range(n):
board[i][j] = 1
if open_knight_tour_helper(board, (i, j), 1):
return board
board[i][j] = 0
msg = f"Open Knight Tour cannot be performed on a board of size {n}"
raise ValueError(msg)
if __name__ == "__main__":
import doctest
doctest.testmod()