Files
aoc2025/src/day8.rs

250 lines
5.5 KiB
Rust

use std::{cmp::Reverse, collections::BinaryHeap, fmt::Display};
use aoc_runner_derive::{aoc, aoc_generator};
use itertools::Itertools;
#[derive(PartialEq, Clone, Debug)]
struct Junction {
pos: (i64, i64, i64),
}
fn squared_distance(a: &Junction, b: &Junction) -> u64 {
// if a.pos == b.pos {
// 0
// } else {
(a.pos.0 - b.pos.0).pow(2) as u64
+ (a.pos.1 - b.pos.1).pow(2) as u64
+ (a.pos.2 - b.pos.2).pow(2) as u64
// }
}
impl Junction {
fn squared_distance(&self, other: &Junction) -> u64 {
squared_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<Junction>,
circuits: Vec<Vec<usize>>,
}
impl Circuits {
fn find_circuit(&self, junction: usize) -> usize {
self.circuits
.iter()
.enumerate()
.find(|(_i, c)| c.contains(&junction))
.map(|(i, _c)| i)
.unwrap()
}
fn merge_circuits(&mut self, a: usize, b: usize) {
if a == b {
return;
}
let mut items = std::mem::take(&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);
self.merge_circuits(a_circuit, b_circuit); // both are already in circuits, merge them
}
}
#[aoc_generator(day8)]
fn parse(input: &str) -> Circuits {
let junctions = input.lines().map(|l| l.into()).collect_vec();
Circuits {
circuits: Vec::from_iter((0..junctions.len()).map(|i| vec![i])),
junctions,
}
}
fn part1_impl(input: &Circuits, n: usize) -> u64 {
let mut circuits = input.clone();
for (a, b, _d) in circuits
.junctions
.iter()
.enumerate()
.tuple_combinations()
.map(|((a_pos, a), (b_pos, b))| (a_pos, b_pos, a.squared_distance(b)))
.sorted_unstable_by_key(|(_a_pos, _b_pos, d)| *d)
.take(n)
{
circuits.connect(a, b)
}
circuits
.circuits
.iter()
.map(|c| c.len())
.sorted_unstable()
.rev()
.take(3)
.reduce(|a, b| a * b)
.unwrap() as u64
}
#[aoc(day8, part1, Sorted)]
fn part1(input: &Circuits) -> u64 {
part1_impl(input, 1000)
}
#[aoc(day8, part2, Sorted)]
fn part2(input: &Circuits) -> u64 {
let mut circuits = input.clone();
for (a, b, _d) in circuits
.junctions
.iter()
.enumerate()
.tuple_combinations()
.map(|((a_pos, a), (b_pos, b))| (a_pos, b_pos, a.squared_distance(b)))
.sorted_unstable_by_key(|(_a_pos, _b_pos, d)| *d)
{
circuits.connect(a, b);
if circuits.circuits.len() == 1 {
return (circuits.junctions[a].pos.0 * circuits.junctions[b].pos.0) as u64;
}
}
panic!()
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct JunctionPair {
d: u64,
a: usize,
b: usize,
}
fn make_heap(circuits: &Circuits) -> BinaryHeap<Reverse<JunctionPair>> {
BinaryHeap::from_iter(
circuits
.junctions
.iter()
.enumerate()
.tuple_combinations()
.map(|((a_pos, a), (b_pos, b))| {
Reverse(JunctionPair {
a: a_pos,
b: b_pos,
d: a.squared_distance(b),
})
}),
)
}
fn part1_heaped_impl(input: &Circuits, n: usize) -> u64 {
let mut circuits = input.clone();
let mut distances = make_heap(&circuits);
for _ in 0..n {
let pair = distances.pop().unwrap().0;
circuits.connect(pair.a, pair.b);
}
circuits
.circuits
.iter()
.map(|c| c.len())
.sorted_unstable()
.rev()
.take(3)
.reduce(|a, b| a * b)
.unwrap() as u64
}
#[aoc(day8, part1, Heaped)]
fn part1_heaped(input: &Circuits) -> u64 {
part1_heaped_impl(input, 1000)
}
#[aoc(day8, part2, Heaped)]
fn part2_heaped(input: &Circuits) -> u64 {
let mut circuits = input.clone();
let mut distances = make_heap(&circuits);
while let Some(Reverse(jp)) = distances.pop() {
circuits.connect(jp.a, jp.b);
if circuits.circuits.len() == 1 {
return (circuits.junctions[jp.a].pos.0 * circuits.junctions[jp.b].pos.0) as u64;
}
}
panic!()
}
#[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 part1_heaped_example() {
assert_eq!(part1_heaped_impl(&parse(EXAMPLE), 10), 40);
}
#[test]
fn part2_example() {
assert_eq!(part2(&parse(EXAMPLE)), 25272);
}
#[test]
fn part2_heaped_example() {
assert_eq!(part2_heaped(&parse(EXAMPLE)), 25272);
}
}