From c43f19f2e337a39b79cfbe0dba3042448cc3c56b Mon Sep 17 00:00:00 2001 From: Andrew Coleman Date: Fri, 8 Dec 2023 13:44:47 -0500 Subject: [PATCH] day 08 solution --- 2023/src/day08.rs | 157 ++++++++++++++++++++++++++++++++++++++++++++++ 2023/src/lib.rs | 1 + 2 files changed, 158 insertions(+) create mode 100644 2023/src/day08.rs diff --git a/2023/src/day08.rs b/2023/src/day08.rs new file mode 100644 index 0000000..c051d75 --- /dev/null +++ b/2023/src/day08.rs @@ -0,0 +1,157 @@ +use aoc_runner_derive::aoc; +use num::integer::lcm; +use std::collections::{BTreeMap, HashSet}; + +type NodePath<'a> = BTreeMap<&'a str, &'a str>; + +struct Path<'a> { + path: Vec<&'a str>, + left: NodePath<'a>, + right: NodePath<'a>, +} + +fn parse(input: &str) -> Path { + let mut lines = input.lines(); + let path = lines + .next() + .unwrap() + .split("") + .filter_map(|s| if s.is_empty() { None } else { Some(s) }) + .collect::>(); + + let mut left: NodePath = BTreeMap::new(); + let mut right: NodePath = BTreeMap::new(); + + for line in lines { + if line.is_empty() { + continue; + } + let parts1 = line.split(" = ").collect::>(); + let src = parts1[0]; + let parts2 = parts1[1] + .trim_start_matches("(") + .trim_end_matches(")") + .split(", ") + .collect::>(); + + left.insert(src, parts2[0]); + right.insert(src, parts2[1]); + } + + Path { path, left, right } +} + +impl<'a> Path<'a> { + fn traverse(&'a mut self) -> u64 { + let mut cur = "AAA"; + let mut steps = 0; + + for dir in self.path.iter().cycle() { + steps += 1; + match *dir { + "L" => cur = self.left.get(cur).unwrap(), + "R" => cur = self.right.get(cur).unwrap(), + n @ _ => panic!("Invalid character {:?}!", n), + } + if cur == "ZZZ" { + break; + } + } + + steps + } + + fn ghost_traverse(&'a mut self) -> u64 { + let mut unique: HashSet<&str> = HashSet::new(); + for (src, _dest) in &self.left { + if src.ends_with("A") { + unique.insert(src); + } + } + for (src, _dest) in &self.right { + if src.ends_with("A") { + unique.insert(src); + } + } + let mut steps = 1; + + for ghost in unique.into_iter() { + let mut gsteps = 0; + let mut cur = ghost; + for dir in self.path.iter().cycle() { + gsteps += 1; + match *dir { + "L" => cur = self.left.get(cur).unwrap(), + "R" => cur = self.right.get(cur).unwrap(), + n @ _ => panic!("Invalid path character {:?}!", n), + } + if cur.ends_with("Z") { + steps = lcm(gsteps, steps); + break; + } + } + } + + steps + } +} + +#[aoc(day8, part1)] +fn part1(input: &str) -> u64 { + parse(input).traverse() +} + +#[aoc(day8, part2)] +fn part2(input: &str) -> u64 { + parse(input).ghost_traverse() +} + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE_DATA1: &'static str = r#"RL + +AAA = (BBB, CCC) +BBB = (DDD, EEE) +CCC = (ZZZ, GGG) +DDD = (DDD, DDD) +EEE = (EEE, EEE) +GGG = (GGG, GGG) +ZZZ = (ZZZ, ZZZ) +"#; + + #[test] + fn sample_data1() { + assert_eq!(part1(&SAMPLE_DATA1), 2); + } + + const SAMPLE_DATA2: &'static str = r#"LLR + +AAA = (BBB, BBB) +BBB = (AAA, ZZZ) +ZZZ = (ZZZ, ZZZ) +"#; + + #[test] + fn sample_data2() { + assert_eq!(part1(&SAMPLE_DATA2), 6); + } + + const SAMPLE_DATA_P2: &'static str = r#"LR + +11A = (11B, XXX) +11B = (XXX, 11Z) +11Z = (11B, XXX) +22A = (22B, XXX) +22B = (22C, 22C) +22C = (22Z, 22Z) +22Z = (22B, 22B) +XXX = (XXX, XXX) +"#; + + #[test] + fn sample_data_part2() { + assert_eq!(part2(&SAMPLE_DATA_P2), 6); + } +} diff --git a/2023/src/lib.rs b/2023/src/lib.rs index 6bac528..4d0316e 100644 --- a/2023/src/lib.rs +++ b/2023/src/lib.rs @@ -7,5 +7,6 @@ mod day04; mod day05; mod day06; mod day07; +mod day08; aoc_lib! { year = 2023 }