Advertisement
Guest User

Untitled

a guest
Jan 19th, 2023
797
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.13 KB | None | 0 0
  1. from typing import Iterator
  2. from aoclib import show_answers, get_puzzle_input, CArray
  3. import cmath
  4. import numpy as np
  5. import re
  6.  
  7. def move(z:complex, dz:complex, dist:int, data:np.ndarray,
  8.          trans:dict[tuple[complex,complex],tuple[complex,complex]]) -> tuple[complex,complex]:
  9.     for _ in range(dist):
  10.         nz = z + dz
  11.         ndz = dz
  12.         if (nz,dz) in trans:
  13.             nz, ndz = trans[(nz,dz)]
  14.         if data[nz] == "#":
  15.             break
  16.         z,dz = nz,ndz
  17.     return z,dz
  18.  
  19. def wrap_trans(data:np.ndarray) -> dict[tuple[complex,complex],tuple[complex,complex]]:
  20.     trans = {}
  21.     top = np.argmax(data!=" ",axis=0)+np.arange(len(data[0]))*1j
  22.     bottom = len(data)-1-np.argmax(data[::-1]!=" ",axis=0)+np.arange(len(data[0]))*1j
  23.     left = np.argmax(data!=" ",axis=1)*1j+np.arange(len(data))
  24.     right = (len(data[0])-1-np.argmax(data[:,::-1]!=" ",axis=1))*1j+np.arange(len(data))
  25.     for t,b in zip(top,bottom):
  26.         trans[(t-1,-1+0j)] = b,-1+0j
  27.         trans[(b+1,1+0j)] = t,1+0j
  28.     for l,r in zip(left,right):
  29.         trans[(l-1j,-1j)] = r,-1j
  30.         trans[(r+1j,1j)] = l,1j
  31.     return trans
  32.  
  33. def cube_trans(data:np.ndarray) -> dict[tuple[complex,complex],tuple[complex,complex]]:
  34.     at_forward_up = (-1,-1,-1), (0,1,0), (0,0,1)
  35.     side = int(np.sqrt(np.sum(data!=" ")/6))
  36.     z = np.argmax(data[1]!=" ")*1j
  37.     dz = 1j
  38.     corners = {}
  39.     trans = {}
  40.     for _ in range(14):
  41.         at, forward, up = np.array(at_forward_up)
  42.         zs = z + np.arange(side)*dz
  43.         key = tuple(at), tuple(at+2*forward)
  44.         if key in corners:
  45.             zs0, dz0 = corners[key]
  46.             for z0,z1 in zip(zs0[::-1],zs):
  47.                 trans[(z0,dz0*1j)] = z1-dz*1j,dz/1j
  48.                 trans[(z1,dz*1j)] = z0-dz0*1j,dz0/1j
  49.         else:
  50.             corners[key[::-1]] = zs, dz
  51.  
  52.         turn_right = data[z + (side-1j)*dz] == " "
  53.         go_forward = data[z + side*dz] == " "
  54.         at += 2*forward
  55.         if turn_right:
  56.             forward = np.cross(forward, up)
  57.             z += (side-1j)*dz
  58.             dz *= -1j
  59.         elif go_forward:
  60.             s = at@up
  61.             forward, up = -s*up, s*forward
  62.             z += side*dz
  63.         else:
  64.             up = np.cross(forward, up)
  65.             forward = -forward
  66.             z += (side-1)*dz
  67.             dz *= 1j
  68.         at_forward_up = at, forward, up
  69.     return trans
  70.  
  71. def solutions() -> Iterator[int]:
  72.     data, cmds = get_puzzle_input(year=2022, day=22, example=False, as_lines=False).split("\n\n")
  73.     cmds = [(int(d),dict(R=-1j,L=1j).get(r,1)) for d,r in re.findall("(\d+)(R|L)?",cmds)]
  74.     data = data.split("\n")
  75.     width = max(map(len,data))
  76.  
  77.     data = np.array([list(f"{line:<{width}}") for line in data])
  78.     data = np.pad(data,1,mode="constant",constant_values=" ").view(CArray)
  79.  
  80.     for trans in wrap_trans(data),cube_trans(data):
  81.         z = 1 + np.argmax(data[1]!=" ")*1j
  82.         dz = 1j
  83.         for step,rot in cmds:
  84.             z, dz = move(z, dz, step, data, trans)
  85.             dz *= rot
  86.         face = int(1 - 2*cmath.phase(dz)/cmath.pi)%4
  87.         yield int(1000*z.real + 4*z.imag + face)
  88.  
  89. show_answers(solutions)
  90.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement