website/content/wiki/aoc/2021/02.md

83 lines
3.4 KiB
Markdown
Raw Permalink Normal View History

2023-04-08 02:32:54 +02:00
---
2023-06-06 20:43:21 +02:00
title: "AoC 2021 - Day 2"
2023-04-08 02:32:54 +02:00
date: 2021-12-02T19:46:19+01:00
day: 2
stars: 2
---
Day 2 of Advent of Code is [here,](https://adventofcode.com/2021/day/2) and it turned out to be pretty easy. Starting off all we get is a list of instructions of the form `<instruction> <parameter>` where `<instruction>` is one of `forward`, `up` or `down` and `<parameter>` is an integer.
My idea to parse this list of instructions was to create a list of tuples of the form `(instruction, parameter)` using a regular expression `^([a-z]+) ([0-9]+)$`. This regular expression makes use of [capturing groups](https://docs.python.org/3/howto/regex.html#grouping) to extract the instruction and the parameter separately. The groups are accessed using `match.group(1)` and `match.group(2)`.
```python
pattern = re.compile('^([a-z]+) ([0-9]+)$')
def load_input() -> list[Tuple[str, int]]:
with open('../.input/day02', 'r') as f:
return [
(match.group(1), int(match.group(2))) for match
in (pattern.search(line.strip()) for line in f.readlines())
]
```
## Task 1
Here my idea was to use a dictionary to store functions (lambdas) under keys `'forward'`, `'up'` and `'down'`. The functions are then called with the parameter as argument together with the current position.
I used `functools.reduce` to fold the list of instructions into a single final position. Reduce represents in Python the mathematical concept of a [fold](https://en.wikipedia.org/wiki/Fold_(higher-order_function)), which also is a commonly used operation in functional programming. The function `functools.reduce` takes a function, a list and an initial value as arguments. The function is applied to the initial value and the first element of the list, then the result is used as the initial value for the next iteration. The result of the last iteration is returned.
This operation can be represented as `foldl f z xs` in some other functional languages:
```haskell
-- Haskell
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
```
Here's my solution:
```python
def solve1() -> int:
numbers = load_input()
instructions = {
"forward": lambda arg, x, y: (x + arg, y),
"up": lambda arg, x, y: (x, y + arg),
"down": lambda arg, x, y: (x, y - arg),
}
x, y = reduce(lambda pos, item: instructions[item[0]](item[1], *pos), numbers, (0, 0))
return abs(x) * abs(y)
```
The key part is right here:
```python
# unpacking the final position into x and y
x, y = reduce(
# Function to apply which takes the previous result (accumulator) and the next item in the list.
# We use dict to get the function from the dictionary.
# We use *pos to unpack accumulated value into function arguments.
lambda pos, item: instructions[item[0]](item[1], *pos),
# list of instructions to fold
numbers,
# initial value for the accumulator
(0, 0)
)
```
## Task 2
Task 2 was pretty much identical to task 1. I used a dictionary to store functions (lambdas) under keys `'forward'`, `'up'` and `'down'`, except that the functions have different bodies and they take an additional parameter `a` for aim.
```python
def solve2() -> int:
numbers = load_input()
instructions = {
"forward": lambda arg, x, y, a: (x + arg, y + a * arg, a),
"up": lambda arg, x, y, a: (x, y, a - arg),
"down": lambda arg, x, y, a: (x, y, a + arg),
}
x, y, _ = reduce(lambda pos, item: instructions[item[0]](item[1], *pos), numbers, (0, 0, 0))
return abs(x) * abs(y)
```