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
}