Day 5 to kill your computer

This commit is contained in:
Pascal Phelipot 2023-12-07 21:02:03 +01:00
parent 6b7afc5a93
commit 48cfa80dec
2 changed files with 91 additions and 52 deletions

View File

@ -1,7 +1,7 @@
use std::collections::HashMap;
use regex::Regex; use regex::Regex;
use std::collections::HashMap;
use log::{debug, info}; use log::{debug, info, trace};
use super::utils::Part; use super::utils::Part;
@ -9,14 +9,19 @@ use super::utils::Part;
struct Range { struct Range {
range_start: u64, range_start: u64,
src_range_start: u64, src_range_start: u64,
length: u64 length: u64,
} }
impl Range { impl Range {
fn map(&self, n: u64) -> Option<u64> { fn map(&self, n: u64) -> Option<u64> {
if (self.src_range_start..(self.src_range_start+self.length)).contains(&n) { if (self.src_range_start..(self.src_range_start + self.length)).contains(&n) {
debug!("matching range {} -> {} [{:?}]", n, self.range_start + n - self.src_range_start, self); trace!(
return Some(self.range_start + n - self.src_range_start ) "matching range {} -> {} [{:?}]",
n,
self.range_start + n - self.src_range_start,
self
);
return Some(self.range_start + n - self.src_range_start);
} }
None None
} }
@ -25,17 +30,19 @@ impl Range {
struct Map { struct Map {
start_domain: String, start_domain: String,
target_domain: String, target_domain: String,
ranges: Vec<Range> ranges: Vec<Range>,
} }
impl Map { impl Map {
fn new() -> Self { fn new() -> Self {
Map { ..Default::default() } Map {
..Default::default()
}
} }
fn map(&self, n: u64) -> u64 { fn map(&self, n: u64) -> u64 {
for range in &self.ranges { for range in &self.ranges {
if let Some(value) = range.map(n) { if let Some(value) = range.map(n) {
return value return value;
} }
} }
n n
@ -45,77 +52,103 @@ impl Map {
#[derive(Default)] #[derive(Default)]
struct Dataset { struct Dataset {
seeds: Vec<u64>, seeds: Vec<u64>,
maps: HashMap<String, Map> maps: HashMap<String, Map>,
} }
impl Dataset { impl Dataset {
fn new() -> Self { fn new() -> Self {
Dataset { ..Default::default() } Dataset {
..Default::default()
}
} }
} }
pub fn solve(lines: Vec<String>, part: Part) -> u64 { pub fn solve(lines: Vec<String>, part: Part) -> u64 {
let parts : Vec<String> = lines.into_iter() let parts: Vec<String> = lines.into_iter().filter(|l| !l.is_empty()).collect();
.filter(|l| !l.is_empty())
.collect();
let mut dataset = Dataset::new(); let mut dataset = Dataset::new();
debug!("{:?}", parts); debug!("{:?}", parts);
let (seeds_line, other_lines) = parts.split_at(1); let (seeds_line, other_lines) = parts.split_at(1);
dataset.seeds = seeds_line.first().unwrap().split(' ').skip(1).map(|e| e.parse().unwrap()).collect(); dataset.seeds = seeds_line
.first()
.unwrap()
.split(' ')
.skip(1)
.map(|e| e.parse().unwrap())
.collect();
debug!("Seeds : {:?}", dataset.seeds); debug!("Seeds : {:?}", dataset.seeds);
let mut current_map : Map = Map::new(); let mut current_map: Map = Map::new();
let regex_map_header = Regex::new(r"^(\w*)-to-(\w*)").unwrap(); let regex_map_header = Regex::new(r"^(\w*)-to-(\w*)").unwrap();
for line in other_lines { for line in other_lines {
if let Some(captures) = regex_map_header.captures(line) { if let Some(captures) = regex_map_header.captures(line) {
if !current_map.ranges.is_empty() { if !current_map.ranges.is_empty() {
debug!("New mapping detected, saving the last one"); debug!("New mapping detected, saving the last one");
dataset.maps.insert(current_map.start_domain.to_owned(), current_map); dataset
.maps
.insert(current_map.start_domain.to_owned(), current_map);
current_map = Map::new(); current_map = Map::new();
} }
current_map.start_domain = captures[1].to_owned(); current_map.start_domain = captures[1].to_owned();
current_map.target_domain = captures[2].to_owned(); current_map.target_domain = captures[2].to_owned();
}else{ } else {
let components : [u64; 3] = line.split(' ').map(|e| e.parse().unwrap()).collect::<Vec<_>>().try_into().unwrap(); let components: [u64; 3] = line
.split(' ')
.map(|e| e.parse().unwrap())
.collect::<Vec<_>>()
.try_into()
.unwrap();
debug!("new range: {:?}", components); debug!("new range: {:?}", components);
current_map.ranges.push( current_map.ranges.push(Range {
Range { range_start: components[0],
range_start: components[0], src_range_start: components[1],
src_range_start: components[1], length: components[2],
length: components[2] });
}
);
} }
} }
dataset.maps.insert(current_map.start_domain.to_owned(), current_map); dataset
.maps
.insert(current_map.start_domain.to_owned(), current_map);
info!("Loaded {} mapping categories", dataset.maps.len()); info!("Loaded {} mapping categories", dataset.maps.len());
for (src, map) in &dataset.maps { for (src, map) in &dataset.maps {
info!("{} -> {} with {} ranges", src, map.target_domain, map.ranges.len()); info!(
"{} -> {} with {} ranges",
src,
map.target_domain,
map.ranges.len()
);
} }
match part { if let Part::Two = part {
Part::One => { // Convert the seeds to the new format !
let mut min_location = u64::MAX; let original_seeds = dataset.seeds;
for seed in dataset.seeds { let mut new_seeds : Vec<u64> = Vec::new();
let mut stage = "seed";
let mut value = seed;
while let Some(map) = dataset.maps.get(stage) {
debug!("Stage {} -> {}", stage, map.target_domain);
value = map.map(value);
stage = &map.target_domain;
}
min_location = min_location.min(value);
info!("Final stage seed {} -> location {}", seed, value);
}
min_location for chunk in original_seeds.chunks_exact(2) {
}, let range = chunk[0]..(chunk[0]+chunk[1]);
Part::Two => todo!() // FIXME : don't do that it will eat all your memory: new_seeds.append(&mut range.collect::<Vec<u64>>());
}
debug!("New seeds: {:?}", new_seeds);
dataset.seeds = new_seeds;
} }
let mut min_location = u64::MAX;
for seed in dataset.seeds {
let mut stage = "seed";
let mut value = seed;
while let Some(map) = dataset.maps.get(stage) {
debug!("Stage {} -> {}", stage, map.target_domain);
value = map.map(value);
stage = &map.target_domain;
}
min_location = min_location.min(value);
info!("Final stage seed {} -> location {}", seed, value);
}
min_location
} }
#[cfg(test)] #[cfg(test)]
@ -131,22 +164,20 @@ mod tests {
map.ranges.push(Range { map.ranges.push(Range {
range_start: 50, range_start: 50,
src_range_start: 98, src_range_start: 98,
length: 2 length: 2,
}); });
map.ranges.push(Range { map.ranges.push(Range {
range_start: 52, range_start: 52,
src_range_start: 50, src_range_start: 50,
length: 48 length: 48,
}); });
assert_eq!(map.map(1), 1); assert_eq!(map.map(1), 1);
assert_eq!(map.map(99), 51); assert_eq!(map.map(99), 51);
assert_eq!(map.map(51), 53); assert_eq!(map.map(51), 53);
} }
#[test] #[test]
fn test_part2(){ fn test_part2() {}
}
} }

View File

@ -4,6 +4,7 @@ mod day1;
mod day2; mod day2;
mod day3; mod day3;
mod day4; mod day4;
mod day5;
mod utils; mod utils;
use log::info; use log::info;
use utils::Part; use utils::Part;
@ -17,6 +18,13 @@ fn main() {
.format_timestamp(None) .format_timestamp(None)
.init(); .init();
let data = utils::lines_from_file("./datasets/adventofcode.com_2023_day_5_input.txt").expect("Can't load the data");
//let data = utils::lines_from_file("./datasets/test.txt").expect("Can't load the data");
let result = day5::solve(data.clone(), Part::One);
let result2 = day5::solve(data, Part::Two);
info!("Result part 1: {}", result);
info!("Result part 2: {}", result2);
} }