From aade7c356e16d01436f53d8c4e759fa737fef7d2 Mon Sep 17 00:00:00 2001 From: traxys Date: Mon, 8 Dec 2025 14:36:49 +0100 Subject: [PATCH] Day 8 --- src/bin/day8.rs | 183 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 src/bin/day8.rs diff --git a/src/bin/day8.rs b/src/bin/day8.rs new file mode 100644 index 0000000..ab31427 --- /dev/null +++ b/src/bin/day8.rs @@ -0,0 +1,183 @@ +use std::time::Instant; + +use aoc_2025::{load, print_res}; +use bstr::BString; + +#[derive(Debug, Clone, Copy)] +pub struct Point3 { + pub x: u64, + pub y: u64, + pub z: u64, +} + +impl Point3 { + pub fn dist2(&self, other: &Point3) -> u64 { + self.x.abs_diff(other.x).pow(2) + + self.y.abs_diff(other.y).pow(2) + + self.z.abs_diff(other.z).pow(2) + } +} + +type Parsed = Vec; + +#[inline(never)] +pub fn parsing(input: &BString) -> color_eyre::Result { + str::from_utf8(input)? + .lines() + .map(|v| { + let Some((x, r)) = v.split_once(',') else { + color_eyre::eyre::bail!("Invalid coordinates: {v}"); + }; + let Some((y, z)) = r.split_once(',') else { + color_eyre::eyre::bail!("Invalid coordinates: {v}"); + }; + + Ok(Point3 { + x: x.parse()?, + y: y.parse()?, + z: z.parse()?, + }) + }) + .collect() +} + +#[derive(Clone, Copy, Debug)] +struct UnionFindEntry { + parent: u16, + size: u16, +} + +#[derive(Debug)] +struct UnionFind { + entries: Vec, + set_count: usize, +} + +impl UnionFind { + pub fn new(size: usize) -> Self { + assert!(size <= u16::MAX as usize); + let mut entries = Vec::with_capacity(size); + for i in 0..size as u16 { + entries.push(UnionFindEntry { parent: i, size: 1 }); + } + + Self { + entries, + set_count: size, + } + } + + pub fn find(&mut self, mut x: u16) -> u16 { + if self.entries[x as usize].parent != x { + let mut root = x; + + while self.entries[root as usize].parent != root { + root = self.entries[root as usize].parent; + } + + while self.entries[x as usize].parent != root { + let parent = self.entries[x as usize].parent; + self.entries[x as usize].parent = root; + x = parent; + } + + root + } else { + x + } + } + + pub fn union(&mut self, a: u16, b: u16) { + let mut ra = self.find(a); + let mut rb = self.find(b); + + if ra == rb { + return; + } + + if self.entries[ra as usize].size < self.entries[rb as usize].size { + std::mem::swap(&mut ra, &mut rb); + } + + self.entries[rb as usize].parent = ra; + self.entries[ra as usize].size += self.entries[rb as usize].size; + self.set_count -= 1; + } + + pub fn sets(&self) -> impl Iterator { + self.entries.iter().enumerate().filter_map(|(i, e)| { + if e.parent as usize == i { + Some((e.parent, e.size as usize)) + } else { + None + } + }) + } + + pub fn set_count(&self) -> usize { + self.set_count + } +} + +#[inline(never)] +pub fn part1(input: Parsed) { + let mut pairs: Vec<_> = (0..input.len()) + .flat_map(|ia| ((ia + 1)..input.len()).map(move |ib| (ia, ib))) + .collect(); + pairs.sort_unstable_by_key(|&(ia, ib)| input[ia].dist2(&input[ib])); + + let mut uf = UnionFind::new(input.len()); + for &(ia, ib) in pairs.iter().take(1000) { + uf.union(ia as u16, ib as u16); + } + + let mut sets: Vec<_> = uf.sets().collect(); + sets.sort_unstable_by_key(|&(_, s)| s); + + let mut largests = 1; + for _ in 0..3 { + largests *= sets.pop().unwrap().1; + } + + print_res!("Size product is: {largests}"); +} + +#[inline(never)] +pub fn part2(input: Parsed) { + let mut pairs: Vec<_> = (0..input.len()) + .flat_map(|ia| ((ia + 1)..input.len()).map(move |ib| (ia, ib))) + .collect(); + pairs.sort_unstable_by_key(|&(ia, ib)| input[ia].dist2(&input[ib])); + + let mut uf = UnionFind::new(input.len()); + for &(ia, ib) in &pairs { + uf.union(ia as u16, ib as u16); + if uf.set_count() == 1 { + print_res!("X product of last boxes: {}", input[ia].x * input[ib].x); + return; + } + } + + panic!("Not all boxes have been connected"); +} + +pub fn main() -> color_eyre::Result<()> { + let context = load()?; + + let start = Instant::now(); + let parsed = parsing(&context.input)?; + let elapsed = humantime::format_duration(start.elapsed()); + + let start = Instant::now(); + if context.part == 1 { + part1(parsed); + } else { + part2(parsed); + } + let elapsed_part = humantime::format_duration(start.elapsed()); + + println!(" Parsing: {elapsed}"); + println!(" Solving: {elapsed_part}"); + + Ok(()) +}