From 43c57b58dfc7a8303a4ee8c728269b129cc524e4 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Mon, 8 Dec 2025 08:42:26 +0200 Subject: [PATCH] Finish day-08 part-1 --- 2025/Cargo.lock | 31 +++++++ 2025/Cargo.toml | 1 + 2025/day-08/Cargo.toml | 28 ++++++ 2025/day-08/benches/benchmarks.rs | 21 +++++ 2025/day-08/src/bin/part1.rs | 12 +++ 2025/day-08/src/bin/part2.rs | 12 +++ 2025/day-08/src/lib.rs | 2 + 2025/day-08/src/part1.rs | 142 ++++++++++++++++++++++++++++++ 2025/day-08/src/part2.rs | 23 +++++ 9 files changed, 272 insertions(+) create mode 100644 2025/day-08/Cargo.toml create mode 100644 2025/day-08/benches/benchmarks.rs create mode 100644 2025/day-08/src/bin/part1.rs create mode 100644 2025/day-08/src/bin/part2.rs create mode 100644 2025/day-08/src/lib.rs create mode 100644 2025/day-08/src/part1.rs create mode 100644 2025/day-08/src/part2.rs diff --git a/2025/Cargo.lock b/2025/Cargo.lock index fa6ab88..051b77d 100644 --- a/2025/Cargo.lock +++ b/2025/Cargo.lock @@ -205,6 +205,22 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "day-08" +version = "0.1.0" +dependencies = [ + "divan", + "glam", + "itertools", + "miette", + "nom", + "rstest", + "test-log", + "thiserror", + "tracing", + "tracing-subscriber", +] + [[package]] name = "divan" version = "0.1.21" @@ -301,6 +317,15 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +[[package]] +name = "glam" +version = "0.30.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46" +dependencies = [ + "mint", +] + [[package]] name = "glob" version = "0.3.3" @@ -416,6 +441,12 @@ dependencies = [ "adler2", ] +[[package]] +name = "mint" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" + [[package]] name = "nom" version = "8.0.0" diff --git a/2025/Cargo.toml b/2025/Cargo.toml index 2a90bb1..9285223 100644 --- a/2025/Cargo.toml +++ b/2025/Cargo.toml @@ -8,6 +8,7 @@ members = [ "day-05", "day-06", "day-07", + "day-08", ] default-members = ["day-*"] resolver = "2" diff --git a/2025/day-08/Cargo.toml b/2025/day-08/Cargo.toml new file mode 100644 index 0000000..c27145b --- /dev/null +++ b/2025/day-08/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "day-08" +version = "0.1.0" +edition = "2024" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +itertools.workspace = true +nom.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +miette.workspace = true +thiserror.workspace = true +glam = { workspace = true, features = ["mint"] } + +[dev-dependencies] +divan.workspace = true +rstest.workspace = true +test-log.workspace = true + +[[bench]] +name = "day-08-bench" +path = "benches/benchmarks.rs" +harness = false + +[lints] +workspace = true diff --git a/2025/day-08/benches/benchmarks.rs b/2025/day-08/benches/benchmarks.rs new file mode 100644 index 0000000..0ddf332 --- /dev/null +++ b/2025/day-08/benches/benchmarks.rs @@ -0,0 +1,21 @@ +use day_08::*; + +fn main() { + divan::main(); +} + +#[divan::bench] +fn part1() { + part1::process(divan::black_box(include_str!( + "../input1.txt", + ))) + .unwrap(); +} + +#[divan::bench] +fn part2() { + part2::process(divan::black_box(include_str!( + "../input2.txt", + ))) + .unwrap(); +} diff --git a/2025/day-08/src/bin/part1.rs b/2025/day-08/src/bin/part1.rs new file mode 100644 index 0000000..7fdd9e6 --- /dev/null +++ b/2025/day-08/src/bin/part1.rs @@ -0,0 +1,12 @@ +use day_08::part1::process; +use miette::{Context, Result}; + +#[tracing::instrument] +fn main() -> Result<()> { + tracing_subscriber::fmt::init(); + + let file = include_str!("../../input1.txt"); + let result = process(file, 1000).context("process part 1")?; + println!("{result}"); + Ok(()) +} diff --git a/2025/day-08/src/bin/part2.rs b/2025/day-08/src/bin/part2.rs new file mode 100644 index 0000000..4d52dbd --- /dev/null +++ b/2025/day-08/src/bin/part2.rs @@ -0,0 +1,12 @@ +use day_08::part2::process; +use miette::{Context, Result}; + +#[tracing::instrument] +fn main() -> Result<()> { + tracing_subscriber::fmt::init(); + + let file = include_str!("../../input2.txt"); + let result = process(file).context("process part 2")?; + println!("{result}"); + Ok(()) +} diff --git a/2025/day-08/src/lib.rs b/2025/day-08/src/lib.rs new file mode 100644 index 0000000..faaf542 --- /dev/null +++ b/2025/day-08/src/lib.rs @@ -0,0 +1,2 @@ +pub mod part1; +pub mod part2; diff --git a/2025/day-08/src/part1.rs b/2025/day-08/src/part1.rs new file mode 100644 index 0000000..1834e01 --- /dev/null +++ b/2025/day-08/src/part1.rs @@ -0,0 +1,142 @@ +use glam::Vec3; +use miette::miette; +use std::{collections::HashSet, str::FromStr}; + +fn vec3_from_str(s: &str) -> Result { + let coords = s + .trim() + .split(',') + .map(|x| { + x.parse::() + .map_err(|_| "Invalid coordinate value".to_string()) + }) + .collect::, _>>()?; + if coords.len() == 3 { + let vec = Vec3::new(coords[0], coords[1], coords[2]); + return Ok(vec); + } + + Err("Expected exactly 3 coordinates".to_string()) +} + +#[derive(Debug, Clone)] +struct UnionFind { + parent: Vec, + size: Vec, +} + +impl UnionFind { + fn new(n: usize) -> Self { + Self { + parent: (0..n).collect(), + size: vec![1; n], + } + } + + fn find(&mut self, x: usize) -> usize { + if self.parent[x] != x { + self.parent[x] = self.find(self.parent[x]); + } + self.parent[x] + } + + fn union(&mut self, mut x: usize, mut y: usize) { + x = self.find(x); + y = self.find(y); + if x == y { + return; + } + if self.size[x] < self.size[y] { + (x, y) = (y, x); + } + self.parent[y] = x; + self.size[x] += self.size[y]; + } +} + +#[derive(Debug, Clone)] +struct Coordinates(Vec); + +impl Coordinates { + fn solve(&self, size: usize) -> usize { + let n = self.0.len(); + + let mut pairs = Vec::new(); + for i in 0..n { + for j in (i + 1)..n { + let dist = self.0[i].distance(self.0[j]); + pairs.push((dist, i, j)); + } + } + + pairs.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); + + let mut uf = UnionFind::new(n); + for (_, i, j) in pairs.iter().take(size) { + if uf.find(*i) != uf.find(*j) { + uf.union(*i, *j); + } + } + + let mut roots = HashSet::new(); + for i in 0..n { + roots.insert(uf.find(i)); + } + + let mut sizes = roots.iter().map(|&root| uf.size[root]).collect::>(); + + sizes.sort_by(|a, b| b.cmp(a)); + sizes[0] * sizes[1] * sizes[2] + } +} + +impl FromStr for Coordinates { + type Err = String; + fn from_str(s: &str) -> Result { + let coords = s + .lines() + .map(vec3_from_str) + .collect::, _>>()?; + Ok(Self(coords)) + } +} + +#[tracing::instrument] +#[allow(clippy::missing_panics_doc)] +#[allow(clippy::missing_errors_doc)] +pub fn process(input: &str, size: usize) -> miette::Result { + let coords = Coordinates::from_str(input).map_err(|e| miette!("{e}"))?; + Ok(coords.solve(size)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_process() -> miette::Result<()> { + let input = "162,817,812 +57,618,57 +906,360,560 +592,479,940 +352,342,300 +466,668,158 +542,29,236 +431,825,988 +739,650,466 +52,470,668 +216,146,977 +819,987,18 +117,168,530 +805,96,715 +346,949,466 +970,615,88 +941,993,340 +862,61,35 +984,92,344 +425,690,689"; + let result = 40; + assert_eq!(process(input, 10)?, result); + Ok(()) + } +} diff --git a/2025/day-08/src/part2.rs b/2025/day-08/src/part2.rs new file mode 100644 index 0000000..ede34a7 --- /dev/null +++ b/2025/day-08/src/part2.rs @@ -0,0 +1,23 @@ +use miette::Result; + +#[tracing::instrument] +#[allow(clippy::missing_panics_doc)] +#[allow(clippy::missing_errors_doc)] +pub fn process(input: &str) -> Result { + todo!("day xx - part 2"); + Ok(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_process() -> Result<()> { + let input = ""; + todo!("haven't built test yet"); + let result = 0; + assert_eq!(process(input)?, result); + Ok(()) + } +}