Skip to content

Commit eed2767

Browse files
committed
2023 d22: Add original solution
1 parent 70e2139 commit eed2767

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed

2023/original_solutions/day22.py

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#!/usr/bin/env python3
2+
3+
from utils.all import *
4+
5+
advent.setup(2023, 22)
6+
DEBUG = 'debug' in map(str.lower, sys.argv)
7+
fin = advent.get_input() if not DEBUG else io.StringIO('''\
8+
1,0,1~1,2,1
9+
0,0,2~2,0,2
10+
0,2,3~2,2,3
11+
0,0,4~0,2,4
12+
2,0,5~2,2,5
13+
0,1,6~2,1,6
14+
1,1,8~1,1,9
15+
''')
16+
17+
def draw_xz(space):
18+
g = {}
19+
maxz = max(map(itemgetter(2), space))
20+
for x in range(10): g[maxz - 0 + 1, x] = '-'
21+
for (x, _, z), ident in space.items(): g[maxz - z + 1, x] = ident
22+
dump_sparse_matrix(g)
23+
24+
def draw_yz(space):
25+
g = {}
26+
maxz = max(map(itemgetter(1), space))
27+
for y in range(10): g[maxz - 0 + 1, y] = '-'
28+
for (_, y, z), ident in space.items(): g[maxz - z + 1, y] = ident
29+
dump_sparse_matrix(g)
30+
31+
def fall_one(space, ident, ax, ay, az, bx, by, bz):
32+
assert (az != bz) + (ax != bx) + (ay != by) <= 1
33+
# eprint('brick', ident, (ax, ay, az, bx, by, bz), 'falls')
34+
35+
below = set()
36+
37+
if ax != bx:
38+
# Find first z (from az down) for which x,ay,z is occupied
39+
for minz in range(az, -1, -1):
40+
for x in autorange(ax, bx):
41+
# eprint('>', (x, ay, minz))
42+
if (x, ay, minz) in space:
43+
# eprint('> obstacle', (x, ay, minz))
44+
below.add(space[x, ay, minz])
45+
46+
if below:
47+
break
48+
49+
minz += 1
50+
# eprint(minz)
51+
for x in autorange(ax, bx):
52+
space[x, ay, minz] = ident
53+
elif ay != by:
54+
# Find first z (from az down) for which ax,y,z is occupied
55+
for minz in range(az, -1, -1):
56+
for y in autorange(ay, by):
57+
# eprint('>', (ax, y, minz))
58+
if (ax, y, minz) in space:
59+
# eprint('> obstacle', (ax, y, minz))
60+
below.add(space[ax, y, minz])
61+
62+
if below:
63+
break
64+
65+
minz += 1
66+
# eprint(minz)
67+
for y in autorange(ay, by):
68+
space[ax, y, minz] = ident
69+
else:
70+
zlo = min(az, bz)
71+
72+
# Find first z (from z down) for which ax,ay,z is occupied
73+
for minz in range(zlo, -1, -1):
74+
# eprint('>', (ax, ay, minz))
75+
if (ax, ay, minz) in space:
76+
# eprint('> obstacle', (ax, ay, minz))
77+
below = {space[ax, ay, minz]}
78+
break
79+
80+
minz += 1
81+
# eprint(minz)
82+
83+
delta = zlo - minz
84+
az -= delta
85+
bz -= delta
86+
87+
for z in autorange(az, bz):
88+
space[ax, ay, z] = ident
89+
90+
return below
91+
92+
93+
def fall():
94+
bricks.sort(key=lambda b: min(b[2], b[5]))
95+
space = {}
96+
supports = {}
97+
98+
for identifier, b in enumerate(bricks):
99+
below = fall_one(space, identifier, *b)
100+
supports[identifier] = below
101+
# draw_xz(space)
102+
# draw_yz(space)
103+
104+
# for x in sorted(supports):
105+
# print(x, bricks[x], 'supported by', supports[x])
106+
107+
deletable = set(range(len(bricks)))
108+
109+
# I cannot delete a brick if it's the only one supporting some other brick
110+
for below in supports.values():
111+
if len(below) == 1:
112+
deletable.discard(next(iter(below)))
113+
114+
return deletable, supports
115+
116+
117+
lines = read_lines(fin)
118+
bricks = []
119+
120+
for line in lines:
121+
coords = extract_ints(line)
122+
bricks.append(coords)
123+
124+
deletable, supports = fall()
125+
126+
ans1 = len(deletable)
127+
advent.print_answer(1, ans1)
128+
129+
130+
def find_falling(supported_by, supports, root):
131+
falling = {root}
132+
q = deque([root])
133+
134+
while q:
135+
brick = q.popleft()
136+
137+
for a in supported_by[brick]:
138+
if all(s in falling for s in supports[a]):
139+
falling.add(a)
140+
q.append(a)
141+
142+
falling.discard(root)
143+
return falling
144+
145+
not_deletable = set(range(len(bricks))) - deletable
146+
assert len(deletable) + len(not_deletable) == len(bricks)
147+
148+
# 33333
149+
# 2 4
150+
# 111 4
151+
# 0 4
152+
# not_deletable = {0, 1}
153+
# supports = {
154+
# 0: set(),
155+
# 1: {0},
156+
# 2: {1},
157+
# 3: {2,4},
158+
# 4: set(),
159+
# }
160+
161+
supported_by = defaultdict(set)
162+
for brick, below in supports.items():
163+
for b in below:
164+
supported_by[b].add(brick)
165+
166+
tot = 0
167+
for brick in not_deletable:
168+
falling = find_falling(supported_by, supports, brick)
169+
# print('deleting', brick, 'would make these fall:', falling)
170+
tot += len(falling)
171+
172+
advent.print_answer(2, tot)

0 commit comments

Comments
 (0)