Skip to content

Commit cadcda0

Browse files
committed
solve: day8
1 parent c03d137 commit cadcda0

File tree

3 files changed

+167
-1
lines changed

3 files changed

+167
-1
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
resolver = "2"
33
members = [
44
"aoc_lib/aoc_derive",
5-
"aoc_lib/utils", "day1", "day2", "day3", "day4", "day5", "day6", "day7",
5+
"aoc_lib/utils", "day1", "day2", "day3", "day4", "day5", "day6", "day7", "day8",
66
]
77

88
[workspace.dependencies]

day8/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "day8"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
aoc_derive.path = '../aoc_lib/aoc_derive'
8+
utils.path = '../aoc_lib/utils'
9+
derive_more.workspace = true
10+
itertools.workspace = true
11+
lazy-regex.workspace = true
12+
parse-display.workspace = true
13+
rayon.workspace = true
14+
regex.workspace = true
15+
num.workspace = true
16+
17+
[dev-dependencies]
18+
pretty_assertions.workspace = true

day8/src/main.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
use std::collections::{BTreeSet, HashMap, HashSet};
2+
3+
use aoc_derive::aoc_main;
4+
use itertools::Itertools;
5+
use utils::*;
6+
7+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
8+
struct Pos {
9+
x: i64,
10+
y: i64,
11+
z: i64,
12+
}
13+
14+
impl Pos {
15+
fn dist(&self, other: &Pos) -> i64 {
16+
(((self.x - other.x).pow(2) + (self.y - other.y).pow(2) + (self.z - other.z).pow(2)) as f64)
17+
.sqrt()
18+
.floor() as i64
19+
}
20+
}
21+
22+
#[derive(Debug, Clone)]
23+
struct JunctionBox {
24+
pos: Pos,
25+
connections: HashSet<Pos>,
26+
}
27+
28+
impl JunctionBox {
29+
fn get_circuit(&self, boxes: &HashMap<Pos, JunctionBox>) -> HashSet<Pos> {
30+
let mut circuit = HashSet::from([self.pos]);
31+
let mut next = self.connections.clone();
32+
while !next.is_empty() {
33+
circuit.extend(next.iter().copied());
34+
next = next
35+
.iter()
36+
.flat_map(|b| boxes[b].connections.iter().copied())
37+
.filter(|b| !circuit.contains(b))
38+
.collect();
39+
}
40+
circuit
41+
}
42+
}
43+
44+
fn connect_next(
45+
distances: &[(Pos, Pos)],
46+
boxes: &mut HashMap<Pos, JunctionBox>,
47+
) -> Option<(Pos, Pos)> {
48+
let pair = distances.iter().find(|(b1, b2)| !boxes[b1].connections.contains(b2));
49+
if let Some((shortest1, shortest2)) = pair {
50+
boxes.get_mut(shortest1).unwrap().connections.insert(*shortest2);
51+
boxes.get_mut(shortest2).unwrap().connections.insert(*shortest1);
52+
}
53+
54+
pair.copied()
55+
}
56+
57+
fn part1(
58+
distances: &[(Pos, Pos)],
59+
mut boxes: HashMap<Pos, JunctionBox>,
60+
iterations: usize,
61+
) -> usize {
62+
for _ in 0..iterations {
63+
connect_next(distances, &mut boxes);
64+
}
65+
66+
boxes
67+
.values()
68+
.map(|b| b.get_circuit(&boxes).into_iter().collect::<BTreeSet<_>>())
69+
.unique()
70+
.map(|c| c.len())
71+
.sorted()
72+
.rev()
73+
.take(3)
74+
.product::<usize>()
75+
}
76+
77+
fn part2(distances: &[(Pos, Pos)], mut boxes: HashMap<Pos, JunctionBox>) -> i64 {
78+
loop {
79+
let (b1, b2) = connect_next(distances, &mut boxes)
80+
.expect("Should find solution before running out of connections");
81+
if boxes.get(&b1).unwrap().get_circuit(&boxes).len() == boxes.len() {
82+
return b1.x * b2.x;
83+
}
84+
}
85+
}
86+
87+
#[aoc_main(1000)]
88+
fn solve(input: Input, iterations: usize) -> impl Into<Solution> {
89+
let boxes: HashMap<_, JunctionBox> = input
90+
.lines()
91+
.map(|line| {
92+
let (x, y, z) = extract_numbers(line).collect_tuple().unwrap();
93+
let pos = Pos { x, y, z };
94+
(pos, JunctionBox { pos, connections: HashSet::new() })
95+
})
96+
.collect();
97+
98+
let distances = boxes
99+
.values()
100+
.enumerate()
101+
.flat_map(|(i, b1)| {
102+
boxes
103+
.values()
104+
.enumerate()
105+
.filter_map(move |(j, b2)| (j < i).then_some((b1.pos, b2.pos)))
106+
})
107+
.sorted_by_key(|(b1, b2)| b1.dist(b2))
108+
.collect_vec();
109+
110+
(part1(&distances, boxes.clone(), iterations), part2(&distances, boxes))
111+
}
112+
113+
#[cfg(test)]
114+
mod tests {
115+
use super::*;
116+
#[test]
117+
fn test_examples() {
118+
pretty_assertions::assert_eq!(
119+
solve(
120+
Input::from(
121+
r#"162,817,812
122+
57,618,57
123+
906,360,560
124+
592,479,940
125+
352,342,300
126+
466,668,158
127+
542,29,236
128+
431,825,988
129+
739,650,466
130+
52,470,668
131+
216,146,977
132+
819,987,18
133+
117,168,530
134+
805,96,715
135+
346,949,466
136+
970,615,88
137+
941,993,340
138+
862,61,35
139+
984,92,344
140+
425,690,689"#
141+
),
142+
10
143+
)
144+
.into(),
145+
Solution::from((40.to_string(), 25272.to_string()))
146+
);
147+
}
148+
}

0 commit comments

Comments
 (0)