Advent of Code solutions in Rust

feat: day 8 of AoC 2025

+144 -2
+142
aoc_2025/src/day08.rs
··· 1 + use std::num::ParseIntError; 2 + 3 + use aoc_companion::prelude::*; 4 + use aoc_utils::linalg::{ParseVectorError, Vector}; 5 + use itertools::Itertools as _; 6 + 7 + pub(crate) struct Door { 8 + boxes: Vec<Vector<i64, 3>>, 9 + } 10 + 11 + impl<'input> Solution<'input> for Door { 12 + fn parse(input: &'input str) -> Result<Self, ParseVectorError<ParseIntError>> { 13 + input 14 + .lines() 15 + .map(str::parse) 16 + .try_collect() 17 + .map(|boxes| Door { boxes }) 18 + } 19 + 20 + fn part1(&self) -> usize { 21 + networks(&self.boxes, 1000) 22 + .iter() 23 + .counts() 24 + .into_values() 25 + .sorted() 26 + .rev() 27 + .take(3) 28 + .product() 29 + } 30 + 31 + fn part2(&self) -> i64 { 32 + let (Vector([x1, _, _]), Vector([x2, _, _])) = final_connection(&self.boxes); 33 + x1 * x2 34 + } 35 + } 36 + 37 + fn networks(boxes: &[Vector<i64, 3>], n_connect: usize) -> Vec<usize> { 38 + boxes 39 + .iter() 40 + .enumerate() 41 + .tuple_combinations() 42 + .sorted_unstable_by_key(|((_, pi), (_, pj))| (**pi - **pj).norm_l2_sq()) 43 + .take(n_connect) 44 + .fold( 45 + (0..boxes.len()).collect_vec(), 46 + |mut assoc, ((i, _), (j, _))| { 47 + update_network_associations(&mut assoc, i, j); 48 + assoc 49 + }, 50 + ) 51 + } 52 + 53 + fn final_connection(boxes: &[Vector<i64, 3>]) -> (Vector<i64, 3>, Vector<i64, 3>) { 54 + boxes 55 + .iter() 56 + .enumerate() 57 + .tuple_combinations() 58 + .sorted_unstable_by_key(|((_, pi), (_, pj))| (**pi - **pj).norm_l2_sq()) 59 + .scan( 60 + ((0..boxes.len()).collect_vec(), boxes.len()), 61 + |(assoc, n_grp), ((i, pi), (j, pj))| { 62 + if update_network_associations(assoc, i, j) == UpdateOutcome::NetworksConnected { 63 + *n_grp -= 1; 64 + } 65 + Some(((*pi, *pj), *n_grp)) 66 + }, 67 + ) 68 + .find(|(_, n_grp)| *n_grp == 1) 69 + .expect("at least 2 junction boxes should be present") 70 + .0 71 + } 72 + 73 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 74 + enum UpdateOutcome { 75 + AlreadyConnected, 76 + NetworksConnected, 77 + } 78 + 79 + fn update_network_associations(assoc: &mut [usize], i: usize, j: usize) -> UpdateOutcome { 80 + let lhs = assoc[i]; 81 + let rhs = assoc[j]; 82 + 83 + if lhs != rhs { 84 + assoc 85 + .iter_mut() 86 + .filter(|x| **x == rhs) 87 + .for_each(|x| *x = lhs); 88 + 89 + UpdateOutcome::NetworksConnected 90 + } else { 91 + UpdateOutcome::AlreadyConnected 92 + } 93 + } 94 + 95 + #[cfg(test)] 96 + mod tests { 97 + use super::*; 98 + 99 + const EXAMPLE_BOXES: &[Vector<i64, 3>] = &[ 100 + Vector([162, 817, 812]), 101 + Vector([57, 618, 57]), 102 + Vector([906, 360, 560]), 103 + Vector([592, 479, 940]), 104 + Vector([352, 342, 300]), 105 + Vector([466, 668, 158]), 106 + Vector([542, 29, 236]), 107 + Vector([431, 825, 988]), 108 + Vector([739, 650, 466]), 109 + Vector([52, 470, 668]), 110 + Vector([216, 146, 977]), 111 + Vector([819, 987, 18]), 112 + Vector([117, 168, 530]), 113 + Vector([805, 96, 715]), 114 + Vector([346, 949, 466]), 115 + Vector([970, 615, 88]), 116 + Vector([941, 993, 340]), 117 + Vector([862, 61, 35]), 118 + Vector([984, 92, 344]), 119 + Vector([425, 690, 689]), 120 + ]; 121 + 122 + #[test] 123 + fn example_network_counts() { 124 + itertools::assert_equal( 125 + networks(EXAMPLE_BOXES, 10) 126 + .iter() 127 + .counts() 128 + .into_values() 129 + .sorted() 130 + .rev(), 131 + [5, 4, 2, 2, 1, 1, 1, 1, 1, 1, 1], 132 + ); 133 + } 134 + 135 + #[test] 136 + fn example_final_connection() { 137 + assert_eq!( 138 + final_connection(EXAMPLE_BOXES), 139 + (Vector([216, 146, 977]), Vector([117, 168, 530])) 140 + ) 141 + } 142 + }
+2 -2
aoc_2025/src/main.rs
··· 7 7 mod day05; 8 8 mod day06; 9 9 mod day07; 10 - // mod day08; 10 + mod day08; 11 11 // mod day09; 12 12 // mod day10; 13 13 // mod day11; ··· 25 25 door!(2025-12-05 ~> day05), 26 26 door!(2025-12-06 ~> day06), 27 27 door!(2025-12-07 ~> day07), 28 - // door!(2025-12-08 ~> day08), 28 + door!(2025-12-08 ~> day08), 29 29 // door!(2025-12-09 ~> day09), 30 30 // door!(2025-12-10 ~> day10), 31 31 // door!(2025-12-11 ~> day11),