use std::{f64, fmt::Display}; use aoc_runner_derive::{aoc, aoc_generator}; use itertools::Itertools; #[derive(Eq, PartialEq, Clone, Debug, Hash)] struct Junction { pos: (i64, i64, i64), } fn distance(a: &Junction, b: &Junction) -> f64 { if a.pos == b.pos { f64::MAX // ugh } else { (((a.pos.0 - b.pos.0).pow(2) + (a.pos.1 - b.pos.1).pow(2) + (a.pos.2 - b.pos.2).pow(2)) as f64) .sqrt() } } impl Junction { fn distance(&self, other: &Junction) -> f64 { distance(self, other) } } impl Display for Junction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( "({},{},{})", self.pos.0, self.pos.1, self.pos.2 )) } } impl From<&str> for Junction { fn from(value: &str) -> Self { Self { pos: value .split(',') .map(|v| v.parse().unwrap()) .collect_tuple() .unwrap(), } } } #[derive(Clone)] struct Circuits { junctions: Vec, circuits: Vec>, } impl Circuits { fn find_circuit(&self, junction: usize) -> Option { self.circuits .iter() .enumerate() .find(|(_i, c)| c.contains(&junction)) .map(|(i, _c)| i) } fn merge_circuits(&mut self, a: usize, b: usize) { if a == b { return; } let mut items = Vec::new(); std::mem::swap(&mut items, &mut self.circuits[b]); self.circuits[a].append(&mut items); self.circuits.remove(b); } fn connect(&mut self, a: usize, b: usize) { let a_circuit = self.find_circuit(a); let b_circuit = self.find_circuit(b); match (a_circuit, b_circuit) { (None, None) => self.circuits.push(vec![a, b]), // both are unconnected (Some(a_circuit), Some(b_circuit)) => { self.merge_circuits(a_circuit, b_circuit); // both are already in circuits, merge them } (Some(a_circuit), None) => self.circuits[a_circuit].push(b), // one is in a circuit, so add the other to the existing circuit (None, Some(b_circuit)) => self.circuits[b_circuit].push(a), }; } } #[aoc_generator(day8)] fn parse(input: &str) -> Circuits { let junctions = input.lines().map(|l| l.into()).collect_vec(); Circuits { junctions, circuits: Vec::new(), } } fn part1_impl(input: &Circuits, n: usize) -> u64 { let mut circuits = input.clone(); for (a, b, _d) in circuits .junctions .iter() .enumerate() .combinations(2) .map(|p| (p[0].0, p[1].0, p[0].1.distance(p[1].1))) .sorted_by(|a, b| a.2.partial_cmp(&b.2).unwrap()) .take(n) { circuits.connect(a, b) } circuits .circuits .iter() .sorted_by_key(|c| c.len()) .rev() .take(3) .map(|c| c.len()) .reduce(|a, b| a * b) .unwrap() as u64 } #[aoc(day8, part1)] fn part1(input: &Circuits) -> u64 { part1_impl(input, 1000) } #[aoc(day8, part2)] fn part2(input: &Circuits) -> u64 { 0 } #[cfg(test)] mod tests { use super::*; const EXAMPLE: &str = "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"; #[test] fn part1_example() { assert_eq!(part1_impl(&parse(EXAMPLE), 10), 40); } #[test] fn part2_example() { assert_eq!(part2(&parse(EXAMPLE)), 0); } }