From f00b61e254b4e4d09fc7ab361cccfb6591be5715 Mon Sep 17 00:00:00 2001 From: Daniel Lin Date: Thu, 5 Dec 2024 06:56:18 -0500 Subject: [PATCH] Day 5: Print Queue --- README.md | 2 +- rs/Cargo.lock | 28 +++++++++ rs/Cargo.toml | 4 +- rs/benches/criterion.rs | 8 ++- rs/src/day5.rs | 130 ++++++++++++++++++++++++++++++++++++++++ rs/src/lib.rs | 1 + rs/src/main.rs | 12 +++- 7 files changed, 180 insertions(+), 5 deletions(-) create mode 100644 rs/src/day5.rs diff --git a/README.md b/README.md index effe123..4a07f91 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,4 @@ Development occurs in language-specific directories: |[Day2.hs](hs/src/Day2.hs)|[Day2.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day2.kt)|[day2.py](py/aoc2024/day2.py)|[day2.rs](rs/src/day2.rs)| |[Day3.hs](hs/src/Day3.hs)|[Day3.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day3.kt)|[day3.py](py/aoc2024/day3.py)|[day3.rs](rs/src/day3.rs)| |[Day4.hs](hs/src/Day4.hs)|[Day4.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day4.kt)|[day4.py](py/aoc2024/day4.py)|[day4.rs](rs/src/day4.rs)| -|[Day5.hs](hs/src/Day5.hs)|[Day5.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day5.kt)|[day5.py](py/aoc2024/day5.py)|| +|[Day5.hs](hs/src/Day5.hs)|[Day5.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day5.kt)|[day5.py](py/aoc2024/day5.py)|[day5.rs](rs/src/day5.rs)| diff --git a/rs/Cargo.lock b/rs/Cargo.lock index b16a193..702e03a 100644 --- a/rs/Cargo.lock +++ b/rs/Cargo.lock @@ -23,15 +23,23 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +[[package]] +name = "anyhow" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" + [[package]] name = "aoc2024" version = "0.1.0" dependencies = [ + "anyhow", "array-util", "criterion", "indoc", "itertools 0.13.0", "pretty_assertions", + "thiserror", ] [[package]] @@ -473,6 +481,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinytemplate" version = "1.2.1" diff --git a/rs/Cargo.toml b/rs/Cargo.toml index e105df5..7d8c620 100644 --- a/rs/Cargo.toml +++ b/rs/Cargo.toml @@ -17,8 +17,10 @@ name = "aoc2024" path = "src/main.rs" [dependencies] -array-util = "1.0.2" +anyhow = "1.0" +array-util = "1" itertools = "0.13" +thiserror = "2" [dev-dependencies] criterion = "0.5.1" diff --git a/rs/benches/criterion.rs b/rs/benches/criterion.rs index ee0c6c9..8d0bf68 100644 --- a/rs/benches/criterion.rs +++ b/rs/benches/criterion.rs @@ -1,4 +1,4 @@ -use aoc2024::{day1, day2, day3, day4}; +use aoc2024::{day1, day2, day3, day4, day5}; use criterion::{black_box, Criterion}; use std::env; use std::fs; @@ -38,6 +38,12 @@ fn aoc2024_bench(c: &mut Criterion) -> io::Result<()> { g.bench_function("part 2", |b| b.iter(|| day4::part2(black_box(&data)))); g.finish(); + let data = get_day_input(5)?; + let mut g = c.benchmark_group("day 5"); + g.bench_function("part 1", |b| b.iter(|| day5::part1(black_box(&data)))); + g.bench_function("part 2", |b| b.iter(|| day5::part2(black_box(&data)))); + g.finish(); + Ok(()) } diff --git a/rs/src/day5.rs b/rs/src/day5.rs new file mode 100644 index 0000000..8765c64 --- /dev/null +++ b/rs/src/day5.rs @@ -0,0 +1,130 @@ +use std::collections::BTreeSet; +use std::mem::swap; +use std::num::ParseIntError; + +#[derive(Debug, Eq, PartialEq, thiserror::Error)] +pub enum Error { + #[error("Format")] + Format, + #[error("Parse({0})")] + Parse(ParseIntError), +} + +impl From for Error { + fn from(value: ParseIntError) -> Self { + Self::Parse(value) + } +} + +type ParsedInput = (BTreeSet<(u32, u32)>, Vec>); + +fn parse(data: &str) -> Result { + let (deps, updates) = data.split_once("\n\n").ok_or(Error::Format)?; + let deps = deps + .lines() + .map(|line| { + let (x, y) = line.split_once('|').ok_or(Error::Format)?; + Ok((x.parse()?, y.parse()?)) + }) + .collect::>()?; + let updates = updates + .lines() + .map(|line| line.split(',').map(|page| page.parse::()).collect()) + .collect::, _>>()?; + Ok((deps, updates)) +} + +pub fn part1(data: &str) -> Result { + let (deps, updates) = parse(data)?; + Ok(updates + .into_iter() + .filter_map(|pages| { + if pages + .iter() + .enumerate() + .all(|(i, x)| pages[i + 1..].iter().all(|y| !deps.contains(&(*y, *x)))) + { + pages.get(pages.len() / 2).copied() + } else { + None + } + }) + .sum()) +} + +pub fn part2(data: &str) -> Result { + let (deps, updates) = parse(data)?; + Ok(updates + .into_iter() + .filter_map(|mut pages| { + let mut deranged = false; + for i in 0..pages.len() { + let pages = &mut pages[i..]; + 'scan: loop { + let (x, pages) = pages.split_first_mut().unwrap(); + for y in pages.iter_mut() { + if deps.contains(&(*y, *x)) { + deranged = true; + swap(x, y); + continue 'scan; + } + } + break; + } + } + if deranged { + pages.get(pages.len() / 2).copied() + } else { + None + } + }) + .sum()) +} + +#[cfg(test)] +mod tests { + use super::*; + use indoc::indoc; + use pretty_assertions::assert_eq; + + static EXAMPLE: &str = indoc! {" + 47|53 + 97|13 + 97|61 + 97|47 + 75|29 + 61|13 + 75|53 + 29|13 + 97|29 + 53|29 + 61|53 + 97|53 + 61|29 + 47|13 + 75|47 + 97|75 + 47|61 + 75|61 + 47|29 + 75|13 + 53|13 + + 75,47,61,53,29 + 97,61,53,29,13 + 75,29,13 + 75,97,47,61,53 + 61,13,29 + 97,13,75,29,47 + "}; + + #[test] + fn part1_examples() { + assert_eq!(Ok(143), part1(EXAMPLE)); + } + + #[test] + fn part2_examples() { + assert_eq!(Ok(123), part2(EXAMPLE)); + } +} diff --git a/rs/src/lib.rs b/rs/src/lib.rs index 5415e92..acd1d2c 100644 --- a/rs/src/lib.rs +++ b/rs/src/lib.rs @@ -2,3 +2,4 @@ pub mod day1; pub mod day2; pub mod day3; pub mod day4; +pub mod day5; diff --git a/rs/src/main.rs b/rs/src/main.rs index 99a57c3..8e6dcbe 100644 --- a/rs/src/main.rs +++ b/rs/src/main.rs @@ -1,4 +1,4 @@ -use aoc2024::{day1, day2, day3, day4}; +use aoc2024::{day1, day2, day3, day4, day5}; use std::collections::HashSet; use std::env; use std::fs; @@ -13,7 +13,7 @@ fn get_day_input(day: u8) -> io::Result { fs::read_to_string(Path::new(&datadir).join(format!("day{}.txt", day))) } -fn main() -> io::Result<()> { +fn main() -> anyhow::Result<()> { let args = env::args().skip(1).collect::>(); if args.is_empty() || args.contains("1") { @@ -48,5 +48,13 @@ fn main() -> io::Result<()> { println!(); } + if args.is_empty() || args.contains("5") { + println!("Day 5"); + let data = get_day_input(5)?; + println!("{:?}", day5::part1(&data)?); + println!("{:?}", day5::part2(&data)?); + println!(); + } + Ok(()) }