switch to fastrand

This commit is contained in:
2023-01-02 21:21:02 -08:00
parent 73f70e4fa9
commit 0c7bbbe200
5 changed files with 63 additions and 87 deletions
+18 -23
View File
@@ -1,9 +1,5 @@
use enum_map::{Enum, EnumMap};
use rand::{
Rng,
seq::SliceRandom,
distributions::{Distribution, Uniform},
};
use fastrand::Rng;
use crate::stack::Stack;
@@ -84,11 +80,11 @@ impl Game {
// new game with random starting positions
pub fn new_random() -> Self {
let mut game = Self::default();
let mut rng = rand::thread_rng();
let rng = Rng::new();
let mut dice = *&COLORS;
dice.shuffle(&mut rng);
rng.shuffle(&mut dice);
for color in dice {
let roll = rng.gen_range(1..=3);
let roll = rng.usize(1..=3);
game.squares[roll - 1].assume_stack_mut().push(color);
game.camels[color] = roll - 1;
}
@@ -187,8 +183,7 @@ impl Game {
None
}
fn finish_leg_random(&mut self) -> Option<Color> {
let mut rng = rand::thread_rng();
fn finish_leg_random(&mut self, rng: &Rng) -> Option<Color> {
let mut leg_dice: Stack<Color, 5> = Stack::new();
for (color, rolled) in self.dice {
if !rolled {
@@ -196,9 +191,9 @@ impl Game {
}
}
(&mut leg_dice[..]).shuffle(&mut rng);
rng.shuffle(&mut leg_dice[..]);
for color in leg_dice.iter() {
let roll = rng.gen_range(1..=3);
let roll = rng.usize(1..=3);
if let Some(winner) = self.advance(*color, roll) {
return Some(winner);
}
@@ -206,22 +201,19 @@ impl Game {
None
}
fn finish_game_random(&mut self) -> Color {
if let Some(winner) = self.finish_leg_random() {
fn finish_game_random(&mut self, rng: &Rng) -> Color {
if let Some(winner) = self.finish_leg_random(rng) {
return winner;
}
let mut dice = COLORS; // makes a copy of the constant
// we are now guaranteed to be at the start of a new leg,
// so we don't need to check the dice state
let mut rng = rand::thread_rng();
let roll_dist = Uniform::from(1..=3);
let mut dice = COLORS; // makes a copy of the constant
loop {
// easiest if we shuffle at the start of the leg
dice.shuffle(&mut rng);
rng.shuffle(&mut dice);
for i in 0..5 {
let roll = roll_dist.sample(&mut rng);
let roll = rng.usize(1..=3);
if let Some(winner) = self.advance(dice[i], roll) {
return winner;
}
@@ -234,8 +226,9 @@ impl Game {
let mut projection = *self;
let mut scores: EnumMap<Color, usize> = EnumMap::default();
let rng = Rng::new();
for i in 0..count {
let winner = projection.finish_game_random();
let winner = projection.finish_game_random(&rng);
scores[winner] += 1;
projection.set_state(&orig_camels, &orig_dice);
}
@@ -324,7 +317,8 @@ mod test {
for _ in 0..100 {
let mut projection = game; // copy?
assert_eq!(projection.squares[1].assume_stack(), &Stack::from([Purple, Blue]));
projection.finish_leg_random();
let rng = Rng::new();
projection.finish_leg_random(&rng);
// since we already rolled Green, it can't have moved
assert_eq!(projection.camels[Green], 3);
// likewise purple
@@ -349,7 +343,8 @@ mod test {
game.set_state(&camel_state, &Default::default());
// since there are no tiles involved, and multiple camels are on 15, there must be a winner
assert!(matches!(game.finish_leg_random(), Some(_)));
let rng = Rng::new();
assert!(matches!(game.finish_leg_random(&rng), Some(_)));
}
#[test]
+24 -10
View File
@@ -3,24 +3,38 @@ use std::time::Instant;
mod stack;
mod game;
use game::Game;
use game::{Game, Color::*};
fn main() {
let n_games = 10_000_000;
let n_games = 200_000;
let start = Instant::now();
let game = Game::new_random();
let _scores = game.project_outcomes(n_games);
// let mut game = Game::new();
// let camel_state = [
// (Blue, 5),
// (Purple, 5),
// (Red, 7),
// (Yellow, 8),
// (Green, 10),
// ];
// game.set_state(&camel_state, &Default::default());
let start = Instant::now();
let scores = game.project_outcomes(n_games);
let end = Instant::now();
let elapsed = end.duration_since(start);
let secs = elapsed.as_secs();
let hundredths = elapsed.subsec_millis() / 10; // technically not accurate but good enough for now
let rate = (10_000_000 as f64) / elapsed.as_secs_f64();
let secs = (elapsed.as_secs_f64() * 100f64).round() / 100f64;
let rate = (n_games as f64) / elapsed.as_secs_f64();
println!("Test completed:");
println!("{n_games} in {secs}.{hundredths:02} seconds", );
println!("Games per second: {rate}");
println!("{n_games} in {secs} seconds", );
println!("Games per second: {rate}\n");
let total = scores.values().sum::<usize>() as f64;
for (color, score) in scores {
let fract = (score as f64) / total;
let pct = (fract * 10_000f64).round() / 100f64;
println!("{color:?}: {pct}%");
}
}