1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! RS-Poker is a library for poker
//! Currently RS-Poker supports:
//!
//! * Hand Iteration.
//! * Hand Ranking.
//! * Hand Range parsing.
//! * Hand Range generation.
//! * ICM tournament values
//! * Monte carlo holdem
//! * Holdem Game State with action validation
//! * Holdem agents
//! * Holdem game simulation
//!
//! Our focus is on correctness and performance.
//!
//! ## Core library
//!
//! The core of the library contains code that is relevant
//! to all poker variants. Card suits, values, hand
//! values, and datastructures used in other parts of the crate.
//!
//! ## Holdem
//!
//! Holdem is the best supported variant.
//!
//! ### Starting Hands
//!
//! The StartingHand module contains the following key components:
//!
//! `Suitedness`: This is an enum type that represents how the suits of a hand
//! correspond to each other. It has three variants:
//!
//! * Suited: All of the cards are the same suit.
//! * OffSuit: None of the cards are the same suit.
//! * Any: Makes no promises about the suit.
//!
//! `HoldemStartingHand`: This represents the two-card starting hand of
//! Texas Hold'em. It can generate all possible actual starting
//! hands given two values and a suitedness condition.
//!
//! ```rust
//! use rs_poker::core::Value;
//! use rs_poker::holdem::{StartingHand, Suitedness};
//!
//! let hands = StartingHand::default(Value::Ace, Value::Ace, Suitedness::OffSuit).possible_hands();
//! assert_eq!(6, hands.len());
//! ```
//!
//! ### Range parsing
//!
//! A lot of discussion online is around ranges. For example:
//! "High Jack had a range of KQo+ and 99+"
//!
//! The range parsing module allows turning those range strings into vectors of
//! possible hands.
//!
//! ```
//! use rs_poker::holdem::RangeParser;
//! let hands = RangeParser::parse_one("KQo+").unwrap();
//!
//! // There are 24 different combinations of off suit
//! // connectors King + Queen or higher
//! assert_eq!(24, hands.len());
//! ```
//! ### Monte Carlo Game simulation
//!
//! Sometimes it's important to know your expected equity
//! in a pot vs a given set of card. In doing that it's useful
//! to quickly simulate what could happen.
//!
//! The `MonteCarloGame` strcut does that:
//!
//! ``` rust
//! use rs_poker::core::{Card, Hand, Suit, Value};
//! use rs_poker::holdem::MonteCarloGame;
//!
//! let hero = Hand::new_with_cards(vec![
//! Card::new(Value::Jack, Suit::Spade),
//! Card::new(Value::Jack, Suit::Heart),
//! ]);
//! let villan = Hand::new_with_cards(vec![
//! Card::new(Value::Ace, Suit::Spade),
//! Card::new(Value::King, Suit::Spade),
//! ]);
//! let mut monte_sim = MonteCarloGame::new(vec![hero, villan]).unwrap();
//! let mut wins: [u64; 2] = [0, 0];
//! for _ in 0..100_000 {
//! let r = monte_sim.simulate();
//! monte_sim.reset();
//! // You can handle ties however you like here
//! wins[r.0.ones().next().unwrap()] += 1
//! }
//!
//! // Jacks hold up most of the time
//! assert!(wins[0] > wins[1]);
//! ```
//!
//! ## Simulated ICM
//!
//! Not all chips are equal; when rewards for tounaments are highly in favor of
//! placing higher, sometimes the correct decision comes down to expected value
//! of the whole tournament.
//!
//! ```
//! use rand::{Rng, rng};
//! use rs_poker::simulated_icm::simulate_icm_tournament;
//!
//! let payments = vec![10_000, 6_000, 4_000, 1_000, 800];
//! let mut rng = rng();
//! let chips: Vec<i32> = (0..4).map(|_| rng.random_range(100..300_000)).collect();
//! let simulated_results = simulate_icm_tournament(&chips, &payments);
//!
//! // There's one payout per player still remaining.
//! // You can run this over and over again to get an
//! // average expected value.
//! assert_eq!(chips.len(), simulated_results.len());
//! ```
//!
//! ## Holdem arena
//!
//! Can you program a bot that can beat the best poker players in
//! the world? This is your starting place to do that. Implement on Trait
//! `Agent` and you can simulate Texas Holdem games with your agent.
//!
//! ```rust,ignore
//! fn act(&mut self, id: u128, game_state: &GameState) -> AgentAction;
//! ```
//!
//! Your agent takes in the current game ID and the current game state. From
//! there it returns what action it would like to play. If you're agent is
//! better than others it will have +EV and win more money.
//!
//! The arena is code to simulate different strategies and get outcomes. It
//! includes utilities to run tournaments of agents to see who would win in an
//! elimination style game.
//!
//! For example if you want to simulate the different between different vpip's.
//! Simply code an agent with configurable starting hand range and see what the
//! expected values are. The arena is configurable for number of players from
//! heads up all the way to full ring.
//!
//! ### Stability
//!
//! The holdem arena is the newest addition to `rs-poker` and the most
//! experimental. So it's the most likely to change in the future.
//!
//! ### Internals
//!
//! The arena has several parts:
//! * `GameState` this holds the current state of all the chips, bets, player
//! all in status, and if players are active in a hand or round.
//! * `Agent` is the trait needed to implement different automatic players in
//! the poker.
//! * `Historian` is the trait implemented to recieve actions just after they
//! are applied to the gamestate. This allows the historian to follow along
//! and record the game state.
//! * `HoldemSimulation` this is the main wrapper struct that handles calling
//! the agents and force folding the agents for any invalid actions.
//! * `HoldemSimulationBuilder` that is used to construcst single simulations.
//! `GameState` and `Agents` are required the rest are optional
//! * `HoldemCompetition` that keeps track of all simulation based stats from
//! simluations genreated via `HoldemSimulationGenerator`.
//! * Each `HoldemSimulationGenerator` is built of `AgentsGenerator`,
//! `HistorianGenerator`, and `GameStateGenerator`
//!
//!
//! ### Example
//!
//! ```
//! use rs_poker::arena::{
//! AgentGenerator, CloneGameStateGenerator, GameState,
//! agent::{CallingAgentGenerator, RandomAgentGenerator},
//! competition::{HoldemCompetition, StandardSimulationIterator},
//! };
//! let agent_gens: Vec<Box<dyn AgentGenerator>> = vec![
//! Box::<RandomAgentGenerator>::default(),
//! Box::<CallingAgentGenerator>::default(),
//! Box::<CallingAgentGenerator>::default(),
//! ];
//! let stacks = vec![100.0; 3];
//! let game_state = GameState::new_starting(stacks, 10.0, 5.0, 0.0, 0);
//! let sim_gen = StandardSimulationIterator::new(
//! agent_gens,
//! vec![], // no historians
//! CloneGameStateGenerator::new(game_state),
//! );
//! let mut competition = HoldemCompetition::new(sim_gen);
//! let _first_results = competition.run(100).unwrap();
//! ```
extern crate rand;
/// Allow all the core poker functionality to be used
/// externally. Everything in core should be agnostic
/// to poker style.
/// The holdem specific code. This contains range
/// parsing, game state, and starting hand code.
/// Given a tournament calculate the implied
/// equity in the total tournament.