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)