input: Write a python function to find the shortest path in a grid with obstacles using the A* algorithm.
  assert find_path(6, 8, (1, 0), (4, 6), [(2, 4), (3, 3)]) == [(1, 0), (1, 3), (2, 3), (3, 3), (4, 4), (4, 5), (4, 6)]
  assert find_path(3, 3, (0, 0), (2, 2), [(1, 1)]) == [(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)]
  assert find_path(5, 5, (0, 0), (4, 4), [(2, 2), (1, 1), (3, 3)]) == [(0, 0), (0, 1), (1, 2), (2, 3), (3, 4), (4, 4)]
output: def find_path(rows, cols, start, goal, obstacles):
      from heapq import heappop, heappush
      import math
  
      def heuristic(a, b):
          return abs(a[0] - b[0]) + abs(a[1] - b[1])
  
      def get_neighbors(node):
          neighbors = []
          for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
              x, y = node[0] + dx, node[1] + dy
              if 0 <= x < rows and 0 <= y < cols and (x, y) not in obstacles:
                  neighbors.append((x, y))
          return neighbors
  
      open_set = []
      heappush(open_set, (0 + heuristic(start, goal), 0, start))
      came_from = {}
      g_score = {start: 0}
  
      while open_set:
          _, current_cost, current = heappop(open_set)
  
          if current == goal:
              path = []
              while current in came_from:
                  path.append(current)
                  current = came_from[current]
              path.append(start)
              return path[::-1]
  
          for neighbor in get_neighbors(current):
              tentative_g_score = current_cost + 1
              if neighbor not in g_score or tentative_g_score < g_score[neighbor]:
                  came_from[neighbor] = current
                  g_score[neighbor] = tentative_g_score
                  heappush(open_set, (tentative_g_score + heuristic(neighbor, goal), tentative_g_score, neighbor))
  
      return []