advent2023/sechszehn.py

143 lines
4.6 KiB
Python
Executable File

#!/usr/bin/env python3
import cv2 as cv
from useful import *
sin = {0:0,90:-1,180:0,270:1}
cos = {0:1,90:0,180:-1,270:0}
mirrors = np.array([[{'/':45,'\\':135,'-':0,'|':90,'.':69}[c] for c in l] for l in lines(open(0))])
size = mirrors.shape[0] # w = h anyway, don't bother genericizing
done = np.zeros((size * 4, 4, size, size), dtype=bool)
verbose = len(sys.argv) > 1 and '-v' in sys.argv[1]
draw_every = 1 if verbose and '-vv' in sys.argv[1] else 666
if verbose:
"""
why aren't there single characters identifiers for top left or northwest?
let's use initials of countries: canada, soviet, brazil, australia
in addition to udlr and also "middle"
"""
initials = {letter: np.asarray(coords) / 2 for coords, letter in
np.ndenumerate(np.array(list('cuslmrbda')).reshape((3, 3)))}
mirror_rections = {45:'bs',135:'ca',0:'lr',90:'ud'}
frame = 0
cv.namedWindow('lasers', flags=cv.WINDOW_GUI_NORMAL | cv.WINDOW_AUTOSIZE)
def draw(linger=16):
global canvas, frame
for (y, x), angle in np.ndenumerate(mirrors): # just put them on top
if angle == 69:
continue
line(y, x, mirror_rections[angle], (0x7f, 0x9a, 0xa5)[::-1])
zewm = 840 // canvas.shape[0]
cv.imshow('lasers', np.kron(canvas, np.ones((zewm, zewm, 1), dtype='uint8')))
#cv.imwrite('lasers%04d.png' % frame, np.kron(canvas, np.ones((zewm, zewm, 1), dtype='uint8')))
frame += 1
cv.waitKey(linger)
def line(y, x, rection, color):
global canvas, scale
off1 = initials[rection[0]]
off2 = initials[rection[1]]
cv.line(canvas,
(x * scale + int(off1[1] * (scale - 1)),
y * scale + int(off1[0] * (scale - 1))),
(x * scale + int(off2[1] * (scale - 1)),
y * scale + int(off2[0] * (scale - 1))),
color,
1)
scale = 7
canvas = np.kron(np.ones((size * scale, size * scale, 1)),
np.asarray((0x43, 0x3c, 0x34))[::-1].reshape((1, 1, 3))).astype('uint8')
draw(1000)
def nextvel(y, x, vel):
here = mirrors[y,x]
if here == 69:
return [vel, ]
elif here == 45: # 0->90 avv, 180->270 avv: (init % 180)
return [(vel // 180) * 180 + (90 - vel % 180), ]
elif here == 135: # 0->270 avv, 180->90 avv
return [270 - vel, ]
elif here == 0:
if vel % 180 == 90:
return [0, 180]
else:
return [vel, ]
elif here == 90:
if vel % 180 == 0:
return [90, 270]
else:
return [vel, ]
else:
raise ValueError('omg')
def nexts(y, x, vel, beam):
return [(y + sin[n], x + cos[n], n, beam) for n in nextvel(y, x, vel)]
def is_done(y, x, vel, beam):
return (not all(n >= 0 and n < size for d, n in enumerate((y, x))) or
done[beam,vel // 90,y,x])
def set_done(y, x, vel, beam):
done[beam,vel // 90,y,x] = True
def test(y, x, vel, beam):
global totest
if is_done(y, x, vel, beam):
return
set_done(y, x, vel, beam)
totest += nexts(y, x, vel, beam)
if verbose and beam % draw_every == 0: # meh
here = mirrors[y,x]
if here == 69:
line(y, x, {0:'lr',90:'ud'}[vel%180], (0, 0, 255))
elif here == 45: # 0->90 avv, 180->270 avv: (init % 180)
if vel in (0, 270):
line(y, x, 'lm', (0, 0, 255))
line(y, x, 'mu', (0, 0, 255))
else:
line(y, x, 'rm', (0, 0, 255))
line(y, x, 'md', (0, 0, 255))
elif here == 135:
if vel in (0, 90):
line(y, x, 'lm', (0, 0, 255))
line(y, x, 'md', (0, 0, 255))
else:
line(y, x, 'rm', (0, 0, 255))
line(y, x, 'mu', (0, 0, 255))
elif here == 0:
if vel == 90:
line(y, x, 'dm', (0, 0, 255))
elif vel == 270:
line(y, x, 'um', (0, 0, 255))
elif here == 90:
if vel == 0:
line(y, x, 'lm', (0, 0, 255))
elif vel == 180:
line(y, x, 'rm', (0, 0, 255))
totest=deque()
for step in range(size):
totest += [(step, 0, 0, step), ]
totest += [(step, size - 1, 180, step + size), ]
totest += [(0, step, 270, step + size * 2), ]
totest += [(size - 1, step, 90, step + size * 3), ]
while totest:
if verbose:
draw()
for _ in range(len(totest)):
test(*totest.popleft())
print(np.count_nonzero(done[0].sum(axis=0).astype(bool)))
print(max(np.count_nonzero(done.sum(axis=1).astype(bool), axis=(1, 2))))
if verbose:
draw(0)