diff --git a/Cargo.toml b/Cargo.toml index 4de3f8d..020ed76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [features] -full = ["day1", "day2", "day3", "day4", "day5", "day6", "day7", "day8"] +full = ["day1", "day2", "day3", "day4", "day5", "day6", "day7", "day8", "day9"] default = ["full"] day1 = [] day2 = [] @@ -14,6 +14,7 @@ day5 = [] day6 = [] day7 = [] day8 = [] +day9 = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/inputs/day09/example.txt b/inputs/day09/example.txt new file mode 100644 index 0000000..5ff5aae --- /dev/null +++ b/inputs/day09/example.txt @@ -0,0 +1 @@ +2333133121414131402 \ No newline at end of file diff --git a/src/day08.rs b/src/day08.rs index 6ff8fb4..e2a4f17 100644 --- a/src/day08.rs +++ b/src/day08.rs @@ -79,7 +79,7 @@ pub fn pt1(input: &str) -> usize { debug!("new Map: \n{new_map}"); antinode_positions.len() } -// 225 is too high + pub fn pt2(input: &str) -> usize { let char_positions = input .lines() diff --git a/src/day09.rs b/src/day09.rs new file mode 100644 index 0000000..ef43565 --- /dev/null +++ b/src/day09.rs @@ -0,0 +1,154 @@ +use std::collections::HashSet; + +use tracing::debug; + +pub fn pt1(input: &str) -> usize { + let mut disk_layout = input + .chars() + .map(|c| c.to_digit(10).unwrap()) + .enumerate() + .flat_map(|(i, c)| { + (0..c) + .map(|_| if i % 2 == 0 { Some(i / 2) } else { None }) + .collect::>() + }) + .collect::>(); + let mut min_pos = usize::MAX; + let dl = disk_layout.clone(); + debug!( + "disk layout: {}", + disk_layout + .iter() + .map(|v| match v { + Some(v) => v.to_string(), + None => ".".to_string(), + }) + .collect::() + ); + for (pos, segment) in disk_layout.iter_mut().enumerate() { + if segment.is_none() && pos < min_pos { + let next_candidate = dl + .iter() + .enumerate() + .rev() + .find(|(i, v)| v.is_some() && *i < min_pos); + match next_candidate { + Some((i, a)) => { + segment.replace(a.unwrap()); + min_pos = i; + } + None => break, + }; + } + } + debug!( + "disk layout: {}", + disk_layout + .iter() + .enumerate() + .filter(|(i, _v)| *i < min_pos) + .map(|(_i, v)| match v { + Some(v) => v.to_string(), + None => ".".to_string(), + }) + .collect::() + ); + disk_layout + .into_iter() + .enumerate() + .filter(|(i, v)| v.is_some() && *i < min_pos) + .map(|(i, v)| v.unwrap() * i) + .sum() +} + +#[derive(Clone)] +pub(crate) enum Segment { + Empty(usize), + File(usize, usize), +} + +pub fn pt2(input: &str) -> usize { + let mut final_disk: Vec = Vec::new(); + let disk_layout = input + .chars() + .map(|c| c.to_digit(10).unwrap()) + .enumerate() + .map(|(i, c)| { + if i % 2 == 0 { + Segment::File(c as usize, i / 2) + } else { + Segment::Empty(c as usize) + } + }) + .collect::>(); + let mut moved_segments = HashSet::new(); + for (outer_pos, segment) in disk_layout.iter().enumerate() { + let mut s = segment.clone(); + if moved_segments.contains(&outer_pos) { + s = match s { + Segment::File(v, _i) => Segment::Empty(v), + Segment::Empty(v) => Segment::Empty(v), + }; + } + moved_segments.insert(outer_pos); + match s { + Segment::File(v, i) => { + final_disk.append(&mut vec![i; v]); + } + Segment::Empty(v) => { + let mut remaining_space = v; + while remaining_space > 0 { + if let Some((pos, v, i)) = disk_layout + .iter() + .enumerate() + .filter(|(_pos, s)| match s { + Segment::Empty(_) => false, + Segment::File(_v, _i) => true, + }) + .filter(|(pos, _s)| !moved_segments.contains(pos)) + .map(|(pos, s)| match s { + Segment::Empty(_) => unreachable!(), + Segment::File(v, i) => (pos, *v, *i), + }) + .rev() + .find(|(_pos, v, _i)| *v <= remaining_space) + { + final_disk.append(&mut vec![i; v]); + remaining_space -= v; + moved_segments.insert(pos); + } else { + final_disk.append(&mut vec![0; remaining_space]); + remaining_space = 0; + } + } + } + } + } + debug!("disk layout: {final_disk:?}"); + final_disk.iter().enumerate().map(|(a, b)| a * *b).sum() +} + +#[cfg(test)] +mod tests { + use tracing_test::traced_test; + + use super::{pt1, pt2}; + use std::fs::read_to_string; + + #[traced_test] + #[test] + pub fn test_pt1() { + let input = + read_to_string("./inputs/day09/example.txt").expect("Missing example.txt for day09"); + assert_eq!(pt1(&input), 1928) + } + + #[traced_test] + #[test] + pub fn test_pt2() { + let input = + read_to_string("./inputs/day09/example.txt").expect("Missing example.txt for day09"); + + assert_eq!(pt2(&input), 2858) + } +} diff --git a/src/main.rs b/src/main.rs index a0e4a18..bfa0cc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,8 @@ mod day06; mod day07; #[cfg(feature = "day8")] mod day08; +#[cfg(feature = "day9")] +mod day09; fn main() -> eyre::Result<()> { color_eyre::install()?; @@ -41,6 +43,8 @@ fn main() -> eyre::Result<()> { solve_day(7, day07::pt1, day07::pt2); #[cfg(feature = "day8")] solve_day(8, day08::pt1, day08::pt2); + #[cfg(feature = "day9")] + solve_day(9, day09::pt1, day09::pt2); Ok(()) }