Skip to content

Commit

Permalink
Replaced panics with results
Browse files Browse the repository at this point in the history
  • Loading branch information
bkushigian committed Oct 31, 2024
1 parent c46f953 commit 7158131
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 138 deletions.
10 changes: 6 additions & 4 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use postflop_solver::*;

fn main() {
fn main() -> Result<(), String> {
// ranges of OOP and IP in string format
// see the documentation of `Range` for more details about the format
let oop_range = "66+,A8s+,A5s-A4s,AJo+,K9s+,KQo,QTs+,JTs,96s+,85s+,75s+,65s,54s";
Expand Down Expand Up @@ -105,7 +105,7 @@ fn main() {
);

// play `Bet(120)`
game.play(1);
game.play(1)?;

// get available actions (IP)
let actions = game.available_actions();
Expand All @@ -130,7 +130,7 @@ fn main() {
assert!((strategy[ksjs] + strategy[ksjs + 250] + strategy[ksjs + 500] - 1.0).abs() < 1e-6);

// play `Call`
game.play(1);
game.play(1)?;

// confirm that the current node is a chance node (i.e., river node)
assert!(game.is_chance_node());
Expand All @@ -140,8 +140,10 @@ fn main() {
assert!(game.possible_cards() & (1 << card_7s) != 0);

// deal "7s"
game.play(card_7s as usize);
game.play(card_7s as usize)?;

// back to the root node
game.back_to_root();

Ok(())
}
23 changes: 13 additions & 10 deletions examples/node_locking.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use postflop_solver::*;

fn main() {
normal_node_locking();
partial_node_locking();
fn main() -> Result<(), String> {
normal_node_locking()?;
partial_node_locking()?;
Ok(())
}

fn normal_node_locking() {
fn normal_node_locking() -> Result<(), String> {
let card_config = CardConfig {
range: ["AsAh,QsQh".parse().unwrap(), "KsKh".parse().unwrap()],
flop: flop_from_str("2s3h4d").unwrap(),
Expand All @@ -26,8 +27,8 @@ fn normal_node_locking() {
game.allocate_memory(false);

// node locking must be performed after allocating memory and before solving
game.play(1); // OOP all-in
game.lock_current_strategy(&[0.25, 0.75]); // lock IP's strategy: 25% fold, 75% call
game.play(1)?; // OOP all-in
game.lock_current_strategy(&[0.25, 0.75])?; // lock IP's strategy: 25% fold, 75% call
game.back_to_root();

solve(&mut game, 1000, 0.001, false);
Expand All @@ -41,8 +42,8 @@ fn normal_node_locking() {
assert!((strategy_oop[3] - 1.0).abs() < 1e-3); // AA always all-in

game.allocate_memory(false);
game.play(1);
game.lock_current_strategy(&[0.5, 0.5]); // lock IP's strategy: 50% fold, 50% call
game.play(1)?;
game.lock_current_strategy(&[0.5, 0.5])?; // lock IP's strategy: 50% fold, 50% call
game.back_to_root();

solve(&mut game, 1000, 0.001, false);
Expand All @@ -54,9 +55,10 @@ fn normal_node_locking() {
assert!((strategy_oop[1] - 0.0).abs() < 1e-3); // AA never check
assert!((strategy_oop[2] - 1.0).abs() < 1e-3); // QQ always bet
assert!((strategy_oop[3] - 1.0).abs() < 1e-3); // AA always bet
Ok(())
}

fn partial_node_locking() {
fn partial_node_locking() -> Result<(), String> {
let card_config = CardConfig {
range: ["AsAh,QsQh,JsJh".parse().unwrap(), "KsKh".parse().unwrap()],
flop: flop_from_str("2s3h4d").unwrap(),
Expand All @@ -77,7 +79,7 @@ fn partial_node_locking() {
game.allocate_memory(false);

// lock OOP's strategy: only JJ is locked and the rest is not
game.lock_current_strategy(&[0.8, 0.0, 0.0, 0.2, 0.0, 0.0]); // JJ: 80% check, 20% all-in
game.lock_current_strategy(&[0.8, 0.0, 0.0, 0.2, 0.0, 0.0])?; // JJ: 80% check, 20% all-in

solve(&mut game, 1000, 0.001, false);
game.cache_normalized_weights();
Expand All @@ -90,4 +92,5 @@ fn partial_node_locking() {
assert!((strategy_oop[3] - 0.2).abs() < 1e-3); // JJ bet 20% (locked)
assert!((strategy_oop[4] - 0.3).abs() < 1e-3); // QQ bet 30%
assert!((strategy_oop[5] - 1.0).abs() < 1e-3); // AA always bet
Ok(())
}
2 changes: 1 addition & 1 deletion src/game/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl Game for PostFlopGame {
fn set_solved(&mut self) {
self.state = State::Solved;
let history = self.action_history.clone();
self.apply_history(&history);
self.apply_history(&history).unwrap();
}

#[inline]
Expand Down
35 changes: 20 additions & 15 deletions src/game/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,16 @@ impl PostFlopGame {
/// [`back_to_root`]: #method.back_to_root
/// [`play`]: #method.play
#[inline]
pub fn apply_history(&mut self, history: &[usize]) {
pub fn apply_history(&mut self, history: &[usize]) -> Result<(), String> {
if self.state <= State::Uninitialized {
panic!("Game is not successfully initialized");
return Err("Game is not successfully initialized".to_string());
}

self.back_to_root();
for &action in history {
self.play(action);
self.play(action)?;
}
Ok(())
}

/// Returns whether the current node is a terminal node.
Expand Down Expand Up @@ -271,13 +272,13 @@ impl PostFlopGame {
/// **Time complexity:** *O*(#(OOP private hands) + #(IP private hands))
///
/// [`available_actions`]: #method.available_actions
pub fn play(&mut self, action: usize) {
pub fn play(&mut self, action: usize) -> Result<(), String> {
if self.state < State::MemoryAllocated {
panic!("Memory is not allocated");
return Err("Memory is not allocated".to_string());
}

if self.is_terminal_node() {
panic!("Terminal node is not allowed");
return Err("Terminal node is not allowed".to_string());
}

// chance node
Expand All @@ -286,7 +287,7 @@ impl PostFlopGame {
if self.storage_mode == BoardState::Flop
|| (!is_turn && self.storage_mode == BoardState::Turn)
{
panic!("Storage mode is not compatible");
return Err("Storage mode is not compatible".to_string());
}

let actual_card = if action == usize::MAX {
Expand Down Expand Up @@ -353,7 +354,7 @@ impl PostFlopGame {

// panic if the action is not found
if action_index == usize::MAX {
panic!("Invalid action");
return Err("Invalid action".to_string());
}

// update the state
Expand All @@ -373,7 +374,7 @@ impl PostFlopGame {
// panic if the action is invalid
let node = self.node();
if action >= node.num_actions() {
panic!("Invalid action");
return Err("Invalid action".to_string());
}

let player = node.player();
Expand Down Expand Up @@ -421,6 +422,8 @@ impl PostFlopGame {

self.action_history.push(action);
self.is_normalized_weight_cached = false;

Ok(())
}

/// Computes the normalized weights and caches them.
Expand Down Expand Up @@ -867,21 +870,21 @@ impl PostFlopGame {
/// This method must be called after allocating memory and before solving the game.
/// Panics if the memory is not yet allocated or the game is already solved.
/// Also, panics if the current node is a terminal node or a chance node.
pub fn lock_current_strategy(&mut self, strategy: &[f32]) {
pub fn lock_current_strategy(&mut self, strategy: &[f32]) -> Result<(), String> {
if self.state < State::MemoryAllocated {
panic!("Memory is not allocated");
return Err("Memory is not allocated".to_string());
}

if self.state == State::Solved {
panic!("Game is already solved");
return Err("Game is already solved".to_string());
}

if self.is_terminal_node() {
panic!("Terminal node is not allowed");
return Err("Terminal node is not allowed".to_string());
}

if self.is_chance_node() {
panic!("Chance node is not allowed");
return Err("Chance node is not allowed".to_string());
}

let mut node = self.node();
Expand All @@ -890,7 +893,7 @@ impl PostFlopGame {
let num_hands = self.num_private_hands(player);

if strategy.len() != num_actions * num_hands {
panic!("Invalid strategy length");
return Err("Invalid strategy length".to_string());
}

let mut locking = vec![-1.0; num_actions * num_hands];
Expand Down Expand Up @@ -922,6 +925,8 @@ impl PostFlopGame {
node.is_locked = true;
let index = self.node_index(&node);
self.locking_strategy.insert(index, locking);

Ok(())
}

/// Unlocks the strategy of the current node.
Expand Down
Loading

0 comments on commit 7158131

Please sign in to comment.