def astar_search(grid_size, start, goal, obstacles, costFn, logger): """ A* search algorithm finds the optimal path from the start cell to the goal cell in the 2D grid world. After expanding a node, to retrieve the cost of a child node at location (x,y), please call costFn((x,y)). In all of the grid maps, the cost is always 1. See depth_first_search() for details. """ n_rows, n_cols = grid_size start_row, start_col = start goal_row, goal_col = goal ########################################## # Choose a proper container yourself from # OrderedSet, Stack, Queue, PriorityQueue # for the open set and closed set. open_set = PriorityQueue(order="min", f=lambda v: abs(v)) closed_set = Queue() ########################################## ########################################## # Set up visualization logger hook # Please do not modify these four lines closed_set.logger = logger logger.closed_set = closed_set open_set.logger = logger logger.open_set = open_set ########################################## movement = [] # ---------------------------------------- # finish the code below to implement a Manhattan distance heuristic # ---------------------------------------- ############################################################################# def heuristic(row, col): R = goal[0] - row C = goal[1] - col hyp = R * R + C * C return abs(math.sqrt(hyp)) def Gvalue(row, col): return abs(goal[0] - row + goal[1] - col) def backtrace(parent, start, goal): path = [goal] while path[-1] != start: path.append(parent[path[-1]]) path.reverse() iter = len(path) i = 0 while i < iter - 1: R = path[i + 1][0] - path[i][0] C = path[i + 1][1] - path[i][1] if (R == -1 and C == 0): movement.append(ACTIONS[0]) if (R == 0 and C == -1): movement.append(ACTIONS[1]) if (R == 1 and C == 0): movement.append(ACTIONS[2]) if (R == 0 and C == 1): movement.append(ACTIONS[3]) i += 1 return movement, closed_set def TransitionModel(node, value): current_row = node[0] current_col = node[1] direction_can_go = [] #down TempRow = current_row TempCol = current_col + 1 TempNode = (TempRow, TempCol) # print("TempNode ::",TempNode) if TempNode not in obstacles and TempRow >= 0 and TempCol >= 0 and TempRow <= n_rows and TempCol <= n_cols: returnnode = [ TempNode, Gvalue(TempRow, TempCol) + heuristic(TempRow, TempCol) ] direction_can_go.append(returnnode) #right TempRow = current_row + 1 TempCol = current_col TempNode = (TempRow, TempCol) if TempNode not in obstacles and TempRow >= 0 and TempCol >= 0 and TempRow <= n_rows and TempCol <= n_cols: returnnode = [ TempNode, Gvalue(TempRow, TempCol) + heuristic(TempRow, TempCol) ] direction_can_go.append(returnnode) #left TempRow = current_row - 1 TempCol = current_col TempNode = (TempRow, TempCol) if TempNode not in obstacles and TempRow >= 0 and TempCol >= 0 and TempRow <= n_rows and TempCol <= n_cols: returnnode = [ TempNode, Gvalue(TempRow, TempCol) + heuristic(TempRow, TempCol) ] direction_can_go.append(returnnode) #up TempRow = current_row TempCol = current_col - 1 TempNode = (TempRow, TempCol) if TempNode not in obstacles and TempRow >= 0 and TempCol >= 0 and TempRow <= n_rows and TempCol <= n_cols: returnnode = [ TempNode, Gvalue(TempRow, TempCol) + heuristic(TempRow, TempCol) ] direction_can_go.append(returnnode) # print("direction_can_go ::",direction_can_go) #print("node ::",node) return direction_can_go open_set.put(start, 0) parent = {} while len(open_set): node, value = open_set.pop() if node == goal: print("found it") movement, closed_set = backtrace(parent, start, goal) print("movement ::", movement) return movement, closed_set #costFn(node) closed_set.add(node) actions_posible = TransitionModel(node, value) for nextnode in actions_posible: #print("nextnode ::" ,nextnode[0]) if nextnode[0] not in closed_set and nextnode[0] not in open_set: open_set.put(nextnode[0], nextnode[1]) parent[nextnode[0]] = node elif nextnode[0] in open_set and nextnode[1] < open_set.get( nextnode[0]): open_set.put(nextnode[0], nextnode[1]) parent[nextnode[0]] = node ############################################################################# return movement, closed_set
def depth_first_search(grid_size, start, goal, obstacles, costFn, logger): """ DFS algorithm finds the path from the start cell to the goal cell in the 2D grid world. After expanding a node, to retrieve the cost of a child node at location (x,y), please call costFn((x,y)). Parameters ---------- grid_size: tuple, (n_rows, n_cols) (number of rows of the grid, number of cols of the grid) start: tuple, (row, col) location of the start cell; row and col are counted from 0, i.e. the 1st row is 0 goal: tuple, (row, col) location of the goal cell obstacles: tuple, ((row, col), (row, col), ...) locations of obstacles in the grid the cells where obstacles are located are not allowed to access costFn: a function that returns the cost of landing to a cell (x,y) after taking an action. logger: a logger to visualize the search process. Do not do anything to it. Returns ------- movement along the path from the start to goal cell: list of actions The first returned value is the movement list found by the search algorithm from the start cell to the end cell. The movement list should be a list object composed of actions that should move the agent from the start to goal cell along the path as found by the algorithm. For example, if nodes in the path from the start to end cell are: (0, 0) (start) -> (0, 1) -> (1, 1) -> (1, 0) (goal) then, the returned movement list should be [(0,1), (1,0), (0, -1)] which means: move right, down, left. Return an EMPTY list if the search algorithm fails finding any available path. closed set: list of location tuple (row, col) The second returned value is the closed set, namely, the cells are expanded during search. """ n_rows, n_cols = grid_size start_row, start_col = start goal_row, goal_col = goal ########################################## # Choose a proper container yourself from # OrderedSet, Stack, Queue, PriorityQueue # for the open set and closed set. open_set = Stack() closed_set = Queue() ########################################## ########################################## # Set up visualization logger hook # Please do not modify these four lines closed_set.logger = logger logger.closed_set = closed_set open_set.logger = logger logger.open_set = open_set ########################################## parent = [ # the immediate predecessor cell from which to reach a cell [None for __ in range(n_cols)] for _ in range(n_rows) ] actions = [ # the action that the parent took to reach the cell [None for __ in range(n_cols)] for _ in range(n_rows) ] movement = [] path_storage = {} # ---------------------------------------- # finish the code below # ---------------------------------------- ############################################################################# open_set.add(start) i = 0 while open_set: child = [] node_current = open_set.pop() closed_set.add(node_current) current_row, current_col = node_current child = [ (current_row - 1, current_col), (current_row, current_col + 1), (current_row + 1, current_col), (current_row, current_col - 1) ] for child_node in child: child_row, child_col = child_node if child_node not in obstacles and child_row >= 0 and child_col >= 0 and child_row < n_rows and child_col < n_cols: print('Child node is', child_node) if child_node not in closed_set and child_node not in open_set: if child_node == goal: path_storage[child_node] = node_current path_list = [goal] print('The goal is', goal) while path_list[-1] != start: print('The parent node is', path_storage[path_list[-1]]) path_list.append(path_storage[path_list[-1]]) path_list.reverse() iter = 0 for iter in range(len(path_list) - 1): r, c = path_list[iter] r2, c2 = path_list[iter + 1] row = r2 - r col = c2 - c if (row == -1 and col == 0): movement.append((-1, 0)) if (row == 0 and col == -1): movement.append((0, -1)) if (row == 1 and col == 0): movement.append((1, 0)) if (row == 0 and col == 1): movement.append((0, 1)) iter += 1 return movement, closed_set else: open_set.add(child_node) path_storage[child_node] = node_current i += 1 ############################################################################# return movement, closed_set
def astar_search(grid_size, start, goal, obstacles, costFn, logger): """ A* search algorithm finds the optimal path from the start cell to the goal cell in the 2D grid world. After expanding a node, to retrieve the cost of a child node at location (x,y), please call costFn((x,y)). See depth_first_search() for details. """ n_rows, n_cols = grid_size start_row, start_col = start goal_row, goal_col = goal ########################################## # Choose a proper container yourself from # OrderedSet, Stack, Queue, PriorityQueue # for the open set and closed set. open_set = PriorityQueue(order="min", f=lambda v: v) closed_set = Queue() ########################################## ########################################## # Set up visualization logger hook # Please do not modify these four lines closed_set.logger = logger logger.closed_set = closed_set open_set.logger = logger logger.open_set = open_set ########################################## parent = [ # the immediate predecessor cell from which to reach a cell [None for __ in range(n_cols)] for _ in range(n_rows) ] actions = [ # the action that the parent took to reach the cell [None for __ in range(n_cols)] for _ in range(n_rows) ] movement = [] path_storage = {} # ---------------------------------------- # finish the code below to implement a Manhattan distance heuristic # ---------------------------------------- def heuristic(row, col): ############################################################################# return abs(goal_row - row) + abs(goal_col - col) ############################################################################# open_set.put(start, 0) i = 0 while open_set: child = [] node_current, v = open_set.pop() closed_set.add(node_current) current_row, current_col = node_current h = heuristic(current_row, current_col) cost_child = v - h child = [((current_row + 1, current_col), cost_child + costFn( (current_row + 1, current_col)) + heuristic(current_row + 1, current_col)), ((current_row, current_col - 1), cost_child + costFn( (current_row, current_col - 1)) + heuristic(current_row, current_col - 1)), ((current_row - 1, current_col), cost_child + costFn( (current_row - 1, current_col)) + heuristic(current_row - 1, current_col)), ((current_row, current_col + 1), cost_child + costFn( (current_row, current_col + 1)) + heuristic(current_row, current_col + 1))] for node in child: child_node, cost = node child_row, child_col = child_node if child_node not in obstacles and child_row >= 0 and child_col >= 0 and child_row < n_rows and child_col < n_cols: if child_node not in closed_set and child_node not in open_set: print('Child node is', node) if child_node == goal: path_storage[child_node] = node_current path_list = [goal] print('The goal is', goal) while path_list[-1] != start: print('The parent node is', path_storage[path_list[-1]]) path_list.append(path_storage[path_list[-1]]) path_list.reverse() iter = 0 for iter in range(len(path_list) - 1): r, c = path_list[iter] r2, c2 = path_list[iter + 1] row = r2 - r col = c2 - c if (row == -1 and col == 0): movement.append((-1, 0)) if (row == 0 and col == -1): movement.append((0, -1)) if (row == 1 and col == 0): movement.append((1, 0)) if (row == 0 and col == 1): movement.append((0, 1)) iter += 1 return movement, closed_set else: open_set.put(child_node, cost) path_storage[child_node] = node_current return movement, closed_set
def astar_search(grid_size, start, goal, obstacles, costFn, logger): """ A* search algorithm finds the optimal path from the start cell to the goal cell in the 2D grid world. After expanding a node, to retrieve the cost of a child node at location (x,y), please call costFn((x,y)). See depth_first_search() for details. """ n_rows, n_cols = grid_size start_row, start_col = start goal_row, goal_col = goal ########################################## # Choose a proper container yourself from # OrderedSet, Stack, Queue, PriorityQueue # for the open set and closed set. open_set = PriorityQueue(order="min", f=lambda v: v) closed_set = Queue() ########################################## ########################################## # Set up visualization logger hook # Please do not modify these four lines closed_set.logger = logger logger.closed_set = closed_set open_set.logger = logger logger.open_set = open_set ########################################## parent = [ # the immediate predecessor cell from which to reach a cell [None for __ in range(n_cols)] for _ in range(n_rows) ] actions = [ # the action that the parent took to reach the cell [None for __ in range(n_cols)] for _ in range(n_rows) ] movement = [] # ---------------------------------------- # finish the code below to implement a Manhattan distance heuristic # ---------------------------------------- def heuristic(row, col): ############################################################################# h = abs(goal_row - row) + abs(goal_col - col) return h ############################################################################# ''' Based on the pseudo code from the slides ''' open_set.put(start, 0 + heuristic(start_row, start_col)) while open_set: children = [] currentNode, v = open_set.pop() if currentNode == goal: # reach the goal start backtrace closed_set.add( currentNode ) # for the same outcome with the demo video provided p_row, p_col = goal while (p_row, p_col) != start: movement.append(actions[p_row][p_col]) p_row, p_col = parent[p_row][p_col] movement.reverse() return movement, closed_set currentNode_row, currentNode_col = currentNode closed_set.add(currentNode) currentNodeHeuristic = heuristic(currentNode_row, currentNode_col) # get the g cost for compute the children's total costs g_cost = v - currentNodeHeuristic # get the children children = [((currentNode_row - 1, currentNode_col), g_cost + costFn( (currentNode_row - 1, currentNode_col)) + heuristic(currentNode_row - 1, currentNode_col)), ((currentNode_row, currentNode_col - 1), g_cost + costFn( (currentNode_row, currentNode_col - 1)) + heuristic(currentNode_row, currentNode_col - 1)), ((currentNode_row + 1, currentNode_col), g_cost + costFn( (currentNode_row + 1, currentNode_col)) + heuristic(currentNode_row + 1, currentNode_col)), ((currentNode_row, currentNode_col + 1), g_cost + costFn( (currentNode_row, currentNode_col + 1)) + heuristic(currentNode_row, currentNode_col + 1))] # expand the children for child in children: node, cost = child child_row, child_col = node if ( child_row, child_col ) not in obstacles and child_row >= 0 and child_col >= 0 and child_row < n_rows and child_col < n_cols: if node not in closed_set and node not in open_set: open_set.put(node, cost) parent[child_row][child_col] = currentNode action = (child_row - currentNode_row, child_col - currentNode_col) if action == (-1, 0): actions[child_row][child_col] = ACTIONS[0] if action == (0, -1): actions[child_row][child_col] = ACTIONS[1] if action == (1, 0): actions[child_row][child_col] = ACTIONS[2] if action == (0, 1): actions[child_row][child_col] = ACTIONS[3] # if need to update with a lower cost elif child in open_set and open_set.get( (child_row, child_col)) > cost: open_set.put(node, cost) parent[child_row][child_col] = currentNode action = (child_row - currentNode_row, child_col - currentNode_col) if action == (-1, 0): actions[child_row][child_col] = ACTIONS[0] if action == (0, -1): actions[child_row][child_col] = ACTIONS[1] if action == (1, 0): actions[child_row][child_col] = ACTIONS[2] if action == (0, 1): actions[child_row][child_col] = ACTIONS[3]
def uniform_cost_search(grid_size, start, goal, obstacles, costFn, logger): n_rows, n_cols = grid_size start_row, start_col = start goal_row, goal_col = goal # Choose a proper container yourself from # OrderedSet, Stack, Queue, PriorityQueue # for the open set and closed set. open_set = PriorityQueue(order="min", f=lambda v: v) closed_set = Queue() # Set up visualization logger hook # Please do not modify these four lines closed_set.logger = logger logger.closed_set = closed_set open_set.logger = logger logger.open_set = open_set parents = [ # the immediate predecessor cell from which to reach a cell [None for __ in range(n_cols)] for _ in range(n_rows) ] actions = [ # the action that the parent took to reach the cell [None for __ in range(n_cols)] for _ in range(n_rows) ] movement = [] # Variables initialization open_set.put(start, 0) child_rows = 0 child_cols = 0 g = 0 # Path cost # Define UCS while (open_set != []): # Take out (x,y)(parent) and path v(cost) from openset temp_parent = open_set.pop() parent, v = temp_parent # Determine whether the agent reach to goal if parent == goal: # Search path for j in range(n_cols * n_rows): parent_rows, parent_cols = parent # Stop and return if parent == start: print(movement) return movement, closed_set # record action and put it in to movement() act = actions[parent_rows][parent_cols] movement.insert(0, act) # Update parent parent = parents[parent_rows][parent_cols] # Add parent to closedset closed_set.add(parent) # Search each direction of the parent for i in range(4): # Moving direction action_rows = action[i][0] action_cols = action[i][1] # Calculate child position parent_rows, parent_cols = parent child_rows, child_cols = parent_rows + action_rows, parent_cols + action_cols child = child_rows, child_cols # Update g(path cost) g = v + costFn((parent_rows, parent_cols)) # Restrict the node in the map if (child not in obstacles) and (child_rows < n_rows) and ( child_cols < n_cols) and (child_rows >= 0) and (child_cols >= 0): if (child not in open_set) and (child not in closed_set): # Add into openset open_set.put(child, g) # Record parent and action for each step parents[child_rows][child_cols] = parent actions[child_rows][child_cols] = tuple(action[i]) # Update path cost if agent already in open list elif (child in open_set) and (g < open_set.get(child)): # Add into openset open_set.put(child, g) # Record parent and action for each step parents[child_rows][child_cols] = parent actions[child_rows][child_cols] = tuple(action[i])
def depth_first_search(grid_size, start, goal, obstacles, costFn, logger): n_rows, n_cols = grid_size start_row, start_col = start goal_row, goal_col = goal # Choose a proper container yourself from # OrderedSet, Stack, Queue, PriorityQueue # for the open set and closed set. open_set = Stack() # Use Stack for openset closed_set = Queue() # Set up visualization logger hook # Please do not modify these four lines closed_set.logger = logger logger.closed_set = closed_set open_set.logger = logger logger.open_set = open_set parents = [ # the immediate predecessor cell from which to reach a cell [None for __ in range(n_cols)] for _ in range(n_rows) ] actions = [ # the action that the parent took to reach the cell [None for __ in range(n_cols)] for _ in range(n_rows) ] movement = [] # Define DFS open_set.add(start) while (open_set != []): parent = open_set.pop() closed_set.add(parent) # Search each direction of the parent for i in range(4): # Moving direction action_rows = action[i][0] action_cols = action[i][1] # Calculate child position parent_rows, parent_cols = parent child_rows, child_cols = parent_rows + action_rows, parent_cols + action_cols child = child_rows, child_cols # Restrict the node in the map if (child not in obstacles) and (child_rows < n_rows) and ( child_cols < n_cols) and (child_rows >= 0) and (child_cols >= 0): if (child not in open_set) and (child not in closed_set): # Record parent and action for each step parents[child_rows][child_cols] = parent actions[child_rows][child_cols] = tuple(action[i]) if child == goal: # Find Path for j in range(n_cols * n_rows): # find action for each step act = actions[child_rows][child_cols] # Save action in movement[] movement.insert(0, act) # find parent for each step parent = parents[child_rows][child_cols] child_rows, child_cols = parent # Stop and return results if parent == start: return movement, closed_set else: open_set.add(child)
def astar_search(grid_size, start, goal, obstacles, costFn, logger): """ A* search algorithm finds the optimal path from the start cell to the goal cell in the 2D grid world. After expanding a node, to retrieve the cost of a child node at location (x,y), please call costFn((x,y)). In all of the grid maps, the cost is always 1. See depth_first_search() for details. """ n_rows, n_cols = grid_size start_row, start_col = start goal_row, goal_col = goal ########################################## # Choose a proper container yourself from # OrderedSet, Stack, Queue, PriorityQueue # for the open set and closed set. open_set = PriorityQueue(order="min", f=lambda v: v) #closed_set = PriorityQueue(order="min", f=lambda v: abs(v)) closed_set = Queue() ########################################## ########################################## # Set up visualization logger hook # Please do not modify these four lines closed_set.logger = logger logger.closed_set = closed_set open_set.logger = logger logger.open_set = open_set ########################################## movement = [] #the path below is the dictionary that maintains key value pairs of parent and child pathTrace = {} path = {} # ---------------------------------------- # finish the code below to implement a Manhattan distance heuristic # ---------------------------------------- def heuristic(row, col): ############################################################################# #pass return abs(goal_row - row) + abs(goal_col - col) ############################################################################# def Backtrace(): ############################################################################# path = [goal] print('goal is', goal) while path[-1] != start: #if pathTrace[path[-1]] in closed_set and pathTrace[path[-1]] not in path: print('parent is', pathTrace[path[-1]]) path.append(pathTrace[path[-1]]) path.reverse() print(path) num = 0 while num < len(path) - 1: row, col = path[num] parentRow, parentCol = path[num + 1] dRow = parentRow - row dCol = parentCol - col if (dRow == -1 and dCol == 0): movement.append((-1, 0)) if (dRow == 0 and dCol == -1): movement.append((0, -1)) if (dRow == 1 and dCol == 0): movement.append((1, 0)) if (dRow == 0 and dCol == 1): movement.append((0, 1)) num += 1 return movement ############################################################################# open_set.put(start, 0) i = 0 while open_set: children = [] currentNode, v = open_set.pop() if currentNode == goal: movement = Backtrace() return movement, closed_set currentNode_row, currentNode_col = currentNode closed_set.add(currentNode) currentNodeHeuristic = heuristic(currentNode_row, currentNode_col) costG = v - currentNodeHeuristic children = [((currentNode_row - 1, currentNode_col), costG + costFn( (currentNode_row - 1, currentNode_col)) + heuristic(currentNode_row - 1, currentNode_col)), ((currentNode_row + 1, currentNode_col), costG + costFn( (currentNode_row + 1, currentNode_col)) + heuristic(currentNode_row + 1, currentNode_col)), ((currentNode_row, currentNode_col - 1), costG + costFn( (currentNode_row, currentNode_col - 1)) + heuristic(currentNode_row, currentNode_col - 1)), ((currentNode_row, currentNode_col + 1), costG + costFn( (currentNode_row, currentNode_col + 1)) + heuristic(currentNode_row, currentNode_col + 1))] for childNode in children: node, cost = childNode node_row, node_col = node if ( node_row, node_col ) not in obstacles and node_row >= 0 and node_col >= 0 and node_row < n_rows and node_col < n_cols: if node not in closed_set and node not in open_set: open_set.put(node, cost) pathTrace[node] = currentNode print('Path Trace loop 1', pathTrace[node]) elif childNode in open_set and open_set.get( (node_row, node_col)) > cost: open_set.put(node, cost) pathTrace[node] = currentNode print('Path Trace loop 2', pathTrace[node_row, node_col])
def depth_first_search(grid_size, start, goal, obstacles, costFn, logger): """ DFS algorithm finds the path from the start cell to the goal cell in the 2D grid world. Parameters ---------- grid_size: tuple, (n_rows, n_cols) (number of rows of the grid, number of cols of the grid) start: tuple, (row, col) location of the start cell; row and col are counted from 0, i.e. the 1st row is 0 goal: tuple, (row, col) location of the goal cell obstacles: tuple, ((row, col), (row, col), ...) locations of obstacles in the grid the cells where obstacles are located are not allowed to access costFn: a function that returns the cost of landing to a cell (x,y) after taking an action. The default cost is 1, regardless of the action and the landing cell, i.e. every time the agent moves NSEW it costs one unit. logger: a logger to visualize the search process. Do not do anything to it. Returns ------- movement along the path from the start to goal cell: list of actions The first returned value is the movement list found by the search algorithm from the start cell to the end cell. The movement list should be a list object who is composed of actions that should made moving from the start to goal cell along the path found the algorithm. For example, if nodes in the path from the start to end cell are: (0, 0) (start) -> (0, 1) -> (1, 1) -> (1, 0) (goal) then, the returned movement list should be [(0,1), (1,0), (0, -1)] which means: move right, down, left. Return an EMPTY list if the search algorithm fails finding any available path. closed set: list of location tuple (row, col) The second returned value is the closed set, namely, the cells are expanded during search. """ n_rows, n_cols = grid_size start_row, start_col = start goal_row, goal_col = goal ########################################## # Choose a proper container yourself from # OrderedSet, Stack, Queue, PriorityQueue # for the open set and closed set. # open_set = OrderedSet() # closed_set = OrderedSet() open_set = Stack() closed_set = Queue() ########################################## ########################################## # Set up visualization logger hook # Please do not modify these four lines closed_set.logger = logger logger.closed_set = closed_set open_set.logger = logger logger.open_set = open_set ########################################## movement = [] pathTrace = {} def Backtrace(): ############################################################################# path = [goal] print('goal is', goal) while path[-1] != start: #if pathTrace[path[-1]] not in path: print('parent is', pathTrace[path[-1]]) path.append(pathTrace[path[-1]]) path.reverse() num = 0 while num < len(path) - 1: row, col = path[num] parentRow, parentCol = path[num + 1] dRow = parentRow - row dCol = parentCol - col if (dRow == -1 and dCol == 0): movement.append((-1, 0)) if (dRow == 0 and dCol == -1): movement.append((0, -1)) if (dRow == 1 and dCol == 0): movement.append((1, 0)) if (dRow == 0 and dCol == 1): movement.append((0, 1)) num += 1 return movement # ---------------------------------------- # finish the code below # ---------------------------------------- ############################################################################# open_set.add(start) i = 0 while open_set: i = i + 1 children = [] currentNode = open_set.pop() closed_set.add(currentNode) currentNode_row, currentNode_col = currentNode children = [(currentNode_row - 1, currentNode_col), (currentNode_row + 1, currentNode_col), (currentNode_row, currentNode_col - 1), (currentNode_row, currentNode_col + 1)] for childNode in children: node_row, node_col = childNode if childNode not in obstacles and node_row >= 0 and node_col >= 0 and node_row < n_rows and node_col < n_cols: if childNode not in closed_set and childNode not in open_set: if childNode == goal: pathTrace[childNode] = currentNode movement = Backtrace() return movement, closed_set else: open_set.add(childNode) pathTrace[childNode] = currentNode ############################################################################# return movement, closed_set