diff --git a/2021/src/bin/day21.rs b/2021/src/bin/day21.rs index ab2da28..81842a7 100644 --- a/2021/src/bin/day21.rs +++ b/2021/src/bin/day21.rs @@ -1,8 +1,8 @@ #![feature(test)] +use fnv::FnvHashMap; extern crate test; type Parsed = (u16, u16); - const INPUT: Parsed = (7, 3); fn part1((p1, p2): Parsed) -> usize { @@ -22,8 +22,48 @@ fn part1((p1, p2): Parsed) -> usize { .unwrap() } +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +struct GameState { + odd_round: bool, + // Tuples are (position, total score) + scores: [(u16, usize); 2], +} + +// Just this, but cached and grouped because I don’t want to calc it in the loop +// iproduct!(1..=3, 1..=3, 1..=3).map(|(a, b, c)| a + b + c) +const POSSIBLE_ROLLS: [(u16, usize); 7] = [(6, 7), (5, 6), (7, 6), (4, 3), (8, 3), (3, 1), (9, 1)]; + fn part2((p1, p2): Parsed) -> usize { - unimplemented!() + let start = GameState { odd_round: false, scores: [(p1, 0), (p2, 0)] }; + let mut games = FnvHashMap::default(); + games.insert(start, 1); + let mut p1_wins = 0; + let mut p2_wins = 0; + while !games.is_empty() { + games = games + .iter() + .flat_map(|(start, count)| POSSIBLE_ROLLS.iter().map(move |(die, count2)| (start, count * count2, die))) + .map(|(start, count, die)| { + let mut scores = start.scores; + let mut points = scores[start.odd_round as usize].0 + die; + points -= (points - 1) / 10 * 10; + scores[start.odd_round as usize].0 = points; + scores[start.odd_round as usize].1 += points as usize; + (GameState { odd_round: !start.odd_round, scores }, count) + }) + .fold(FnvHashMap::default(), |mut acc, (state, count)| { + // Remove done games and queue the rest for another round + if state.scores[0].1 >= 21 { + p1_wins += count; + } else if state.scores[1].1 >= 21 { + p2_wins += count; + } else { + *acc.entry(state).or_insert(0) += count; + } + acc + }) + } + p1_wins.max(p2_wins) } fn main() { @@ -41,8 +81,18 @@ mod tests { assert_eq!(part1((4, 8)), 739785); } + #[test] + fn part2_test() { + assert_eq!(part2((4, 8)), 444356092776315); + } + #[bench] fn part1_bench(b: &mut test::Bencher) { b.iter(|| assert_eq!(part1(test::black_box(INPUT)), 551901)) } + + #[bench] + fn part2_bench(b: &mut test::Bencher) { + b.iter(|| assert_eq!(part2(test::black_box(INPUT)), 272847859601291)) + } }