Giter VIP home page Giter VIP logo

postflop-solver's Introduction

postflop-solver

An open-source postflop solver library written in Rust

Documentation: https://b-inary.github.io/postflop_solver/postflop_solver/

Related repositories

Note: The primary purpose of this library is to serve as a backend engine for the GUI applications (WASM Postflop and Desktop Postflop). The direct use of this library by the users/developers is not a critical purpose by design. Therefore, breaking changes are often made without version changes. See CHANGES.md for details about breaking changes.

Usage

  • Cargo.toml
[dependencies]
postflop-solver = { git = "https://github.com/b-inary/postflop-solver" }
  • Examples
use postflop_solver::*;

// ranges of OOP and IP in string format
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 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,
};

// bet sizes -> 60% of the pot, geometric size, and all-in
// raise sizes -> 2.5x of the previous bet
// see the documentation of `BetSizeCandidates` for more details
let bet_sizes = BetSizeCandidates::try_from(("60%, e, a", "2.5x")).unwrap();

let tree_config = TreeConfig {
    initial_state: BoardState::Turn,
    starting_pot: 200,
    effective_stack: 900,
    rake_rate: 0.0,
    rake_cap: 0.0,
    flop_bet_sizes: [bet_sizes.clone(), bet_sizes.clone()], // [OOP, IP]
    turn_bet_sizes: [bet_sizes.clone(), bet_sizes.clone()],
    river_bet_sizes: [bet_sizes.clone(), bet_sizes.clone()],
    turn_donk_sizes: None, // use default bet sizes
    river_donk_sizes: Some(DonkSizeCandidates::try_from("50%").unwrap()),
    add_allin_threshold: 1.5, // add all-in if (maximum bet size) <= 1.5x pot
    force_allin_threshold: 0.15, // force all-in if (SPR after the opponent's call) <= 0.15
    merging_threshold: 0.1,
};

// build the game tree
let action_tree = ActionTree::new(tree_config).unwrap();
let mut game = PostFlopGame::with_config(card_config, action_tree).unwrap();

// obtain the private hands
let oop_hands = game.private_cards(0);
assert_eq!(card_to_string(oop_hands[0].0).unwrap(), "4c");
assert_eq!(card_to_string(oop_hands[0].1).unwrap(), "5c");

// check memory usage
let (mem_usage, mem_usage_compressed) = game.memory_usage();
println!(
    "Memory usage without compression: {:.2}GB",
    mem_usage as f64 / (1024.0 * 1024.0 * 1024.0)
);
println!(
    "Memory usage with compression: {:.2}GB",
    mem_usage_compressed as f64 / (1024.0 * 1024.0 * 1024.0)
);

// allocate memory without compression
game.allocate_memory(false);

// allocate memory with compression
// game.allocate_memory(true);

// solve the game
let max_num_iterations = 1000;
let target_exploitability = game.tree_config().starting_pot as f32 * 0.005;
let exploitability = solve(&mut game, max_num_iterations, target_exploitability, true);
println!("Exploitability: {:.2}", exploitability);

// solve the game manually
// for i in 0..max_num_iterations {
//     solve_step(&game, i);
//     if (i + 1) % 10 == 0 {
//         let exploitability = compute_exploitability(&game);
//         if exploitability <= target_exploitability {
//             println!("Exploitability: {:.2}", exploitability);
//             break;
//         }
//     }
// }
// finalize(&mut game);

// get equity and EV of a specific hand
game.cache_normalized_weights();
let equity = game.equity(0); // `0` means OOP player
let ev = game.expected_values(0);
println!("Equity of oop_hands[0]: {:.2}%", 100.0 * equity[0]);
println!("EV of oop_hands[0]: {:.2}", ev[0]);

// get equity and EV of whole hand
let weights = game.normalized_weights(0);
let average_equity = compute_average(&equity, weights);
let average_ev = compute_average(&ev, weights);
println!("Average equity: {:.2}%", 100.0 * average_equity);
println!("Average EV: {:.2}", average_ev);

// get available actions (OOP)
let actions = game.available_actions();
assert_eq!(format!("{:?}", actions), "[Check, Bet(120), Bet(216), AllIn(900)]");

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

// get available actions (IP)
let actions = game.available_actions();
assert_eq!(format!("{:?}", actions), "[Fold, Call, Raise(300)]");

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

// confirm that the current node is a chance node (i.e., river node)
assert!(game.is_chance_node());

// confirm that "7s" may be dealt
let card_7s = card_from_str("7s").unwrap();
assert!(game.possible_cards() & (1 << card_7s) != 0);

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

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

Implementation details

  • Algorithm: The solver uses Discounted CFR algorithm. Currently, the value of γ is set to 3.0, rather than the 2.0 recommended by the original paper. Also, the solver reset the cumulative strategy when the number of iterations is a power of 4.
  • Precision: 32-bit floating-point numbers are used in most places. When calculating summations, temporal values use 64-bit floating-point numbers. If the compression feature is enabled, each game node stores the values by 16-bit integers with a single 32-bit floating-point scaling factor.
  • Handling isomorphism: The solver does not perform any abstraction. However, isomorphic chances (turn and river deals) are combined into one. For example, if the flop is monotone, the three non-dealt suits are isomorphic, allowing us to skip the calculation for two of the three suits.

Crate features

  • bincode: Uses bincode crate (2.0.0-rc.2) to serialize and deserialize the PostFlopGame struct. Disabled by default.
  • custom-alloc: Uses custom memory allocator in solving process (only available in nightly Rust). It significantly reduces the number of calls of the default allocator, so it is recommended to use this feature when the default allocator is not so efficient. Note that this feature assumes that, at most, only one instance of PostFlopGame is available when solving in a program. Disabled by default.
  • rayon: Uses rayon crate for parallelization. Enabled by default.

License

Copyright (C) 2022 Wataru Inariba

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.

postflop-solver's People

Contributors

b-inary avatar ydm avatar bkushigian avatar dpmaloney avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.