123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- from util import get_input
- from more_itertools import flatten
- from itertools import product
- def parse(line):
- action, stuff = line.split()
- coords = [s[2:].split("..") for s in stuff.split(",")]
- coords = [(int(a), int(b) + 1) for [a, b] in coords]
- return (action == 'on', tuple(coords))
- input = get_input("22.input", fn=parse)
- def part1():
- area = set()
- for (on, coords) in input:
- for x in range(max(coords[0][0], -50), min(coords[0][1], 50)):
- for y in range(max(coords[1][0], -50), min(coords[1][1], 50)):
- for z in range(max(coords[2][0], -50), min(coords[2][1], 50)):
- if on:
- area.add((x, y, z))
- else:
- area.discard((x, y, z))
- print("Part 1:", len(area))
- def split_by_plane(blk, coord, val):
- res = list()
- nblk = list(blk)
- if val > blk[coord][0]:
- nblk[coord] = (blk[coord][0], min(val, blk[coord][1]))
- res.append(tuple(nblk))
- if val < blk[coord][1]:
- nblk[coord] = (max(val, blk[coord][0]), blk[coord][1])
- res.append(tuple(nblk))
- return res
- def split_by(a, b):
- res = {a}
- for coord in range(3):
- for idx in range(2):
- res = set(flatten([split_by_plane(block, coord, b[coord][idx]) for block in res]))
- return res
- def intersects_coord(a, b):
- r = range(b[0] + 1, b[1])
- return a[0] in r or a[1] in r or (a[0] == b[0] and a[1] == b[1])
- def intersects(a, b):
- for coord in range(3):
- if not (intersects_coord(a[coord], b[coord]) or intersects_coord(b[coord], a[coord])):
- return False
- return True
- def contains(b, a):
- for coord in range(3):
- if range(a[coord][0], a[coord][1]) not in range(b[coord][0], b[coord][1]):
- return False
- return True
- def check_intersection(aa, bb):
- for a in aa:
- for b in bb:
- if a != b:
- if intersects(a, b):
- raise ValueError("Intersection", a, b)
- def block_size(a):
- return (a[0][1] - a[0][0]) * (a[1][1] - a[1][0]) * (a[2][1] - a[2][0])
- def test_split():
- orig = ((-10, 10), (-10, 10), (-10, 10))
- rem = ((-5, 5), (-5, 5), (-5, 5))
- split = set(split_by(orig, rem))
- print(block_size(orig))
- print(block_size(rem))
- print(sum(block_size(a) for a in split))
- res = split.difference({rem})
- print(sum(block_size(a) for a in res))
- check_intersection(split, split)
- area = set()
- for i, (on, nblock) in enumerate(input):
- print(i, "/", len(input))
- if not on:
- for eblock in list(area):
- if not intersects(eblock, nblock):
- continue
- if eblock == nblock:
- continue
- if contains(nblock, eblock):
- area.remove(eblock)
- continue
- area.remove(eblock)
- newarea = set(split_by(eblock, nblock))
- area = area.union(newarea)
- area = {b for b in area if not intersects(b, nblock)}
- else:
- nblocks = {nblock}
- possible_issues = [a for a in area if intersects(a, nblock)]
- for eblock in possible_issues:
- for nblock in list(nblocks):
- if eblock == nblock:
- continue
- if not intersects(eblock, nblock):
- continue
- nblocks.remove(nblock)
- new = set(split_by(nblock, eblock))
- new = {b for b in new if not intersects(b, eblock)}
- nblocks = nblocks.union(new)
- area = area.union(nblocks)
- part1()
- print("Part 2:", sum(block_size(b) for b in area))
|