diff --git a/2021/benches/days.rs b/2021/benches/days.rs index d5e92fa..7c65473 100644 --- a/2021/benches/days.rs +++ b/2021/benches/days.rs @@ -7,7 +7,7 @@ use criterion::criterion_main; use criterion::BenchmarkId; use criterion::Criterion; -const DAYS_IMPLEMENTED: usize = 20; +const DAYS_IMPLEMENTED: usize = 21; fn read_input(day: usize) -> Vec { let input_path = format!("inputs/{:02}.txt", day); diff --git a/2021/src/day21.rs b/2021/src/day21.rs index ff30d6f..77043ba 100644 --- a/2021/src/day21.rs +++ b/2021/src/day21.rs @@ -96,8 +96,57 @@ pub fn part1(input: &mut dyn Read) -> String { (loser_score * rolls).to_string() } -pub fn part2(_input: &mut dyn Read) -> String { - todo!() +const MAX_TURNS: usize = 21; // Trivial upper bound, could be lower +const ROLLS: [i32; 7] = [3, 4, 5, 6, 7, 8, 9]; +const FREQS: [u64; 7] = [1, 3, 6, 7, 6, 3, 1]; + +fn multiverse_recursive( + remaining: &mut [u64], + wins: &mut [u64], + turn: usize, + alive: u64, + pos: i32, + score: i32, +) { + for (roll, freq) in ROLLS.into_iter().zip(FREQS) { + let new_pos = (pos + roll - 1) % 10 + 1; + let new_score = score + new_pos; + let new_alive = alive * freq; + + if new_score >= 21 { + wins[turn] += new_alive; + } else { + remaining[turn] += new_alive; + multiverse_recursive(remaining, wins, turn + 1, new_alive, new_pos, new_score); + } + } +} + +fn multiverse(pos: i32) -> ([u64; MAX_TURNS], [u64; MAX_TURNS]) { + let mut alive = [0; MAX_TURNS]; + let mut wins = [0; MAX_TURNS]; + + alive[0] = 1; + + multiverse_recursive(&mut alive, &mut wins, 1, 1, pos, 0); + + (wins, alive) +} + +pub fn part2(input: &mut dyn Read) -> String { + let (a, b) = read_input(input); + + let (a_wins, a_alive) = multiverse(a); + let (b_wins, b_alive) = multiverse(b); + + let a_winner: u64 = a_wins[1..] + .into_iter() + .zip(b_alive) + .map(|(&a, b)| a * b) + .sum(); + let b_winner: u64 = b_wins.into_iter().zip(a_alive).map(|(a, b)| a * b).sum(); + + a_winner.max(b_winner).to_string() } #[cfg(test)] @@ -112,4 +161,9 @@ mod tests { fn sample_part1() { test_implementation(part1, SAMPLE, 739785); } + + #[test] + fn sample_part2() { + test_implementation(part2, SAMPLE, 444356092776315u64); + } }