Skip to content

Commit 7dc2c74

Browse files
Add Day2
1 parent 66cf73a commit 7dc2c74

File tree

3 files changed

+93
-2
lines changed

3 files changed

+93
-2
lines changed

Diff for: README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
## Solutions
44

5-
| Advent of Code Page | Part 1 | Part 2 | Day |
6-
|-----------------------------------------------------------|--------|--------|----------------------|
5+
| Advent of Code Page | Part 1 | Part 2 | Day |
6+
|------------------------------------------------------------------|--------|--------|------------------------|
77
| [Day 1: Historian Hysteria](https://adventofcode.com/2024/day/1) ||| [day1.py](aoc/day1.py) |
8+
| [Day 2: Red-Nosed Reports](https://adventofcode.com/2024/day/2) ||| [day2.py](aoc/day2.py) |
89

910

Diff for: aoc/day2.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import sys
2+
from pathlib import Path
3+
4+
5+
def load_reports(report_path: Path) -> list[list[int]]:
6+
return [[int(x) for x in line.split()] for line in report_path.read_text().splitlines() if line]
7+
8+
9+
def is_safe(levels: list[int]) -> bool:
10+
differences = [(j - i) for (i, j) in zip(levels, levels[1:])]
11+
return all([1 <= d <= 3 for d in differences]) or all([-3 <= d <= -1 for d in differences])
12+
13+
14+
def unsafe_level_index(levels: list[int], min_level=1, max_level=3) -> int | None:
15+
for i in range(len(levels) - 1):
16+
if not min_level <= (levels[i + 1] - levels[i]) <= max_level:
17+
return i
18+
19+
20+
def is_safe_dampened(levels: list[int]) -> bool:
21+
for ranges in [(1, 3), (-3, -1)]:
22+
i = unsafe_level_index(levels, *ranges)
23+
if i is None:
24+
return True
25+
elif unsafe_level_index(levels[:i] + levels[i + 1 :], *ranges) is None:
26+
return True
27+
elif unsafe_level_index(levels[: i + 1] + levels[i + 2 :], *ranges) is None:
28+
return True
29+
return False
30+
31+
32+
def count_report_safe(report_path: Path, is_safe_fn=is_safe) -> int:
33+
reports = load_reports(report_path)
34+
return sum([is_safe_fn(levels) for levels in reports])
35+
36+
37+
def main():
38+
print(count_report_safe(Path(sys.argv[1])))
39+
print(count_report_safe(Path(sys.argv[1]), is_safe_dampened))
40+
41+
42+
if __name__ == "__main__":
43+
main()

Diff for: test/test_day2.py

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
5+
from aoc.day2 import count_report_safe, is_safe, is_safe_dampened
6+
7+
TESTDATA = """
8+
7 6 4 2 1
9+
1 2 7 8 9
10+
9 7 6 2 1
11+
1 3 2 4 5
12+
8 6 4 4 1
13+
1 3 6 7 9
14+
"""
15+
16+
17+
def test_day2_is_safe():
18+
assert is_safe([7, 6, 4, 2, 1])
19+
assert not is_safe([1, 2, 7, 8, 9])
20+
assert not is_safe([9, 7, 6, 2, 1])
21+
assert not is_safe([1, 3, 2, 4, 5])
22+
assert not is_safe([8, 6, 4, 4, 1])
23+
assert is_safe([1, 3, 6, 7, 9])
24+
25+
26+
@pytest.fixture(scope="session")
27+
def input_path(tmpdir_factory):
28+
p = Path(tmpdir_factory.mktemp("data")) / "input.txt"
29+
p.write_text(TESTDATA)
30+
return p
31+
32+
33+
def test_day2_count_reports(input_path):
34+
assert count_report_safe(input_path) == 2
35+
36+
37+
def test_day2_is_dampened_safe():
38+
assert is_safe_dampened([7, 6, 4, 2, 1])
39+
assert not is_safe_dampened([1, 2, 7, 8, 9])
40+
assert not is_safe_dampened([9, 7, 6, 2, 1])
41+
assert is_safe_dampened([1, 3, 2, 4, 5])
42+
assert is_safe_dampened([8, 6, 4, 4, 1])
43+
assert is_safe_dampened([1, 3, 6, 7, 9])
44+
45+
46+
def test_day2_count_dampened_reports(input_path):
47+
assert count_report_safe(input_path, is_safe_fn=is_safe_dampened) == 4

0 commit comments

Comments
 (0)