diff --git a/src/bin/23.rs b/src/bin/23.rs new file mode 100644 index 0000000..e646ed7 --- /dev/null +++ b/src/bin/23.rs @@ -0,0 +1,129 @@ +use aoc::*; +use itertools::Itertools; +use std::cmp::Ordering; +use std::collections::{HashMap, HashSet, VecDeque}; +use std::fmt; + +const INPUT: &str = include_str!("../../input/23"); + +fn main() { + assert_example!(part1, "23-test", 7); + println!("Part 1: {}", part1(INPUT)); + assert_example!(part2, "23-test", "co,de,ka,ta"); + println!("Part 2: {}", part2(INPUT)); +} + +fn part1(input: &str) -> usize { + let graph = parse(input); + + let mut result = HashSet::new(); + + let starts = graph.keys().filter(|c| c.0 == 't'); + + for start in starts { + for n1 in graph.get(start).unwrap() { + for n2 in graph.get(n1).unwrap() { + if n1 == n2 { + continue; + } + if graph.get(n2).unwrap().contains(start) { + let mut group = vec![*n1, *n2, *start]; + group.sort(); + result.insert(group); + } + } + } + } + + result.len() +} + +fn part2(input: &str) -> String { + let graph = parse(input); + graph + .keys() + .map(|&start| clique(&graph, start)) + .max_by_key(|group| group.len()) + .unwrap() + .into_iter() + .sorted() + .join(",") +} + +fn clique(graph: &Graph, start: Computer) -> HashSet { + let mut clique = HashSet::new(); + + let mut queue = VecDeque::new(); + queue.push_back(start); + + while let Some(current) = queue.pop_front() { + let mut all_connected = true; + for c in &clique { + if !graph.get(c).unwrap().contains(¤t) { + all_connected = false; + break; + } + } + if !all_connected { + continue; + } + clique.insert(current); + let next = graph.get(¤t).unwrap(); + queue.extend(next); + } + + clique +} + +type Graph = HashMap>; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +struct Computer(char, char); + +impl Computer { + fn parse(s: &str) -> Self { + if s.len() != 2 { + panic!("computer needs 2 chars, got '{s}'"); + } + + let mut chars = s.chars(); + Self(chars.next().unwrap(), chars.next().unwrap()) + } +} + +impl Ord for Computer { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0).then(self.1.cmp(&other.1)) + } +} + +impl PartialOrd for Computer { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl fmt::Display for Computer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}{}", self.0, self.1) + } +} + +fn parse(input: &str) -> Graph { + let mut graph: Graph = HashMap::new(); + + for line in input.lines() { + let (left, right) = parse_line(line); + graph.entry(left).or_default().push(right); + graph.entry(right).or_default().push(left); + } + + graph +} + +fn parse_line(line: &str) -> (Computer, Computer) { + let (left, right) = line.split_once('-').unwrap(); + let left = Computer::parse(left); + let right = Computer::parse(right); + (left, right) +}