advent_of_code_2023/Day05/rust/src/main.rs

126 lines
3.8 KiB
Rust

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use std::str::FromStr;
// Define type aliases for clarity
type SeedNumber = (i32, i32);
type CategoryItem = (i32, i32, i32);
type SeedNumbers = Vec<SeedNumber>;
type Category = Vec<CategoryItem>;
type Categories = Vec<Category>;
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
fn parse_input(file_path: &str) -> io::Result<(SeedNumbers, Categories)> {
let mut seeds_numbers = SeedNumbers::new();
let mut categories = Categories::new();
let mut current_category = Category::new();
let mut is_seed_line = true;
for line in read_lines(file_path)? {
let line = line?;
if line.is_empty() {
if !current_category.is_empty() {
categories.push(current_category);
current_category = Category::new();
}
is_seed_line = false;
continue;
}
if is_seed_line {
let seeds_ranges: Vec<i32> = line
.split_whitespace()
.skip(1)
.filter_map(|s| i32::from_str(s).ok())
.collect();
for seeds in seeds_ranges.chunks(2) {
if seeds.len() == 2 {
seeds_numbers.extend((seeds[0]..seeds[0] + seeds[1]).map(|n| (n, n + 1)));
}
}
} else {
let numbers: Vec<i32> = line
.split_whitespace()
.filter_map(|s| i32::from_str(s).ok())
.collect();
if numbers.len() == 3 {
current_category.push((numbers[0], numbers[1], numbers[2]));
}
}
}
if !current_category.is_empty() {
categories.push(current_category);
}
Ok((seeds_numbers, categories))
}
fn process_categories(mut seeds_numbers: SeedNumbers, categories: Categories) -> SeedNumbers {
for category in categories {
let mut sources = SeedNumbers::new();
while let Some((start, end)) = seeds_numbers.pop() {
let mut is_mapped = false;
for &(destination, source, length) in &category {
let overlap_start = std::cmp::max(start, source);
let overlap_end = std::cmp::min(end, source + length);
if overlap_start < overlap_end {
sources.push((
overlap_start - source + destination,
overlap_end - source + destination,
));
if overlap_start > start {
seeds_numbers.push((start, overlap_start));
}
if end > overlap_end {
seeds_numbers.push((overlap_end, end));
}
is_mapped = true;
break;
}
}
if !is_mapped {
sources.push((start, end));
}
}
seeds_numbers = sources;
}
seeds_numbers
}
fn find_lowest_location(file_path: &str) -> io::Result<i32> {
let (seeds_numbers, categories) = parse_input(file_path)?;
let processed_seeds = process_categories(seeds_numbers, categories);
let lowest_location = processed_seeds
.iter()
.map(|&(start, _)| start)
.min()
.unwrap();
Ok(lowest_location)
}
fn main() -> io::Result<()> {
// Test
let test_result = find_lowest_location("../test.txt")?;
assert_eq!(
test_result, 46,
"Test failed. Expected 46, got {}",
test_result
);
println!("Test passed.");
// Process actual input
let result = find_lowest_location("../input.txt")?;
println!("Total result from input.txt: {}", result);
Ok(())
}