Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions puzzles/solutions/2023/d05/input_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import itertools
import re


Mapping = list[tuple[int, int, int]]


def get_map_ranges(lines: list[str]) -> Mapping:
ranges = []
for line in lines:
destination_range_start, source_range_start, range_length = map(
int, line.split()
)
difference = destination_range_start - source_range_start
source_range_end = source_range_start + range_length
ranges.append((source_range_start, source_range_end, difference))
return ranges


def get_category_mapping(mapping: str) -> tuple[str, Mapping]:
lines = mapping.splitlines()
source = re.match(r"([a-z]+)", lines[0]).group()
ranges = get_map_ranges(lines[1:])
return source, ranges


def get_all_mappings(mapping_lines: str) -> list[Mapping]:
source_to_destination_categories_ranges = []
for mapping in mapping_lines.split("\n\n"):
source, ranges = get_category_mapping(mapping)
source_to_destination_categories_ranges.append(ranges)
return source_to_destination_categories_ranges


def get_initial_data(
input_text: str,
) -> tuple[list[int], list[Mapping]]:
first_line, mapping_lines = input_text.split("\n\n", maxsplit=1)
initial_seeds = [int(number) for number in re.findall(r"\d+", first_line)]
mappings = get_all_mappings(mapping_lines)
return initial_seeds, mappings


def get_seeds_ranges(seeds: list[int]) -> list[range]:
return [
range(seed_number, seed_number + range_length)
for seed_number, range_length in itertools.batched(seeds, 2)
]


def reverse_mappings(mappings: list[Mapping]) -> list[Mapping]:
reversed_mappings = []
for name, mapping in reversed(mappings):
reversed_mapping = []
for mapping_range in mapping:
(
original_destination_range_start,
original_source_range_start,
original_difference,
) = mapping_range
reversed_range = (
original_destination_range_start + original_difference,
original_source_range_start + original_difference,
-original_difference,
)
reversed_mapping.append(reversed_range)
reversed_mappings.append(reversed_mapping)

return reversed_mappings
26 changes: 26 additions & 0 deletions puzzles/solutions/2023/d05/p1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import math
import sys
import input_parsing


def get_answer(input_text: str) -> int:
"""Return the lowest location number hat corresponds to any of the initial seed numbers."""
initial_seeds, mappings = input_parsing.get_initial_data(input_text)
lowest_location_number = math.inf
for seed in initial_seeds:
running_value = seed
for mapping_ranges in mappings:
for source_range_start, source_range_end, difference in mapping_ranges:
if source_range_start <= running_value < source_range_end:
running_value += difference
break
lowest_location_number = min(lowest_location_number, running_value)

return lowest_location_number


if __name__ == "__main__":
try:
print(get_answer(sys.argv[1]))
except IndexError:
pass # Don't crash if no input was passed through command line arguments.
33 changes: 33 additions & 0 deletions puzzles/solutions/2023/d05/p2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import itertools
import sys

import input_parsing


def does_seed_exist(running_value: int, seeds_ranges: list[range]) -> bool:
return any(running_value in seed_range for seed_range in seeds_ranges)


def get_answer(input_text: str) -> int:
"""Return the lowest location number hat corresponds to any of the initial seed numbers."""
seeds_list, mappings = input_parsing.get_initial_data(input_text)
seeds_ranges = input_parsing.get_seeds_ranges(seeds_list)
mappings = input_parsing.reverse_mappings(mappings)

for location_number in itertools.count(start=1):
running_value = location_number
for mapping_ranges in mappings:
for source_range_start, source_range_end, difference in mapping_ranges:
if source_range_start <= running_value < source_range_end:
running_value += difference
break
print(location_number, running_value)
if does_seed_exist(running_value, seeds_ranges):
return running_value


if __name__ == "__main__":
try:
print(get_answer(sys.argv[1]))
except IndexError:
pass # Don't crash if no input was passed through command line arguments.