use aoc23::prelude::*; use derive_builder::Builder; use itertools::Itertools; use rayon::prelude::*; use std::{collections::HashMap, str::FromStr, time::Instant}; extern crate regex; use regex::Regex; use std::error::Error; #[derive(Debug, Builder, Clone)] struct Node<'a> { id: &'a str, left: &'a str, right: &'a str, } impl<'a> Node<'a> { fn new(s: &'a str) -> Result { let re = Regex::new(r"(?\w{3}).*?(?\w{3}).*?(?\w{3})")? .captures(s) .expect("No match for regex"); Ok(NodeBuilder::default() .id(re.name("id").expect("no id").as_str()) .left(re.name("left").expect("no left").as_str()) .right(re.name("right").expect("no right").as_str()) .build()?) } } #[derive(Debug, Clone)] enum Dir { Left, Right, } impl Dir { fn from_char(c: &char) -> Self { match c { 'L' => Self::Left, 'R' => Self::Right, _ => panic!("Not valid dir"), } } } #[derive(Debug, Builder, Clone)] struct Map<'a> { dirs: Vec, nodes: HashMap<&'a str, Node<'a>>, } impl<'a> Map<'a> { fn new(s: &'a str) -> Result { let mut splits = s.trim().split("\n\n"); Ok(MapBuilder::default() .dirs( splits .next() .unwrap() .trim() .chars() .map(|c| Dir::from_char(&c)) .collect(), ) .nodes( splits .next() .unwrap() .trim() .lines() .map(|a| Node::new(a.trim()).map(|n| (n.id, n))) .try_collect()?, ) .build()?) } } fn part1(input: String) -> Result { // parse let start = Instant::now(); let map: Map = Map::new(&input)?; let parsed_time = start.elapsed(); // algo let start = Instant::now(); let mut answer = 0; let mut current = map.nodes.get("AAA").expect("no start node"); let mut dir = map.dirs.iter().cycle(); while !current.id.eq("ZZZ") { match dir.next().unwrap() { Dir::Left => current = map.nodes.get(current.left).expect("no left node"), Dir::Right => current = map.nodes.get(current.right).expect("no right node"), } answer += 1; } let algo_time = start.elapsed(); // output println!("Day 5, part 1: {answer}"); println!("\tparse: {parsed_time:?}"); println!("\talgo: {algo_time:?}"); Ok(answer) } fn part2(input: String) -> Result { // parse let start = Instant::now(); let map: Map = Map::new(&input)?; let parsed_time = start.elapsed(); // algo let start = Instant::now(); let answer = 0; let algo_time = start.elapsed(); // output println!("Day 5, part 2: {answer}"); println!("\tparse: {parsed_time:?}"); println!("\talgo: {algo_time:?}"); Ok(answer) } // TODO come back and revise for a faster solution #[tokio::main] async fn main() -> Result<()> { println!("Day {DAY}"); println!("====="); let input = utils::aoc::get_puzzle_input(DAY).await?; part1(input.clone())?; // part2(input.clone())?; Ok(()) } static DAY: u8 = 8; #[cfg(test)] mod tests { use super::*; #[test] fn test_part_1_b() -> Result<()> { assert_eq!( part1( "RL AAA = (BBB, CCC) BBB = (DDD, EEE) CCC = (ZZZ, GGG) DDD = (DDD, DDD) EEE = (EEE, EEE) GGG = (GGG, GGG) ZZZ = (ZZZ, ZZZ)" .to_owned(), )?, 2 ); assert_eq!( part1( "LLR AAA = (BBB, BBB) BBB = (AAA, ZZZ) ZZZ = (ZZZ, ZZZ)" .to_owned() )?, 6 ); Ok(()) } // #[test] fn test_part_2() -> Result<()> { // assert_eq!(part2("REPLACE".to_owned())?, 0); Ok(()) } }