input: Write a python function to find the path from start_position to goal_position using the A* algorithm.
  assert a_star((0, 0), (2, 2), [(1, 0), (1, 1), (1, 2)]) == [(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)]
  assert a_star((0, 0), (1, 1), []) == [(0, 0), (0, 1), (1, 1)]
  assert a_star((0, 0), (3, 3), [(1, 1), (2, 2)]) == [(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3)]
output: def a_star(start_position, goal_position, obstacles):
      from heapq import heappop, heappush
      from math import inf, sqrt
  
      def heuristic(a, b):
          return sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2)
  
      def get_neighbors(pos):
          neighbors = [(0, 1), (1, 0), (0, -1), (-1, 0)]
          result = []
          for n in neighbors:
              neighbor = (pos[0] + n[0], pos[1] + n[1])
              if neighbor not in obstacles:
                  result.append(neighbor)
          return result
  
      open_list = []
      heappush(open_list, (0 + heuristic(start_position, goal_position), 0, start_position))
      came_from = {}
      g_score = {start_position: 0}
      f_score = {start_position: heuristic(start_position, goal_position)}
  
      while open_list:
          _, current_g, current = heappop(open_list)
  
          if current == goal_position:
              path = []
              while current in came_from:
                  path.append(current)
                  current = came_from[current]
              path.append(start_position)
              return path[::-1]
  
          for neighbor in get_neighbors(current):
              tentative_g_score = current_g + 1
  
              if tentative_g_score < g_score.get(neighbor, inf):
                  came_from[neighbor] = current
                  g_score[neighbor] = tentative_g_score
                  f_score[neighbor] = tentative_g_score + heuristic(neighbor, goal_position)
                  heappush(open_list, (f_score[neighbor], tentative_g_score, neighbor))
  
      return []