aoc/22/day18.py
2023-12-04 10:25:33 +01:00

113 lines
2.9 KiB
Python
Executable file

#!/bin/python3
from collections import defaultdict
from queue import Queue
cubes = set()
with open('inputs/day18.txt', 'r') as f:
for line in f:
[x, y, z] = line.split(',')
cubes.add((int(x), int(y), int(z)))
def ordered(pos1, pos2):
if pos1 > pos2:
return (pos1, pos2)
else:
return (pos2, pos1)
sides = defaultdict(lambda: 0)
for cube in cubes:
(x, y, z) = cube
cube_sides = []
cube_sides.append(ordered(cube, (x+1, y, z)))
cube_sides.append(ordered(cube, (x-1, y, z)))
cube_sides.append(ordered(cube, (x, y+1, z)))
cube_sides.append(ordered(cube, (x, y-1, z)))
cube_sides.append(ordered(cube, (x, y, z+1)))
cube_sides.append(ordered(cube, (x, y, z-1)))
for side in cube_sides:
sides[side] += 1
outer_sides = set()
for side, count in sides.items():
if count == 1:
outer_sides.add(side)
print("part1", len(outer_sides))
# Determine max and min coords
x_min = min(map(lambda a: a[0], cubes))
x_max = max(map(lambda a: a[0], cubes))
y_min = min(map(lambda a: a[1], cubes))
y_max = max(map(lambda a: a[1], cubes))
z_min = min(map(lambda a: a[2], cubes))
z_max = max(map(lambda a: a[2], cubes))
# Increase bounds by 1 in all directions
x_min -= 1
x_max += 1
y_min -= 1
y_max += 1
z_min -= 1
z_max += 1
# Flood fill from 1 corner to find all reachable cubes.
class SetQueue(Queue):
def _init(self, maxsize):
self.queue = set()
def _put(self, item):
self.queue.add(item)
def _get(self):
return self.queue.pop()
cube_queue = SetQueue()
cube_queue.put((x_min, y_min, z_min))
reachable_cubes = set()
max_cubes = (x_max - x_min) * (y_max - y_min) * (z_max - z_min)
while True:
if cube_queue.empty():
break
cube = cube_queue.get()
(x, y, z) = cube
reachable_cubes.add(cube)
neighbors = []
neighbors.append((x+1, y, z))
neighbors.append((x-1, y, z))
neighbors.append((x, y+1, z))
neighbors.append((x, y-1, z))
neighbors.append((x, y, z+1))
neighbors.append((x, y, z-1))
for neighbor in neighbors:
(nx, ny, nz) = neighbor
if nx < x_min or nx > x_max or ny < y_min or ny > y_max or nz < z_min or nz > z_max:
continue
if neighbor in reachable_cubes or neighbor in cubes:
continue
# Found a new empty cube!
cube_queue.put(neighbor)
# Okay, found all empty cube outside the droplet.
surface_sides = set()
for empty in reachable_cubes:
(x, y, z) = empty
cube_sides = []
cube_sides.append(ordered(empty, (x+1, y, z)))
cube_sides.append(ordered(empty, (x-1, y, z)))
cube_sides.append(ordered(empty, (x, y+1, z)))
cube_sides.append(ordered(empty, (x, y-1, z)))
cube_sides.append(ordered(empty, (x, y, z+1)))
cube_sides.append(ordered(empty, (x, y, z-1)))
for side in cube_sides:
if side in outer_sides:
surface_sides.add(side)
print("part2", len(surface_sides))