Compare commits

..

4 Commits
master ... riir

5 changed files with 87 additions and 63 deletions

1
.gitignore vendored
View File

@ -1,2 +1 @@
/target /target
*.etl*

63
Cargo.lock generated
View File

@ -13,7 +13,7 @@ name = "cup"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"enum-map", "enum-map",
"fastrand", "rand",
] ]
[[package]] [[package]]
@ -37,22 +37,27 @@ dependencies = [
] ]
[[package]] [[package]]
name = "fastrand" name = "getrandom"
version = "1.8.0" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [ dependencies = [
"instant", "cfg-if",
"libc",
"wasi",
] ]
[[package]] [[package]]
name = "instant" name = "libc"
version = "0.1.12" version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
dependencies = [
"cfg-if", [[package]]
] name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
@ -72,6 +77,36 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.107" version = "1.0.107"
@ -88,3 +123,9 @@ name = "unicode-ident"
version = "1.0.6" version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

View File

@ -6,12 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
enum-map = "2.4.2" "enum-map" = "2.4.2"
fastrand = "1.8.0" "rand" = "0.8.5"
# [profile.release]
# lto = true
[profile.perf]
inherits = "release"
debug = true

View File

@ -1,5 +1,9 @@
use enum_map::{Enum, EnumMap}; use enum_map::{Enum, EnumMap};
use fastrand::Rng; use rand::{
Rng,
seq::SliceRandom,
distributions::{Distribution, Uniform},
};
use crate::stack::Stack; use crate::stack::Stack;
@ -80,11 +84,11 @@ impl Game {
// new game with random starting positions // new game with random starting positions
pub fn new_random() -> Self { pub fn new_random() -> Self {
let mut game = Self::default(); let mut game = Self::default();
let rng = Rng::new(); let mut rng = rand::thread_rng();
let mut dice = *&COLORS; let mut dice = *&COLORS;
rng.shuffle(&mut dice); dice.shuffle(&mut rng);
for color in dice { for color in dice {
let roll = rng.usize(1..=3); let roll = rng.gen_range(1..=3);
game.squares[roll - 1].assume_stack_mut().push(color); game.squares[roll - 1].assume_stack_mut().push(color);
game.camels[color] = roll - 1; game.camels[color] = roll - 1;
} }
@ -183,7 +187,8 @@ impl Game {
None None
} }
fn finish_leg_random(&mut self, rng: &Rng) -> Option<Color> { fn finish_leg_random(&mut self) -> Option<Color> {
let mut rng = rand::thread_rng();
let mut leg_dice: Stack<Color, 5> = Stack::new(); let mut leg_dice: Stack<Color, 5> = Stack::new();
for (color, rolled) in self.dice { for (color, rolled) in self.dice {
if !rolled { if !rolled {
@ -191,9 +196,9 @@ impl Game {
} }
} }
rng.shuffle(&mut leg_dice[..]); (&mut leg_dice[..]).shuffle(&mut rng);
for color in leg_dice.iter() { for color in leg_dice.iter() {
let roll = rng.usize(1..=3); let roll = rng.gen_range(1..=3);
if let Some(winner) = self.advance(*color, roll) { if let Some(winner) = self.advance(*color, roll) {
return Some(winner); return Some(winner);
} }
@ -201,19 +206,22 @@ impl Game {
None None
} }
fn finish_game_random(&mut self, rng: &Rng) -> Color { fn finish_game_random(&mut self) -> Color {
if let Some(winner) = self.finish_leg_random(rng) { if let Some(winner) = self.finish_leg_random() {
return winner; 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, // we are now guaranteed to be at the start of a new leg,
// so we don't need to check the dice state // 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 { loop {
// easiest if we shuffle at the start of the leg // easiest if we shuffle at the start of the leg
rng.shuffle(&mut dice); dice.shuffle(&mut rng);
for i in 0..5 { for i in 0..5 {
let roll = rng.usize(1..=3); let roll = roll_dist.sample(&mut rng);
if let Some(winner) = self.advance(dice[i], roll) { if let Some(winner) = self.advance(dice[i], roll) {
return winner; return winner;
} }
@ -226,9 +234,8 @@ impl Game {
let mut projection = *self; let mut projection = *self;
let mut scores: EnumMap<Color, usize> = EnumMap::default(); let mut scores: EnumMap<Color, usize> = EnumMap::default();
let rng = Rng::new();
for i in 0..count { for i in 0..count {
let winner = projection.finish_game_random(&rng); let winner = projection.finish_game_random();
scores[winner] += 1; scores[winner] += 1;
projection.set_state(&orig_camels, &orig_dice); projection.set_state(&orig_camels, &orig_dice);
} }
@ -317,8 +324,7 @@ mod test {
for _ in 0..100 { for _ in 0..100 {
let mut projection = game; // copy? let mut projection = game; // copy?
assert_eq!(projection.squares[1].assume_stack(), &Stack::from([Purple, Blue])); assert_eq!(projection.squares[1].assume_stack(), &Stack::from([Purple, Blue]));
let rng = Rng::new(); projection.finish_leg_random();
projection.finish_leg_random(&rng);
// since we already rolled Green, it can't have moved // since we already rolled Green, it can't have moved
assert_eq!(projection.camels[Green], 3); assert_eq!(projection.camels[Green], 3);
// likewise purple // likewise purple
@ -343,8 +349,7 @@ mod test {
game.set_state(&camel_state, &Default::default()); game.set_state(&camel_state, &Default::default());
// since there are no tiles involved, and multiple camels are on 15, there must be a winner // since there are no tiles involved, and multiple camels are on 15, there must be a winner
let rng = Rng::new(); assert!(matches!(game.finish_leg_random(), Some(_)));
assert!(matches!(game.finish_leg_random(&rng), Some(_)));
} }
#[test] #[test]

View File

@ -3,38 +3,24 @@ use std::time::Instant;
mod stack; mod stack;
mod game; mod game;
use game::{Game, Color::*}; use game::Game;
fn main() { fn main() {
let n_games = 200_000; let n_games = 10_000_000;
let game = Game::new_random();
// 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 start = Instant::now();
let scores = game.project_outcomes(n_games); let game = Game::new_random();
let _scores = game.project_outcomes(n_games);
let end = Instant::now(); let end = Instant::now();
let elapsed = end.duration_since(start); let elapsed = end.duration_since(start);
let secs = (elapsed.as_secs_f64() * 100f64).round() / 100f64; let secs = elapsed.as_secs();
let rate = (n_games as f64) / elapsed.as_secs_f64(); 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();
println!("Test completed:"); println!("Test completed:");
println!("{n_games} in {secs} seconds", ); println!("{n_games} in {secs}.{hundredths:02} seconds", );
println!("Games per second: {rate}\n"); println!("Games per second: {rate}");
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}%");
}
} }