Day 8 part 2, easy peasy Least Common Multiple
This commit is contained in:
parent
cab03a2ed5
commit
82fc810e11
4 changed files with 138 additions and 75 deletions
|
@ -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()
|
||||||
|
|
|
@ -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
15
src/utils/math.rs
Normal 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))
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue