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 derive_builder::Builder;
use itertools::Itertools; use itertools::Itertools;
use rayon::prelude::*; use rayon::prelude::*;
use std::{ use std::{cmp::Ordering, str::FromStr, time::Instant};
cmp::Ordering,
str::FromStr,
time::Instant,
};
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
enum Strength { enum Strength {
@ -27,7 +23,7 @@ impl Strength {
// let mut counts = [0; 14]; let mut jokers = 0; // let mut counts = [0; 14]; let mut jokers = 0;
for card in hand.cards.iter() { for card in hand.cards.iter() {
if card == &1 { if card == &1 {
for possible_count in possible_counts.iter() { } for possible_count in possible_counts.iter() {}
jokers += 1; jokers += 1;
} else { } else {
counts[*card as usize - 2] += 1; counts[*card as usize - 2] += 1;
@ -38,7 +34,9 @@ impl Strength {
// for possible real hands like full house etc, need to branch off and try all // for possible real hands like full house etc, need to branch off and try all
// combinations... // combinations...
counts.iter().fold(Strength::HighCard, |strength, &count| { counts.iter().fold(Strength::HighCard, |strength, &count| {
std::cmp::max(strength.clone(), match count { std::cmp::max(
strength.clone(),
match count {
5 => Strength::FiveOfAKind, 5 => Strength::FiveOfAKind,
4 => Strength::FourOfAKind, 4 => Strength::FourOfAKind,
3 => match strength { 3 => match strength {
@ -52,7 +50,8 @@ impl Strength {
_ => Strength::OnePair, _ => Strength::OnePair,
}, },
_ => strength, _ => strength,
}) },
)
}) })
} }
@ -62,7 +61,9 @@ impl Strength {
for card in hand.cards.iter() { for card in hand.cards.iter() {
counts[*card as usize - 2] += 1; 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, 5 => Strength::FiveOfAKind,
4 => Strength::FourOfAKind, 4 => Strength::FourOfAKind,
3 => match strength { 3 => match strength {
@ -116,7 +117,13 @@ impl FromStr for Hand {
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut splits = s.trim().split_whitespace(); 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) { if let Some(digit) = c.to_digit(10) {
digit digit
} else { } else {
@ -141,14 +148,17 @@ impl FromStr for Hand {
_ => panic!("invalid card: {}", c), _ => panic!("invalid card: {}", c),
} }
} }
}).collect()).bid(splits.next().unwrap().parse()?).build()?) })
.collect(),
)
.bid(splits.next().unwrap().parse()?)
.build()?)
} }
} }
fn calculate(input: String) -> Result<usize> { fn calculate(input: String) -> Result<usize> {
let start = Instant::now(); let start = Instant::now();
let answer = let answer = input
input
.lines() .lines()
.map(|line| line.parse::<Hand>().unwrap()) .map(|line| line.parse::<Hand>().unwrap())
.sorted() .sorted()

View file

@ -2,13 +2,19 @@ use aoc23::prelude::*;
use derive_builder::Builder; use derive_builder::Builder;
use itertools::Itertools; use itertools::Itertools;
use rayon::prelude::*; use rayon::prelude::*;
use std::{collections::HashMap, time::Instant}; use std::{
collections::HashMap,
io::{self, Write},
time::Instant,
};
extern crate regex; extern crate regex;
use regex::Regex; use regex::Regex;
use static_init::dynamic; 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] #[dynamic]
static RE_PARSE_NODE: Regex = static RE_PARSE_NODE: Regex =
Regex::new(r"(?<id>\w{3}).*?(?<left>\w{3}).*?(?<right>\w{3})").expect("re_parse_node invalid"); 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> { impl<'a> Node<'a> {
fn new(s: &'a str) -> Result<Self> { 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"); let re = RE_PARSE_NODE.captures(s).expect("No match for regex");
Ok(NodeBuilder::default() Ok(NodeBuilder::default()
.id(re.name("id").expect("no id").as_str()) .id(re.name("id").expect("no id").as_str())
@ -108,7 +112,7 @@ fn part1(input: String) -> Result<usize> {
Ok(answer) Ok(answer)
} }
fn part2(input: String) -> Result<usize> { fn part2(input: String) -> Result<u64> {
// parse // parse
let start = Instant::now(); let start = Instant::now();
let map: Map = Map::new(&input)?; let map: Map = Map::new(&input)?;
@ -116,7 +120,25 @@ fn part2(input: String) -> Result<usize> {
// algo // algo
let start = Instant::now(); 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(); let algo_time = start.elapsed();
// output // output
@ -133,8 +155,7 @@ async fn main() -> Result<()> {
println!("====="); println!("=====");
let input = utils::aoc::get_puzzle_input(DAY).await?; let input = utils::aoc::get_puzzle_input(DAY).await?;
part1(input.clone())?; part1(input.clone())?;
part2(input.clone())?;
// part2(input.clone())?;
Ok(()) Ok(())
} }
@ -145,7 +166,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn test_part_1_b() -> Result<()> { fn test_part_1() -> Result<()> {
assert_eq!( assert_eq!(
part1( part1(
"RL "RL
@ -174,6 +195,22 @@ ZZZ = (ZZZ, ZZZ)"
); );
Ok(()) 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 aoc;
pub mod common; pub mod common;
pub mod config; pub mod config;
pub mod math;