Skip to content

Commit b728110

Browse files
committed
ref: update KnightTour implementation
- The `MOVES` list is declared as a class attribute since it does not change and is shared among all instances. - The `board_size` is passed to the constructor, and `board` is initialized as an instance attribute. - The `get_valid_moves` method now uses instance variables instead of relying on class variables. - `solve_knight_tour` and `find_knight_tour` methods now directly access `self.board_size` and `self.board`.
1 parent f9070f2 commit b728110

File tree

1 file changed

+54
-60
lines changed

1 file changed

+54
-60
lines changed

backtracking/knight_tour.py

+54-60
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,86 @@
11
from __future__ import annotations
22

3+
from typing import ClassVar
34

4-
class KnightTour:
5+
6+
class KnightTourSolver:
57
"""
6-
Represents a Knight's Tour problem solver.
8+
Represents a solver for the Knight's Tour problem.
79
"""
810

9-
@staticmethod
10-
def get_valid_moves(position: tuple[int, int], n: int) -> list[tuple[int, int]]:
11+
MOVES: ClassVar[list[tuple[int, int]]] = [
12+
(1, 2),
13+
(-1, 2),
14+
(1, -2),
15+
(-1, -2),
16+
(2, 1),
17+
(-2, 1),
18+
(2, -1),
19+
(-2, -1),
20+
]
21+
22+
def __init__(self, board_size: int):
23+
"""
24+
Initializes the KnightTourSolver with the specified board size.
25+
"""
26+
self.board_size = board_size
27+
self.board = [[0 for _ in range(board_size)] for _ in range(board_size)]
28+
29+
def get_valid_moves(self, position: tuple[int, int]) -> list[tuple[int, int]]:
1130
"""
1231
Find all the valid positions a knight can move to from the current position.
1332
14-
>>> KnightTour.get_valid_moves((1, 3), 4)
33+
>>> solver = KnightTourSolver(4)
34+
>>> solver.get_valid_moves((1, 3))
1535
[(2, 1), (0, 1), (3, 2)]
1636
"""
1737
y, x = position
18-
moves = [
19-
(y + 1, x + 2),
20-
(y - 1, x + 2),
21-
(y + 1, x - 2),
22-
(y - 1, x - 2),
23-
(y + 2, x + 1),
24-
(y + 2, x - 1),
25-
(y - 2, x + 1),
26-
(y - 2, x - 1),
38+
return [
39+
(y + dy, x + dx)
40+
for dy, dx in self.MOVES
41+
if 0 <= y + dy < self.board_size and 0 <= x + dx < self.board_size
2742
]
28-
valid_moves = [(y, x) for y, x in moves if 0 <= y < n and 0 <= x < n]
29-
return valid_moves
3043

31-
@staticmethod
32-
def is_board_complete(board: list[list[int]]) -> bool:
44+
def solve_knight_tour(self, position: tuple[int, int], move_number: int) -> bool:
3345
"""
34-
Check if the board (matrix) has been completely filled with non-zero values.
35-
36-
>>> KnightTour.is_board_complete([[1]])
37-
True
38-
>>> KnightTour.is_board_complete([[1, 2], [3, 0]])
39-
False
46+
Helper function to solve the Knight's Tour problem recursively.
4047
"""
41-
return not any(elem == 0 for row in board for elem in row)
42-
43-
@staticmethod
44-
def solve_knight_tour(
45-
board: list[list[int]], pos: tuple[int, int], curr: int
46-
) -> bool:
47-
"""
48-
Helper function to solve knight tour problem.
49-
"""
50-
51-
if KnightTour.is_board_complete(board):
48+
if move_number == self.board_size * self.board_size + 1:
5249
return True
5350

54-
n = len(board)
55-
for move in KnightTour.get_valid_moves(pos, n):
56-
y, x = move
57-
if board[y][x] == 0:
58-
board[y][x] = curr + 1
59-
if KnightTour.solve_knight_tour(board, move, curr + 1):
51+
for next_position in self.get_valid_moves(position):
52+
ny, nx = next_position
53+
if self.board[ny][nx] == 0:
54+
self.board[ny][nx] = move_number
55+
if self.solve_knight_tour(next_position, move_number + 1):
6056
return True
61-
board[y][x] = 0
57+
self.board[ny][nx] = 0
6258

6359
return False
6460

65-
@staticmethod
66-
def find_knight_tour(n: int) -> list[list[int]]:
61+
def find_knight_tour(self) -> list[list[int]]:
6762
"""
68-
Find the solution for the knight tour problem for a board of size n. Raises
69-
ValueError if the tour cannot be performed for the given size.
63+
Find a solution for the Knight's Tour problem for the initialized board size.
64+
Raises ValueError if the tour cannot be performed for the given size.
7065
71-
>>> KnightTour.find_knight_tour(1)
66+
>>> solver = KnightTourSolver(1)
67+
>>> solver.find_knight_tour()
7268
[[1]]
73-
>>> KnightTour.find_knight_tour(2)
69+
>>> solver = KnightTourSolver(2)
70+
>>> solver.find_knight_tour()
7471
Traceback (most recent call last):
7572
...
7673
ValueError: Knight's tour cannot be performed on a board of size 2
7774
"""
78-
if n < 1:
79-
raise ValueError("Board size must be at least 1")
80-
81-
board = [[0 for _ in range(n)] for _ in range(n)]
82-
83-
for i in range(n):
84-
for j in range(n):
85-
board[i][j] = 1
86-
if KnightTour.solve_knight_tour(board, (i, j), 1):
87-
return board
88-
board[i][j] = 0
89-
error_message = f"Knight's tour cannot be performed on a board of size {n}"
75+
for i in range(self.board_size):
76+
for j in range(self.board_size):
77+
self.board[i][j] = 1
78+
if self.solve_knight_tour((i, j), 2):
79+
return self.board
80+
self.board[i][j] = 0
81+
error_message = (
82+
f"Knight's tour cannot be performed on a board of size {self.board_size}"
83+
)
9084
raise ValueError(error_message)
9185

9286

0 commit comments

Comments
 (0)