From efd024d72a2c67d4cd86e2a033dbc56b786c9a62 Mon Sep 17 00:00:00 2001 From: Tapajyoti Bose Date: Thu, 18 Jun 2020 10:54:26 +0530 Subject: [PATCH 1/2] Added (Open) Knight Tour Algorithm --- backtracking/knight_tour.py | 98 +++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 backtracking/knight_tour.py diff --git a/backtracking/knight_tour.py b/backtracking/knight_tour.py new file mode 100644 index 000000000000..1d2c81e8a5e3 --- /dev/null +++ b/backtracking/knight_tour.py @@ -0,0 +1,98 @@ +# Knight Tour Intro: https://www.youtube.com/watch?v=ab_dY3dZFHM + +from typing import List, Tuple + + +def get_valid_pos(position: Tuple[int], n: int) -> List[Tuple[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 position in positions: + y_test, x_test = position + if (y_test < n and y_test >= 0) and (x_test < n and x_test >= 0): + permissible_positions.append(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 + ''' + + for row in board: + for elem in row: + if (elem == 0): + return False + return True + + +def open_knight_tour_helper(board: List[List[int]], pos: Tuple[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 + + >>> open_knight_tour(1) + [[1]] + + >>> open_knight_tour(2) # None returned + ''' + + 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 + + return None + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From dd874b38ee8434341d5d240121f925f53642f1c4 Mon Sep 17 00:00:00 2001 From: Tapajyoti Bose Date: Thu, 18 Jun 2020 14:11:28 +0530 Subject: [PATCH 2/2] Implemented Suggestions --- backtracking/knight_tour.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/backtracking/knight_tour.py b/backtracking/knight_tour.py index 1d2c81e8a5e3..7d1e03b837e9 100644 --- a/backtracking/knight_tour.py +++ b/backtracking/knight_tour.py @@ -5,7 +5,7 @@ def get_valid_pos(position: Tuple[int], n: int) -> List[Tuple[int]]: ''' - Find all the valid positions a knight can move to from the current position + 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)] @@ -26,7 +26,7 @@ def get_valid_pos(position: Tuple[int], n: int) -> List[Tuple[int]]: for position in positions: y_test, x_test = position - if (y_test < n and y_test >= 0) and (x_test < n and x_test >= 0): + if 0 <= y_test < n and 0 <= x_test < n: permissible_positions.append(position) return permissible_positions @@ -34,7 +34,7 @@ def get_valid_pos(position: Tuple[int], n: int) -> List[Tuple[int]]: def is_complete(board: List[List[int]]) -> bool: ''' - Check if the board (matrix) has been completely filled with non-zero values + Check if the board (matrix) has been completely filled with non-zero values. >>> is_complete([[1]]) True @@ -43,16 +43,12 @@ def is_complete(board: List[List[int]]) -> bool: False ''' - for row in board: - for elem in row: - if (elem == 0): - return False - return True + 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], curr: int) -> bool: ''' - Helper function to solve knight tour problem + Helper function to solve knight tour problem. ''' if is_complete(board): @@ -72,12 +68,16 @@ def open_knight_tour_helper(board: List[List[int]], pos: Tuple[int], curr: int) def open_knight_tour(n: int) -> List[List[int]]: ''' - Find the solution for the knight tour problem for a board of size n + 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) # None returned + >>> open_knight_tour(2) + Traceback (most recent call last): + ... + ValueError: Open Kight Tour cannot be performed on a board of size 2 ''' board = [[0 for i in range(n)] for j in range(n)] @@ -89,7 +89,7 @@ def open_knight_tour(n: int) -> List[List[int]]: return board board[i][j] = 0 - return None + raise ValueError(f"Open Kight Tour cannot be performed on a board of size {n}") if __name__ == "__main__":