Skip to content

Commit d3e4b2c

Browse files
feat: add 2023 day 09
1 parent 2768264 commit d3e4b2c

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import re
2+
from functools import partial
3+
from itertools import pairwise
4+
from operator import itemgetter
5+
from typing import Iterable
6+
7+
from adventofcode.util.exceptions import SolutionNotFoundError
8+
from adventofcode.registry.decorators import register_solution
9+
from adventofcode.util.input_helpers import get_input_for_day
10+
11+
12+
pattern = re.compile("-?\\d+")
13+
14+
15+
def parse_sequence(sequence: str) -> Iterable[int]:
16+
return map(int, pattern.findall(sequence))
17+
18+
19+
def parse_sequence_backwards(sequence: str) -> Iterable[int]:
20+
return reversed(list(map(int, pattern.findall(sequence))))
21+
22+
23+
def parse_input(data: list[str]) -> Iterable[Iterable[int]]:
24+
return map(parse_sequence, data)
25+
26+
27+
def parse_input_reversed(data: list[str]) -> Iterable[Iterable[int]]:
28+
return map(parse_sequence_backwards, data)
29+
30+
31+
def find_next_in_sequence(sequence: Iterable[int], lines: list[list[int]] | None) -> int:
32+
numbers = list(sequence)
33+
lines = [numbers] if lines is None or len(lines) == 0 else lines
34+
differences = [y-x for (x, y) in pairwise(numbers)]
35+
lines.append(differences)
36+
37+
if any(differences):
38+
return find_next_in_sequence(differences, lines)
39+
40+
return sum(map(itemgetter(-1), lines))
41+
42+
43+
def solve_part_one(data: list[str]) -> int:
44+
parsed_input = parse_input(data)
45+
find_next = partial(find_next_in_sequence, lines=None)
46+
return sum(map(find_next, parsed_input))
47+
48+
49+
def solve_part_two(data: list[str]) -> int:
50+
parsed_input = parse_input_reversed(data)
51+
find_next = partial(find_next_in_sequence, lines=None)
52+
return sum(map(find_next, parsed_input))
53+
54+
55+
@register_solution(2023, 9, 1)
56+
def part_one(input_data: list[str]):
57+
answer = solve_part_one(input_data)
58+
59+
if not answer:
60+
raise SolutionNotFoundError(2023, 9, 1)
61+
62+
return answer
63+
64+
65+
@register_solution(2023, 9, 2)
66+
def part_two(input_data: list[str]):
67+
answer = solve_part_two(input_data)
68+
69+
if not answer:
70+
raise SolutionNotFoundError(2023, 9, 2)
71+
72+
return answer
73+
74+
75+
if __name__ == '__main__':
76+
data = get_input_for_day(2023, 9)
77+
part_one(data)
78+
part_two(data)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import pytest
2+
3+
from adventofcode.year_2023.day_09_2023 import part_two, part_one, parse_input, parse_sequence, find_next_in_sequence
4+
5+
test_input = [
6+
"0 3 6 9 12 15",
7+
"1 3 6 10 15 21",
8+
"10 13 16 21 30 45",
9+
]
10+
11+
12+
@pytest.mark.parametrize(["sequence", "expected"], [
13+
("0 3 6 9 12 15", [0, 3, 6, 9, 12, 15]),
14+
("1 3 6 10 15 21", [1, 3, 6, 10, 15, 21]),
15+
("10 13 16 21 30 45", [10, 13, 16, 21, 30, 45]),
16+
("-1 -20 -300 400", [-1, -20, -300, 400]),
17+
("5 1 -3 -7 -11 -15 -19 -23 -27 -31 -35 -39 -43 -47 -51 -55 -59 -63 -67 -71 -75", [5, 1, -3, -7, -11, -15, -19, -23, -27, -31, -35, -39, -43, -47, -51, -55, -59, -63, -67, -71, -75]),
18+
])
19+
def test_parse_sequence(sequence, expected):
20+
assert list(parse_sequence(sequence)) == expected
21+
22+
23+
def test_parse_input():
24+
assert len(list(parse_input(test_input))) == 3
25+
26+
27+
@pytest.mark.parametrize(["sequence", "expected"], [
28+
([13, 23, 46, 97, 206, 436, 919, 1924, 3971, 8016, 15765, 30243, 56875, 105604, 195153, 361805, 677725, 1289089,
29+
2494076, 4898137, 9713336], 19316320),
30+
([-7, 6, 44, 130, 309, 672, 1404, 2864, 5712, 11113, 21082, 39112, 71386, 129156, 233312, 422773, 769075,
31+
1400300, 2538072, 4551386, 8029995], 13877188
32+
),
33+
([5, 1, -3, -7, -11, -15, -19, -23, -27, -31, -35, -39, -43, -47, -51, -55, -59, -63, -67, -71, -75,
34+
], -79)
35+
])
36+
def test_find_next_in_sequence(sequence, expected):
37+
assert find_next_in_sequence(sequence, []) == expected
38+
39+
40+
def test_part_one():
41+
assert part_one(test_input) == 114
42+
43+
44+
def test_part_two():
45+
assert part_two(test_input) == 'x'

0 commit comments

Comments
 (0)