adventofcode2023/day5/src/main.rs
2023-12-05 17:24:51 +01:00

117 lines
4.3 KiB
Rust

use std::fs::read_to_string;
use std::cmp;
use std::time::Instant;
fn get_numbers_in_line(str: &str) -> Vec<u32> {
return str.split_whitespace().filter_map(|substr| substr.parse::<u32>().ok()).collect();
}
fn process(seeds: Vec<u32>, mut maps: Vec<Vec<(u32, u32, u32)>>) -> (u32, u32) {
for map in maps.iter_mut() {
map.sort_unstable_by(|&(a, _, _), &(b, _, _)| a.partial_cmp(&b).unwrap());
}
// Task 1 - mapping a single number through all mappings
let min1 = seeds.iter().map(
|seed| {
let mut n = *seed;
for map in &maps {
for &(src, dst, len) in map {
if src > n {
break;
}
if n - src < len {
n = n - src + dst;
break;
}
}
}
n
}
).min().unwrap();
// Task 2 - mapping whole ranges through all mappings
// Array of ranges that will be mapped for task 2 initialized with pairs from seeds
let mut processed_ranges: Vec<_> = (0..seeds.len()).step_by(2).map(|i| (seeds[i], seeds[i + 1])).collect();
let mut swap_ranges: Vec<(u32, u32)> = Vec::new();
// Map the processed_ranged through all mappings
for map in &maps {
swap_ranges.clear();
for &(working_start, working_len) in &processed_ranges {
// Applying one or more mapping ranges to the processed range
let (mut temp_start, mut temp_len) = (working_start, working_len);
for &(src, dst, len) in map {
// Working range starts before mapping range, means it's an unmapped segment
if temp_start < src {
let out_len = cmp::min(temp_len, src - temp_start);
swap_ranges.push((temp_start, out_len));
if out_len == temp_len {
break;
}
temp_start = src;
}
// Working range is sure to not begin before mapping range
let start_diff = temp_start - src;
// Mapping range is so short that it doesn't reach the working range, skip
if len <= start_diff {
continue;
}
let out_len = cmp::min(temp_len, len - start_diff);
swap_ranges.push((dst + start_diff, out_len));
if out_len == temp_len {
break;
}
// Consume the beginning of the working range
temp_start += out_len;
temp_len -= out_len;
}
}
std::mem::swap(&mut processed_ranges, &mut swap_ranges);
}
// Find the range with lowest starting value, length is guaranteed to be nonzero
let min2 = processed_ranges.iter().map(|&(start, _)| start).min().unwrap();
(min1, min2)
}
fn main() {
let time_start = Instant::now();
// Set of inputs
let mut seeds: Vec<u32> = Vec::new();
// Mappings of ranges, ordered in order to easily find the lowest relevant range
// Order: (src, dst, len)
let mut maps: Vec<Vec<(u32, u32, u32)>> = Vec::new();
maps.push(Vec::new());
let mut did_read_seeds: bool = false;
for line in read_to_string("input.txt").unwrap().lines() {
if !did_read_seeds {
seeds = get_numbers_in_line(line);
did_read_seeds = true;
} else {
let numbers = get_numbers_in_line(line);
if numbers.len() == 3 {
maps.last_mut().unwrap().push((numbers[1],numbers[0], numbers[2]));
} else {
if !maps.last().unwrap().is_empty() {
maps.push(Vec::new());
}
}
}
}
if maps.last().unwrap().is_empty() {
maps.pop();
}
let time_start_no_io = Instant::now();
// for _ in 0..1000 {
// let (_, _) = process(seeds.clone(), maps.clone());
// }
let (min1, min2) = process(seeds, maps);
// Measurements
let elapsed = time_start.elapsed().as_nanos();
let elapsed_no_io = time_start_no_io.elapsed().as_nanos();
println!("Time: {}ns", elapsed);
println!("Time without file i/o: {}ns", elapsed_no_io);
println!("Min location1: {}", min1);
println!("Min location2: {}", min2);
}