diff --git a/day08/Cargo.toml b/day08/Cargo.toml index 2e961e4..c2ce9d2 100644 --- a/day08/Cargo.toml +++ b/day08/Cargo.toml @@ -18,3 +18,7 @@ name = "day08_part1" name = "day08_part2" [dependencies] +regex = "1.10.2" +anyhow = "1.0.75" +rayon = "1.8.0" +num = "0.4" \ No newline at end of file diff --git a/day08/resources/test_input03.txt b/day08/resources/test_input03.txt new file mode 100644 index 0000000..a8e2c98 --- /dev/null +++ b/day08/resources/test_input03.txt @@ -0,0 +1,10 @@ +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) \ No newline at end of file diff --git a/day08/src/bin/day08_part1.rs b/day08/src/bin/day08_part1.rs index b8cb88f..359fde8 100644 --- a/day08/src/bin/day08_part1.rs +++ b/day08/src/bin/day08_part1.rs @@ -1,13 +1,15 @@ use std::time::Instant; +use anyhow::Result; use day08::{get_moves_to_solve, parse_input}; -fn main() { +fn main() -> Result<()> { let input = include_str!("../../resources/input.txt"); let now = Instant::now(); - let map = parse_input(input); - let result = get_moves_to_solve(&map); + let map = parse_input(input)?; + let result = get_moves_to_solve(&map)?; let elapsed = now.elapsed(); println!("Result: {}, Elapsed: {:?}", result, elapsed); + Ok(()) } #[cfg(test)] @@ -15,20 +17,24 @@ mod tests { use super::*; #[test] - fn run_test_input_01() { + fn run_test_input_01() -> Result<()>{ let input = include_str!("../../resources/test_input01.txt"); - let map = parse_input(input); - let result = get_moves_to_solve(&map); + let map = parse_input(input)?; + println!("Map: {:?}", map); + let result = get_moves_to_solve(&map)?; assert_eq!(2, result); + Ok(()) } #[test] - fn run_test_input_02() { + fn run_test_input_02() -> Result<()> { let input = include_str!("../../resources/test_input02.txt"); - let map = parse_input(input); - let result = get_moves_to_solve(&map); + let map = parse_input(input)?; + println!("Map: {:?}", map); + let result = get_moves_to_solve(&map)?; assert_eq!(6, result); + Ok(()) } } diff --git a/day08/src/bin/day08_part2.rs b/day08/src/bin/day08_part2.rs index 6f4a20a..508e459 100644 --- a/day08/src/bin/day08_part2.rs +++ b/day08/src/bin/day08_part2.rs @@ -1,11 +1,27 @@ use std::time::Instant; -use day08::{get_moves_to_solve, parse_input}; +use anyhow::Result; +use day08::{get_moves_to_solve_ghost, parse_input}; -fn main() { +fn main() -> Result<()> { let input = include_str!("../../resources/input.txt"); let now = Instant::now(); - let map = parse_input(input); - let result = get_moves_to_solve(&map); + let map = parse_input(input)?; + let result = get_moves_to_solve_ghost(&map)?; let elapsed = now.elapsed(); println!("Result: {}, Elapsed: {:?}", result, elapsed); + Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn run_test_input_03() -> Result<()>{ + let input = include_str!("../../resources/test_input03.txt"); + let map = parse_input(input)?; + let result = get_moves_to_solve_ghost(&map)?; + assert_eq!(6, result); + Ok(()) + } +} \ No newline at end of file diff --git a/day08/src/lib.rs b/day08/src/lib.rs index 171bc56..799ccb1 100644 --- a/day08/src/lib.rs +++ b/day08/src/lib.rs @@ -1,4 +1,10 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap}; +use regex::Regex; +use anyhow::Result; +use std::fmt::Display; +use std::error::Error; +use std::fmt; +use rayon::prelude::*; #[derive(Eq, PartialEq, Debug, Hash, Copy, Clone)] pub enum Move { @@ -6,22 +12,106 @@ pub enum Move { L } -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Location { - left_id: String, - right_id: String +impl TryFrom for Move { + type Error = (); + + fn try_from(value: char) -> Result { + match value { + 'R' => Ok(Self::R), + 'L' => Ok(Self::L), + _ => Err(()) + } + } +} + +#[derive(Debug, Eq, PartialEq)] +pub struct Location<'a> { + left_id: & 'a str, + right_id: & 'a str } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct Map { +#[derive(Debug, Eq, PartialEq)] +pub struct Map <'a> { move_sequence: Vec, - locations: BTreeMap + locations: BTreeMap<& 'a str, Location<'a>> +} + +#[derive(Debug, Eq, PartialEq)] +struct ParsingError; + +impl Error for ParsingError {} + +impl Display for ParsingError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Could not parse input for puzzle!") + } } -pub fn parse_input(input: &str) -> Map { - todo!("Not Implemented!"); +pub fn parse_input(input: &str) -> Result { + let mapping_re = Regex::new(r"^(?P\w+)\s*=\s*\((?P\w+),\s*(?P\w+)\)$")?; + let mut lines_iter = input.lines(); + + let moves: Vec= lines_iter.next().ok_or(ParsingError {})?.chars().filter_map(|x| Move::try_from(x).ok()).collect(); + lines_iter.next(); + let mut locations = BTreeMap::new(); + for line in lines_iter { + let location_line_matches = mapping_re.captures(line).ok_or(ParsingError {})?; + let id = location_line_matches.name("id").ok_or(ParsingError {})?.as_str(); + let left = location_line_matches.name("left").ok_or(ParsingError {})?.as_str(); + let right = location_line_matches.name("right").ok_or(ParsingError {})?.as_str(); + + locations.insert(id, Location { + left_id: left, + right_id: right + }); + } + Ok(Map { + move_sequence: moves, + locations, + }) +} + +pub fn get_moves_to_solve(map: &Map) -> Result { + let mut moves_count = 0_u64; + let mut current_element_id = "AAA"; + for map_move in map.move_sequence.iter().cycle() { + let current_location = map.locations.get(current_element_id).ok_or(ParsingError {})?; + current_element_id = match map_move { + Move::R => current_location.right_id, + Move::L => current_location.left_id + }; + moves_count += 1; + + if current_element_id.eq("ZZZ") { + break; + } + } + Ok(moves_count) +} + +fn get_moves_from_location(map: &Map, start_location: &str) -> Option { + let mut moves_count= 0_u64; + let mut current_element_id = start_location; + for map_move in map.move_sequence.iter().cycle() { + current_element_id = match map.locations.get(current_element_id) + { + Some(current_location) => match map_move { + Move::R => current_location.right_id, + Move::L => current_location.left_id + }, + None => return None + }; + moves_count += 1; + + if current_element_id.ends_with('Z') { + break; + } + } + Some(moves_count) } -pub fn get_moves_to_solve(map: &Map) -> u64 { - todo!{"Not Implemented!"} +pub fn get_moves_to_solve_ghost(map: &Map) -> Result { + let moves_to_z_location: Vec = map.locations.par_iter().filter(|(id, _)| id.ends_with('A')).map(| (x, _)| *x).filter_map(|x| get_moves_from_location(&map, x)).collect(); + let moves_count: u64 = moves_to_z_location.par_iter().cloned().reduce(|| 1_u64, |x, y| num::integer::lcm(x, y)); + Ok(moves_count) } \ No newline at end of file