advent-of-code/2021/src/bin/day21.rs

99 lines
3.0 KiB
Rust
Raw Normal View History

2021-12-21 09:38:17 +01:00
#![feature(test)]
2021-12-21 23:24:40 +01:00
use fnv::FnvHashMap;
2021-12-21 09:38:17 +01:00
extern crate test;
2021-12-21 15:45:38 +01:00
type Parsed = (u16, u16);
const INPUT: Parsed = (7, 3);
fn part1((p1, p2): Parsed) -> usize {
(1..=100)
.cycle()
2021-12-21 16:02:44 +01:00
.skip(1)
.step_by(3)
2021-12-21 21:57:53 +01:00
.zip(1..)
.scan([(p2, 0), (p1, 0)], |mut scores, (die, round)| {
2021-12-21 16:02:44 +01:00
let mut points = scores[round & 1].0 + die * 3;
2021-12-21 21:57:53 +01:00
points -= (points - 1) / 10 * 10;
2021-12-21 15:45:38 +01:00
scores[round & 1].0 = points;
scores[round & 1].1 += points;
2021-12-21 21:57:53 +01:00
Some((round, (scores[0].1, scores[1].1)))
2021-12-21 15:45:38 +01:00
})
2021-12-21 21:57:53 +01:00
.find_map(|(r, (s1, s2))| (s1 >= 1000 || s2 >= 1000).then(|| r * 3 * (s1.min(s2) as usize)))
2021-12-21 15:45:38 +01:00
.unwrap()
2021-12-21 09:38:17 +01:00
}
2021-12-21 23:24:40 +01:00
#[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)];
2021-12-21 15:45:38 +01:00
fn part2((p1, p2): Parsed) -> usize {
2021-12-21 23:24:40 +01:00
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)
2021-12-21 09:38:17 +01:00
}
fn main() {
2021-12-21 15:45:38 +01:00
println!("Part 1: {}", part1(INPUT));
println!("Part 2: {}", part2(INPUT));
2021-12-21 09:38:17 +01:00
}
#[cfg(test)]
mod tests {
use super::*;
use aoc2021::*;
2021-12-21 15:45:38 +01:00
#[test]
fn part1_test() {
assert_eq!(part1((4, 8)), 739785);
}
2021-12-21 16:02:44 +01:00
2021-12-21 23:24:40 +01:00
#[test]
fn part2_test() {
assert_eq!(part2((4, 8)), 444356092776315);
}
2021-12-21 16:02:44 +01:00
#[bench]
fn part1_bench(b: &mut test::Bencher) {
b.iter(|| assert_eq!(part1(test::black_box(INPUT)), 551901))
}
2021-12-21 23:24:40 +01:00
#[bench]
fn part2_bench(b: &mut test::Bencher) {
b.iter(|| assert_eq!(part2(test::black_box(INPUT)), 272847859601291))
}
2021-12-21 09:38:17 +01:00
}