Advent of Code 2023 - Day 05
I'ts that time of year again, the Advent of Code! I'm completing the challenges this year using Rust 🦀 Day 5 was a very memory intensive day 😋!
I'll be posting them here, just to document how I'm solving each challenge, and I highly encourage you to check them out before reading my solutions!
Here's the Day 5 Challenge
BTW: Complete Part 1 to have access to Part 2 😉
Solution for Part 1
use std::cmp::Ordering;
use std::fs::File;
use std::io::Read;
use std::io::Result;
use std::path::Path;
use std::str::FromStr;
struct Mapping {
dest_range_start: i64,
source_range_start: i64,
range_length: i64,
}
fn main() {
let input_file_path = "path_to_input_file";
let file_content = read_file(input_file_path)
.unwrap_or_else(|_| panic!("Could not open {input_file_path}"));
let file_sections: Vec<&str> = file_content.split("\r\n\r\n").collect();
let seeds = &file_sections[0];
let sections = &file_sections[1..];
let seeds: Vec<&str> = if let Some((_, seeds_as_str)) = seeds.split_once(' ') {
seeds_as_str.split(' ').collect()
} else {
panic!("Seed header was empty")
};
let mut mappings: Vec<Vec<Mapping>> = Vec::new();
for section in sections.iter() {
let (_, map) = section.split_once("\r\n").unwrap();
let mut inner_vec : Vec<Mapping> = Vec::new();
for line in map.split("\r\n") {
let range_definition = line.split(' ')
.map(|i| {
i64::from_str(i).unwrap()
})
.collect::<Vec<i64>>();
let range_definition: &[i64] = range_definition.as_slice();
inner_vec.push(
Mapping {
dest_range_start: range_definition[0],
source_range_start: range_definition[1],
range_length: range_definition[2],
}
)
}
mappings.push(inner_vec);
}
for map in mappings.iter_mut() {
map.sort_by(|a, b| {
if b.source_range_start < a.source_range_start {
Ordering::Greater
} else {
Ordering::Less
}
});
}
let mut lowest_location_number = 0;
for seed in seeds {
let mut current_input = i64::from_str(seed).unwrap();
for map in mappings.as_slice() {
let adequate_mapping = map.iter().find(|i| {
(i.source_range_start..i.source_range_start + i.range_length).contains(¤t_input)
});
match adequate_mapping {
None => { continue }
Some(mapping) => {
current_input = (current_input - mapping.source_range_start) + mapping.dest_range_start
}
}
}
if lowest_location_number == 0 || lowest_location_number > current_input {
lowest_location_number = current_input;
}
}
println!("Lowest location number: {}", lowest_location_number);
}
fn read_file<P: AsRef<Path>>(filename: P) -> Result<String> {
let mut file = File::open(filename)?;
let mut file_as_str = String::new();
file.read_to_string(&mut file_as_str).unwrap();
Ok(file_as_str)
}
Solution for Part 2
use std::cmp::Ordering;
use std::fs::File;
use std::io::Read;
use std::io::Result;
use std::path::Path;
use std::str::FromStr;
use std::time::SystemTime;
struct Mapping {
dest_range_start: i64,
source_range_start: i64,
range_length: i64,
}
fn main() {
let curr_time = SystemTime::now();
let input_file_path = "path_to_input_file";
let file_content = read_file(input_file_path)
.unwrap_or_else(|_| panic!("Could not open {input_file_path}"));
let file_sections: Vec<&str> = file_content.split("\r\n\r\n").collect();
let seeds = &file_sections[0];
let sections = &file_sections[1..];
let seed_ranges: Vec<(i64, i64)> = if let Some((_, seeds_as_str)) = seeds.split_once(' ') {
let mut result: Vec<(i64, i64)> = Vec::new();
let seed_initial_parse = seeds_as_str.split(' ')
.map(|i| i64::from_str(i).unwrap())
.collect::<Vec<i64>>();
for range_identifier in seed_initial_parse.chunks(2) {
result.push((range_identifier[0], range_identifier[1]));
}
result
} else {
panic!("Seed header was empty")
};
let mut mappings: Vec<Vec<Mapping>> = Vec::new();
for section in sections.iter() {
let (_, map) = section.split_once("\r\n").unwrap();
let mut inner_vec: Vec<Mapping> = Vec::new();
for line in map.split("\r\n") {
let range_definition = line.split(' ')
.map(|i| {
i64::from_str(i).unwrap()
})
.collect::<Vec<i64>>();
let range_definition: &[i64] = range_definition.as_slice();
inner_vec.push(
Mapping {
dest_range_start: range_definition[0],
source_range_start: range_definition[1],
range_length: range_definition[2],
}
)
}
mappings.push(inner_vec);
}
for map in mappings.iter_mut() {
map.sort_by(|a, b| {
if b.source_range_start < a.source_range_start {
Ordering::Greater
} else {
Ordering::Less
}
});
}
let mut lowest_location_number = 0;
for (seed_base, seed_range) in seed_ranges {
for seed in seed_base..seed_base+seed_range {
let result = process_seed(seed, &mappings);
if lowest_location_number == 0 || lowest_location_number > result {
lowest_location_number = result;
}
}
}
println!("Lowest location number: {}", lowest_location_number);
let elapsed = curr_time.elapsed().unwrap();
println!("Execution time: {} ms", elapsed.as_millis())
}
fn process_seed(seed: i64, mappings: &Vec<Vec<Mapping>>) -> i64 {
let mut current_input = seed;
for map in mappings.as_slice() {
let adequate_mapping = map.iter().find(|i| {
(i.source_range_start..i.source_range_start + i.range_length).contains(¤t_input)
});
match adequate_mapping {
None => { continue; }
Some(mapping) => {
current_input = (current_input - mapping.source_range_start) + mapping.dest_range_start
}
}
}
current_input
}
fn read_file<P: AsRef<Path>>(filename: P) -> Result<String> {
let mut file = File::open(filename)?;
let mut file_as_str = String::new();
file.read_to_string(&mut file_as_str).unwrap();
Ok(file_as_str)
}