From 2671d317ff4104ec44b62e79db3700d3e38c4a9f Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Tue, 30 Aug 2022 00:21:11 -0400 Subject: [PATCH] Added Puzzle.py --- Puzzle.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 Puzzle.py diff --git a/Puzzle.py b/Puzzle.py new file mode 100644 index 0000000..8a2bcdd --- /dev/null +++ b/Puzzle.py @@ -0,0 +1,140 @@ +from typing import Deque + + +def solve_puzzle(Board: list, Source: tuple, Destination: tuple): + """Finds a solution to a 2-D puzzle using a breadth first search + + Parameters + ---------- + Board : list + A graph represented as an adjacency matrix + Source : tuple + The starting point on the board + Destination : tuple + The goal position + """ + + queue = Deque() + queue.append(Source) + visited = {} + + if is_valid(Source[0], Source[1], Board, visited): + visited[Source] = 0 + if Source == Destination: + return [Destination] + else: + return None + + while len(queue) > 0: + r, c = queue.popleft() + possible_moves = [(1, 0), (0, 1), (-1, 0), (0, -1)] + for move_row, move_col in possible_moves: + row, col = r + move_row, c + move_col + if is_valid(row, col, Board, visited): + queue.append((row, col)) + visited[(row, col)] = visited[(r, c)] + 1 + if (row, col) == Destination: + return get_path(row, col, visited) + + return None + + +def is_valid(x: int, y: int, Board: list, visited: dict) -> bool: + """Helper for solve_puzzle() to determine if a move is valid + + Parameters + ---------- + x : int + Row + y : int + Column + Board : list + A graph represented as an adjacency matrix + visited : dict + Coordinates that have been visited + + Returns + ------- + bool + True if the move is valid, otherwise False + """ + + exists = False + valid = False + + if (x >= 0 and y >= 0) and (x < len(Board) and y < len(Board[0])): + exists = True + + target = (x, y) + if exists: + if Board[x][y] != "#" and target not in visited: + valid = True + + return valid and exists + + +def get_path(row: int, col: int, visited: dict) -> tuple: + """Helper for solve_puzzle() to assemble the steps and moves into lists + + Parameters + ---------- + row : int + Row on the board + col : int + Column on the board + visited : dict + Coordinates that have been visited + + Returns + ------- + tuple + List of tuples containing coordinates traversed, and list of directions + """ + + steps = visited[(row, col)] + steps -= 1 + path = [(row, col)] + directions = [] + + while steps >= 0: + if (row - 1, col) in visited and visited[(row - 1, col)] == steps: + path.append((row - 1, col)) + directions.append("D") + row -= 1 + if (row + 1, col) in visited and visited[(row + 1, col)] == steps: + path.append((row + 1, col)) + directions.append("U") + row += 1 + if (row, col - 1) in visited and visited[(row, col - 1)] == steps: + path.append((row, col - 1)) + directions.append("R") + col -= 1 + if (row, col + 1) in visited and visited[(row, col + 1)] == steps: + path.append((row, col + 1)) + directions.append("L") + col += 1 + steps -= 1 + + path.reverse() + directions.reverse() + return (path, directions) + + +# ------------------------------ Basic Testing --------------------------------# + +if __name__ == "__main__": + + puzzle = [ + ["-", "-", "-", "-", "-"], + ["-", "-", "#", "-", "-"], + ["-", "-", "-", "-", "-"], + ["#", "-", "#", "#", "-"], + ["-", "#", "-", "-", "-"], + ] + + source = (0, 2) + destination = (2, 2) + # source = (0, 0) + # destination = (4, 4) + result = solve_puzzle(puzzle, source, destination) + print(result)