Advent of Code 2021: Day 2

Advent of Code 2021 - Day 2

Day 2 puzzle description

Day 2 puzzle description can be found on my Github.

My solution

Part 1

Nothing fancy here - I used simple dictionary for keeping submarine directions state ("up" | "down" | "forward") while iterating over commands and did the math at the end to calculate final position.

Part 2

For second part, I felt a little in the OOP mood and decided to model submarine as a simple class. It consists of three attributes - aim, depth, horizontal_position (all three having 0 as their default value since that's the position we start from) - and three methods that mutate these attributes based on logic described in part 2 of the puzzle - up, down, forward.

Submarine object has also final_position property that returns actual answer for this puzzle.

Something (maybe?) interesting (if you haven't heard about builtin getattr method) is handle_string_command method which accepts a command in a form of string (i.e. 'up 5' or 'forward 10') and dynamically invokes appropriate method handling particular command type (up, down or forward).

It's done by using getattr (see official Python's getattr documentation for reference or type help(getattr) while being in Python shell). It's basically a way to dynamically retrieve an attribute of an object. We call it on self (submarine object instance) and then put parentheses after it with with a val input parameter inside which results in calling retrieved attribute (a method in this case) with val argument.

For command up 5 code getattr(self, 'up')(5) is equivalent to self.up(5).

Shut up and show me the code!

from dataclasses import dataclass
from typing import List, Tuple


def parse_line(line: str) -> Tuple[str, int]:
    direction, val = line.split(" ")
    return direction, int(val)


def solution_part_1(lines: List[str]) -> int:
    directions_sums = {
        "up": 0,
        "down": 0,
        "forward": 0,
    }

    for line in lines:
        direction, val = parse_line(line)
        directions_sums[direction] += val

    depth = directions_sums["down"] - directions_sums["up"]
    horizontal_position = directions_sums["forward"]

    return depth * horizontal_position


@dataclass
class Submarine:
    aim: int = 0
    depth: int = 0
    horizontal_position: int = 0

    def down(self, value: int) -> None:
        self.aim += value

    def up(self, value: int) -> None:
        self.aim -= value

    def forward(self, value: int) -> None:
        self.horizontal_position += value
        self.depth += self.aim * value

    def handle_string_command(self, s: str) -> None:
        command, val = parse_line(s)
        getattr(self, command)(val)

    @property
    def final_position(self) -> int:
        return self.depth * self.horizontal_position


def solution_part_2(lines: List[str]) -> int:
    submarine = Submarine()
    for line in lines:
        submarine.handle_string_command(line)
    return submarine.final_position


if __name__ == "__main__":
    with open("aoc-day-2-input.txt", "rt") as f:
        data = f.readlines()
    print(solution_part_1(data))
    print(solution_part_2(data))

That's it for AoC day 2 :).

Take care,
Kuba

Thanks for reading the article, I really appreciate it! Have you heard about Braintrust - the first decentralized talent network? Whether you're a freelancer looking for a job, an employer looking for hiring talents, or you just have a wide network of connections - there's something for you there!

Go check it out and register with below link (yeah - it's my referral link and it's free - no hidden costs):

Registration link