Skip to content

Commit

Permalink
Tmp commit
Browse files Browse the repository at this point in the history
  • Loading branch information
bkushigian committed Aug 17, 2024
1 parent c2c52f5 commit a23e319
Show file tree
Hide file tree
Showing 5 changed files with 387 additions and 112 deletions.
267 changes: 190 additions & 77 deletions examples/file_io_debug.rs
Original file line number Diff line number Diff line change
@@ -1,97 +1,210 @@
use std::fs::File;

use postflop_solver::*;

fn main() {
// see `basic.rs` for the explanation of the following code
fn recursive_compare_strategies_helper(
saved: &mut PostFlopGame,
loaded: &mut PostFlopGame,
storage_mode: BoardState,
) {
let history = saved.history().to_vec();
saved.cache_normalized_weights();
loaded.cache_normalized_weights();

// Check if OOP hands have the same evs
let evs_oop_1 = saved.expected_values(0);
let ws_oop_1 = saved.weights(0);
let evs_oop_2 = loaded.expected_values(1);
let ws_oop_2 = saved.weights(0);

assert!(ws_oop_1.len() == ws_oop_2.len());
for (w1, w2) in ws_oop_1.iter().zip(ws_oop_2) {
assert!((w1 - w2).abs() < 0.001);
}
for (i, (e1, e2)) in evs_oop_1.iter().zip(&evs_oop_2).enumerate() {
assert!((e1 - e2).abs() < 0.001, "ev diff({}): {}", i, e1 - e2);
}

let ev_oop_1 = compute_average(&evs_oop_1, &ws_oop_1);
let ev_oop_2 = compute_average(&evs_oop_2, &ws_oop_2);

let ev_diff = (ev_oop_1 - ev_oop_2).abs();
println!("EV Diff: {:0.2}", ev_diff);
assert!((ev_oop_1 - ev_oop_2).abs() < 0.01);
for child_index in 0..saved.available_actions().len() {
saved.play(child_index);
loaded.play(child_index);

recursive_compare_strategies_helper(saved, loaded, storage_mode);

saved.apply_history(&history);
loaded.apply_history(&history);
}
}

fn compare_strategies(
saved: &mut PostFlopGame,
loaded: &mut PostFlopGame,
storage_mode: BoardState,
) {
saved.back_to_root();
loaded.back_to_root();
saved.cache_normalized_weights();
loaded.cache_normalized_weights();
for (i, ((e1, e2), cards)) in saved
.expected_values(0)
.iter()
.zip(loaded.expected_values(0))
.zip(saved.private_cards(0))
.enumerate()
{
println!("ev {}: {}:{}", hole_to_string(*cards).unwrap(), e1, e2);
}
for (i, ((e1, e2), cards)) in saved
.expected_values(1)
.iter()
.zip(loaded.expected_values(1))
.zip(saved.private_cards(1))
.enumerate()
{
println!("ev {}: {}:{}", hole_to_string(*cards).unwrap(), e1, e2);
}
recursive_compare_strategies_helper(saved, loaded, storage_mode);
}

fn print_strats_at_current_node(
g1: &mut PostFlopGame,
g2: &mut PostFlopGame,
actions: &Vec<Action>,
) {
let action_string = actions
.iter()
.map(|a| format!("{:?}", a))
.collect::<Vec<String>>()
.join(":");

let oop_range = "66+,A8s+,A5s-A4s,AJo+,K9s+,KQo,QTs+,JTs,96s+,85s+,75s+,65s,54s";
let ip_range = "QQ-22,AQs-A2s,ATo+,K5s+,KJo+,Q8s+,J8s+,T7s+,96s+,86s+,75s+,64s+,53s+";
let player = g1.current_player();

println!(
"\x1B[32;1mActions To Reach Node\x1B[0m: [{}]",
action_string
);
// Print high level node data
if g1.is_chance_node() {
println!("\x1B[32;1mPlayer\x1B[0m: Chance");
} else if g1.is_terminal_node() {
if player == 0 {
println!("\x1B[32;1mPlayer\x1B[0m: OOP (Terminal)");
} else {
println!("\x1B[32;1mPlayer\x1B[0m: IP (Terminal)");
}
} else {
if player == 0 {
println!("\x1B[32;1mPlayer\x1B[0m: OOP");
} else {
println!("\x1B[32;1mPlayer\x1B[0m: IP");
}
let private_cards = g1.private_cards(player);
let strat1 = g1.strategy_by_private_hand();
let strat2 = g2.strategy_by_private_hand();
let weights1 = g1.weights(player);
let weights2 = g2.weights(player);
let actions = g1.available_actions();

// Print both games strategies
for ((cards, (w1, s1)), (w2, s2)) in private_cards
.iter()
.zip(weights1.iter().zip(strat1))
.zip(weights2.iter().zip(strat2))
{
let hole_cards = hole_to_string(*cards).unwrap();
print!("\x1B[34;1m{hole_cards}\x1B[0m@({:.2} v {:.2}) ", w1, w2);
let mut action_frequencies = vec![];
for (a, (freq1, freq2)) in actions.iter().zip(s1.iter().zip(s2)) {
action_frequencies.push(format!(
"\x1B[32;1m{:?}\x1B[0m: \x1B[31m{:0.3}\x1B[0m v \x1B[33m{:0>.3}\x1B[0m",
a, freq1, freq2
))
}
println!("{}", action_frequencies.join(" "));
}
}
}

fn main() {
let oop_range = "AA,QQ";
let ip_range = "KK";

let card_config = CardConfig {
range: [oop_range.parse().unwrap(), ip_range.parse().unwrap()],
flop: flop_from_str("Td9d6h").unwrap(),
turn: card_from_str("Qc").unwrap(),
river: NOT_DEALT,
flop: flop_from_str("3h3s3d").unwrap(),
..Default::default()
};

let bet_sizes = BetSizeOptions::try_from(("60%, e, a", "2.5x")).unwrap();

let tree_config = TreeConfig {
initial_state: BoardState::Turn,
starting_pot: 200,
effective_stack: 900,
starting_pot: 100,
effective_stack: 100,
rake_rate: 0.0,
rake_cap: 0.0,
flop_bet_sizes: [bet_sizes.clone(), bet_sizes.clone()],
turn_bet_sizes: [bet_sizes.clone(), bet_sizes.clone()],
river_bet_sizes: [bet_sizes.clone(), bet_sizes],
turn_donk_sizes: None,
river_donk_sizes: Some(DonkSizeOptions::try_from("50%").unwrap()),
add_allin_threshold: 1.5,
force_allin_threshold: 0.15,
merging_threshold: 0.1,
flop_bet_sizes: [("e", "").try_into().unwrap(), ("e", "").try_into().unwrap()],
turn_bet_sizes: [("e", "").try_into().unwrap(), ("e", "").try_into().unwrap()],
river_bet_sizes: [("e", "").try_into().unwrap(), ("e", "").try_into().unwrap()],
..Default::default()
};

let action_tree = ActionTree::new(tree_config).unwrap();
let mut game = PostFlopGame::with_config(card_config, action_tree).unwrap();
game.allocate_memory(false);

let max_num_iterations = 20;
let target_exploitability = game.tree_config().starting_pot as f32 * 0.01;
solve(&mut game, max_num_iterations, target_exploitability, true);
let r = game.set_target_storage_mode(BoardState::Turn);
println!("{r:?}");

// save the solved game tree to a file
// 4th argument is zstd compression level (1-22); requires `zstd` feature to use
save_data_to_file(&game, "memo string", "filename.bin", None).unwrap();

// load the solved game tree from a file
// 2nd argument is the maximum memory usage in bytes
let (mut game2, _memo_string): (PostFlopGame, _) =
load_data_from_file("filename.bin", None).unwrap();

println!("Game 1 Internal Data");
game.print_internal_data();
println!("Game 2 Internal Data");
game2.print_internal_data();

// check if the loaded game tree is the same as the original one
game.cache_normalized_weights();
game2.cache_normalized_weights();
assert_eq!(game.equity(0), game2.equity(0));

// discard information after the river deal when serializing
// this operation does not lose any information of the game tree itself
game2.set_target_storage_mode(BoardState::Turn).unwrap();

// compare the memory usage for serialization
println!(
"Memory usage of the original game tree: {:.2}MB", // 11.50MB
game.target_memory_usage() as f64 / (1024.0 * 1024.0)
);
println!(
"Memory usage of the truncated game tree: {:.2}MB", // 0.79MB
game2.target_memory_usage() as f64 / (1024.0 * 1024.0)
);
let mut game1 = PostFlopGame::with_config(card_config, action_tree).unwrap();
game1.allocate_memory(false);

solve(&mut game1, 100, 0.01, false);

// save (turn)
game1.set_target_storage_mode(BoardState::Turn).unwrap();
save_data_to_file(&game1, "", "tmpfile.flop", None).unwrap();

// load (turn)
let mut game2: PostFlopGame = load_data_from_file("tmpfile.flop", None).unwrap().0;
// compare_strategies(&mut game, &mut game2, BoardState::Turn);
assert!(game2.rebuild_and_resolve_forgotten_streets().is_ok());

let mut actions_so_far = vec![];

// Print Root Node
print_strats_at_current_node(&mut game1, &mut game2, &actions_so_far);

// overwrite the file with the truncated game tree
// game tree constructed from this file cannot access information after the river deal
save_data_to_file(&game2, "memo string", "filename.bin", None).unwrap();
let (mut game3, _memo_string): (PostFlopGame, String) =
load_data_from_file("filename.bin", None).unwrap();
// OOP: Check
actions_so_far.push(game1.available_actions()[0]);
game1.play(0);
game2.play(0);
print_strats_at_current_node(&mut game1, &mut game2, &actions_so_far);

// IP: Check
actions_so_far.push(game1.available_actions()[0]);
game1.play(0);
game2.play(0);
print_strats_at_current_node(&mut game1, &mut game2, &actions_so_far);

game.play(0);
game.play(0);
println!("Game X/X Actions: {:?}", game.available_actions());
// Chance: 2c
actions_so_far.push(game1.available_actions()[0]);
game1.play(0);
game2.play(0);
print_strats_at_current_node(&mut game1, &mut game2, &actions_so_far);

// OOP: CHECK
actions_so_far.push(game1.available_actions()[0]);
game1.play(0);
game2.play(0);
println!("Game2 X/X Actions: {:?}", game.available_actions());
game3.play(0);
game3.play(0);
println!("Game3 X/X Actions: {:?}", game3.available_actions());
print_strats_at_current_node(&mut game1, &mut game2, &actions_so_far);

// IP: CHECK
actions_so_far.push(game1.available_actions()[0]);
game1.play(0);
game2.play(0);
print_strats_at_current_node(&mut game1, &mut game2, &actions_so_far);

// CHANCE: 0
actions_so_far.push(game1.available_actions()[1]);
game1.play(1);
game2.play(1);
print_strats_at_current_node(&mut game1, &mut game2, &actions_so_far);

// delete the file
std::fs::remove_file("filename.bin").unwrap();
// compare_strategies(&mut game, &mut game2, BoardState::Turn);
}
42 changes: 42 additions & 0 deletions src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ mod tests {
use crate::action_tree::*;
use crate::card::*;
use crate::range::*;
use crate::solver::solve;
use crate::utility::*;

#[test]
Expand Down Expand Up @@ -375,4 +376,45 @@ mod tests {
assert!((root_ev_oop - 45.0).abs() < 1e-4);
assert!((root_ev_ip - 15.0).abs() < 1e-4);
}

#[test]
fn test_reload_and_resolve() {
let oop_range = "AA,QQ";
let ip_range = "KK";

let card_config = CardConfig {
range: [oop_range.parse().unwrap(), ip_range.parse().unwrap()],
flop: flop_from_str("3h3s3d").unwrap(),
..Default::default()
};

let tree_config = TreeConfig {
starting_pot: 100,
effective_stack: 100,
rake_rate: 0.0,
rake_cap: 0.0,
flop_bet_sizes: [("e", "").try_into().unwrap(), ("e", "").try_into().unwrap()],
turn_bet_sizes: [("e", "").try_into().unwrap(), ("e", "").try_into().unwrap()],
river_bet_sizes: [("e", "").try_into().unwrap(), ("e", "").try_into().unwrap()],
..Default::default()
};

let action_tree = ActionTree::new(tree_config).unwrap();
let mut game = PostFlopGame::with_config(card_config, action_tree).unwrap();
println!(
"memory usage: {:.2}GB",
game.memory_usage().0 as f64 / (1024.0 * 1024.0 * 1024.0)
);
game.allocate_memory(false);

solve(&mut game, 100, 0.01, false);

// save (turn)
game.set_target_storage_mode(BoardState::Turn).unwrap();
save_data_to_file(&game, "", "tmpfile.flop", None).unwrap();

// load (turn)
let mut game: PostFlopGame = load_data_from_file("tmpfile.flop", None).unwrap().0;
assert!(game.rebuild_and_resolve_forgotten_streets().is_ok());
}
}
Loading

0 comments on commit a23e319

Please sign in to comment.