Skip to content

Commit 33872ba

Browse files
committed
Add day 22
1 parent 1adf8cd commit 33872ba

File tree

2 files changed

+290
-0
lines changed

2 files changed

+290
-0
lines changed

2022/day22/aoc_tools.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
from collections import deque, defaultdict, Counter
2+
import itertools
3+
import re
4+
from typing import TypeVar, Generator, Iterable, Tuple, List
5+
6+
_T = TypeVar("T")
7+
8+
def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]:
9+
elements_iter = iter(elements)
10+
last_element = next(elements_iter)
11+
for element in elements_iter:
12+
yield (last_element, element)
13+
last_element = element
14+
15+
def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]:
16+
elements_list = list(elements)
17+
for i in range(len(elements_list)):
18+
for j in range(i + 1, len(elements_list)):
19+
yield (elements_list[i], elements_list[j])
20+
21+
def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]:
22+
elements_list = list(elements)
23+
for i in range(len(elements_list)):
24+
for j in range(len(elements_list)):
25+
if j == i: continue
26+
yield (elements_list[i], elements_list[j])
27+
28+
29+
# yes, everyone else calls this "cumsum" but ohwell
30+
def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]:
31+
rsum = []
32+
elements_iter = iter(elements)
33+
34+
if start is None:
35+
rsum.append(next(elements_iter))
36+
else:
37+
rsum.append(start)
38+
39+
for element in elements_iter:
40+
rsum.append(rsum[-1] + element)
41+
return rsum
42+
43+
44+
# I did some rough experiments with this version vs. a version that uses a deque, which
45+
# has efficient popleft, and it seems like this version actually wins because of how slow
46+
# iterating over a deque is (which you have to do if you want to use the results)
47+
def rolling_window(
48+
elements: Iterable[_T],
49+
window_size: int,
50+
) -> Generator[Tuple[_T, ...], None, None]:
51+
current_window = []
52+
for element in elements:
53+
current_window.append(element)
54+
if len(current_window) > window_size:
55+
del current_window[0]
56+
if len(current_window) == window_size:
57+
yield current_window
58+
59+
60+
61+
# this is like slightly borked because it doesn't get negative numbers
62+
# oh well i guess
63+
#nums_regex = regex.compile("([^\\d]*)((?P<nums>\\d+)([^\\d]*))*")
64+
65+
def nums(s):
66+
m = nums_regex.match(s)
67+
vals = m.capturesdict()["nums"]
68+
return [int(x) for x in vals]
69+
70+
def nums(s):
71+
m = re.findall("-?\d+", s)
72+
return [int(x) for x in m]
73+
74+
def numsp(s):
75+
m = re.findall("-?\d+", s)
76+
return [int(x) for x in m]
77+
78+
def sign(x):
79+
if x < 0:
80+
return -1
81+
elif x == 0:
82+
return 0
83+
else:
84+
return 1
85+
86+
87+
# underscored names are in case functions get shadowed by accident
88+
adjp = _adjp = adjacent_pairs
89+
ap = _ap = all_pairs
90+
at = _at = all_tuples
91+
rw = _rw = rolling_window
92+
rsum = _rsum = rolling_sum
93+
94+
dd = _dd = defaultdict
95+
ctr = _ctr = Counter

2022/day22/day22.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import string
2+
from collections import defaultdict
3+
from aoc_tools import *
4+
import functools
5+
import sys
6+
#sys.setrecursionlimit(10000000)
7+
# RIGHT0 DOWN1 LEFT2 UP3
8+
dirs = ((0,1),(1,0),(0,-1),(-1,0))
9+
dirs3 = ((1,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1))
10+
11+
with open(r"input.txt") as f:
12+
s = f.read()[:-1]#.strip()
13+
print("\n".join(x[:60] for x in s.split("\n")[:6]))
14+
15+
g = defaultdict(lambda : "")
16+
17+
gs, ins = s.split("\n\n")
18+
19+
for i, r in enumerate(gs.split("\n")):
20+
for j in range(len(r)):
21+
if r[j] != " ":
22+
g[i,j] = r[j]
23+
24+
path = ["".join(v) for k, v in itertools.groupby(ins, str.isdigit)]
25+
26+
initx,inity = 0,0
27+
while g[initx,inity] != ".":
28+
inity += 1
29+
30+
31+
cx,cy,mdir = initx,inity,0
32+
33+
34+
35+
for z,p in enumerate(path):
36+
#print(z)
37+
if p == "R":
38+
mdir = (mdir + 1) % 4
39+
elif p == "L":
40+
mdir = (mdir - 1) % 4
41+
else:
42+
steps = int(p)
43+
dx,dy = dirs[mdir]
44+
for _ in range(steps):
45+
cx += dx
46+
cy += dy
47+
# check if off the board or blocked
48+
if g[cx,cy] == "#":
49+
cx -= dx
50+
cy -= dy
51+
break
52+
if g[cx,cy] == "":
53+
# SLOW!
54+
nx,ny = cx - dx, cy - dy
55+
while g[nx,ny] != "":
56+
nx -= dx
57+
ny -= dy
58+
nx += dx
59+
ny += dy
60+
if g[nx,ny] == "#":
61+
cx -= dx
62+
cy -= dy
63+
break
64+
cx = nx
65+
cy = ny
66+
67+
print(1000 * (cx + 1) + 4 * (cy + 1) + mdir)
68+
69+
70+
71+
# right = +dir
72+
73+
# 5 6 .
74+
# 4 /|\
75+
# 2 3 |
76+
# 1
77+
# 50x50 cells
78+
79+
cx,cy,mdir = initx,inity,0
80+
81+
def region(cx,cy):
82+
rx = cx//50
83+
ry = cy//50
84+
if (rx,ry) == (0,1): return 5
85+
if (rx,ry) == (0,2): return 6
86+
if (rx,ry) == (1,1): return 4
87+
if (rx,ry) == (2,1): return 3
88+
if (rx,ry) == (2,0): return 2
89+
if (rx,ry) == (3,0): return 1
90+
91+
def nxt(cx,cy,mdir):
92+
dx,dy = dirs[mdir]
93+
r = region(cx,cy)
94+
invalid = (g[cx + dx, cy + dy] == "")
95+
if mdir == 0:
96+
if r == 1 and invalid:
97+
# bottom of 3
98+
d = cx - 150
99+
return (149, 50 + d), 3, 3
100+
elif r == 3 and invalid:
101+
# right edge of 6
102+
d = cx - 100
103+
return (49 - d, 149), 2, 6
104+
elif r == 4 and invalid:
105+
# bottom of 6
106+
d = cx - 50
107+
return (49, 100 + d), 3, 6
108+
elif r == 6 and invalid:
109+
# right edge of 3
110+
d = cx
111+
return (149 - d, 99), 2, 3
112+
else:
113+
return (cx+dx, cy+dy), mdir, None
114+
elif mdir == 1:
115+
if r == 1 and invalid:
116+
# top of 6
117+
d = cy
118+
return (0, 100 + d), 1, 6
119+
elif r == 3 and invalid:
120+
# right of 1
121+
d = cy - 50
122+
return (150 + d, 49), 2, 1
123+
elif r == 6 and invalid:
124+
# right of 4
125+
d = cy - 100
126+
return (50 + d, 99), 2, 4
127+
else:
128+
return (cx+dx, cy+dy), mdir, None
129+
elif mdir == 2:
130+
if r == 1 and invalid:
131+
# top of 5
132+
d = cx - 150
133+
return (0, 50 + d), 1, 5
134+
elif r == 2 and invalid:
135+
# left of 5
136+
d = cx - 100
137+
return (49 - d, 50), 0, 5
138+
elif r == 4 and invalid:
139+
# top of 2
140+
d = cx - 50
141+
return (100, d), 1, 2
142+
elif r == 5 and invalid:
143+
# left of 2
144+
d = cx
145+
return (149 - d, 0), 0, 2
146+
else:
147+
return (cx+dx, cy+dy), mdir, None
148+
elif mdir == 3: # theory: 0,3
149+
if r == 2 and invalid:
150+
# left of 4
151+
d = cy
152+
return (50 + d, 50), 0, 4
153+
elif r == 5 and invalid:
154+
# left of 1
155+
d = cy - 50
156+
return (150 + d, 0), 0, 1
157+
elif r == 6 and invalid:
158+
# bottom of 1
159+
d = cy - 100
160+
return (199, d), 3, 1
161+
else:
162+
return (cx+dx, cy+dy), mdir, None
163+
164+
##
165+
### correctness test ish
166+
##for (x,y),v in list(g.items()):
167+
## if v == "": continue
168+
## for mdir in range(3):
169+
## (nx,ny),ndir,_ = nxt(x,y,mdir)
170+
## (nnx,nny),nndir,_ = nxt(nx,ny,(ndir + 2) % 4)
171+
## assert nnx == x and nny == y and (nndir + 2) % 4 == mdir
172+
173+
174+
cx,cy,mdir = initx,inity,0
175+
176+
for z,p in enumerate(path):
177+
if p == "R":
178+
mdir = (mdir + 1) % 4
179+
elif p == "L":
180+
mdir = (mdir - 1) % 4
181+
else:
182+
steps = int(p)
183+
for _ in range(steps):
184+
(nx,ny),nmd,nr = nxt(cx,cy,mdir)
185+
if nr is not None:
186+
assert region(nx,ny) == nr, "new region chk"
187+
if g[nx,ny] == "#":
188+
break
189+
assert g[nx,ny] == "."
190+
cx = nx
191+
cy = ny
192+
mdir = nmd
193+
194+
print(1000 * (cx + 1) + 4 * (cy + 1) + mdir)
195+

0 commit comments

Comments
 (0)