from __future__ import annotations

import time

_SCREAMING_SNAKE_CASE 		=      list[tuple[int, int]]

_SCREAMING_SNAKE_CASE 		=      [
    [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],
]

_SCREAMING_SNAKE_CASE 		=      [[-1, 0], [0, -1], [1, 0], [0, 1]]  # up, left, down, right
class SCREAMING_SNAKE_CASE_		:







   def __init__(     self						:						List[Any]   ,   lowerCamelCase_						:						int   ,   lowerCamelCase_						:						int   ,   lowerCamelCase_						:						int   ,   lowerCamelCase_						:						int   ,   lowerCamelCase_						:						Node | None				):





    """simple docstring"""


    UpperCamelCase			  =      pos_x
    UpperCamelCase			  =      pos_y
    UpperCamelCase			  =      (pos_y, pos_x)
    UpperCamelCase			  =      goal_x
    UpperCamelCase			  =      goal_y
    UpperCamelCase			  =      parent





class SCREAMING_SNAKE_CASE_		:







   def __init__(     self						:						Union[str, Any]   ,   lowerCamelCase_						:						tuple[int, int]   ,   lowerCamelCase_						:						tuple[int, int]				):





    """simple docstring"""


    UpperCamelCase			  =      Node(start[1]   ,   start[0]   ,   goal[1]   ,   goal[0]   ,   lowerCamelCase_				)
    UpperCamelCase			  =      Node(goal[1]   ,   goal[0]   ,   goal[1]   ,   goal[0]   ,   lowerCamelCase_				)

    UpperCamelCase			  =      [self.start]
    UpperCamelCase			  =      False







   def 						lowerCamelCase_	(     self						:						Optional[int]				):





    """simple docstring"""


    while self.node_queue:
     UpperCamelCase			  =      self.node_queue.pop(0				)

     if current_node.pos == self.target.pos:
      UpperCamelCase			  =      True
      return self.retrace_path(lowerCamelCase_				)

     UpperCamelCase			  =      self.get_successors(lowerCamelCase_				)

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

    if not self.reached:
     return [self.start.pos]
    return None







   def 						lowerCamelCase_	(     self						:						List[str]   ,   lowerCamelCase_						:						Node				):





    """simple docstring"""


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

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

     successors.append(
         Node(lowerCamelCase_   ,   lowerCamelCase_   ,   self.target.pos_y   ,   self.target.pos_x   ,   lowerCamelCase_				)				)
    return successors







   def 						lowerCamelCase_	(     self						:						List[str]   ,   lowerCamelCase_						:						Node | None				):





    """simple docstring"""


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





class SCREAMING_SNAKE_CASE_		:







   def __init__(     self						:						int   ,   lowerCamelCase_						:						Dict   ,   lowerCamelCase_						:						Optional[int]				):





    """simple docstring"""


    UpperCamelCase			  =      BreadthFirstSearch(lowerCamelCase_   ,   lowerCamelCase_				)
    UpperCamelCase			  =      BreadthFirstSearch(lowerCamelCase_   ,   lowerCamelCase_				)
    UpperCamelCase			  =      False







   def 						lowerCamelCase_	(     self						:						int				):





    """simple docstring"""


    while self.fwd_bfs.node_queue or self.bwd_bfs.node_queue:
     UpperCamelCase			  =      self.fwd_bfs.node_queue.pop(0				)
     UpperCamelCase			  =      self.bwd_bfs.node_queue.pop(0				)

     if current_bwd_node.pos == current_fwd_node.pos:
      UpperCamelCase			  =      True
      return self.retrace_bidirectional_path(
          lowerCamelCase_   ,   lowerCamelCase_				)

     UpperCamelCase			  =      current_bwd_node
     UpperCamelCase			  =      current_fwd_node

     UpperCamelCase			  =      {
         self.fwd_bfs: self.fwd_bfs.get_successors(lowerCamelCase_				),
         self.bwd_bfs: self.bwd_bfs.get_successors(lowerCamelCase_				),
     }

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

    if not self.reached:
     return [self.fwd_bfs.start.pos]
    return None







   def 						lowerCamelCase_	(     self						:						Dict   ,   lowerCamelCase_						:						Node   ,   lowerCamelCase_						:						Node				):





    """simple docstring"""


    UpperCamelCase			  =      self.fwd_bfs.retrace_path(lowerCamelCase_				)
    UpperCamelCase			  =      self.bwd_bfs.retrace_path(lowerCamelCase_				)
    bwd_path.pop()
    bwd_path.reverse()
    UpperCamelCase			  =      fwd_path + bwd_path
    return path


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

     doctest.testmod()
     _SCREAMING_SNAKE_CASE 		=      (0, 0)
     _SCREAMING_SNAKE_CASE 		=      (len(grid) - 1, len(grid[0]) - 1)
     for elem in grid:
          print(elem)

     _SCREAMING_SNAKE_CASE 		=      time.time()
     _SCREAMING_SNAKE_CASE 		=      BreadthFirstSearch(init, goal)
     _SCREAMING_SNAKE_CASE 		=      bfs.search()
     _SCREAMING_SNAKE_CASE 		=      time.time() - start_bfs_time

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

     _SCREAMING_SNAKE_CASE 		=      time.time()
     _SCREAMING_SNAKE_CASE 		=      BidirectionalBreadthFirstSearch(init, goal)
     _SCREAMING_SNAKE_CASE 		=      bd_bfs.search()
     _SCREAMING_SNAKE_CASE 		=      time.time() - start_bd_bfs_time

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

_SCREAMING_SNAKE_CASE 		=      """ABCDEFGHIJKLMNOPQRSTUVWXYZ"""





def 			lowercase(	)		->	None:







 '''simple docstring'''
 UpperCamelCase			  =      input("""Enter message: """       )
 UpperCamelCase			  =      input("""Enter key [alphanumeric]: """       )
 UpperCamelCase			  =      input("""Encrypt/Decrypt [e/d]: """       )

 if mode.lower().startswith("""e"""       ):
  UpperCamelCase			  =      """encrypt"""
  UpperCamelCase			  =      encrypt_message(UpperCamelCase_							,					UpperCamelCase_       )
 elif mode.lower().startswith("""d"""       ):
  UpperCamelCase			  =      """decrypt"""
  UpperCamelCase			  =      decrypt_message(UpperCamelCase_							,					UpperCamelCase_       )

 print(f"""\n{mode.title()}ed message:"""       )
 print(UpperCamelCase_       )





def 			lowercase(	UpperCamelCase_							,					UpperCamelCase_       )		->	str:







 '''simple docstring'''
 return translate_message(UpperCamelCase_							,					UpperCamelCase_							,					"""encrypt"""       )





def 			lowercase(	UpperCamelCase_							,					UpperCamelCase_       )		->	str:







 '''simple docstring'''
 return translate_message(UpperCamelCase_							,					UpperCamelCase_							,					"""decrypt"""       )





def 			lowercase(	UpperCamelCase_							,					UpperCamelCase_							,					UpperCamelCase_       )		->	str:







 '''simple docstring'''
 UpperCamelCase			  =      []
 UpperCamelCase			  =      0
 UpperCamelCase			  =      key.upper()

 for symbol in message:
  UpperCamelCase			  =      LETTERS.find(symbol.upper()       )
  if num != -1:
   if mode == "encrypt":
    num += LETTERS.find(key[key_index]       )
   elif mode == "decrypt":
    num -= LETTERS.find(key[key_index]       )

   num %= len(UpperCamelCase_       )

   if symbol.isupper():
    translated.append(LETTERS[num]       )
   elif symbol.islower():
    translated.append(LETTERS[num].lower()       )

   key_index += 1
   if key_index == len(UpperCamelCase_       ):
    UpperCamelCase			  =      0
  else:
   translated.append(UpperCamelCase_       )
 return "".join(UpperCamelCase_       )


if __name__ == "__main__":
     main()
