diff --git a/src/bin/day7.rs b/src/bin/day7.rs index 8eab145..359d300 100644 --- a/src/bin/day7.rs +++ b/src/bin/day7.rs @@ -2,11 +2,7 @@ use aoc23::prelude::*; use derive_builder::Builder; use itertools::Itertools; use rayon::prelude::*; -use std::{ - cmp::Ordering, - str::FromStr, - time::Instant, -}; +use std::{cmp::Ordering, str::FromStr, time::Instant}; #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)] enum Strength { @@ -27,7 +23,7 @@ impl Strength { // let mut counts = [0; 14]; let mut jokers = 0; for card in hand.cards.iter() { if card == &1 { - for possible_count in possible_counts.iter() { } + for possible_count in possible_counts.iter() {} jokers += 1; } else { counts[*card as usize - 2] += 1; @@ -38,7 +34,36 @@ impl Strength { // for possible real hands like full house etc, need to branch off and try all // combinations... counts.iter().fold(Strength::HighCard, |strength, &count| { - std::cmp::max(strength.clone(), match count { + std::cmp::max( + strength.clone(), + match count { + 5 => Strength::FiveOfAKind, + 4 => Strength::FourOfAKind, + 3 => match strength { + Strength::TwoPair => Strength::FullHouse, + Strength::OnePair => Strength::FullHouse, + _ => Strength::ThreeOfAKind, + }, + 2 => match strength { + Strength::ThreeOfAKind => Strength::FullHouse, + Strength::OnePair => Strength::TwoPair, + _ => Strength::OnePair, + }, + _ => strength, + }, + ) + }) + } + + #[cfg(not(feature = "part2"))] + fn for_hand(hand: &Hand) -> Self { + let mut counts = [0; 15]; + for card in hand.cards.iter() { + counts[*card as usize - 2] += 1; + } + counts + .iter() + .fold(Strength::HighCard, |strength, &count| match count { 5 => Strength::FiveOfAKind, 4 => Strength::FourOfAKind, 3 => match strength { @@ -53,30 +78,6 @@ impl Strength { }, _ => strength, }) - }) - } - - #[cfg(not(feature = "part2"))] - fn for_hand(hand: &Hand) -> Self { - let mut counts = [0; 15]; - for card in hand.cards.iter() { - counts[*card as usize - 2] += 1; - } - counts.iter().fold(Strength::HighCard, |strength, &count| match count { - 5 => Strength::FiveOfAKind, - 4 => Strength::FourOfAKind, - 3 => match strength { - Strength::TwoPair => Strength::FullHouse, - Strength::OnePair => Strength::FullHouse, - _ => Strength::ThreeOfAKind, - }, - 2 => match strength { - Strength::ThreeOfAKind => Strength::FullHouse, - Strength::OnePair => Strength::TwoPair, - _ => Strength::OnePair, - }, - _ => strength, - }) } } @@ -116,45 +117,54 @@ impl FromStr for Hand { fn from_str(s: &str) -> std::result::Result { let mut splits = s.trim().split_whitespace(); - Ok(HandBuilder::default().cards(splits.next().unwrap().chars().map(|c| { - if let Some(digit) = c.to_digit(10) { - digit - } else { - match c { - #[cfg(not(feature = "part2"))] - 'A' => 14, - #[cfg(feature = "part2")] - 'A' => 13, - #[cfg(not(feature = "part2"))] - 'K' => 13, - #[cfg(feature = "part2")] - 'K' => 12, - #[cfg(not(feature = "part2"))] - 'Q' => 12, - #[cfg(feature = "part2")] - 'Q' => 11, - #[cfg(not(feature = "part2"))] - 'J' => 11, - #[cfg(feature = "part2")] - 'J' => 1, - 'T' => 10, - _ => panic!("invalid card: {}", c), - } - } - }).collect()).bid(splits.next().unwrap().parse()?).build()?) + Ok(HandBuilder::default() + .cards( + splits + .next() + .unwrap() + .chars() + .map(|c| { + if let Some(digit) = c.to_digit(10) { + digit + } else { + match c { + #[cfg(not(feature = "part2"))] + 'A' => 14, + #[cfg(feature = "part2")] + 'A' => 13, + #[cfg(not(feature = "part2"))] + 'K' => 13, + #[cfg(feature = "part2")] + 'K' => 12, + #[cfg(not(feature = "part2"))] + 'Q' => 12, + #[cfg(feature = "part2")] + 'Q' => 11, + #[cfg(not(feature = "part2"))] + 'J' => 11, + #[cfg(feature = "part2")] + 'J' => 1, + 'T' => 10, + _ => panic!("invalid card: {}", c), + } + } + }) + .collect(), + ) + .bid(splits.next().unwrap().parse()?) + .build()?) } } fn calculate(input: String) -> Result { let start = Instant::now(); - let answer = - input - .lines() - .map(|line| line.parse::().unwrap()) - .sorted() - .enumerate() - .map(|(idx, hand)| hand.bid * (idx + 1)) - .sum(); + let answer = input + .lines() + .map(|line| line.parse::().unwrap()) + .sorted() + .enumerate() + .map(|(idx, hand)| hand.bid * (idx + 1)) + .sum(); let algo_time = start.elapsed(); // ``` diff --git a/src/bin/day8.rs b/src/bin/day8.rs index c4eb7ae..c304789 100644 --- a/src/bin/day8.rs +++ b/src/bin/day8.rs @@ -2,13 +2,19 @@ use aoc23::prelude::*; use derive_builder::Builder; use itertools::Itertools; use rayon::prelude::*; -use std::{collections::HashMap, time::Instant}; +use std::{ + collections::HashMap, + io::{self, Write}, + time::Instant, +}; extern crate regex; use regex::Regex; use static_init::dynamic; +// Regex is slow to compile, don't do it in the parse loop, pull it out here and +// compile it once and reuse below. #[dynamic] static RE_PARSE_NODE: Regex = Regex::new(r"(?\w{3}).*?(?\w{3}).*?(?\w{3})").expect("re_parse_node invalid"); @@ -22,8 +28,6 @@ struct Node<'a> { impl<'a> Node<'a> { fn new(s: &'a str) -> Result { - // let re = Regex::new(r"(?``\w{3}) = ((?``\w{3}), (?``\w{3}))")? - // let re = Regex::new(r"(?``\w{3})._?(?``\w{3})._?(?``\w{3})")? let re = RE_PARSE_NODE.captures(s).expect("No match for regex"); Ok(NodeBuilder::default() .id(re.name("id").expect("no id").as_str()) @@ -108,7 +112,7 @@ fn part1(input: String) -> Result { Ok(answer) } -fn part2(input: String) -> Result { +fn part2(input: String) -> Result { // parse let start = Instant::now(); let map: Map = Map::new(&input)?; @@ -116,7 +120,25 @@ fn part2(input: String) -> Result { // algo let start = Instant::now(); - let answer = 0; + let currents: Vec<&Node> = map.nodes.values().filter(|n| n.id.ends_with("A")).collect(); + let steps: Vec = currents + .par_iter() + .map(|node| { + let mut dir = map.dirs.iter().cycle(); + let mut steps = 0; + let mut current = *node; + while !current.id.ends_with("Z") { + 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"), + } + steps += 1; + // println!("Step: {answer}"); let _ = std::io::stdout().flush(); + } + steps + }) + .collect(); + let answer = utils::math::find_lcm(&steps[..]); let algo_time = start.elapsed(); // output @@ -133,8 +155,7 @@ async fn main() -> Result<()> { println!("====="); let input = utils::aoc::get_puzzle_input(DAY).await?; part1(input.clone())?; - - // part2(input.clone())?; + part2(input.clone())?; Ok(()) } @@ -145,7 +166,7 @@ mod tests { use super::*; #[test] - fn test_part_1_b() -> Result<()> { + fn test_part_1() -> Result<()> { assert_eq!( part1( "RL @@ -174,6 +195,22 @@ ZZZ = (ZZZ, ZZZ)" ); Ok(()) } - // #[test] fn test_part_2() -> Result<()> { - // assert_eq!(part2("REPLACE".to_owned())?, 0); Ok(()) } + + #[test] + fn test_part_2() -> Result<()> { + part2( + "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)" + .to_owned(), + ); + Ok(()) + } } diff --git a/src/utils/math.rs b/src/utils/math.rs new file mode 100644 index 0000000..2d19ad7 --- /dev/null +++ b/src/utils/math.rs @@ -0,0 +1,15 @@ +fn gcd(a: u64, b: u64) -> u64 { + if b == 0 { + a + } else { + gcd(b, a % b) + } +} + +fn lcm(a: u64, b: u64) -> u64 { + a / gcd(a, b) * b +} + +pub fn find_lcm(numbers: &[u64]) -> u64 { + numbers.iter().cloned().fold(1, |acc, num| lcm(acc, num)) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 169203c..fe592e9 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,4 @@ pub mod aoc; pub mod common; pub mod config; +pub mod math;