diff --git a/day05.py b/day05.py index 22c1098..12f51cf 100644 --- a/day05.py +++ b/day05.py @@ -1,6 +1,5 @@ from __future__ import annotations from dataclasses import dataclass -from itertools import chain import bisect from typing import Optional @@ -12,81 +11,57 @@ from aoc import Aoc2, AocSameParser class Map: def __init__(self, group: list[str]) -> None: - self.maps = [] + self.maps: list[tuple[int, int, int]] = [] for line in group: start_dest, start_source, id_range = [ int(d) for d in re.findall(r"\d+", line) ] - bisect.insort(self.maps, (start_source, start_dest - start_source, id_range)) + bisect.insort( + self.maps, + (start_source, start_dest - start_source, start_source + id_range - 1), + ) zero_offsets = [] start = 0 - for source, _, rnge in self.maps: - if source > start: - zero_offsets.append((start, 0, source - start)) - start = source + rnge + for section_start, _, section_end in self.maps: + if section_start > start: + zero_offsets.append((start, 0, start - 1)) + start = section_end + 1 for m in zero_offsets: bisect.insort(self.maps, m) def find_bucket(self, x: int) -> Optional[int]: - for idx, (source, offset, rnge) in enumerate(self.maps): - if x >= source and x < source + rnge: + for idx, (source, _, end) in enumerate(self.maps): + if source <= x <= end: return idx return None - def __getitem__(self, x: int) -> int: - for (source, offset, rnge) in self.maps: - if x >= source and x < source + rnge: + for source, offset, end in self.maps: + if source <= x <= end: return x + offset return x - # index = bisect.bisect_left(self.maps, (x+1,0,0)) - 1 - # # index -= 1 - # # if index < 0: - # # return x - # - # source, offset, id_range = self.maps[index] - # if x >= source + id_range: - # return x - # # if x < source + id_range: - # return x + offset - # return x - # - def merge(self, other: Map) -> None: - newmappings = [] - for osource, ooffset, orange in other.maps: - for ssource, soffset, srange in self.maps: - if osource >= ssource and osource <= ssource + srange: - pass - - - - - - - def __repr__(self) -> str: - l = [] - for k, offset, x in self.maps: - l.append(f"{k} - {k + x - 1}: {k+offset} - {k+ offset + x - 1}") - return f"Map({', '.join(l)})" + map_strs = [] + for k, offset, end in self.maps: + map_strs.append(f"{k} - {end}: {k+offset} - {k+ offset + end}") + return f"Map({', '.join(map_strs)})" @dataclass class Problem: seeds: list[int] - maps: dict[tuple[str,str],Map] + maps: dict[tuple[str, str], Map] - def convert(self, cat_from: str, cat_to:str, value: int) -> int: + def convert(self, cat_from: str, cat_to: str, value: int) -> int: target = cat_from output_value = value while target != cat_to: - new_target= list(filter(lambda fromto: fromto[0] == target, self.maps.keys()))[0][1] - # print(f"{target}({output_value})") + new_target = list( + filter(lambda fromto: fromto[0] == target, self.maps.keys()) + )[0][1] output_value = self.maps[(target, new_target)][output_value] - # print(f"{new_target}({output_value})") - # self.cache[(old_value, target)] = (output_value, new_target) target = new_target return output_value @@ -97,6 +72,7 @@ class Problem: raise RuntimeError(f'Could not parse "{line}" as group header') return header_match["source"], header_match["dest"] + @staticmethod def from_input(inpt: str) -> Problem: lines = inpt.splitlines() @@ -105,7 +81,7 @@ class Problem: raise RuntimeError(f'Could not parse "{lines[0]}" as seed list') seeds = [int(s) for s in seed_match.captures("seedid")] - groups = [] + groups: list[list[str]] = [] for line in lines[1:]: if not line: groups.append([]) @@ -123,62 +99,57 @@ class Day05(AocSameParser[Problem], Aoc2[Problem, Problem]): return Problem.from_input(inpt) def part1(self, inpt: Problem) -> int: - # print([inpt.maps["seed", "soil"][x] for x in range(100)]) - # - # print(inpt.maps["soil", "fertilizer"]) - # print(inpt.maps["soil", "fertilizer"][14]) - # print([inpt.maps["soil", "fertilizer"][x] for x in range(100)]) - return (min([inpt.convert("seed", "location", x) for x in inpt.seeds])) - + return min(inpt.convert("seed", "location", x) for x in inpt.seeds) def part2(self, inpt: Problem) -> int: - order = [("seed", "soil"), ("soil", "fertilizer"), ("fertilizer", "water"), ("water", "light"), ("light", "temperature"), ("temperature", "humidity"), ("humidity", "location")] + order = [ + ("seed", "soil"), + ("soil", "fertilizer"), + ("fertilizer", "water"), + ("water", "light"), + ("light", "temperature"), + ("temperature", "humidity"), + ("humidity", "location"), + ] seed_iter = iter(inpt.seeds) - seeds = list(zip(seed_iter, seed_iter)) + seeds = [ + (start, start + rnge - 1) + for (start, rnge) in list(zip(seed_iter, seed_iter)) + ] for translation in order: - print(translation) - print(seeds) new_to_convert = [] current_map = inpt.maps[translation] - # print(seeds[1]) - # print(current_map) for seed in seeds: - cur_range_start, cur_range_range = seed - cur_end = cur_range_start + cur_range_range - index = current_map.find_bucket(cur_range_start) + cur_start, cur_end = seed + index = current_map.find_bucket(cur_start) if index is not None: - # print(current_map.maps[index]) - section_start, section_offset, section_range = current_map.maps[index] - section_end = section_start + section_range - - if cur_end <= section_end: - new_to_convert.append((cur_range_start + section_offset, cur_range_range)) - else: - # split - new_to_convert.append((cur_range_start + section_offset, section_end - cur_range_start)) + section_start, section_offset, section_end = current_map.maps[index] + while cur_end > section_end: if index == len(current_map.maps) - 1: - new_to_convert.append((section_end, cur_end - section_start)) - else: - next_start, next_offset, next_range = current_map.maps[index + 1] - next_end = next_start + next_range - new_to_convert.append((section_end + next_offset, cur_end - section_start)) - if cur_end > next_end: - print("Oh no") + new_to_convert.append((section_end + 1, cur_end)) + break + new_to_convert.append( + (cur_start + section_offset, section_end + section_offset) + ) + index += 1 + section_start, section_offset, section_end = current_map.maps[ + index + ] + cur_start = section_start + new_to_convert.append( + (cur_start + section_offset, cur_end + section_offset) + ) else: new_to_convert.append(seed) seeds = new_to_convert - - - - # return min(inpt.convert("seed", "location", x) for x in seeds) + return min(x[0] for x in seeds) - -def main(): +def main() -> None: day5 = Day05() - print(day5.run_example_2()) + day5.run() if __name__ == "__main__":