Advertisement
Guest User

AoC 2022 - Day 22

a guest
Jan 2nd, 2023
601
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.73 KB | None | 0 0
  1. """
  2. Advent of Code 2022 Day 22
  3. """
  4. import re
  5. import sys
  6.  
  7. from advent_tools import get_daily_input
  8.  
  9. DAY = 22
  10.  
  11. TEST = sys.argv[1] == "test" if len(sys.argv) > 1 else False
  12.  
  13. TEST_DATA = """
  14.        ...#
  15.        .#..
  16.        #...
  17.        ....
  18. ...#.......#
  19. ........#...
  20. ..#....#....
  21. ..........#.
  22.        ...#....
  23.        .....#..
  24.        .#......
  25.        ......#.
  26.  
  27. 10R5L5R10L4R5L5
  28. """
  29.  
  30. if TEST:
  31.     def get_daily_input(_):
  32.         for line in TEST_DATA.strip("\n").split("\n"):
  33.             yield line.strip("\n")
  34.  
  35.  
  36. def load_data() -> tuple[list[str], list[str]]:
  37.     data_stream = get_daily_input(DAY)
  38.     map_data: list[str] = []
  39.     path_data: list[str] = []
  40.     for d in data_stream:
  41.         if d:
  42.             map_data.append(d)
  43.         else:
  44.             path_data = re.findall(r"\d+|[LR]", next(data_stream))
  45.     width = max(len(m) for m in map_data)
  46.     return list([m.ljust(width) for m in map_data]), path_data
  47.  
  48.  
  49. class MonkeyMap:
  50.     adjacent = {
  51.         "u": "rflb", "d": "rblf",
  52.         "f": "rdlu", "b": "ldru",
  53.         "l": "fdbu", "r": "bdfu"
  54.     }
  55.  
  56.     facings = ">v<^"
  57.  
  58.     def __init__(self, mm):
  59.         self.map = mm
  60.         self.height = len(mm)
  61.         self.width = len(mm[0])
  62.         self.face_size = self.find_face_size(self.height, self.width)
  63.         self.face_map = self.build_face_map()
  64.  
  65.     def build_face_map(self):
  66.         def _add_sides(r_in: int, c_in: int) -> None:
  67.             cf, ce = face_map[r_in][c_in]
  68.  
  69.             for r, c, d, e in [(r_in - 1, c_in, 3, 1), (r_in + 1, c_in, 1, 3),
  70.                                (r_in, c_in - 1, 2, 0), (r_in, c_in + 1, 0, 2)]:
  71.                 if 0 <= r < len(face_map) \
  72.                         and 0 <= c < len(face_map[0]) \
  73.                         and not face_map[r][c] \
  74.                         and self.map[r * self.face_size][c * self.face_size] != " ":
  75.                     nf = ce[d]
  76.                     ne = self.adjacent[nf]
  77.                     while ne[e] != cf:
  78.                         ne = ne[1:] + ne[0]
  79.                     face_map[r][c] = (nf, ne)
  80.                     _add_sides(r, c)
  81.  
  82.         face_map: list[list[tuple[str, str] | None]] = [
  83.             [None for _ in range(self.width // self.face_size)]
  84.             for _ in range(self.height // self.face_size)
  85.         ]
  86.  
  87.         first_face = (0, re.search(r"[.#]", self.map[0]).start() // self.face_size)
  88.         face_map[0][first_face[1]] = ("u", "rflb")
  89.         _add_sides(*first_face)
  90.  
  91.         return face_map
  92.  
  93.     def next_position_2d(self, r: int, c: int, f: str) -> tuple[int, int]:
  94.         if f == ">":
  95.             if c + 1 < self.width and self.map[r][c + 1] == ".":
  96.                 c += 1
  97.             elif c + 1 >= self.width or self.map[r][c + 1] == " ":
  98.                 nc = c
  99.                 while nc - 1 >= 0 and self.map[r][nc - 1] != " ":
  100.                     nc -= 1
  101.                 if self.map[r][nc] == ".":
  102.                     c = nc
  103.         elif f == "<":
  104.             if c > 0 and self.map[r][c - 1] == ".":
  105.                 c -= 1
  106.             elif c - 1 < 0 or self.map[r][c - 1] == " ":
  107.                 nc = c
  108.                 while nc + 1 < self.width and self.map[r][nc + 1] != " ":
  109.                     nc += 1
  110.                 if self.map[r][nc] == ".":
  111.                     c = nc
  112.         elif f == "v":
  113.             if r + 1 < self.height and self.map[r + 1][c] == ".":
  114.                 r += 1
  115.             elif r + 1 >= self.height or self.map[r + 1][c] == " ":
  116.                 nr = r
  117.                 while nr - 1 >= 0 and self.map[nr - 1][c] != " ":
  118.                     nr -= 1
  119.                 if self.map[nr][c] == ".":
  120.                     r = nr
  121.         elif f == "^":
  122.             if r > 0 and self.map[r - 1][c] == ".":
  123.                 r -= 1
  124.             elif r - 1 < 0 or self.map[r - 1][c] == " ":
  125.                 nr = r
  126.                 while nr + 1 < self.height and self.map[nr + 1][c] != " ":
  127.                     nr += 1
  128.                 if self.map[nr][c] == ".":
  129.                     r = nr
  130.  
  131.         return r, c
  132.  
  133.     def next_position_3d(self, r: int, c: int, f: str) -> tuple[int, int, str]:
  134.         def _find_face(target_face: str) -> tuple[int, int]:
  135.             for i in range(len(self.face_map)):
  136.                 for j in range(len(self.face_map[i])):
  137.                     if self.face_map[i][j] and self.face_map[i][j][0] == target_face:
  138.                         return i, j
  139.  
  140.         nr, nc, nf = r, c, f
  141.         if f == ">" and c + 1 < self.width and self.map[r][c + 1] != " ":
  142.             nc += 1
  143.         elif f == "<" and c > 0 and self.map[r][c - 1] != " ":
  144.             nc -= 1
  145.         elif f == "^" and r > 0 and self.map[r - 1][c] != " ":
  146.             nr -= 1
  147.         elif f == "v" and r + 1 < self.height and self.map[r + 1][c] != " ":
  148.             nr += 1
  149.         else:
  150.             cf_name, cf_edges = self.face_map[r // self.face_size][c // self.face_size]
  151.             nf_name = cf_edges[self.facings.find(f)]
  152.             nf_map_row, nf_map_col = _find_face(nf_name)
  153.             nf_edges = self.face_map[nf_map_row][nf_map_col][1]
  154.  
  155.             nf = self.facings[nf_edges.find(cf_name) - 2]
  156.             nr = nf_map_row * self.face_size + (
  157.                 (self.face_size - 1) if nf == "^" else 0
  158.             )
  159.             nc = nf_map_col * self.face_size + (
  160.                 (self.face_size - 1) if nf == "<" else 0
  161.             )
  162.  
  163.             if (f == ">" and nf == "v") or (f == "<" and nf == "^"):   # 90 cw h to v
  164.                 nc += self.face_size - 1 - r % self.face_size
  165.             elif (f == "^" and nf == ">") or (f == "v" and nf == "<"):  # 90 cw v to h
  166.                 nr += c % self.face_size
  167.             elif (f == ">" and nf == "^") or (f == "<" and nf == "v"):  # 90 ccw h to v
  168.                 nc += r % self.face_size
  169.             elif (f == "v" and nf == ">") or (f == "^" and nf == "<"):  # 90 ccw v to h
  170.                 nr += self.face_size - 1 - c % self.face_size
  171.             elif (f == ">" and nf == "<") or (f == "<" and nf == ">"):  # 180 horiz
  172.                 nr += self.face_size - 1 - r % self.face_size
  173.             elif (f == "^" and nf == "v") or (f == "v" and nf == "^"):  # 180 vert
  174.                 nc += self.face_size - 1 - c % self.face_size
  175.             elif (f == "<" and nf == "<") or (f == ">" and nf == ">"):  # 0 horiz
  176.                 nr += r % self.face_size
  177.             elif (f == "^" and nf == "^") or (f == "v" and nf == "v"):  # 0 vert
  178.                 nc += c % self.face_size
  179.  
  180.         if self.map[nr][nc] == ".":
  181.             r, c, f = nr, nc, nf
  182.  
  183.         return r, c, f
  184.  
  185.     def walk_map(self, path: list[str], cube: bool = False) -> int:
  186.         row, column = 0, self.map[0].find(".")
  187.         facing = ">"
  188.  
  189.         for p in path:
  190.             if p.isnumeric():
  191.                 for _ in range(int(p)):
  192.                     if cube:
  193.                         row, column, facing = self.next_position_3d(row, column, facing)
  194.                     else:
  195.                         row, column = self.next_position_2d(row, column, facing)
  196.             elif p == "L":
  197.                 facing = self.facings[self.facings.find(facing) - 1]
  198.             else:
  199.                 facing = self.facings[(self.facings.find(facing) + 1) % 4]
  200.  
  201.         return 1000 * (row + 1) + 4 * (column + 1) + self.facings.find(facing)
  202.  
  203.     @classmethod
  204.     def find_face_size(cls, h, w) -> int:
  205.         for x, y in [(2, 5), (3, 4), (4, 3), (5, 2)]:
  206.             if h // x == w // y:
  207.                 return h // x
  208.  
  209.  
  210. def main():
  211.     monkey_map_data, path_data = load_data()
  212.     monkey_map = MonkeyMap(monkey_map_data)
  213.     print(f"Part 1: {monkey_map.walk_map(path_data)}")
  214.     print(f"Part 2: {monkey_map.walk_map(path_data, cube=True)}")
  215.  
  216.  
  217. if __name__ == "__main__":
  218.     main()
  219.  
  220. """
  221. Part 1: 75254
  222. Part 2: 108311
  223. """
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement