diff --git a/src/day8.rs b/src/day8.rs index 39c283e..650693b 100644 --- a/src/day8.rs +++ b/src/day8.rs @@ -1,17 +1,138 @@ +use std::{ + collections::{HashMap, HashSet}, + f64, + fmt::{Display, Write}, + iter::repeat_n, +}; + use aoc_runner_derive::{aoc, aoc_generator}; +use cached::proc_macro::cached; +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>, + checked: HashSet<(usize, usize)>, +} + +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); + self.checked.insert((a, 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) -> String { - todo!() +fn parse(input: &str) -> Circuits { + let junctions = input.lines().map(|l| l.into()).collect_vec(); + + Circuits { + junctions, + circuits: Vec::new(), + checked: HashSet::new(), + } } #[aoc(day8, part1)] -fn part1(input: &str) -> u64 { - 0 +fn part1(input: &Circuits) -> 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(1000) + { + println!( + "connecting {} <-> {} = {d}", + circuits.junctions[a], circuits.junctions[b] + ); + circuits.connect(a, b) + } + println!("{:?}", circuits.circuits); + circuits + .circuits + .iter() + .take(3) + .map(|c| c.len()) + .reduce(|a, b| a * b) + .unwrap() as u64 } #[aoc(day8, part2)] -fn part2(input: &str) -> u64 { +fn part2(input: &Circuits) -> u64 { 0 } @@ -19,11 +140,30 @@ fn part2(input: &str) -> u64 { mod tests { use super::*; - const EXAMPLE: &str = ""; + 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(&parse(EXAMPLE)), 0); + assert_eq!(part1(&parse(EXAMPLE)), 40); } #[test]