|
4 | 4 | # |
5 | 5 |
|
6 | 6 | import sys |
| 7 | +from enum import Enum |
| 8 | +from enum import auto |
| 9 | +from enum import unique |
7 | 10 |
|
8 | 11 | from aoc.common import InputData |
9 | 12 | from aoc.common import SolutionBase |
10 | 13 | from aoc.common import aoc_samples |
11 | 14 |
|
12 | | -Input = list[int] |
13 | | -Output1 = int |
14 | | -Output2 = int |
15 | | - |
16 | | - |
17 | 15 | TEST = """\ |
18 | 16 | L68 |
19 | 17 | L30 |
|
31 | 29 | TOTAL = 100 |
32 | 30 |
|
33 | 31 |
|
| 32 | +@unique |
| 33 | +class Count(Enum): |
| 34 | + LANDED_ON_ZERO = auto() |
| 35 | + PASSED_BY_ZERO = auto() |
| 36 | + |
| 37 | + def count(self, dial: int, rotation: int) -> int: |
| 38 | + match self: |
| 39 | + case Count.LANDED_ON_ZERO: |
| 40 | + return 1 if (dial + rotation) % TOTAL == 0 else 0 |
| 41 | + case Count.PASSED_BY_ZERO: |
| 42 | + if rotation >= 0: |
| 43 | + return (dial + rotation) // TOTAL |
| 44 | + return ((TOTAL - dial) % TOTAL - rotation) // TOTAL |
| 45 | + |
| 46 | + |
| 47 | +Input = list[int] |
| 48 | +Output1 = int |
| 49 | +Output2 = int |
| 50 | + |
| 51 | + |
34 | 52 | class Solution(SolutionBase[Input, Output1, Output2]): |
35 | 53 | def parse_input(self, input_data: InputData) -> Input: |
36 | 54 | return [ |
37 | 55 | (1 if line[0] == "R" else -1) * int(line[1:]) |
38 | 56 | for line in input_data |
39 | 57 | ] |
40 | 58 |
|
41 | | - def part_1(self, rotations: Input) -> Output1: |
42 | | - dial = START |
43 | | - pos = (dial := (dial + r) % TOTAL for r in rotations) |
44 | | - return sum(p == 0 for p in pos) |
45 | | - |
46 | | - def part_2(self, rotations: Input) -> Output2: |
| 59 | + def solve(self, rotations: Input, count: Count) -> int: |
47 | 60 | dial = START |
48 | 61 | ans = 0 |
49 | | - for r in rotations: |
50 | | - div, mod = divmod(r, TOTAL if r > 0 else -TOTAL) |
51 | | - ans += div |
52 | | - if (r < 0 and dial != 0 and dial + mod <= 0) or ( |
53 | | - r > 0 and dial + mod >= TOTAL |
54 | | - ): |
55 | | - ans += 1 |
56 | | - dial = (dial + r) % TOTAL |
| 62 | + for rotation in rotations: |
| 63 | + ans += count.count(dial, rotation) |
| 64 | + dial = (dial + rotation) % TOTAL |
57 | 65 | return ans |
58 | 66 |
|
| 67 | + def part_1(self, rotations: Input) -> Output1: |
| 68 | + return self.solve(rotations, Count.LANDED_ON_ZERO) |
| 69 | + |
| 70 | + def part_2(self, rotations: Input) -> Output2: |
| 71 | + return self.solve(rotations, Count.PASSED_BY_ZERO) |
| 72 | + |
59 | 73 | @aoc_samples( |
60 | 74 | ( |
61 | 75 | ("part_1", TEST, 3), |
|
0 commit comments