def calculate(start: tuple, goal: tuple, grid: utils.Grid = utils.Grid()): """ Finds path from start to goal using the Bidirectional Search algorithm. NOT FUNCTIONAL YET Starts to search from both start and goal simultaneously, performing Breadth-First search at both ends, until the two searches intersect. :param start: A tuple representing the starting point, e.g. (0, 0). :param goal: A tuple representing the goal point, e.g. (10, 10). :param grid: A Grid object representing the space where start and goal are located, optional. """ queue_start, queue_goal = deque([start]), deque([goal]) visited = [start, goal] child_parent_pairs = dict() found_path = False intersect_point = None while queue_start and queue_goal and not found_path: for i, [q, q_other] in enumerate( zip([queue_start, queue_goal], [queue_goal, queue_start])): if q: x = q.pop() if x == [goal, start][i] or x in q_other: # success found_path = True intersect_point = x print("intersect_point", intersect_point, flush=True) # break for neighbor in grid.get_point_neighbors(x): if neighbor not in visited: visited.append(neighbor) q.appendleft(neighbor) child_parent_pairs[neighbor] = x # if i == 0: # child_parent_pairs[neighbor] = x # else: # child_parent_pairs[x] = neighbor start_to_intersect = utils.calculate_path(intersect_point, start, child_parent_pairs) goal_to_intersect = utils.calculate_path(goal, intersect_point, child_parent_pairs) path = start_to_intersect.extend(reversed(goal_to_intersect[:-1])) print(path) return { "path": path, "visited": visited, "grid": grid.to_ndarray(), "start": start, "goal": goal }
def __init__(self, record=False): QtWidgets.QMainWindow.__init__(self) Ui_MainWindow.__init__(self) self.setupUi(self) self.scene = QtWidgets.QGraphicsScene() # Buttons self.runPathFinding.clicked.connect(self.run_algorithm) self.setCoordinates.clicked.connect(self.show_coordinates) self.setRandomCoordinates.clicked.connect(self.random_coordinates) self.generateMaze.clicked.connect(self.generate_maze) self.runPathFinding.setEnabled(False) self.startXValue.setPlainText("0") self.startYValue.setPlainText("0") self.goalXValue.setPlainText(str(GRIDSIZE - 1)) self.goalYValue.setPlainText(str(GRIDSIZE - 1)) # Combobox for algorithm in ALGORITHMS.keys(): self.chooseAlgorithm.addItem(algorithm) self.maze = False self.grid = utils.Grid() # GridDraw self.draw_grid(self.scene, penGrid, SIDE) # For recording UI for demo GIFs self.record = record self.frame = QImage(int(self.scene.sceneRect().width()), int(self.scene.sceneRect().height()), QImage.Format_ARGB32_Premultiplied) self.painter = QPainter(self.frame)
def main(): # the code here is just for testing, the program can just call calculate() above and skip this grid = utils.Grid(size=19, create_maze=True) res = calculate(grid=grid, start=(0, 0), goal=(18, 18)) # the following allows visualizing results in the terminal (thus only works when script is run from the terminal) utils.visualize_asciimatics(res)
def calculate(start: tuple, goal: tuple, grid: utils.Grid = utils.Grid()): """ Finds path from start to goal using Dijkstra's algorithm. Implementation of this algorithm was based on the example provided in Wikipedia: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Pseudocode :param start: A tuple representing the starting point, e.g. (0, 0). :param goal: A tuple representing the goal point, e.g. (10, 10). :param grid: A Grid object representing the space where start and goal are located, optional. """ pq = utils.PriorityQueue() distances = dict() child_parent_pairs = dict() visited = [] # add all points in grid to priority queue with infinite distances and no parents for y, x in np.ndindex(grid.to_ndarray().shape): distances[(y, x)] = math.inf child_parent_pairs[(y, x)] = "" pq.add_point((y, x), math.inf) # update starting point's distance and priority to 0 distances[start] = 0 pq.add_point(start, priority=0) while pq.has_points(): # get point with lowest priority and remove from queue current_point = pq.get_lowest_priority_point() visited.append(current_point) if current_point == goal: # goal has been reached break for neighbor in grid.get_point_neighbors(current_point): if pq.contains_point(neighbor): # neighbors always 1 step away from current alt_distance = distances[current_point] + 1 if alt_distance < distances[neighbor]: distances[neighbor] = alt_distance # update neighbor's priority with new distance pq.add_point(neighbor, alt_distance) child_parent_pairs[neighbor] = current_point path = utils.calculate_path(start, goal, child_parent_pairs) return { "path": path, "visited": visited, "grid": grid.to_ndarray(), "start": start, "goal": goal }
def main(): # grid = utils.new_grid(20) # # grid[:17, 4] = "+" # grid[1, 1:9] = "+" # grid[10, 6:18] = "+" # grid[10:, 7] = "+" # the code here is just for testing, the program can just call calculate() above and skip this grid = utils.Grid(size=3) res = calculate(grid=grid, start=(0, 0), goal=(2, 2)) # the following allows visualizing results in the terminal (thus only works when script is run from the terminal) utils.visualize_asciimatics(res)
def calculate(start: tuple, goal: tuple, grid: utils.Grid = utils.Grid()): """ Finds path from start to goal using the Depth-First Search algorithm. Works by creating a double ended queue (deque) - by always appending to the **right** of the queue, and then considering the **right-most** points in the queue first, the deque essentially works as a LIFO queue/stack. :param start: A tuple representing the starting point, e.g. (0, 0). :param goal: A tuple representing the goal point, e.g. (10, 10). :param grid: A Grid object representing the space where start and goal are located, optional. """ queue = deque([start]) visited = [] child_parent_pairs = dict() while queue: # stops when all points have been considered or when goal is reached current_point = queue.pop() # get right-most point in queue visited.append(current_point) # mark point as visited if current_point == goal: # goal has been reached # do not return from here, by returning below the case where no path is found is captured break for neighbor in grid.get_point_neighbors(current_point): if neighbor not in visited: # check if already visited this point queue.append(neighbor) # append to the right of the queue child_parent_pairs[neighbor] = current_point path = utils.calculate_path(start, goal, child_parent_pairs) return { "path": path, "visited": visited, "grid": grid.to_ndarray(), "start": start, "goal": goal }
def generate_maze(self): self.runPathFinding.setEnabled(False) window.setCoordinates.setEnabled(False) window.setRandomCoordinates.setEnabled(False) window.generateMaze.setEnabled(False) self.maze = True self.grid = utils.Grid(create_maze=True, size=GRIDSIZE, random_seed=42 if self.record else None) for y in range(GRIDSIZE): for x in range(GRIDSIZE): self.scene.addRect(x * SIDE, y * SIDE, 10, 10, penPoint, wallBrush) for i, (x, y) in enumerate(self.grid.get_maze_history()): QtTest.QTest.qWait(2) self.scene.addRect(x * SIDE, y * SIDE, 10, 10, penPoint, gridBrush) if self.record: self.render_and_save_frame(i, "maze") window.setRandomCoordinates.setEnabled(True)
def calculate(start: tuple, goal: tuple, grid: utils.Grid = utils.Grid(), heuristic: str = "manhattan"): """ Finds path from start to goal using the A* algorithm. Implementation of this algorithm was based on the example provided in Wikipedia: https://en.wikipedia.org/wiki/A*_search_algorithm#Pseudocode :param start: A tuple representing the starting point, e.g. (0, 0). :param goal: A tuple representing the goal point, e.g. (10, 10). :param grid: A Grid object representing the space where start and goal are located, optional. :param heuristic: The heuristic the algorithm will use, defaults to the Manhattan distance. Currently options "manhattan" and "euclidean" are supported. """ if heuristic == "manhattan": h_score = heuristics.manhattan_distance elif heuristic == "euclidean": h_score = heuristics.euclidean_distance else: raise NameError("Heuristic name provided not applicable/erroneous.") visited = [] child_parent_pairs = dict() g_scores = dict() g_scores[start] = 0 f_scores = dict() # the f_score is calculated by f(n) = g(n) + h(n) f_scores[start] = g_scores[start] + \ h_score(start, goal) # the g_score of start is 0 # create a priority queue, and add start to it; priority in A* corresponds to the fScore, and the points with # lowest fScores are considered first pq = utils.PriorityQueue() pq.add_point(start, f_scores[start]) while pq.has_points(): # get point with lowest priority and remove from queue current_point = pq.get_lowest_priority_point() visited.append(current_point) if current_point == goal: # goal has been reached # do not return from here, by returning below the case where no path is found is captured break for neighbor in grid.get_point_neighbors(current_point): # neighbors always 1 step away from current tentative_g_score = g_scores[current_point] + 1 if neighbor in g_scores and g_scores[neighbor] < tentative_g_score: # neighbor already reached from another point that resulted in a lower g_score, so skip it continue # path to this neighbor is better than any previous, so record it child_parent_pairs[neighbor] = current_point g_scores[neighbor] = tentative_g_score f_scores[neighbor] = tentative_g_score + h_score(neighbor, goal) pq.add_point(neighbor, f_scores[neighbor]) path = utils.calculate_path(start, goal, child_parent_pairs) return {"path": path, "visited": visited, "grid": grid.to_ndarray(), "start": start, "goal": goal}