use std::collections::HashSet; struct Map { source: usize, destination: usize, ranges: Vec, } #[derive(Clone, Debug)] struct Range { source_start: usize, destination_start: usize, length: usize, } fn main() { let stdin = std::io::stdin(); let mut firstline = true; let mut seeds: Vec = Vec::new(); let mut maps = Vec::new(); let mut src = 0; let mut dst = 0; for line in stdin.lines() { let line = line.unwrap(); if firstline { if line.len() == 0 { firstline = false; continue; } line.split_once(":") .unwrap() .1 .trim() .split(" ") .map(|e| e.parse().unwrap()) .for_each(|e| seeds.push(e)); continue; } if line.len() == 0 { src += 1; } else if src == dst { dst += 1; maps.push(create_map(src, dst)); } else { let nums: Vec = line.split(" ").map(|e| e.parse().unwrap()).collect(); let range = Range { source_start: nums[1], destination_start: nums[0], length: nums[2], }; maps.last_mut().unwrap().ranges.push(range); } } let mut reduced = reduce_maps(&maps[0], &maps[1]).unwrap(); for i in 2..maps.len() { reduced = reduce_maps(&reduced, &maps[i]).unwrap(); } // Part 1 let mut min = usize::max_value(); for seed in &seeds { let value = lookup_map(*seed, &reduced); if value < min { min = value; } } println!("{}", min); // Part 2 let mut part2 = usize::max_value(); // Create a new map from seeds let mut seed_map = create_map(11, 0); for i in (0..seeds.len()).step_by(2) { seed_map.ranges.push(Range { source_start: seeds[i], destination_start: seeds[i], length: seeds[i+1], }) } let seed_range_reduced = reduce_maps(&seed_map, &reduced).unwrap(); for range in &seed_range_reduced.ranges { let mut found = false; for srange in &seed_map.ranges { if range.source_start >= srange.source_start && range.source_start < srange.source_start + srange.length { found = true; } } if !found { continue; } let value = lookup_map(range.source_start, &seed_range_reduced); if value < part2 { part2 = value; } } println!("{}", part2); } fn create_map(source: usize, destination: usize) -> Map { Map { source, destination, ranges: Vec::new(), } } fn lookup_map(value: usize, map: &Map) -> usize { let mut new_value = value; for range in &map.ranges { if value >= range.source_start && value < range.source_start + range.length { new_value = range.destination_start + (value - range.source_start); break; } } return new_value; } fn lookup_map_inverse(value: usize, map: &Map) -> usize { let mut new_value = value; for range in &map.ranges { if value >= range.destination_start && value < range.destination_start + range.length { new_value = range.source_start + (value - range.destination_start); break; } } return new_value; } fn reduce_maps(source: &Map, destination: &Map) -> Option { if source.destination != destination.source { println!("Incompatible maps"); return None; } let mut breakpoints = HashSet::new(); for range in &source.ranges { breakpoints.insert(range.source_start); breakpoints.insert(range.source_start + range.length); } for range in &destination.ranges { breakpoints.insert(lookup_map_inverse(range.source_start, &source)); breakpoints.insert(lookup_map_inverse( range.source_start + range.length, &source, )); } let mut breakpoints: Vec<_> = breakpoints.into_iter().collect(); breakpoints.sort(); let mut new_ranges = Vec::new(); for i in 0..breakpoints.len() - 1 { new_ranges.push(Range { source_start: breakpoints[i], destination_start: lookup_map(lookup_map(breakpoints[i], &source), &destination), length: breakpoints[i + 1] - breakpoints[i], }) } let map = Map { source: source.source, destination: destination.destination, ranges: new_ranges, }; return Some(map); }