From d7ee48d4590c5b5aaa3bd304eb5865dc05e68442 Mon Sep 17 00:00:00 2001 From: Luc Rubio Date: Tue, 10 Dec 2024 22:25:10 +0100 Subject: [PATCH] AoC 2024 day 10 part 2 --- aoc2024/src/day10/python/solution.py | 54 ++++++++++++++++------ aoc2024/test/day10/python/example5.txt | 7 +++ aoc2024/test/day10/python/example6.txt | 7 +++ aoc2024/test/day10/python/example7.txt | 6 +++ aoc2024/test/day10/python/example8.txt | 8 ++++ aoc2024/test/day10/python/test_solution.py | 17 ++++++- 6 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 aoc2024/test/day10/python/example5.txt create mode 100644 aoc2024/test/day10/python/example6.txt create mode 100644 aoc2024/test/day10/python/example7.txt create mode 100644 aoc2024/test/day10/python/example8.txt diff --git a/aoc2024/src/day10/python/solution.py b/aoc2024/src/day10/python/solution.py index d44507a..c70ce7e 100644 --- a/aoc2024/src/day10/python/solution.py +++ b/aoc2024/src/day10/python/solution.py @@ -13,11 +13,13 @@ # limitations under the License. from typing import Sequence -type Pos = tuple[int, int] +type Pos = tuple[int, int] # (y,x) _TRAILHEAD = 0 _SUMMIT = 9 _IS_GRADUAL_SLOPE = lambda level, next_level: next_level == level + 1 +_IS_WITHIN_BOUNDS = lambda pos, width, height: 0 <= pos[0] < height and 0 <= pos[1] < width +_DIRECTIONS = ((-1, 0), (0, 1), (1, 0), (0, -1)) def _find_trailheads(topographic_map: Sequence[str]) -> tuple[Pos, ...]: @@ -30,28 +32,54 @@ def _find_trailheads(topographic_map: Sequence[str]) -> tuple[Pos, ...]: return tuple(trailhead_positions) -def _count_reachable_summits(topo_map: Sequence[str], curr_pos: Pos, visited: set[Pos], num_summits: list[int]): +def _count_summits(topo_map: Sequence[str], curr_pos: Pos, visited: set[Pos], counter: list[int]): + """Counts the number of summits that can be reached from a position.""" curr_level = int(topo_map[curr_pos[0]][curr_pos[1]]) - if curr_level == _SUMMIT and curr_pos not in visited: - num_summits[0] += 1 - visited.add(curr_pos) + if curr_level == _SUMMIT: + if curr_pos not in visited: + counter[0] += 1 + visited.add(curr_pos) return visited.add(curr_pos) - for step in ((-1, 0), (0, 1), (1, 0), (0, -1)): + for step in _DIRECTIONS: next_pos = (curr_pos[0] + step[0]), (curr_pos[1] + step[1]) - within_map = 0 <= next_pos[0] < len(topo_map) and 0 <= next_pos[1] < len(topo_map[0]) - if next_pos not in visited and within_map: + if next_pos not in visited and _IS_WITHIN_BOUNDS(next_pos, len(topo_map[0]), len(topo_map)): next_level = int(topo_map[next_pos[0]][next_pos[1]]) if _IS_GRADUAL_SLOPE(curr_level, next_level): - _count_reachable_summits(topo_map, next_pos, visited, num_summits) + _count_summits(topo_map, next_pos, visited, counter) + + +def _count_distinct_paths(topo_map: Sequence[str], curr_pos: Pos, counter: list[int]): + """Counts the number of distinct paths from trailhead to summit.""" + curr_level = int(topo_map[curr_pos[0]][curr_pos[1]]) + if curr_level == _SUMMIT: + counter[0] += 1 + return + for step in _DIRECTIONS: + next_pos = (curr_pos[0] + step[0]), (curr_pos[1] + step[1]) + if _IS_WITHIN_BOUNDS(next_pos, len(topo_map[0]), len(topo_map)): + next_level = int(topo_map[next_pos[0]][next_pos[1]]) + if _IS_GRADUAL_SLOPE(curr_level, next_level): + _count_distinct_paths(topo_map, next_pos, counter) def get_score_sum(topographic_map: Sequence[str]) -> int: """Gets the sum of all the trails from all the trailheads.""" - sum_scores = 0 + score_sum = 0 trailheads_positions: tuple[Pos, ...] = _find_trailheads(topographic_map) for trailhead_pos in trailheads_positions: num_summits = [0] # Pass by reference. - _count_reachable_summits(topographic_map, trailhead_pos, set(), num_summits) - sum_scores += num_summits[0] - return sum_scores + _count_summits(topographic_map, trailhead_pos, set(), num_summits) + score_sum += num_summits[0] + return score_sum + + +def get_rating_sum(topographic_map: Sequence[str]) -> int: + """Gets the sum of all distinct trails from all the trailheads.""" + rating_sum = 0 + trailheads_positions: tuple[Pos, ...] = _find_trailheads(topographic_map) + for trailhead_pos in trailheads_positions: + num_trails = [0] # Pass by reference. + _count_distinct_paths(topographic_map, trailhead_pos, num_trails) + rating_sum += num_trails[0] + return rating_sum diff --git a/aoc2024/test/day10/python/example5.txt b/aoc2024/test/day10/python/example5.txt new file mode 100644 index 0000000..6e9256c --- /dev/null +++ b/aoc2024/test/day10/python/example5.txt @@ -0,0 +1,7 @@ +1111801 +1143211 +1151121 +1165431 +1171141 +1187651 +1191111 \ No newline at end of file diff --git a/aoc2024/test/day10/python/example6.txt b/aoc2024/test/day10/python/example6.txt new file mode 100644 index 0000000..9c4cb8f --- /dev/null +++ b/aoc2024/test/day10/python/example6.txt @@ -0,0 +1,7 @@ +1190219 +1111198 +1112117 +6543456 +7651987 +8761111 +9871111 \ No newline at end of file diff --git a/aoc2024/test/day10/python/example7.txt b/aoc2024/test/day10/python/example7.txt new file mode 100644 index 0000000..3ac36ef --- /dev/null +++ b/aoc2024/test/day10/python/example7.txt @@ -0,0 +1,6 @@ +012345 +123456 +234567 +345678 +416789 +567891 \ No newline at end of file diff --git a/aoc2024/test/day10/python/example8.txt b/aoc2024/test/day10/python/example8.txt new file mode 100644 index 0000000..7bb1248 --- /dev/null +++ b/aoc2024/test/day10/python/example8.txt @@ -0,0 +1,8 @@ +89010123 +78121874 +87430965 +96549874 +45678903 +32019012 +01329801 +10456732 \ No newline at end of file diff --git a/aoc2024/test/day10/python/test_solution.py b/aoc2024/test/day10/python/test_solution.py index 8a04d4e..098cbbd 100644 --- a/aoc2024/test/day10/python/test_solution.py +++ b/aoc2024/test/day10/python/test_solution.py @@ -14,7 +14,7 @@ import unittest from common.python3.AdventOfCodeTestCase import AdventOfCodeTestCase -from aoc2024.src.day10.python.solution import get_score_sum +from aoc2024.src.day10.python.solution import get_score_sum, get_rating_sum class TestDay09Solution(AdventOfCodeTestCase): @@ -36,6 +36,21 @@ def test_part1_withExample4_getsScore(self): def test_part1_withPuzzleInput_getsScore(self): self.assertEqual(825, get_score_sum(self.input)) + def test_part2_withExample_getsRating(self): + self.assertEqual(3, get_rating_sum(self.examples[4])) + + def test_part2_withExample2_getsRating(self): + self.assertEqual(13, get_rating_sum(self.examples[5])) + + def test_part2_withExample3_getsRating(self): + self.assertEqual(227, get_rating_sum(self.examples[6])) + + def test_part2_withExample4_getsRating(self): + self.assertEqual(81, get_rating_sum(self.examples[7])) + + def test_part2_withPuzzleInput_getsRating(self): + self.assertEqual(1805, get_rating_sum(self.input)) + if __name__ == '__main__': unittest.main()