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

58 lines
2.6 KiB
Markdown
Raw Normal View History

2023-04-08 02:32:54 +02:00
---
title: "Advent of Code 2021 - Day 4"
date: 2021-12-04T11:23:18+01:00
day: 4
stars: 2
math: true
---
Today's problem is [Day 4](https://adventofcode.com/2021/day/4). This time the challenge is inspied by the game called Bingo. We get a vector of number and a number of bingo boards. We need to find the board that wins first and last, then we need to calculate score for it.
Loading data from file into NumPy arrays:
```python
def loader_np() -> np.ndarray:
with open('../.input/day04', 'r') as f:
data = f.read().splitlines()
vector = np.array([int(x) for x in data[0].split(",")])
tensor = np.array([
np.array(list(map(np.int32, map(str.split, board))))
for board
in [data[i:i+5] for i in range(2, len(data), 6)]
])
return vector, tensor
```
## Task 1
Here we need to find the first winning board. I used the `np.isin` function to find the mask for all board, for example given vector `[1, 2, 3, 4, 5]` and matrix `[[1, 2, 3], [4, 5, 6]]` we get `[[True, True, True], [True, True, False]]`. The vector changes size through slicing, with each iteration of the outer loop - it simulates drawing a new number. The inner for loop iterates through all boards (matrices), there we check if on any axis there's a full row or column of matching numbers. If there's a match we calculate the score.
```python
def solve1() -> int:
vector, tensor = loader_np()
for scale in range(1, len(vector)+1):
for matrix in tensor:
mask = np.isin(matrix, vector[:scale])
if mask.all(axis=0).any() or mask.all(axis=1).any():
return (matrix * ~mask).sum() * vector[scale-1]
```
The score is calculated by taking a dot product of the matrix with the negation of the mask (which eliminates unmarked numbers), summing the remaining values and multiplying by the value of the drawn number.
$$ \text{score} = v_{drawn} \cdot \sum_{i=1}^{n} \sum_{j=1}^{m} \text{board}[i,j] \cdot \neg \text{mask}[i,j] $$
## Task 2
This is analogous to the first task, but we need to find the last winning board. I iterate from the end of the vector. The inner loop is similar to the one in the first task, just needed to find when the first board doesn't match and then calculate another mask for the round afterwards (scale+1).
```python
def solve2() -> int:
vector, tensor = loader_np()
for scale in range(len(vector)+1, 0, -1):
for matrix in tensor:
mask = np.isin(matrix, vector[:scale])
if not mask.all(axis=0).any() and not mask.all(axis=1).any():
prev_mask = np.isin(matrix, vector[:scale+1])
return (matrix * ~prev_mask).sum() * vector[scale]
```