Advent of Code 2020, day 2

Programming Advent of Code, programming, puzzle

Let’s go for Day 2! As before, all my solutions are available in a Git repository: https://git.sr.ht/~schnouki/advent-of-code.

Today’s puzzle is about password policies. Our input is made of line with a simple structure: we can parse them with a simple regular expression, and store the result in a dataclass. (We could also use a namedtuple or even a tuple, but using a class we can add methods and properties, which is often useful.)

from dataclasses import dataclass
import re

RE_LINE = re.compile(r"^(?P<v1>\d+)-(?P<v2>\d+) (?P<char>\w): (?P<password>.+)$")


@dataclass
class Password:
    v1: int
    v2: int
    char: str
    password: str

    @classmethod
    def from_line(cls, line: str) -> "Password":
        mtch = RE_LINE.match(line)
        return cls(
            v1=int(mtch["v1"]),
            v2=int(mtch["v2"]),
            char=mtch["char"],
            password=mtch["password"],
        )

Now, let’s solve this puzzle.

Part 1

A password is valid if the character in the password policy is present the right number of times in the password. OK, not a good password policy for the real world, but enough for the North Pole 😅

It’s probable tempting to use a regular expression here… but it’s also not needed at all: we can just use the string count() method and check if it’s in the right range.

Let’s add this as a property in our dataclass:

@dataclass
class Password:
    ...

    @property
    def is_valid_p1(self) -> bool:
        return self.v1 <= self.password.count(self.char) <= self.v2

Now we just have to count how many times it’s valid for our input.

data = [Password.from_line(line) for line in data.splitlines()]
result = sum(1 for password in data if password.is_valid_p1)

Done. 0.25 ms.

Part 2

Different rule: this time we need to check if the character from the policy is present in only one of the 2 given positions. One, or the other, but not both: did someone say XOR?

@dataclass
class Password:
    ...

    @property
    def is_valid_p2(self) -> bool:
        ok1 = self.password[self.v1 - 1] == self.char
        ok2 = self.password[self.v2 - 1] == self.char
        return ok1 ^ ok2

data = [Password.from_line(line) for line in data.splitlines()]
result = sum(1 for password in data if password.is_valid_p2)

Done as well. 0.23 ms.

That was easy. See you tomorrow! 👋