advent-of-code/2021/python/day09.py

133 lines
4.0 KiB
Python

import sys
from matrix import load_matrix_file, get_neighbors, get_size
import shared
from scipy import ndimage
import imageio
from skimage.measure import euler_number, label
from skimage.morphology import flood_fill, flood
import matplotlib.pyplot as plt
import numpy as np
from math import prod
import random
class Smoke:
def __init__(self, name):
self.footprint = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]])
self.parse(name)
def parse(self, name):
_split = lambda line: list(map(int, [l for l in line]))
self.mx = np.array(load_matrix_file(name, func=_split))
self.mx = np.pad(self.mx, pad_width=1, mode="constant", constant_values="9")
self.set_values()
self.set_coords()
def set_values(self):
self.lows = (
ndimage.generic_filter(self.mx, np.min, footprint=self.footprint) == self.mx
)
self.low_values = self.mx[self.lows][self.mx[self.lows] < 9]
self.highs = (
ndimage.generic_filter(self.mx, np.max, footprint=self.footprint) == self.mx
)
mask = self.mx != 9
self.mask = mask.astype(int)
def set_coords(self):
lc = []
for idr, row in enumerate(self.lows):
if idr == 0 or idr == len(self.lows) - 1:
continue
for idc, col in enumerate(row):
if idc == 0 or idc == len(row) - 1:
continue
if col:
lc.append((idr, idc))
self.low_coords = lc
def total_low_values(self):
return (self.low_values + 1).sum()
def find_chunks(self):
cell_sizes = []
self.cells = []
by_center = []
for x, y in self.low_coords:
current = np.zeros_like(self.mx)
m = flood_fill(self.mask, (x, y), -1, connectivity=1)
current_cell = (m == -1).astype(int)
self.cells.append(current_cell)
cells = np.argwhere(current_cell == 1)
by_center.append((len(cells), (x, y), cells))
cell_sizes.append(len(cells))
cells_sorted = list(set(sorted(cell_sizes)))
self.by_center = by_center
largest = cells_sorted[-3:]
assert len(largest) == 3
self.largest = largest
return prod(largest)
def animate(self, save=False):
largest = [x for x in self.by_center if x[0] in self.largest][-3:]
current = np.zeros_like(self.mx)
shuf = self.by_center[:]
random.shuffle(shuf)
last_file = 0
for idx, (length, xy, c) in enumerate(shuf):
last_file = idx
if length in largest:
continue
for x, y in c:
current[y][x] = 1
fig, ax = plt.subplots()
ax.imshow(current, cmap=plt.cm.gray)
ax.axis("off")
if save:
plt.savefig(f"09/{idx:03}.png")
plt.close()
lg = np.zeros_like(self.mx)
for _, _, c in largest:
for x, y in c:
lg[y][x] = 1
fig, ax = plt.subplots()
ax.imshow(lg)
ax.axis("off")
plt.savefig(f"09/largest.png")
plt.close()
with imageio.get_writer("09/day09.gif", mode="I") as writer:
names = [f"09/{x:03}.png" for x in range(334)]
for filename in names:
try:
image = imageio.imread(filename)
writer.append_data(image)
except FileNotFoundError:
pass
for x in range(15):
image = imageio.imread("09/largest.png")
for x in range(5):
writer.append_data(image)
image = imageio.imread(f"09/{last_file:03}.png")
for x in range(5):
writer.append_data(image)
def main():
s = Smoke(shared.get_fname(9))
print(s.total_low_values())
print(s.find_chunks())
if sys.argv[-1] != "--sample":
s.animate(1)
if __name__ == "__main__":
main()