website/src/content/aoc/2021/11.md

90 lines
2.8 KiB
Markdown
Raw Normal View History

2023-04-08 02:32:54 +02:00
---
title: "Advent of Code 2021 - Day 11"
date: 2021-12-11T09:09:14+01:00
math: true
day: 11
stars: 2
---
Today's challenge is available [here](https://adventofcode.com/2021/day/11).
Octopuses flash when their energy levels reach above 9 and it causes their neignbours to increase their levels by 1 too? This sounds like a perfect job for some kind of a matrix operation...
Loading data:
```py
def load():
with open('../.input/day11') as f:
lines = list(map(str.strip, f.readlines()))
return np.array([int(num) for line in lines for num in line]).reshape((-1, len(lines)))
```
In this chanllenge I decided to try my hand at using convolution matrix to solve this problem. First I had to create a function that would return an array containing octopuses and their neighbours. For this I used the following code:
```py
from numpy.lib.stride_tricks import as_strided
def windows(target, shape=(3, 3), stride: int = 1):
target = np.pad(target, 1, 'constant', constant_values=0)
(t_h, t_w), (w_h, w_w) = target.shape, shape
out_shape = (
1 + (t_h - w_h) // stride,
1 + (t_w - w_w) // stride,
w_h, w_w
)
out_strides = (
target.strides[0] * stride,
target.strides[1] * stride,
target.strides[0], target.strides[1]
)
return as_strided(target, shape=out_shape, strides=out_strides)
```
The function `as_strided` is a pretty esoteric function, which operates directly on memory, which also means a lot can go very wrong when using it.
Here I used this function to calculate how the energy levels would progress through the time:
```py
def step(input: np.ndarray):
current = input + np.ones(input.shape, dtype=np.int32)
flashed = np.zeros(input.shape, dtype=np.bool8)
while np.any(now_flashed := ((current * ~flashed) > 9)):
convolution = np.tensordot(
windows(now_flashed.astype(np.int32)),
np.array([[1, 1, 1], [1, 0, 1], [1, 1, 1]]),
axes=((2, 3), (0, 1))
)
flashed |= now_flashed
current = current + convolution * ~flashed
return np.where(current > 9, 0, current), flashed
```
I used a convolution matrix of the following shape:
$$ \begin{bmatrix} 1 & 1 & 1 \\\\ 1 & 0 & 1 \\\\ 1 & 1 & 1 \end{bmatrix} $$
## Task 1
Here we sum all the flashes that happened in all of the iterations with `np.sum`:
```py
def solve1() -> int:
input = load()
flash_count = 0
for _ in range(100):
input, flashed = step(input)
flash_count += np.sum(flashed)
return flash_count
```
## Task 2
Here we check when all the octopuses flashed with `np.all`:
```py
def solve2() -> int:
input = load()
steps = 0
while (steps := steps + 1):
input, flashed = step(input)
if np.all(flashed):
return steps
```