diff --git a/README.md b/README.md index 353a185b..4ddfccf6 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,4 @@ Development occurs in language-specific directories: |[Day6.hs](hs/src/Day6.hs)|[Day6.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day6.kt)|[day6.py](py/aoc2024/day6.py)|[day6.rs](rs/src/day6.rs)| |[Day7.hs](hs/src/Day7.hs)|[Day7.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day7.kt)|[day7.py](py/aoc2024/day7.py)|[day7.rs](rs/src/day7.rs)| |[Day8.hs](hs/src/Day8.hs)|[Day8.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day8.kt)|[day8.py](py/aoc2024/day8.py)|[day8.rs](rs/src/day8.rs)| -|[Day9.hs](hs/src/Day9.hs)|[Day9.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day9.kt)|[day9.py](py/aoc2024/day9.py)|| +|[Day9.hs](hs/src/Day9.hs)|[Day9.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day9.kt)|[day9.py](py/aoc2024/day9.py)|[day9.rs](rs/src/day9.rs)| diff --git a/rs/benches/criterion.rs b/rs/benches/criterion.rs index b454fb08..01f9911a 100644 --- a/rs/benches/criterion.rs +++ b/rs/benches/criterion.rs @@ -1,4 +1,4 @@ -use aoc2024::{day1, day2, day3, day4, day5, day6, day7, day8}; +use aoc2024::{day1, day2, day3, day4, day5, day6, day7, day8, day9}; use criterion::{black_box, Criterion}; use std::env; use std::fs; @@ -62,6 +62,12 @@ fn aoc2024_bench(c: &mut Criterion) -> io::Result<()> { g.bench_function("part 2", |b| b.iter(|| day8::part2(black_box(&data)))); g.finish(); + let data = get_day_input(9)?; + let mut g = c.benchmark_group("day 9"); + g.bench_function("part 1", |b| b.iter(|| day9::part1(black_box(&data)))); + g.bench_function("part 2", |b| b.iter(|| day9::part2(black_box(&data)))); + g.finish(); + Ok(()) } diff --git a/rs/src/day9.rs b/rs/src/day9.rs new file mode 100644 index 00000000..94b0011e --- /dev/null +++ b/rs/src/day9.rs @@ -0,0 +1,83 @@ +fn tri_range(offset: u64, size: u64) -> u64 { + (2 * offset + size - 1) * size / 2 +} + +pub fn part1(data: &str) -> u64 { + let mut sizes = data + .chars() + .filter_map(|c| c.to_digit(10)) + .collect::>(); + let (mut total, mut offset) = (0, 0); + for i in 0..sizes.len() { + let size = sizes[i]; + if i % 2 == 0 { + total += i as u64 / 2 * tri_range(offset, size.into()); + offset += size as u64; + } else { + let mut free_size = size; + for (j, size) in sizes.iter_mut().enumerate().skip(i + 1).step_by(2).rev() { + if free_size == 0 { + break; + } + let used_size = free_size.min(*size); + total += j as u64 / 2 * tri_range(offset, used_size.into()); + offset += used_size as u64; + free_size -= used_size; + *size -= used_size; + } + } + } + total +} + +pub fn part2(data: &str) -> u64 { + let mut chunks = data + .chars() + .filter_map(|c| c.to_digit(10)) + .scan(0, |acc, size| { + let offset = *acc; + *acc = offset + size; + Some((offset, size)) + }) + .collect::>(); + (0..chunks.len()) + .step_by(2) + .rev() + .map(|i| { + let (mut offset, size) = chunks[i]; + if let Some((free_offset, free_size)) = chunks + .iter_mut() + .take(i) + .skip(1) + .step_by(2) + .find(|(_, free_size)| size <= *free_size) + { + offset = *free_offset; + *free_offset += size; + *free_size -= size; + } + i as u64 / 2 * tri_range(offset.into(), size.into()) + }) + .sum() +} + +#[cfg(test)] +mod tests { + use super::*; + use indoc::indoc; + use pretty_assertions::assert_eq; + + static EXAMPLE: &str = indoc! {" + 2333133121414131402 + "}; + + #[test] + fn part1_examples() { + assert_eq!(1928, part1(EXAMPLE)); + } + + #[test] + fn part2_examples() { + assert_eq!(2858, part2(EXAMPLE)); + } +} diff --git a/rs/src/lib.rs b/rs/src/lib.rs index 65fdb47f..b7940e51 100644 --- a/rs/src/lib.rs +++ b/rs/src/lib.rs @@ -6,3 +6,4 @@ pub mod day5; pub mod day6; pub mod day7; pub mod day8; +pub mod day9; diff --git a/rs/src/main.rs b/rs/src/main.rs index 5aa1c058..eb8f1c24 100644 --- a/rs/src/main.rs +++ b/rs/src/main.rs @@ -1,5 +1,5 @@ use anyhow::anyhow; -use aoc2024::{day1, day2, day3, day4, day5, day6, day7, day8}; +use aoc2024::{day1, day2, day3, day4, day5, day6, day7, day8, day9}; use std::collections::HashSet; use std::env; use std::fs; @@ -81,5 +81,13 @@ fn main() -> anyhow::Result<()> { println!(); } + if args.is_empty() || args.contains("9") { + println!("Day 9"); + let data = get_day_input(9)?; + println!("{:?}", day9::part1(&data)); + println!("{:?}", day9::part2(&data)); + println!(); + } + Ok(()) }