Compare commits

...

2 commits

Author SHA1 Message Date
fad9798aed
Add compare function 2025-07-11 17:22:21 +02:00
027811aa65
Add random player 2025-07-11 17:22:04 +02:00
7 changed files with 188 additions and 13 deletions

52
Cargo.lock generated
View file

@ -13,6 +13,7 @@ name = "card-calculator"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"rand", "rand",
"rayon",
] ]
[[package]] [[package]]
@ -21,6 +22,37 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 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]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@ -95,6 +127,26 @@ dependencies = [
"getrandom", "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]] [[package]]
name = "syn" name = "syn"
version = "2.0.93" version = "2.0.93"

View file

@ -5,3 +5,4 @@ edition = "2021"
[dependencies] [dependencies]
rand = { version = "0.8.5", features = ["small_rng"] } rand = { version = "0.8.5", features = ["small_rng"] }
rayon = "1.10.0"

58
src/bin/compare.rs Normal file
View file

@ -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<dyn PlayerBuilder>], 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::<Vec<_>>();
let e = v
.iter()
.enumerate()
.map(|(i, &c)| i as f64 * c)
.sum::<f64>();
let e = e / 13.0;
println!("Player {i}, {e:.4}, {v:?}");
}
}
fn main() {
let player: Vec<Box<dyn PlayerBuilder>> = 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);
}

View file

@ -2,13 +2,17 @@ use rand::{seq::SliceRandom, Rng};
pub trait Player { pub trait Player {
fn select_card( fn select_card(
&self, &mut self,
hand: &[Card], hand: &[Card],
stack: &[Card], stack: &[Card],
additional_information: &AdditionalInformation, additional_information: &AdditionalInformation,
) -> Card; ) -> Card;
} }
pub trait PlayerBuilder: Sync {
fn build(&self) -> Box<dyn Player>;
}
#[derive(Debug)] #[derive(Debug)]
pub struct AdditionalInformation {} pub struct AdditionalInformation {}
@ -85,7 +89,16 @@ pub fn playeble(card: Card, hand: &[Card], stack_color: Option<Color>) -> bool {
} }
} }
pub fn herzen(player: &[Box<dyn Player>], starting_player: usize, rng: &mut impl Rng) -> Vec<u8> { pub fn herzen(
player_builders: &[Box<dyn PlayerBuilder>],
starting_player: usize,
rng: &mut impl Rng,
) -> Vec<u8> {
let mut player = player_builders
.iter()
.map(|p| p.build())
.collect::<Vec<_>>();
let mut full_deck = (1..=8) let mut full_deck = (1..=8)
.flat_map(|u| { .flat_map(|u| {
[ [

2
src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod game;
pub mod player;

View file

@ -1,4 +1,4 @@
use game::Player; use game::PlayerBuilder;
use rand::{rngs::SmallRng, SeedableRng}; use rand::{rngs::SmallRng, SeedableRng};
mod game; mod game;
@ -122,13 +122,10 @@ mod player;
// } // }
fn main() { fn main() {
let player: Vec<Box<dyn Player>> = vec![ let player: Vec<Box<dyn PlayerBuilder>> = vec![
Box::new(player::highest::Highest {}), Box::new(player::highest::Highest {}),
Box::new(player::highest::Highest {}), Box::new(player::random::Random { seed: 0 }),
Box::new(player::highest::Highest {}), // Box::new(player::cli::Cli {}),
Box::new(player::highest::Highest {}),
Box::new(player::highest::Highest {}),
Box::new(player::cli::Cli {}),
]; ];
let mut rng = SmallRng::from_entropy(); let mut rng = SmallRng::from_entropy();

View file

@ -1,12 +1,18 @@
pub mod cli { pub mod cli {
use crate::game::{playeble, Player}; use crate::game::{playeble, Player, PlayerBuilder};
pub struct Cli {} pub struct Cli {}
impl PlayerBuilder for Cli {
fn build(&self) -> Box<dyn Player> {
Box::new(Cli {})
}
}
impl Player for Cli { impl Player for Cli {
fn select_card( fn select_card(
&self, &mut self,
hand: &[crate::game::Card], hand: &[crate::game::Card],
stack: &[crate::game::Card], stack: &[crate::game::Card],
additional_information: &crate::game::AdditionalInformation, additional_information: &crate::game::AdditionalInformation,
@ -36,13 +42,19 @@ pub mod cli {
pub mod highest { pub mod highest {
use std::cmp::Reverse; use std::cmp::Reverse;
use crate::game::{playeble, Player}; use crate::game::{playeble, Player, PlayerBuilder};
pub struct Highest {} pub struct Highest {}
impl PlayerBuilder for Highest {
fn build(&self) -> Box<dyn Player> {
Box::new(Highest {})
}
}
impl Player for Highest { impl Player for Highest {
fn select_card( fn select_card(
&self, &mut self,
hand: &[crate::game::Card], hand: &[crate::game::Card],
stack: &[crate::game::Card], stack: &[crate::game::Card],
additional_information: &crate::game::AdditionalInformation, 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<dyn Player> {
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()
}
}
}