Advertisement
Guest User

Untitled

a guest
Dec 24th, 2022
336
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.51 KB | None | 0 0
  1. # Input: Map of "#", ".", "^", "v", "<", ">"
  2. # Output: Starting in "." in top row,
  3. #         with each arrow moving one step per minute in its direction
  4. #           (if it hits # then it wraps around),
  5. #         and you simultaneously moving orthogonally or waiting,
  6. #         how many minutes does it take to reach "." in bottom row
  7. #           without overlapping any arrows?
  8.  
  9. # By inspection, there are no ^ or v arrows in starting or ending columns,
  10. # thus arrow pattern repeats every lcm(r, c) minutes
  11. #   (r = # rows between first/last, c = # columns between first/last)
  12. # so we can compute and store each of those states
  13.  
  14. import math
  15. from functools import cache
  16.  
  17. initial_grid = []
  18.  
  19. input_data = open(r"c:\users\edward\desktop\personal\aoc2022\24_input.txt")
  20. for input_line in input_data.read().splitlines():
  21.     input_line = input_line.replace("\n", "")
  22.     initial_grid.append(input_line)
  23.  
  24. number_interior_rows = len(initial_grid) - 2
  25. number_interior_columns = len(initial_grid[0]) - 2
  26. number_minutes = math.lcm(number_interior_rows, number_interior_columns)
  27.  
  28. initial_row = initial_grid[0]
  29. initial_column = -1
  30. for c in range(0, len(initial_row)):
  31.     if initial_row[c] == ".":
  32.         initial_column = c
  33.         break
  34.  
  35. final_row = initial_grid[len(initial_grid) - 1]
  36. final_column = -1
  37. for c in range(0, len(final_row)):
  38.     if final_row[c] == ".":
  39.         final_column = c
  40.         break
  41.  
  42. grids = []
  43. for minute in range(0, number_minutes):
  44.     blocked_positions = []
  45.     for r in range(1, number_interior_rows + 1):
  46.         for c in range(1, number_interior_columns + 1):
  47.             character = initial_grid[r][c]
  48.             if character == ".":
  49.                 continue
  50.             nr, nc = r, c
  51.             if character == "^":
  52.                 nr -= minute
  53.                 while nr < 1:
  54.                     nr += number_interior_rows
  55.             if character == "v":
  56.                 nr += minute
  57.                 while nr > number_interior_rows:
  58.                     nr -= number_interior_rows
  59.             if character == "<":
  60.                 nc -= minute
  61.                 while nc < 1:
  62.                     nc += number_interior_columns
  63.             if character == ">":
  64.                 nc += minute
  65.                 while nc > number_interior_columns:
  66.                     nc -= number_interior_columns
  67.             if not ([nr, nc] in blocked_positions):
  68.                 blocked_positions.append([nr, nc])
  69.     grids.append(blocked_positions)
  70.  
  71. # Backtrack from each variation of final position
  72.  
  73. final_positions = []
  74. for minute in range(0, number_minutes):
  75.     final_positions.append([number_interior_rows + 1, final_column, minute])
  76.  
  77. positions = []
  78. positions.append(final_positions)
  79.  
  80. # Is (r, c) at minute-state (m) a valid position to be?
  81. @cache
  82. def valid_position(r, c, m):
  83.     # Bottom row and not at end
  84.     if r > number_interior_rows and c != final_column:
  85.         return False
  86.     # Top row and not at start
  87.     if r < 1 and c != initial_column:
  88.         return False
  89.     # Too far left or right
  90.     if c < 1 or c > number_interior_columns:
  91.         return False
  92.     # Position occupied by blizzard
  93.     if [r, c] in grids[m]:
  94.         return False
  95.     # Otherwise OK
  96.     return True
  97.  
  98. # Minimum possible steps from current position to goal
  99. #   (Manhattan distance, if any such path avoids a collision at each step)
  100. @cache
  101. def minimum_steps(r, c):
  102.     return (number_interior_rows + 1 - r) + abs(c - final_column)
  103.  
  104. # Perform best-first search
  105. # Begin at the beginning
  106. # Key of paths = minutes elapsed + minimum number of minutes still needed
  107.  
  108. paths = {}
  109. initial_state = [0, initial_column, 0] # row, column, minutes elapsed
  110. initial_minimum_steps = minimum_steps(0, initial_column)
  111. paths[initial_minimum_steps] = [initial_state]
  112.  
  113. minimum_length = -1
  114. while minimum_length == -1:
  115.     key_of_paths_to_extend = min(paths.keys())
  116.     paths_to_extend = paths[key_of_paths_to_extend]
  117.     del paths[key_of_paths_to_extend]
  118.     for path in paths_to_extend:
  119.         r, c, m = path[0], path[1], path[2]
  120.  
  121.         if r == number_interior_rows + 1 and c == final_column:
  122.             if minimum_length == -1 or minimum_length > m:
  123.                 minimum_length = m
  124.             continue
  125.        
  126.         nm = (m + 1) % number_minutes
  127.  
  128.         # Consider going south
  129.         nr, nc = r + 1, c
  130.         if valid_position(nr, nc, nm):
  131.             new_path = [nr, nc, m + 1]
  132.             new_minimum_steps = m + minimum_steps(nr, nc)
  133.             if not (new_minimum_steps in paths.keys()):
  134.                 paths[new_minimum_steps] = [new_path]
  135.             else:
  136.                 if not (new_path in paths[new_minimum_steps]):
  137.                     paths[new_minimum_steps].append(new_path)
  138.  
  139.         # Consider going east
  140.         nr, nc = r, c + 1
  141.         if valid_position(nr, nc, nm):
  142.             new_path = [nr, nc, m + 1]
  143.             new_minimum_steps = m + minimum_steps(nr, nc)
  144.             if not (new_minimum_steps in paths.keys()):
  145.                 paths[new_minimum_steps] = [new_path]
  146.             else:
  147.                 if not (new_path in paths[new_minimum_steps]):
  148.                     paths[new_minimum_steps].append(new_path)
  149.  
  150.         # Consider going west
  151.         nr, nc = r, c - 1
  152.         if valid_position(nr, nc, nm):
  153.             new_path = [nr, nc, m + 1]
  154.             new_minimum_steps = m + minimum_steps(nr, nc)
  155.             if not (new_minimum_steps in paths.keys()):
  156.                 paths[new_minimum_steps] = [new_path]
  157.             else:
  158.                 if not (new_path in paths[new_minimum_steps]):
  159.                     paths[new_minimum_steps].append(new_path)
  160.  
  161.         # Consider going north
  162.         nr, nc = r - 1, c
  163.         if valid_position(nr, nc, nm):
  164.             new_path = [nr, nc, m + 1]
  165.             new_minimum_steps = m + minimum_steps(nr, nc)
  166.             if not (new_minimum_steps in paths.keys()):
  167.                 paths[new_minimum_steps] = [new_path]
  168.             else:
  169.                 if not (new_path in paths[new_minimum_steps]):
  170.                     paths[new_minimum_steps].append(new_path)
  171.  
  172.         # Consider standing still
  173.         nr, nc = r, c
  174.         if valid_position(nr, nc, nm):
  175.             new_path = [nr, nc, m + 1]
  176.             new_minimum_steps = m + minimum_steps(nr, nc)
  177.             if not (new_minimum_steps in paths.keys()):
  178.                 paths[new_minimum_steps] = [new_path]
  179.             else:
  180.                 if not (new_path in paths[new_minimum_steps]):
  181.                     paths[new_minimum_steps].append(new_path)
  182.  
  183. print (minimum_length)
  184.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement