From 9f26142331625d5fcea3e37abb72a28d0f2c5c8b Mon Sep 17 00:00:00 2001 From: Russell Champoux Date: Tue, 9 Dec 2025 15:51:41 -0500 Subject: [PATCH] solution for day 8 --- README.md | 3 +- data/examples/08.txt | 20 ++++++ src/bin/08.rs | 143 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 data/examples/08.txt create mode 100644 src/bin/08.rs diff --git a/README.md b/README.md index e685ba8..01ff3b1 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www. | [Day 5](./src/bin/05.rs) | `159.6µs` | `21.4µs` | | [Day 6](./src/bin/06.rs) | `50.5µs` | `2.5ms` | | [Day 7](./src/bin/07.rs) | `65.4µs` | `55.4µs` | +| [Day 8](./src/bin/08.rs) | `26.4ms` | `27.6ms` | -**Total: 172.62ms** +**Total: 226.62ms** --- diff --git a/data/examples/08.txt b/data/examples/08.txt new file mode 100644 index 0000000..e98a3b6 --- /dev/null +++ b/data/examples/08.txt @@ -0,0 +1,20 @@ +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 diff --git a/src/bin/08.rs b/src/bin/08.rs new file mode 100644 index 0000000..0a29b08 --- /dev/null +++ b/src/bin/08.rs @@ -0,0 +1,143 @@ +advent_of_code::solution!(8); +use std::fmt; +use itertools::{Itertools}; + +#[derive(PartialEq)] +#[derive(Clone)] +struct Position { + pub x: u64, + pub y: u64, + pub z: u64, +} +impl fmt::Debug for Position { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({},{},{})", self.x, self.y, self.z) + } +} +impl fmt::Display for Position { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({},{},{})", self.x, self.y, self.z) + } +} + +fn distance_squared(pos_a: &Position, pos_b: &Position) -> u64 { + pos_a.x.abs_diff(pos_b.x).pow(2) + pos_a.y.abs_diff(pos_b.y).pow(2) + pos_a.z.abs_diff(pos_b.z).pow(2) +} + +pub fn part_one(input: &str) -> Option { + part_one_solve(input, 1000) +} + +fn part_one_solve(input: &str, max_connections: u32 ) -> Option { + let junctions = input.lines().filter(|l| !l.is_empty()).map(|line| { + let (x, y, z) = line.split(',').map(|v| v.parse::().unwrap()).collect_tuple().unwrap(); + Position { x, y, z } + }).collect_vec(); + let size = junctions.len(); + // println!("Junctions: {junctions:?}"); + let mut distances = vec![]; + + for a_idx in 0..size { + for b_idx in a_idx+1..size { + distances.push((a_idx, b_idx, distance_squared(&junctions[a_idx], &junctions[b_idx]))); + } + } + distances.sort_by(|(_, _, a_distance), (_, _, b_distance)| { a_distance.cmp(b_distance) }); + // println!("Distances: {distances:?}"); + + let mut groups = (0..size).map(|idx| vec![idx; 1]).collect_vec(); + // println!("Groups: {groups:?}"); + // println!(); + + + let mut connections_made = 0; + for (start, end, _distance) in distances { + // println!("Considering {} => {}, {} => {} - distance {}", start, end, junctions[start], junctions[end], distance); + // if start and end are not already connected + let (group_a_idx, group_a) = groups.iter().find_position(|g| g.contains(&start)).unwrap(); + connections_made += 1; + if group_a.contains(&end) { + // println!(" Already exists in the same group! skipping"); + continue + } + + let (group_b_idx, group_b) = groups.iter().find_position(|g| g.contains(&end)).unwrap(); + groups[group_a_idx] = [group_a.as_slice(), group_b.as_slice()].concat(); + // group_a.append(&mut group_b); + // groups[group_a_idx] = group_a.append(group_b); + groups.remove(group_b_idx); + // connections_made += 1; + + // println!("Groups after {connections_made} connections: {groups:?}"); + // println!(); + + // connect them by merging their groups + // increment connections_made by 1 + if connections_made >= max_connections { break } + } + + // println!("{groups:?}"); + + let top3: usize = groups.iter().map(|g| g.len()).sorted().rev().take(3).product(); + + // println!("top3 product {top3}"); + + Some(top3 as u64) +} + +pub fn part_two(input: &str) -> Option { + let junctions = input.lines().filter(|l| !l.is_empty()).map(|line| { + let (x, y, z) = line.split(',').map(|v| v.parse::().unwrap()).collect_tuple().unwrap(); + Position { x, y, z } + }).collect_vec(); + let size = junctions.len(); + let mut distances = vec![]; + + for a_idx in 0..size { + for b_idx in a_idx+1..size { + distances.push((a_idx, b_idx, distance_squared(&junctions[a_idx], &junctions[b_idx]))); + } + } + distances.sort_by(|(_, _, a_distance), (_, _, b_distance)| { a_distance.cmp(b_distance) }); + + let mut groups = (0..size).map(|idx| vec![idx; 1]).collect_vec(); + + + let mut last_link = (0, 0); + for (start, end, _distance) in distances { + let (group_a_idx, group_a) = groups.iter().find_position(|g| g.contains(&start)).unwrap(); + if group_a.contains(&end) { + continue + } + + let (group_b_idx, group_b) = groups.iter().find_position(|g| g.contains(&end)).unwrap(); + groups[group_a_idx] = [group_a.as_slice(), group_b.as_slice()].concat(); + groups.remove(group_b_idx); + + if groups.len() == 1 { + last_link = (start, end); + break; + } + } + let (last_junction_from, last_junction_to) = (&junctions[last_link.0], &junctions[last_link.1]); + // println!("Last junction was between {last_junction_from:?} and {last_junction_to:?}"); + + Some(last_junction_from.x * last_junction_to.x) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one_solve(&advent_of_code::template::read_file("examples", DAY), 10); + assert_eq!(result, Some(40)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(25272)); + } +}