# -*- coding: utf-8 -*- from abc import ABC from dataclasses import dataclass from multiprocessing import Pool from typing import Iterator from aoc import BaseAssignment @dataclass class InnerMapping: source: int destination: int length: int def __getitem__(self, item): if self.source <= item < self.source + self.length: return self.destination + item - self.source raise KeyError() class Mapping: def __init__(self): self.mappings = [] def __getitem__(self, item): for mapping in self.mappings: try: return mapping[item] except KeyError: continue return item def update(self, source: int, destination: int, length: int): self.mappings.append(InnerMapping(source, destination, length)) I = tuple[list[int], list[Mapping]] T = int class Assignment(BaseAssignment, ABC): @staticmethod def map_seed_position(seed: int, mappings: list[Mapping]) -> int: value = seed for item in mappings: value = item[value] return value def read_input(self, example=False) -> I: seeds, *almanac = super().read_input(example) _, seeds = seeds.split(": ") seeds = [int(i) for i in seeds.split(" ")] maps: list[Mapping] = [] map = None index = 0 while True: try: line = almanac[index] except IndexError: break if line == "": index += 2 if map is not None: maps.append(map) map = None continue else: index += 1 if map is None: map = Mapping() destination, source, length = line.split(" ") map.update(int(source), int(destination), int(length)) return seeds, [*maps, map] class AssignmentOne(Assignment): example_result = 35 def run(self, input: I) -> T: seeds, almanac = input values = [] for seed in seeds: values.append(self.map_seed_position(int(seed), almanac)) return min(values) def run_per_seed_range(index: int, seeds: list[int], almanac: list[Mapping]): seed, length = int(seeds[index]), int(seeds[index + 1]) value = None for seed in range(seed, seed + length): location = Assignment.map_seed_position(seed, almanac) if value is None or value > location: value = location return value class AssignmentTwo(Assignment): example_result = 46 def run(self, input: I) -> T: seeds, almanac = input value = None with Pool(processes=len(seeds) // 2) as pool: for v in pool.imap_unordered( lambda index: run_per_seed_range(index, seeds, almanac), range(0, len(seeds), 2), ): if value is None or value > v: value = v return value