Skip to content

Commit

Permalink
Started working on batch solve
Browse files Browse the repository at this point in the history
  • Loading branch information
bkushigian committed Oct 13, 2024
1 parent 168db4c commit cca82c6
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ repository = "https://github.com/bkushigian/postflop-solver"
license = "AGPL-3.0-or-later"

[dependencies]
clap = { version = "4.5", features = ["derive"] }
bincode = { version = "2.0.0-rc.3", optional = true }
once_cell = "1.18.0"
rayon = { version = "1.8.0", optional = true }
regex = "1.9.6"
zstd = { version = "0.12.4", optional = true, default-features = false }
serde = {version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[features]
Expand Down
104 changes: 104 additions & 0 deletions examples/batch_solve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use std::path::PathBuf;

use clap::Parser;
use postflop_solver::{cards_from_str, solve, ActionTree, CardConfig, PostFlopGame, TreeConfig};

/// Simple program to greet a person
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Path to configuration file
#[arg(required = true)]
config: String,

/// Boards to run on
#[arg(short, long)]
boards: Option<Vec<String>>,

/// File with boards to run on
#[arg(short, long)]
boards_file: Option<String>,

/// Directory to output solves to
#[arg(short, long, default_value = ".")]
dir: String,

/// Max number of iterations to run
#[arg(short = 'n', long, default_value = "1000")]
max_iterations: u32,

/// Default exploitability as ratio of pot. Defaults to 0.2 (20% of pot),
/// but for accurate solves we recommend choosing a lower value.
#[arg(short = 'e', long, default_value = "0.2")]
exploitability: f32,
}

fn main() {
let args = Args::parse();

let config = std::fs::read_to_string(args.config).expect("Unable to read in config");

let boards = if let Some(boards) = args.boards {
boards
} else {
let boards_files = args
.boards_file
.expect("Must specify boards or boards_file");
let boards_contents =
std::fs::read_to_string(boards_files).expect("Unable to read boards_file");
boards_contents
.lines()
.map(|s| s.to_string())
.collect::<Vec<String>>()
};
let configs_json: serde_json::Value =
serde_json::from_str(&config).expect("Unable to parse config");
let configs_map = configs_json.as_object().expect("Expected a json object");

let card_config = configs_map.get("card_config").unwrap();
let card_config: CardConfig = serde_json::from_value(card_config.clone()).unwrap();

let tree_config = configs_map.get("tree_config").unwrap();
let tree_config: TreeConfig = serde_json::from_value(tree_config.clone()).unwrap();

// Create output directory if needed. Check if ".pfs" files exist, and if so abort
let dir = PathBuf::from(args.dir);
if dir.exists() {
if !dir.is_dir() {
panic!(
"output directory {} exists but is not a directory",
dir.to_str().unwrap()
);
}
for board in &boards {
// create board file name
let board_file_name = board
.chars()
.filter(|c| !c.is_whitespace())
.collect::<String>();
let board_path = dir.join(board_file_name).with_extension("pfs");
if board_path.exists() {
panic!("board path {} already exists", board_path.to_string_lossy());
}
}
} else {
std::fs::create_dir_all(&dir).unwrap();
}

for board in &boards {
let cards =
cards_from_str(&board).expect(format!("Couldn't parse board {}", board).as_str());

let mut game = PostFlopGame::with_config(
card_config.with_cards(cards).unwrap(),
ActionTree::new(tree_config.clone()).unwrap(),
)
.unwrap();

game.allocate_memory(false);

let max_num_iterations = args.max_iterations;
let target_exploitability = game.tree_config().starting_pot as f32 * args.exploitability;
solve(&mut game, max_num_iterations, target_exploitability, true);
}
}

0 comments on commit cca82c6

Please sign in to comment.