#!/usr/bin/env python3 from typing import List, Tuple from tqdm import tqdm lines = (x.strip() for x in open("input.txt")) seeds = () current_map = "undef" maps = {} map_names = [] for line in lines: match line: case "" | "\n": continue case x if x.startswith("seeds: "): rest = x[len("seeds: "):] seeds = tuple(int(y) for y in rest.split(" ")) case x if x.endswith(" map:"): rest = x[:-len(" map:")] current_map = rest maps[current_map] = [] map_names.append(current_map) case x if len(x) and x[0].isdigit(): maps[current_map].append(tuple(int(y) for y in x.split(" "))) case _: pass def parse_map(value: List[Tuple[int, int, int]]): curr = 0 out = [] for dest, src, size in sorted(value, key=lambda x: x[1]): if src > curr: out.append((curr, 0)) if size > 0: out.append((src, dest-src)) curr = src + size out.append((curr, 0)) return out maps = {name: parse_map(value) for name, value in maps.items()} def find_largest_smaller_value(sorted_list, x): low, high = 0, len(sorted_list) while low < high - 1: mid = (low + high) // 2 if sorted_list[mid][0] <= x: low = mid else: high = mid return sorted_list[low], low def merge_list(curr_map, next_map): next_mapping = [] for i in range(0, len(curr_map)): dest_start = curr_map[i][0] + curr_map[i][1] dest_start_idx = find_largest_smaller_value(next_map, dest_start)[1] dest_end = curr_map[i + 1][0] + curr_map[i][1] - 1 if i + 1 < len(curr_map) else None dest_end_idx = find_largest_smaller_value(next_map, dest_end)[1] if dest_end is not None else len(next_map) - 1 for next_start, next_offset in next_map[dest_start_idx:dest_end_idx + 1]: next_mapping.append((max(curr_map[i][0], next_start - curr_map[i][1]), curr_map[i][1] + next_offset)) return next_mapping # merge lists into a single one curr_map = maps[map_names[0]] for name in map_names[1:]: curr_map = merge_list(curr_map, maps[name]) result = float("inf") for i in range(0, len(seeds), 2): dest_start = seeds[i] dest_start_idx = find_largest_smaller_value(curr_map, dest_start)[1] dest_end = seeds[i] + seeds[i+1] - 1 dest_end_idx = find_largest_smaller_value(curr_map, dest_end)[1] for next_start, next_offset in curr_map[dest_start_idx:dest_end_idx + 1]: result = min(result, next_start + next_offset) print(result)