Skip to content

Commit

Permalink
stats
Browse files Browse the repository at this point in the history
  • Loading branch information
mikea committed Oct 24, 2024
1 parent 91876d8 commit b3c8059
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 27 deletions.
2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ watch +WATCH_TARGET='run':
run:
cargo build -r
# bermuda hand
time target/release/bridgitte analyze-del "854.Q873.Q984.65 KQ32.T6.T72.AJ93 9.AJ542.J653.T87 AJT76.K9.AK.KQ42"
time target/release/bridgitte analyze-deal --player N --strain S --stats "854.Q873.Q984.65 KQ32.T6.T72.AJ93 9.AJ542.J653.T87 AJT76.K9.AK.KQ42"

clippy:
cargo clippy -- -D clippy::pedantic -A clippy::missing-panics-doc -A clippy::module_name_repetitions
Expand Down
20 changes: 8 additions & 12 deletions src/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,36 +58,32 @@ impl<T> ByStrain<T> {
}

#[must_use]
pub fn analyze_all(deal: &Deal) -> ByPlayer<ByStrain<u8>> {
let config = Config::default();
pub fn analyze_all(deal: &Deal, config: &Config) -> ByPlayer<ByStrain<u8>> {
ByPlayer::new(|player| {
ByStrain::new(|trumps| {
dbg!(player, trumps);
search(deal, trumps, player, &config)
search(deal, trumps, player, config)
})
})
}

#[must_use]
pub fn analyze_strain(deal: &Deal, strain: &Strain) -> ByPlayer<u8> {
let config = Config::default();
pub fn analyze_strain(deal: &Deal, strain: &Strain, config: &Config) -> ByPlayer<u8> {
ByPlayer::new(|player| {
dbg!(player, strain.trumps);
search(deal, strain.trumps, player, &config)
search(deal, strain.trumps, player, config)
})
}

#[must_use]
pub fn analyze_player(deal: &Deal, player: Player) -> ByStrain<u8> {
let config = Config::default();
pub fn analyze_player(deal: &Deal, player: Player, config: &Config) -> ByStrain<u8> {
ByStrain::new(|trumps| {
dbg!(player, trumps);
search(deal, trumps, player, &config)
search(deal, trumps, player, config)
})
}

#[must_use]
pub fn analyze(deal: &Deal, player: Player, strain: &Strain) -> u8 {
let config = Config::default();
search(deal, strain.trumps, player, &config)
pub fn analyze(deal: &Deal, player: Player, strain: &Strain, config: &Config) -> u8 {
search(deal, strain.trumps, player, config)
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub use play::*;
pub mod analyze;
pub mod counter;
pub mod search;
pub mod stats;

mod trans_table;

Expand Down
19 changes: 13 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use bridgitte::{
analyze::{analyze, analyze_all, analyze_player, analyze_strain, Strain},
Deal, Player,
analyze::{analyze, analyze_all, analyze_player, analyze_strain, Strain}, search::Config, Deal, Player
};
use clap::{Parser, Subcommand};

Expand All @@ -18,6 +17,8 @@ enum Command {
player: Option<Player>,
#[arg(short, long)]
strain: Option<Strain>,
#[arg(short, long)]
stats: bool,
},
}

Expand All @@ -29,23 +30,29 @@ fn main() {
deal,
player,
strain,
stats,
} => {
let deal = Deal::try_from_pbn(&deal).unwrap();
println!("{deal:?}");

let config = Config {
stats,
..Config::default()
};

if let Some(player) = player {
if let Some(strain) = strain {
let analysis = analyze(&deal, player, &strain);
let analysis = analyze(&deal, player, &strain, &config);
dbg!(analysis);
} else {
let analysis = analyze_player(&deal, player);
let analysis = analyze_player(&deal, player, &config);
dbg!(analysis);
}
} else if let Some(strain) = strain {
let analysis = analyze_strain(&deal, &strain);
let analysis = analyze_strain(&deal, &strain, &config);
dbg!(analysis);
} else {
let analysis = analyze_all(&deal);
let analysis = analyze_all(&deal, &config);
dbg!(analysis);
}
}
Expand Down
39 changes: 31 additions & 8 deletions src/search.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::cmp::max;

use crate::{
trans_table::{Empty, TransTable, UnsyncCache}, CardSet, Deal, PlayOfCards, PlayState, PlayedCardsNT, PlayedCardsT, Player, SingleCardSet, Suit
stats::{EmptyStats, Stats, UnsyncStats}, trans_table::{Empty, TransTable, UnsyncCache}, CardSet, Deal, PlayOfCards, PlayState, PlayedCardsNT, PlayedCardsT, Player, SingleCardSet, Suit
};

#[derive(Clone, Copy, Debug)]
Expand Down Expand Up @@ -66,23 +66,29 @@ impl Lattice<u8> for Min {
}

/// zero-window search with transposition table
struct Search<PoC: PlayOfCards, TT: TransTable<PoC>> {
struct Search<PoC, TT, ST>
where PoC: PlayOfCards, TT: TransTable<PoC>, ST: Stats {
state: PlayState<PoC>,
table: TT,
stats: ST,
}

impl<PoC: PlayOfCards, TT: TransTable<PoC>> Search<PoC, TT> {
impl<PoC: PlayOfCards, TT: TransTable<PoC>, ST: Stats> Search<PoC, TT, ST> {
/// returns true if _max_ pair can take `target` tricks.
fn visit(&mut self, target: u8) -> bool {
debug_assert!(target > 0);

self.stats.node_visit();
// start of the round
if self.state.play.is_empty() {
if let Some((lower, upper)) = self.table.get(&self.state) {
self.stats.tt_hit();
if target <= lower {
self.stats.tt_lower_cutoff();
return true;
}
if target > upper {
self.stats.tt_upper_cutoff();
return false;
}
}
Expand All @@ -91,6 +97,7 @@ impl<PoC: PlayOfCards, TT: TransTable<PoC>> Search<PoC, TT> {

// declarare can't take more tricks than available
if target > max_tricks {
self.stats.max_tricks_cutoff();
return false;
}

Expand All @@ -100,11 +107,13 @@ impl<PoC: PlayOfCards, TT: TransTable<PoC>> Search<PoC, TT> {
MinMax::Max => {
if target <= quick_tricks {
// max side can quickly take `target` tricks
self.stats.quick_tricks_cutoff();
return true;
}
}
MinMax::Min => {
if target > (max_tricks - quick_tricks) {
self.stats.quick_tricks_cutoff();
return false;
}
},
Expand Down Expand Up @@ -136,6 +145,7 @@ impl<PoC: PlayOfCards, TT: TransTable<PoC>> Search<PoC, TT> {
};

if z != result {
self.stats.search_cutoff();
bound = L::lower(target);
result = z;
break;
Expand All @@ -150,13 +160,16 @@ impl<PoC: PlayOfCards, TT: TransTable<PoC>> Search<PoC, TT> {
fn run_unbounded(&mut self) -> u8 {
assert_eq!(0, self.state.deal.size() % 4);

self.stats.search_started();

let mut upper = self.state.deal.max_tricks();
let mut lower = 0u8;
let mut guess = upper;

assert!(guess >= lower && guess <= upper);

while lower < upper {
self.stats.guess_iter();
let target = max(guess, lower + 1);
let result = self.visit(target);
if result {
Expand All @@ -169,18 +182,21 @@ impl<PoC: PlayOfCards, TT: TransTable<PoC>> Search<PoC, TT> {
guess = upper;
}
}
self.stats.search_finished();

guess
}
}

pub struct Config {
pub stats: bool,
pub trans_table_size: Option<usize>,
}

impl Default for Config {
fn default() -> Self {
Self {
stats: false,
trans_table_size: Some(100_000_000),
}
}
Expand All @@ -189,10 +205,18 @@ impl Default for Config {
fn with_state<PoC: PlayOfCards>(state: PlayState<PoC>, config: &Config) -> u8 {
if let Some(size) = config.trans_table_size {
let table = UnsyncCache::new(size);
Search { state, table }.run_unbounded()
if config.stats {
Search { state, table, stats: UnsyncStats::default() }.run_unbounded()
} else {
Search { state, table, stats: EmptyStats{} }.run_unbounded()
}
} else {
let table = Empty {};
Search { state, table }.run_unbounded()
if config.stats {
Search { state, table, stats: UnsyncStats::default() }.run_unbounded()
} else {
Search { state, table, stats: EmptyStats{} }.run_unbounded()
}
}
}

Expand Down Expand Up @@ -283,6 +307,7 @@ mod tests {
#[test]
fn bermuda_1983() {
let config = Config {
stats: false,
trans_table_size: None,
};
let trumps = Some(crate::Suit::S);
Expand Down Expand Up @@ -406,9 +431,7 @@ mod tests {

#[test]
fn bermuda_table() {
let config = Config {
trans_table_size: Some(1_000_000),
};
let config = Config::default();
let trumps = Some(crate::Suit::S);

let deal6 = Deal::try_from(ByPlayer::<&str> {
Expand Down
91 changes: 91 additions & 0 deletions src/stats.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
pub trait Stats {
fn guess_iter(&mut self);
fn max_tricks_cutoff(&mut self);
fn node_visit(&mut self);
fn quick_tricks_cutoff(&mut self);
fn search_cutoff(&mut self);
fn search_finished(&mut self);
fn search_started(&mut self);
fn tt_hit(&mut self);
fn tt_lower_cutoff(&mut self);
fn tt_upper_cutoff(&mut self);
}

pub struct EmptyStats {}

impl Stats for EmptyStats {
fn node_visit(&mut self) {}

fn tt_lower_cutoff(&mut self) {}

fn tt_upper_cutoff(&mut self) {}

fn guess_iter(&mut self) {}

fn max_tricks_cutoff(&mut self) {}

fn quick_tricks_cutoff(&mut self) {}

fn search_cutoff(&mut self) {}

fn search_finished(&mut self) {}

fn search_started(&mut self) {}

fn tt_hit(&mut self) {}
}

#[derive(Debug, Default)]
pub struct UnsyncStats {
guess_iter: usize,
max_tricks_cutoff: usize,
nodes: usize,
quick_tricks_cutoff: usize,
search_cutoff: usize,
search: usize,
tt_hit: usize,
tt_lower_cutoff: usize,
tt_upper_cutoff: usize,
}

impl Stats for UnsyncStats {
fn node_visit(&mut self) {
self.nodes += 1;
}

fn tt_lower_cutoff(&mut self) {
self.tt_lower_cutoff += 1;
}

fn tt_upper_cutoff(&mut self) {
self.tt_upper_cutoff += 1;
}

fn guess_iter(&mut self) {
self.guess_iter += 1;
}

fn max_tricks_cutoff(&mut self) {
self.max_tricks_cutoff += 1;
}

fn quick_tricks_cutoff(&mut self) {
self.quick_tricks_cutoff += 1;
}

fn search_cutoff(&mut self) {
self.search_cutoff += 1;
}

fn search_finished(&mut self) {
println!("{self:?}");
}

fn search_started(&mut self) {
self.search += 1;
}

fn tt_hit(&mut self) {
self.tt_hit += 1;
}
}

0 comments on commit b3c8059

Please sign in to comment.