
from __future__ import annotations

import time

lowercase  :					Optional[int]						    =							list[tuple[int, int]]

lowercase  :					List[Any]						    =							[
    [0, 0, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0, 0, 0],  # 0 are free path whereas 1's are obstacles
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0],
    [1, 0, 1, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 1, 0, 0],
]

lowercase  :					Optional[int]						    =							[[-1, 0], [0, -1], [1, 0], [0, 1]]  # up, left, down, right

class __snake_case		:
    def __init__(  self		,snake_case		,snake_case		,snake_case		,snake_case		,snake_case      ):



           '''simple docstring'''







           lowercase							:     Tuple       	=      pos_x
           lowercase							:     str       	=      pos_y
           lowercase							:     Any       	=      (pos_y, pos_x)
           lowercase							:     int       	=      goal_x
           lowercase							:     int       	=      goal_y
           lowercase							:     Dict       	=      parent






class __snake_case		:
    def __init__(  self		,snake_case		,snake_case      ):



           '''simple docstring'''







           lowercase							:     List[Any]       	=      Node(start[1]		,start[0]		,goal[1]		,goal[0]		,snake_case      )
           lowercase							:     Optional[Any]       	=      Node(goal[1]		,goal[0]		,goal[1]		,goal[0]		,snake_case      )

           lowercase							:     Union[str, Any]       	=      [self.start]
           lowercase							:     Any       	=      False
    def       _SCREAMING_SNAKE_CASE       (  self      ):



           '''simple docstring'''







           while self.node_queue:
                  lowercase							:     int       	=      self.node_queue.pop(0      )

                  if current_node.pos == self.target.pos:
                         lowercase							:     Dict       	=      True
                         return self.retrace_path(snake_case      )

                  lowercase							:     List[str]       	=      self.get_successors(snake_case      )

                  for node in successors:
                         self.node_queue.append(snake_case      )

           if not self.reached:
                  return [self.start.pos]
           return None
    def       _SCREAMING_SNAKE_CASE       (  self		,snake_case      ):



           '''simple docstring'''







           lowercase							:     Any       	=      []
           for action in delta:
                  lowercase							:     Any       	=      parent.pos_x + action[1]
                  lowercase							:     str       	=      parent.pos_y + action[0]
                  if not (0 <= pos_x <= len(grid[0]      ) - 1 and 0 <= pos_y <= len(snake_case      ) - 1):
                         continue

                  if grid[pos_y][pos_x] != 0:
                         continue

                  successors.append(
                      Node(snake_case		,snake_case		,self.target.pos_y		,self.target.pos_x		,snake_case      )      )
           return successors
    def       _SCREAMING_SNAKE_CASE       (  self		,snake_case      ):



           '''simple docstring'''







           lowercase							:     Tuple       	=      node
           lowercase							:     str       	=      []
           while current_node is not None:
                  path.append((current_node.pos_y, current_node.pos_x)      )
                  lowercase							:     Tuple       	=      current_node.parent
           path.reverse()
           return path






class __snake_case		:
    def __init__(  self		,snake_case		,snake_case      ):



           '''simple docstring'''







           lowercase							:     int       	=      BreadthFirstSearch(snake_case		,snake_case      )
           lowercase							:     Any       	=      BreadthFirstSearch(snake_case		,snake_case      )
           lowercase							:     Union[str, Any]       	=      False
    def       _SCREAMING_SNAKE_CASE       (  self      ):



           '''simple docstring'''







           while self.fwd_bfs.node_queue or self.bwd_bfs.node_queue:
                  lowercase							:     Any       	=      self.fwd_bfs.node_queue.pop(0      )
                  lowercase							:     Optional[int]       	=      self.bwd_bfs.node_queue.pop(0      )

                  if current_bwd_node.pos == current_fwd_node.pos:
                         lowercase							:     int       	=      True
                         return self.retrace_bidirectional_path(
                             snake_case		,snake_case      )

                  lowercase							:     Dict       	=      current_bwd_node
                  lowercase							:     List[Any]       	=      current_fwd_node

                  lowercase							:     Optional[Any]       	=      {
                      self.fwd_bfs: self.fwd_bfs.get_successors(snake_case      ),
                      self.bwd_bfs: self.bwd_bfs.get_successors(snake_case      ),
                  }

                  for bfs in [self.fwd_bfs, self.bwd_bfs]:
                         for node in successors[bfs]:
                                bfs.node_queue.append(snake_case      )

           if not self.reached:
                  return [self.fwd_bfs.start.pos]
           return None
    def       _SCREAMING_SNAKE_CASE       (  self		,snake_case		,snake_case      ):



           '''simple docstring'''







           lowercase							:     str       	=      self.fwd_bfs.retrace_path(snake_case      )
           lowercase							:     Union[str, Any]       	=      self.bwd_bfs.retrace_path(snake_case      )
           bwd_path.pop()
           bwd_path.reverse()
           lowercase							:     Tuple       	=      fwd_path + bwd_path
           return path


if __name__ == "__main__":
  # all coordinates are given in format [y,x]
  import doctest

  doctest.testmod()
  lowercase  :					Tuple						    =							(0, 0)
  lowercase  :					Union[str, Any]						    =							(len(grid) - 1, len(grid[0]) - 1)
  for elem in grid:
    print(elem)

  lowercase  :					Dict						    =							time.time()
  lowercase  :					Optional[int]						    =							BreadthFirstSearch(init, goal)
  lowercase  :					Dict						    =							bfs.search()
  lowercase  :					Dict						    =							time.time() - start_bfs_time

  print("""Unidirectional BFS computation time : """, bfs_time)

  lowercase  :					List[str]						    =							time.time()
  lowercase  :					int						    =							BidirectionalBreadthFirstSearch(init, goal)
  lowercase  :					Any						    =							bd_bfs.search()
  lowercase  :					Dict						    =							time.time() - start_bd_bfs_time

  print("""Bidirectional BFS computation time : """, bd_bfs_time)




"""simple docstring"""



def   __lowerCAmelCase						(						lowercase  :  List[str]						,							lowercase  :  Union[str, Any]						,							lowercase  :  List[str]						,							lowercase  :  Tuple						,							lowercase  :  List[Any]						,							lowercase  :  int )	->						List[Any]:


    """simple docstring"""





    if index == r:
        for j in range(lowercase ):
            print(data[j]						,							end=" " )
        print(" " )
        return
    #  When no more elements are there to put in data[]
    if i >= n:
        return
    # current is included, put next at next location
    snake_case    :      Union[str, Any]      					=    arr[i]
    combination_util(lowercase						,							lowercase						,							lowercase						,							index + 1						,							lowercase						,							i + 1 )
    # current is excluded, replace it with
    # next (Note that i+1 is passed, but
    # index is not changed)
    combination_util(lowercase						,							lowercase						,							lowercase						,							lowercase						,							lowercase						,							i + 1 )
    # The main function that prints all combinations
    # of size r in arr[] of size n. This function
    # mainly uses combinationUtil()







def   __lowerCAmelCase						(						lowercase  :  Any						,							lowercase  :  Union[str, Any]						,							lowercase  :  Optional[int] )	->						List[Any]:


    """simple docstring"""





    snake_case    :      Any      					=    [0] * r
    # Print all combination using temporary array 'data[]'
    combination_util(lowercase						,							lowercase						,							lowercase						,							0						,							lowercase						,							0 )


if __name__ == "__main__":
  # Driver code to check the function above
  __snake_case   			=    [10, 20, 30, 40, 50]
  print_combination(arr, len(arr), 3)
  # This code is contributed by Ambuj sahu





