Day 8 part 2, easy peasy Least Common Multiple

This commit is contained in:
RingOfStorms (Joshua Bell) 2023-12-08 01:54:20 -06:00
parent cab03a2ed5
commit 82fc810e11
4 changed files with 138 additions and 75 deletions

View file

@ -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 {
@ -38,7 +34,9 @@ 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 {
@ -52,7 +50,8 @@ impl Strength {
_ => Strength::OnePair,
},
_ => strength,
})
},
)
})
}
@ -62,7 +61,9 @@ impl Strength {
for card in hand.cards.iter() {
counts[*card as usize - 2] += 1;
}
counts.iter().fold(Strength::HighCard, |strength, &count| match count {
counts
.iter()
.fold(Strength::HighCard, |strength, &count| match count {
5 => Strength::FiveOfAKind,
4 => Strength::FourOfAKind,
3 => match strength {
@ -116,7 +117,13 @@ impl FromStr for Hand {
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut splits = s.trim().split_whitespace();
Ok(HandBuilder::default().cards(splits.next().unwrap().chars().map(|c| {
Ok(HandBuilder::default()
.cards(
splits
.next()
.unwrap()
.chars()
.map(|c| {
if let Some(digit) = c.to_digit(10) {
digit
} else {
@ -141,14 +148,17 @@ impl FromStr for Hand {
_ => panic!("invalid card: {}", c),
}
}
}).collect()).bid(splits.next().unwrap().parse()?).build()?)
})
.collect(),
)
.bid(splits.next().unwrap().parse()?)
.build()?)
}
}
fn calculate(input: String) -> Result<usize> {
let start = Instant::now();
let answer =
input
let answer = input
.lines()
.map(|line| line.parse::<Hand>().unwrap())
.sorted()

View file

@ -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"(?<id>\w{3}).*?(?<left>\w{3}).*?(?<right>\w{3})").expect("re_parse_node invalid");
@ -22,8 +28,6 @@ struct Node<'a> {
impl<'a> Node<'a> {
fn new(s: &'a str) -> Result<Self> {
// let re = Regex::new(r"(?`<id>`\w{3}) = ((?`<left>`\w{3}), (?`<right>`\w{3}))")?
// let re = Regex::new(r"(?`<id>`\w{3})._?(?`<left>`\w{3})._?(?`<right>`\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<usize> {
Ok(answer)
}
fn part2(input: String) -> Result<usize> {
fn part2(input: String) -> Result<u64> {
// parse
let start = Instant::now();
let map: Map = Map::new(&input)?;
@ -116,7 +120,25 @@ fn part2(input: String) -> Result<usize> {
// 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<u64> = 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(())
}
}

15
src/utils/math.rs Normal file
View file

@ -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))
}

View file

@ -1,3 +1,4 @@
pub mod aoc;
pub mod common;
pub mod config;
pub mod math;