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)
|