input: Write a function to perform breadth-first search (BFS) on a grid to find the path from point A to point B.
  assert bfs_grid_path([[0, 0, 0, 0, 0, 0, 0, 0],
                        [0, 1, 0, 0, 0, 0, 0, 0],
                        [0, 0, 0, 0, 1, 0, 0, 0],
                        [0, 0, 0, 0, 0, 0, 0, 0],
                        [0, 0, 1, 0, 0, 0, 0, 0],
                        [0, 0, 0, 0, 1, 0, 0, 0],
                        [0, 0, 0, 0, 0, 0, 0, 0],
                        [0, 0, 0, 0, 0, 0, 1, 0]]) == [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (7, 1), (7, 2), (7, 3), (7, 4), (7, 5), (7, 6), (7, 7)]
  assert bfs_grid_path([[0, 0, 1],
                        [1, 0, 0],
                        [0, 0, 0]]) == [(0, 0), (0, 1), (1, 1), (2, 1), (2, 2)]
output: def bfs_grid_path(grid):
      from collections import deque
      
      def get_neighbors(position):
          x, y = position
          directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
          neighbors = []
          for direction in directions:
              nx, ny = x + direction[0], y + direction[1]
              if 0 <= nx < len(grid) and 0 <= ny < len(grid[0]) and grid[nx][ny] == 0:
                  neighbors.append((nx, ny))
          return neighbors
  
      start = (0, 0)
      goal = (len(grid) - 1, len(grid[0]) - 1)
      
      queue = deque([(start, [start])])
      visited = set([start])
      
      while queue:
          current_position, path = queue.popleft()
          
          if current_position == goal:
              return path
          
          for neighbor in get_neighbors(current_position):
              if neighbor not in visited:
                  visited.add(neighbor)
                  queue.append((neighbor, path + [neighbor]))
                  
      return []