|
|
@@ -0,0 +1,49 @@ |
|
|
|
@lambda fn: lambda brick: set(fn(brick)) |
|
|
|
def dimension(brick): |
|
|
|
(x1, x2), (y1, y2), (z1, z2) = map(sorted, zip(*brick)) |
|
|
|
for z in range(z1, z2 + 1): |
|
|
|
for y in range(y1, y2 + 1): |
|
|
|
for x in range(x1, x2 + 1): |
|
|
|
yield x, y, z |
|
|
|
|
|
|
|
|
|
|
|
def tetris(bricks): |
|
|
|
floor = {(x, y, 0) for x in range(100) for y in range(100)} |
|
|
|
seen = floor |
|
|
|
for brick in map(dimension, bricks): |
|
|
|
while not seen & brick: |
|
|
|
brick = {(x, y, z - 1) for x, y, z in brick} |
|
|
|
resting_place = {(x, y, z + 1) for x, y, z in brick} |
|
|
|
seen |= resting_place |
|
|
|
yield resting_place |
|
|
|
|
|
|
|
|
|
|
|
def topple(i, depends_on, holds_up): |
|
|
|
gone = set() |
|
|
|
state = {i} |
|
|
|
while state: |
|
|
|
gone |= state |
|
|
|
state = {above |
|
|
|
for below in state |
|
|
|
for above in depends_on.get(below, []) |
|
|
|
if holds_up[above] <= gone |
|
|
|
} |
|
|
|
return gone - {i} |
|
|
|
|
|
|
|
|
|
|
|
text = open(0).read() |
|
|
|
bricks = [[[int(n) for n in sub.split(',')] for sub in line.split('~')] for line in text.splitlines()] |
|
|
|
bricks.sort(key=lambda brick: min(z for x, y, z in brick)) |
|
|
|
bricks = list(tetris(bricks)) |
|
|
|
|
|
|
|
known = {i for i, _ in enumerate(bricks)} |
|
|
|
holds_up, depends_on = {}, {} |
|
|
|
for j, brick in enumerate(bricks): |
|
|
|
j_brick_1_down = {(x, y, z - 1) for x, y, z in brick} |
|
|
|
holds_up[j] = {i for i, i_brick in enumerate(bricks) if i != j and j_brick_1_down & i_brick} |
|
|
|
for i in holds_up[j]: |
|
|
|
depends_on.setdefault(i, set()).add(j) |
|
|
|
|
|
|
|
needed = {v for k, vs in holds_up.items() if len(vs) == 1 for v in vs} |
|
|
|
print(len(known - needed)) |
|
|
|
print(sum(len(topple(i, depends_on, holds_up)) for i in needed)) |