Skip to content

Commit

Permalink
debugging, still issues
Browse files Browse the repository at this point in the history
  • Loading branch information
mikea committed Nov 13, 2024
1 parent ef134af commit 1c1bc4f
Show file tree
Hide file tree
Showing 8 changed files with 670 additions and 171 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ perf.data
perf.data.*
callgrind.out.*
cachegrind.out.*
trace
1 change: 1 addition & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ build:

test:
cargo test
cargo test -r

clean:
rm -rf target
Expand Down
3 changes: 2 additions & 1 deletion src/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl Debug for Strain {
}
}

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ByStrain<T> {
pub c: T,
pub d: T,
Expand Down Expand Up @@ -112,6 +112,7 @@ pub fn analyze(deal: &Deal, player: Player, strain: &Strain, config: &Config) ->
deal: deal.clone(),
trumps: strain.trumps,
declarer: player,
next: None,
},
config,
)
Expand Down
7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod search;

mod trans_table;
mod ui;
pub mod minmax;

#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(u8)]
Expand Down Expand Up @@ -82,7 +83,7 @@ impl FromStr for Player {
}
}

#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub struct ByPlayer<T> {
pub w: T,
pub n: T,
Expand Down Expand Up @@ -137,6 +138,10 @@ impl PlayerMap {
fn to_absolute(&self, next: Player) -> Player {
self.to_absolute[next.index()]
}

fn from_absolute(&self, next: Player) -> Player {
self.from_absolute[next.index()]
}
}

impl Default for PlayerMap {
Expand Down
11 changes: 9 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ enum Command {
#[arg(short, long)]
declarer: Option<Player>,
#[arg(short, long)]
strain: Option<Strain>,
next: Option<Player>,
#[arg(short, long)]
strain: Option<Strain>,
#[arg(long)]
stats: bool,
#[arg(long)]
trace: bool,
},
}

Expand All @@ -33,19 +37,22 @@ pub fn main() {
declarer,
strain,
stats,
trace,
next,
} => {
let deal = Deal::try_from_pbn(&deal).unwrap();
println!("{}", deal.format_as_table());
println!();

let config = Config { stats };
let config = Config { stats, trace };

if let Some(declarer) = declarer {
if let Some(strain) = strain {
let mut s = InitialPosition {
deal,
trumps: strain.trumps,
declarer,
next,
}
.search(&config);
let analysis = s.run_unbounded();
Expand Down
240 changes: 240 additions & 0 deletions src/minmax.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
use crate::{CardMap, Deal, Pair, PlayOfCards, PlayState, PlayedCardsNT, PlayedCardsT, Player, Suit, Trick};

pub(crate) trait Lattice<T> {
fn bottom() -> T;
fn join(t1: T, t2: T) -> T;
}

pub(crate) struct Max {}
impl Lattice<u8> for Max {
fn bottom() -> u8 {
u8::MIN
}

fn join(t1: u8, t2: u8) -> u8 {
t1.max(t2)
}
}

pub struct Min {}
impl Lattice<u8> for Min {
fn bottom() -> u8 {
u8::MAX
}

fn join(t1: u8, t2: u8) -> u8 {
t1.min(t2)
}
}

struct Search<PC: PlayOfCards> {
state: PlayState<PC>,
}

impl<PC: PlayOfCards> Search<PC> {
fn visit(&mut self) -> u8 {
match Pair::from(self.state.next) {
Pair::NS => self.visit_impl::<Max>(),
Pair::WE => self.visit_impl::<Min>(),
}
}

fn visit_impl<L: Lattice<u8>>(&mut self) -> u8 {
if self.state.deal.is_empty() {
return 0;
}

let mut result = L::bottom();
for c in self.state.plays().iter() {
let z = {
let (trick, undo) = self.state.apply_mut(&c);
let z = self.visit();
self.state.undo(undo);
z + trick.as_ref().map(Trick::target_inc).unwrap_or_default()
};
result = L::join(result, z);
}
result
}
}

#[must_use]
pub fn minmax(deal: &Deal, trumps: Option<Suit>, player: Player) -> u8 {
if let Some(trumps) = trumps {
let mut cards = CardMap::default();
let deal = deal.swap_suits(Suit::S, trumps, &mut cards);
let state = PlayState::<PlayedCardsT>::new(&deal, player);
Search { state }.visit()
} else {
let state = PlayState::<PlayedCardsNT>::new(deal, player);
Search { state }.visit()
}
}

#[cfg(test)]
mod tests {
use crate::{minmax::minmax, ByPlayer, Deal, Player, Suit};

#[test]
fn small_deal_2() {
let deal = Deal::try_from(ByPlayer::<&str> {
w: "K6...",
n: "AQ...",
e: "T9...",
s: "72...",
})
.unwrap();
let tricks = minmax(&deal, None, Player::W);
assert_eq!(2, tricks);
}

#[test]
fn small_deal_3() {
let deal = Deal::try_from(ByPlayer::<&str> {
w: "K64...",
n: "AQJ...",
e: "T98...",
s: "752...",
})
.unwrap();
let tricks = minmax(&deal, None, Player::W);
assert_eq!(2, tricks);
}

#[test]
fn small_deal_4() {
let deal = Deal::try_from_pbn("A.KQ9.. .J.K.KQ K.T8..J .A.A.AT")
.unwrap();
let tricks = minmax(&deal, Some(Suit::S), Player::S);
assert_eq!(1, tricks);
}

#[test]
fn small_deal_5() {
let deal = Deal::try_from_pbn(".KQT.. .J..KQ A.9..J .A..AT")
.unwrap();
let tricks = minmax(&deal, Some(Suit::S), Player::W);
assert_eq!(2, tricks);
}

#[test]
fn bermuda_1983() {
// walk bermuda hand backwards
let trumps = Some(Suit::S);

// north
let deal2 = Deal::try_from(ByPlayer::<&str> {
n: ".T6..",
w: ".Q8..",
e: ".AJ..",
s: "6.K..",
})
.unwrap();
assert_eq!(1, minmax(&deal2, trumps, Player::N));

// north
let deal3 = Deal::try_from(ByPlayer::<&str> {
n: ".T6.2.",
w: ".Q87..",
e: ".AJ5..",
s: "6.K9..",
})
.unwrap();
assert_eq!(2, minmax(&deal3, trumps, Player::N));

// south
let deal4 = Deal::try_from(ByPlayer::<&str> {
n: ".T6.2.9",
w: ".Q873..",
e: ".AJ54..",
s: "6.K9..4",
})
.unwrap();
assert_eq!(3, minmax(&deal4, trumps, Player::S));

// too slow for debug builds
#[cfg(not(debug_assertions))]
{
// south
let deal5 = Deal::try_from(ByPlayer::<&str> {
n: ".T6.2.93",
w: ".Q873.8.",
e: ".AJ54.5.",
s: "6.K9..Q4",
})
.unwrap();
assert_eq!(4, minmax(&deal5, trumps, Player::S));
}

// these hands are beyond minmax in release build
/*
// south
let deal6 = Deal::try_from(ByPlayer::<&str> {
n: "2.T6.2.93",
w: ".Q873.84.",
e: ".AJ54.53.",
s: "76.K9..Q4",
})
.unwrap();
// south
let deal7 = Deal::try_from(ByPlayer::<&str> {
n: "32.T6.2.93",
w: "4.Q873.84.",
e: ".AJ54.53.7",
s: "T76.K9..Q4",
})
.unwrap();
// south
let deal8 = Deal::try_from(ByPlayer::<&str> {
n: "32.T6.72.93",
w: "4.Q873.984.",
e: ".AJ54.653.7",
s: "T76.K9.K.Q4",
})
.unwrap();
// south
let deal9 = Deal::try_from(ByPlayer::<&str> {
n: "32.T6.T72.93",
w: "4.Q873.Q984.",
e: ".AJ54.J653.7",
s: "T76.K9.AK.Q4",
})
.unwrap();
// north
let deal10 = Deal::try_from(ByPlayer::<&str> {
n: "32.T6.T72.J93",
w: "4.Q873.Q984.6",
e: ".AJ54.J653.87",
s: "T76.K9.AK.KQ4",
})
.unwrap();
// north
let deal11 = Deal::try_from(ByPlayer::<&str> {
n: "32.T6.T72.AJ93",
w: "4.Q873.Q984.65",
e: ".AJ54.J653.T87",
s: "T76.K9.AK.KQ42",
})
.unwrap();
let deal12 = Deal::try_from(ByPlayer::<&str> {
n: "Q32.T6.T72.AJ93",
w: "54.Q873.Q984.65",
e: ".AJ542.J653.T87",
s: "JT76.K9.AK.KQ42",
})
.unwrap();
let deal13 = Deal::try_from(ByPlayer::<&str> {
n: "KQ32.T6.T72.AJ93",
w: "854.Q873.Q984.65",
e: "9.AJ542.J653.T87",
s: "AJT76.K9.AK.KQ42",
})
.unwrap();
*/
}
}
Loading

0 comments on commit 1c1bc4f

Please sign in to comment.