first_search.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. # ----------
  2. # User Instructions:
  3. #
  4. # Define a function, search() that returns a list
  5. # in the form of [optimal path length, row, col]. For
  6. # the grid shown below, your function should output
  7. # [11, 4, 5].
  8. #
  9. # If there is no valid path from the start point
  10. # to the goal, your function should return the string
  11. # 'fail'
  12. # ----------
  13. # Grid format:
  14. # 0 = Navigable space
  15. # 1 = Occupied space
  16. import heapq
  17. from collections import deque
  18. import numpy as np
  19. from icecream import ic
  20. def check_navigable(grid, goal) -> bool:
  21. """Check if the grid is navigable
  22. Parameters
  23. ----------
  24. grid : np.ndarray
  25. The gird map of the environment
  26. goal : np.ndarray
  27. Goal position the function needs to reach
  28. Returns
  29. -------
  30. bool
  31. It's navigable(True) or not(False)
  32. """
  33. goal = np.array(goal).astype(int)
  34. if all(goal >= np.array([0, 0])) and all(goal <= np.array([len(grid)-1, len(grid[0])-1])):
  35. if not grid[goal[0], goal[1]]:
  36. return True
  37. return False
  38. def heuristic(now_pos, goal) -> float:
  39. """Returns the Euclidian distance between two points
  40. Parameters
  41. ----------
  42. now_pos : np.ndarray
  43. The current position
  44. goal : np.ndarray
  45. The goal position
  46. Returns
  47. -------
  48. float
  49. Distance
  50. """
  51. if type(now_pos) is not np.ndarray:
  52. now_pos = np.array(now_pos)
  53. if type(goal) is not np.ndarray:
  54. goal = np.array(goal)
  55. # return 0
  56. return np.linalg.norm(now_pos-goal)
  57. def search(grid: np.ndarray, init: list, goal: list, cost: list):
  58. open_list = []
  59. heapq.heappush(open_list, np.concatenate(
  60. ([heuristic(init, goal), 0], init)))
  61. closed_list = dict()
  62. closed_list[tuple(init)] = 0
  63. cost_map = np.zeros([len(grid), len(grid[0])]) + float('inf')
  64. cost_map[init[0], init[1]] = 0
  65. expand_map = np.zeros([len(grid), len(grid[0])]) - float('inf')
  66. current_expand_num = 0
  67. expand_map[init[0], init[1]] = current_expand_num
  68. while open_list:
  69. current_center_grid = heapq.heappop(open_list)
  70. current_center_grid = current_center_grid[2:4] # type: list[int]
  71. if check_navigable(grid, current_center_grid):
  72. for move_num in range(len(delta)):
  73. next = current_center_grid+delta[move_num]
  74. ic(type(next), next)
  75. next = next.astype('int') # type: np.ndarray[int]
  76. if check_navigable(grid, next) and tuple(next) not in closed_list:
  77. current_expand_num += 1
  78. current_cost = closed_list[current_center_grid[0],
  79. current_center_grid[1]]+cost
  80. expand_grid = [heuristic(next, goal)+current_expand_num,
  81. current_expand_num] + list(next)
  82. heapq.heappush(open_list, expand_grid)
  83. closed_list[tuple(next)] = current_cost
  84. cost_map[next[0], next[1]] = current_cost
  85. expand_map[next[0], next[1]] = current_expand_num
  86. if all(next == goal):
  87. ic(cost_map)
  88. ic(expand_map)
  89. return cost_map
  90. return "fail"
  91. def show_path(grid, init, goal, cost):
  92. cost_map = search(grid, init, goal, cost)
  93. motion_map = np.array([[' ' for x in range(len(grid[0]))]
  94. for y in range(len(grid))])
  95. motion_map[goal[0], goal[1]] = '*'
  96. if type(cost_map) == np.ndarray:
  97. print('Success')
  98. now_position = goal
  99. while any(now_position != init):
  100. dlt_opst = deque(['v', '>', '^', '<'])
  101. neighbor_grids = np.array([now_position for _ in range(4)])+delta
  102. neighbor_grids = map(lambda pos: pos if check_navigable(
  103. grid, pos) else False, neighbor_grids)
  104. neighbor_cost = {cost_map[pos[0], pos[1]] if type(pos) == np.ndarray else float(
  105. 'INF'): [dlt_opst.popleft(), pos] for pos in neighbor_grids}
  106. next_move, now_position = neighbor_cost[min(neighbor_cost.keys())]
  107. motion_map[now_position[0], now_position[1]] = next_move
  108. return motion_map
  109. elif cost_map == 'fail':
  110. print('Failed to generate a feasible path.')
  111. return motion_map
  112. if __name__ == '__main__':
  113. inf = float('inf')
  114. grid = np.array([[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
  115. [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
  116. [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0],
  117. [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0],
  118. [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0],
  119. [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0],
  120. [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
  121. [0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
  122. [0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0],
  123. [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0]])
  124. init = np.array([0, 0])
  125. # goal = np.array([len(grid)-1, len(grid[0])-1])
  126. goal = np.array([3, 10])
  127. cost = 1
  128. delta = np.array([[-1, 0], # type: np.ndarray[int]
  129. [0, -1], # go left
  130. [1, 0], # go down
  131. [0, 1]]) # go right
  132. delta_name = ['^', '<', 'v', '>']
  133. ic(show_path(grid, init, goal, cost))