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