adventofcode2023/day22/sol.py

155 lines
3.5 KiB
Python
Raw Permalink Normal View History

2023-12-22 06:23:52 +00:00
def overlaps(brick1, brick2):
"""reports whether two bricks overlap with each other"""
# the bricks as a whole overlap if there is any overlap
# on all three axes
for (a,b),(x,y) in zip(brick1, brick2):
2023-12-22 08:54:07 +00:00
if (b < x or a > y):
2023-12-22 06:23:52 +00:00
# ranges don't overlap
return False
return True
assert overlaps([(0,0), (1,2), (2,2)], [(0,0),(1,1),(2,3)])
assert not overlaps([(0,0), (1,1), (2,2)], [(1,1),(1,1),(2,2)])
def read_input(f):
data = []
for line in f:
start, end = line.split("~")
start = [int(x) for x in start.split(',')]
end = [int(x) for x in end .split(',')]
brick = [tuple(sorted([a,b])) for a,b in zip(start,end)]
data.append(brick)
return data
def check(data):
print(data)
for x in data:
for y in data:
if x == y:
assert overlaps(x,y)
else:
assert not overlaps(x,y)
print("ok")
def solve(f):
data = read_input(f)
check(data)
settle(data)
check(data)
2023-12-22 08:54:07 +00:00
disintegrate(data)
2023-12-22 06:47:19 +00:00
cascade(data)
2023-12-22 06:23:52 +00:00
def settle(data):
def zindex(b):
return b[2]
data.sort(key=zindex)
top = 1
2023-12-22 08:54:07 +00:00
for i in range(len(data)):
# first, lower to just above the top of the highest block
2023-12-22 06:23:52 +00:00
b = data[i]
2023-12-22 08:54:07 +00:00
z = b[2][0]
if z > top:
#print("lowering block %d by %d" % (i, z-top))
b = lower(b, z-top)
# lower block one at a time
2023-12-22 06:23:52 +00:00
n = 0
2023-12-22 08:54:07 +00:00
under = data[:i]
while canlower(b, under):
2023-12-22 06:23:52 +00:00
b = lower(b)
n += 1
2023-12-22 08:54:07 +00:00
print("lowering block %d by %d+%d" % (i,z-top,n))
2023-12-22 06:23:52 +00:00
data[i] = b
2023-12-22 08:54:07 +00:00
top = max(top, b[2][1] + 1)
# FIXME: should probably re-sort the data here,
# but it seems to work even if we don't
2023-12-22 06:23:52 +00:00
def canlower(block, under):
if block[2][0] <= 1:
return False
x = lower(block)
2023-12-22 06:27:36 +00:00
for u in reversed(under):
2023-12-22 06:23:52 +00:00
if overlaps(u, x):
return False
return True
def disintegrate(data):
print(*data, sep="\n")
t = 0
#for i,x in enumerate(data):
# assert not canlower(x,data[:i])
for i,x in enumerate(data):
# disintegrate x
2023-12-22 08:54:07 +00:00
# see if any block above x can be lowered
2023-12-22 06:23:52 +00:00
# if yes, then x cannot be disintegrated
candisintegrate = True
for j in range(i+1, len(data)):
if canlower(data[j], data[:i] + data[i+1:j]):
candisintegrate = False
break
print(i, x, candisintegrate)
if candisintegrate:
t += 1
2023-12-22 08:54:07 +00:00
2023-12-22 06:23:52 +00:00
print(t)
return (t)
2023-12-22 08:54:07 +00:00
2023-12-22 06:47:19 +00:00
def cascade(data):
print(*data, sep="\n")
t = 0
#for i,x in enumerate(data):
# assert not canlower(x,data[:i])
for i,x in enumerate(data):
below = data[:i]
upper = data[i+1:]
moved = 0
if below:
top = max(z[1] for x,y,z in below) + 1
else:
top = 1
for b in upper:
# lower blocks one at a time
z = b[2][0]
if z > top:
b = lower(b, z-top)
while canlower(b, below):
b = lower(b)
if b[2][0] < z:
moved += 1
#print("lowering block %d by %d" % (i,n))
below.append(b)
top = max(top, b[2][1]+1)
print(i, x, "caused %d blocks to move" % moved)
t += moved
print(t)
return (t)
2023-12-22 06:23:52 +00:00
def lower(brick, n=1):
"""lower a brick n spaces in the z direction"""
x, y, z = brick
2023-12-22 08:54:07 +00:00
if z[0] > n:
z = z[0]-n, z[1]-n
2023-12-22 06:23:52 +00:00
return [x, y, z]
import sys
solve(sys.stdin)