Skip to content

Commit 97323d0

Browse files
committed
2023 d5 p2
1 parent 35f68b2 commit 97323d0

File tree

2 files changed

+86
-6
lines changed

2 files changed

+86
-6
lines changed

src/advent_of_code/y2023/d5.py

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from typing import Any
2-
from advent_of_code.util import format_solution, puzzle_input
31
from collections import UserDict
42
from dataclasses import dataclass, field
53

4+
from advent_of_code.util import format_solution, puzzle_input
5+
66

77
@dataclass
88
class AlmanacMap(UserDict):
@@ -18,11 +18,53 @@ def set_key_range(self, key_start: int, value_start: int, map_range: int):
1818

1919
def __getitem__(self, key: int) -> int:
2020
for key_start, value_start, map_range in self.key_ranges:
21-
if key_start <= key <= key_start + map_range:
21+
if key_start <= key < key_start + map_range:
2222
return value_start + (key - key_start)
2323
else:
2424
return key
2525

26+
def transform_range(
27+
self, range_start: int, range_size: int
28+
) -> list[tuple[int, int]]:
29+
"""
30+
Transform a range of numbers by the mapping, returning potentially many
31+
(start, size) pairs
32+
"""
33+
untransformed_ranges = [(range_start, range_size)]
34+
result = []
35+
36+
for map_key_start, map_value_start, map_range in self.key_ranges:
37+
new_untransformed = []
38+
for u_range_start, u_range_size in untransformed_ranges:
39+
overlap_min = max(map_key_start, u_range_start)
40+
overlap_max = min(
41+
map_key_start + map_range - 1,
42+
u_range_start + u_range_size - 1,
43+
)
44+
45+
if overlap_min <= overlap_max:
46+
# Transform the overlap and add the rest to the new
47+
# untransformed list
48+
map_offset = overlap_min - map_key_start
49+
transformed_start = map_value_start + map_offset
50+
transformed_size = overlap_max + 1 - overlap_min
51+
result.append((transformed_start, transformed_size))
52+
53+
if overlap_min > u_range_start:
54+
nu_start = u_range_start
55+
nu_size = overlap_min - nu_start
56+
untransformed_ranges.append((nu_start, nu_size))
57+
if overlap_max < (u_range_start + u_range_size - 1):
58+
nu_start = overlap_max + 1
59+
nu_size = (u_range_start + u_range_size) - nu_start
60+
untransformed_ranges.append((nu_start, nu_size))
61+
else:
62+
new_untransformed.append((u_range_start, u_range_size))
63+
64+
untransformed_ranges = new_untransformed
65+
66+
return result + untransformed_ranges
67+
2668

2769
@dataclass
2870
class Almanac:
@@ -84,6 +126,24 @@ def get_seed_location(self, seed: int):
84126
]
85127
]
86128

129+
def get_min_seed_range_location(self, seed_range: tuple[int, int]) -> int:
130+
"""
131+
Get the minimum location across the given seed range
132+
"""
133+
range_mins = set()
134+
for r0 in self.seed_to_soil.transform_range(*seed_range):
135+
for r1 in self.soil_to_fertilizer.transform_range(*r0):
136+
for r2 in self.fertilizer_to_water.transform_range(*r1):
137+
for r3 in self.water_to_light.transform_range(*r2):
138+
for r4 in self.light_to_temperature.transform_range(*r3):
139+
for r5 in self.temperature_to_humidity.transform_range(*r4):
140+
for r6 in self.humidity_to_location.transform_range(
141+
*r5
142+
):
143+
range_mins.add(r6[0])
144+
145+
return min(range_mins)
146+
87147

88148
def part_1(almanac_input: list[str]) -> int:
89149
almanac = Almanac.from_input(almanac_input)
@@ -94,6 +154,17 @@ def part_1(almanac_input: list[str]) -> int:
94154

95155
def part_2(almanac_input: list[str]) -> int:
96156
almanac = Almanac.from_input(almanac_input)
157+
seed_range_values = [
158+
int(s) for s in almanac_input[0].split("seeds:")[1].strip().split()
159+
]
160+
seed_ranges = [
161+
(seed_range_values[i], seed_range_values[i + 1])
162+
for i in range(0, len(seed_range_values), 2)
163+
]
164+
165+
return min(
166+
almanac.get_min_seed_range_location(seed_range) for seed_range in seed_ranges
167+
)
97168

98169

99170
if __name__ == "__main__":

tests/test_y2023/test_d5.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from advent_of_code.y2023.d5 import part_1, part_2, AlmanacMap
2-
1+
from advent_of_code.y2023.d5 import AlmanacMap, part_1, part_2
32

43
TEST_INPUT = [
54
"seeds: 79 14 55 13",
@@ -42,5 +41,15 @@ def test_part_1():
4241
assert part_1(TEST_INPUT) == 35
4342

4443

44+
def test_transform_range():
45+
almanac = AlmanacMap()
46+
almanac.set_key_range(3, 13, 6)
47+
almanac.set_key_range(11, 31, 3)
48+
49+
transformed = almanac.transform_range(7, 6)
50+
51+
assert transformed == [(17, 2), (31, 2), (9, 2)]
52+
53+
4554
def test_part_2():
46-
assert part_2(TEST_INPUT) == 0
55+
assert part_2(TEST_INPUT) == 46

0 commit comments

Comments
 (0)