adventofcode2023/day22/sol.py

155 lines
3.5 KiB
Python

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):
if (b < x or a > y):
# 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)
disintegrate(data)
cascade(data)
def settle(data):
def zindex(b):
return b[2]
data.sort(key=zindex)
top = 1
for i in range(len(data)):
# first, lower to just above the top of the highest block
b = data[i]
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
n = 0
under = data[:i]
while canlower(b, under):
b = lower(b)
n += 1
print("lowering block %d by %d+%d" % (i,z-top,n))
data[i] = b
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
def canlower(block, under):
if block[2][0] <= 1:
return False
x = lower(block)
for u in reversed(under):
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
# see if any block above x can be lowered
# 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
print(t)
return (t)
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)
def lower(brick, n=1):
"""lower a brick n spaces in the z direction"""
x, y, z = brick
if z[0] > n:
z = z[0]-n, z[1]-n
return [x, y, z]
import sys
solve(sys.stdin)