--- /dev/null
+use std::error::Error;
+use std::collections::HashSet;
+
+#[derive(Copy,Clone,Debug)]
+struct Coord {
+ x: i64,
+ y: i64,
+ z: i64,
+}
+
+impl Coord {
+ fn new(x: i64, y: i64, z: i64) -> Self {
+ Self { x, y, z }
+ }
+
+ // Euclidean distance (not square rooted)
+ fn dst(&self, other: Self) -> i64 {
+ (self.x - other.x).pow(2) + (self.y - other.y).pow(2) + (self.z - other.z).pow(2)
+ }
+}
+
+struct Puzzle {
+ boxes: Vec<Coord>,
+}
+
+impl Puzzle {
+ fn new(input: &str) -> Self {
+ let boxes = input.lines()
+ .map(|l| {
+ let coord: Vec<i64> = l.split(',')
+ .map(|v| v.parse::<i64>().unwrap())
+ .collect();
+ Coord::new(coord[0], coord[1], coord[2])
+ })
+ .collect();
+ Self { boxes }
+ }
+
+ fn part1(&self, n_shortest: usize) -> usize {
+ let mut connections: Vec<(i64, (usize, usize))> = Vec::new();
+ for i in 0..self.boxes.len() {
+ for j in (i+1)..self.boxes.len() {
+ connections.push((self.boxes[i].dst(self.boxes[j]), (i, j)));
+ }
+ }
+ connections.sort_by(|a, b| a.0.cmp(&b.0));
+
+ let mut connections: Vec<(usize, usize)> = connections.into_iter()
+ .take(n_shortest)
+ .map(|(_, coord)| coord)
+ .collect();
+ connections.sort();
+
+ let mut circuits: Vec<HashSet<usize>> = Vec::new();
+ connections.iter()
+ .for_each(|(b1, b2)| {
+ if let Some(i) = circuits.iter().position(|s| s.contains(b1)) {
+ if let Some(j) = circuits.iter().position(|s| s.contains(b2)) {
+ circuits.push( circuits[i].union(&circuits[j]).map(|v| *v).collect() );
+ circuits.remove(i.max(j));
+ if i != j {
+ circuits.remove(i.min(j));
+ }
+ } else {
+ circuits[i].insert(*b2);
+ }
+ } else if let Some(i) = circuits.iter().position(|s| s.contains(b2)) {
+ if let Some(j) = circuits.iter().position(|s| s.contains(b1)) {
+ circuits.push( circuits[i].union(&circuits[j]).map(|v| *v).collect() );
+ circuits.remove(i.max(j));
+ if i != j {
+ circuits.remove(i.min(j));
+ }
+ } else {
+ circuits[i].insert(*b1);
+ }
+ } else {
+ let mut s: HashSet<usize> = HashSet::new();
+ s.insert(*b1);
+ s.insert(*b2);
+ circuits.push(s);
+ }
+ circuits.sort_by(|a, b| b.len().cmp(&a.len()));
+ // si b1 est dans un set de circuits
+ // - si b2 est dans un set de circuits
+ // -> relier les sets
+ // - sinon ajouter b2 au set
+ // si b2 est déjà dans un set de circuits
+ // - si b1 est dans un set de circuits
+ // -> relier les sets
+ // - sinon ajouter b1 au set
+ // sinon ajouter set (b1, b2)
+ });
+ let mut circuits: Vec<usize> = circuits.iter().map(|set| set.len()).collect();
+ circuits.sort_by(|a, b| b.cmp(a));
+ circuits.iter().take(3).product()
+ }
+}
+
+pub fn run(input: &str) -> Result<(), Box<dyn Error>> {
+ let p = Puzzle::new(input);
+ println!("part1: {}", p.part1(1000));
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ static TEST_INPUT: &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 test_part1() {
+ assert_eq!(40, Puzzle::new(TEST_INPUT).part1(10));
+ }
+}