From 027811aa6516cdef650ccc0ab50850b12ef642c6 Mon Sep 17 00:00:00 2001 From: hal8174 Date: Fri, 11 Jul 2025 17:22:04 +0200 Subject: [PATCH 1/2] Add random player --- src/game.rs | 17 ++++++++++++-- src/main.rs | 11 ++++----- src/player/mod.rs | 60 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/game.rs b/src/game.rs index 9ade5c0..6587554 100644 --- a/src/game.rs +++ b/src/game.rs @@ -2,13 +2,17 @@ use rand::{seq::SliceRandom, Rng}; pub trait Player { fn select_card( - &self, + &mut self, hand: &[Card], stack: &[Card], additional_information: &AdditionalInformation, ) -> Card; } +pub trait PlayerBuilder: Sync { + fn build(&self) -> Box; +} + #[derive(Debug)] pub struct AdditionalInformation {} @@ -85,7 +89,16 @@ pub fn playeble(card: Card, hand: &[Card], stack_color: Option) -> bool { } } -pub fn herzen(player: &[Box], starting_player: usize, rng: &mut impl Rng) -> Vec { +pub fn herzen( + player_builders: &[Box], + starting_player: usize, + rng: &mut impl Rng, +) -> Vec { + let mut player = player_builders + .iter() + .map(|p| p.build()) + .collect::>(); + let mut full_deck = (1..=8) .flat_map(|u| { [ diff --git a/src/main.rs b/src/main.rs index 9eae78a..ff87753 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use game::Player; +use game::PlayerBuilder; use rand::{rngs::SmallRng, SeedableRng}; mod game; @@ -122,13 +122,10 @@ mod player; // } fn main() { - let player: Vec> = vec![ + let player: Vec> = vec![ Box::new(player::highest::Highest {}), - Box::new(player::highest::Highest {}), - Box::new(player::highest::Highest {}), - Box::new(player::highest::Highest {}), - Box::new(player::highest::Highest {}), - Box::new(player::cli::Cli {}), + Box::new(player::random::Random { seed: 0 }), + // Box::new(player::cli::Cli {}), ]; let mut rng = SmallRng::from_entropy(); diff --git a/src/player/mod.rs b/src/player/mod.rs index a8201a6..c9fe709 100644 --- a/src/player/mod.rs +++ b/src/player/mod.rs @@ -1,12 +1,18 @@ pub mod cli { - use crate::game::{playeble, Player}; + use crate::game::{playeble, Player, PlayerBuilder}; pub struct Cli {} + impl PlayerBuilder for Cli { + fn build(&self) -> Box { + Box::new(Cli {}) + } + } + impl Player for Cli { fn select_card( - &self, + &mut self, hand: &[crate::game::Card], stack: &[crate::game::Card], additional_information: &crate::game::AdditionalInformation, @@ -36,13 +42,19 @@ pub mod cli { pub mod highest { use std::cmp::Reverse; - use crate::game::{playeble, Player}; + use crate::game::{playeble, Player, PlayerBuilder}; pub struct Highest {} + impl PlayerBuilder for Highest { + fn build(&self) -> Box { + Box::new(Highest {}) + } + } + impl Player for Highest { fn select_card( - &self, + &mut self, hand: &[crate::game::Card], stack: &[crate::game::Card], additional_information: &crate::game::AdditionalInformation, @@ -58,3 +70,43 @@ pub mod highest { } } } + +pub mod random { + use rand::{rngs::SmallRng, seq::SliceRandom, SeedableRng}; + + use crate::game::{playeble, Player, PlayerBuilder}; + + pub struct Random { + pub seed: u64, + } + + impl PlayerBuilder for Random { + fn build(&self) -> Box { + Box::new(RandomPlayer { + rng: SmallRng::seed_from_u64(self.seed), + }) + } + } + + struct RandomPlayer { + rng: SmallRng, + } + + impl Player for RandomPlayer { + fn select_card( + &mut self, + hand: &[crate::game::Card], + stack: &[crate::game::Card], + additional_information: &crate::game::AdditionalInformation, + ) -> crate::game::Card { + let _ = additional_information; + let mut h = hand.to_vec(); + + h.sort(); + + h.retain(|&c| playeble(c, hand, stack.first().map(|d| d.color))); + + *h.choose(&mut self.rng).unwrap() + } + } +} From fad9798aeda86a9f338fdc53bb7725e779e6b0fa Mon Sep 17 00:00:00 2001 From: hal8174 Date: Fri, 11 Jul 2025 17:22:21 +0200 Subject: [PATCH 2/2] Add compare function --- Cargo.lock | 52 +++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/bin/compare.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ 4 files changed, 113 insertions(+) create mode 100644 src/bin/compare.rs create mode 100644 src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 7c6b87a..88a1407 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,6 +13,7 @@ name = "card-calculator" version = "0.1.0" dependencies = [ "rand", + "rayon", ] [[package]] @@ -21,6 +22,37 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "getrandom" version = "0.2.15" @@ -95,6 +127,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "syn" version = "2.0.93" diff --git a/Cargo.toml b/Cargo.toml index 7714021..52af351 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] rand = { version = "0.8.5", features = ["small_rng"] } +rayon = "1.10.0" diff --git a/src/bin/compare.rs b/src/bin/compare.rs new file mode 100644 index 0000000..7684ed6 --- /dev/null +++ b/src/bin/compare.rs @@ -0,0 +1,58 @@ +use card_calculator::{ + game::{herzen, PlayerBuilder}, + player, +}; +use rand::{rngs::SmallRng, SeedableRng}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; + +fn compare(player_builders: &[Box], games: usize) { + let r = (0..games) + .into_par_iter() + .map_init(SmallRng::from_entropy, |rng, _| { + herzen(player_builders, 0, rng) + }) + .fold( + || vec![[0_u64; 13]; player_builders.len()], + |mut a, b| { + for (i, p) in b.into_iter().enumerate() { + a[i][p as usize] += 1; + } + a + }, + ) + .reduce_with(|mut a, b| { + for (aa, bb) in a.iter_mut().zip(b.iter()) { + for (aaa, bbb) in aa.iter_mut().zip(bb.iter()) { + *aaa += *bbb; + } + } + a + }) + .unwrap(); + + for (i, v) in r.iter().enumerate() { + let v = v + .iter() + .map(|&p| p as f64 / games as f64) + .collect::>(); + + let e = v + .iter() + .enumerate() + .map(|(i, &c)| i as f64 * c) + .sum::(); + let e = e / 13.0; + println!("Player {i}, {e:.4}, {v:?}"); + } +} + +fn main() { + let player: Vec> = vec![ + Box::new(player::random::Random { seed: 0 }), + Box::new(player::random::Random { seed: 0 }), + Box::new(player::random::Random { seed: 0 }), + Box::new(player::random::Random { seed: 0 }), + ]; + + compare(&player, 10000); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9be52af --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub mod game; +pub mod player;