pub struct Game<Infoset, Action> { /* private fields */ }Expand description
A compact game representation
This structure allows computing approximate equilibria, and evaluating the regret and utility of strategy profiles.
Implementations§
Source§impl<I: Hash + Eq, A: Hash + Eq> Game<I, A>
impl<I: Hash + Eq, A: Hash + Eq> Game<I, A>
Sourcepub fn from_root<T>(root: T) -> Result<Self, GameError>
pub fn from_root<T>(root: T) -> Result<Self, GameError>
Create a game from the root node of an arbitrary game tree
For more information on how to create a game, see the necessary trait IntoGameNode for details on how to structure the input data.
Source§impl<I, A> Game<I, A>
impl<I, A> Game<I, A>
Sourcepub fn solve(
&self,
method: SolveMethod,
max_iter: u64,
max_reg: f64,
num_threads: usize,
params: Option<RegretParams>,
) -> Result<(Strategies<'_, I, A>, RegretBound), SolveError>
pub fn solve( &self, method: SolveMethod, max_iter: u64, max_reg: f64, num_threads: usize, params: Option<RegretParams>, ) -> Result<(Strategies<'_, I, A>, RegretBound), SolveError>
Find an approximate Nash equilibrium of the current game
Often you’ll either want to run with max_iter as usize::MAX and max_reg as a meaningful
regret, or max_iter as a number set based off of the time you have and max_reg set to
0.0, although setting both as a tradeoff also reasonable.
§Arguments
method- The method of solving to use. When in doubt prefer External. See SolveMethod for details on the distinctions.max_iter- The maximum number of iterations to run. The maximum regret of the found strategy is bounded by the square-root of the number of iterations.max_reg- Terminate early if the regret of the returned strategy is going to be less than this value. With this current implementation this is only valid when the method is Full and the params are vanilla. If using other parameters, this should be set lower than the desired regret.num_threads- The number of threads to use for solving. Zero selects based off of thread::available_parallelism. One uses a single threaded variant that’s more efficient when not in a threaded environment.param- Advanced parameters that govern the behavior of the regret and strategy updates. See RegretParams for more details, or set to None to use the default.
§Errors
If num_threads is too large, and this tries to spawn too many threads, or if there are
problems spawning threads. This will not error when num_threads is 1.
Examples found in repository?
172fn main() {
173 let args = Args::parse();
174 let game = create_kuhn(args.num_cards);
175 let (mut strats, _) = game
176 .solve(
177 SolveMethod::External,
178 args.iterations,
179 0.0,
180 args.parallel,
181 None,
182 )
183 .unwrap();
184 strats.truncate(5e-3); // not visible
185 let [player_one_strat, player_two_strat] = strats.as_named();
186 println!("Player One");
187 println!("==========");
188 let mut init = Vec::with_capacity(3);
189 let mut raised = Vec::with_capacity(3);
190 for (&(card, raise), actions) in player_one_strat {
191 if raise { &mut raised } else { &mut init }.push((card, actions));
192 }
193 println!("Initial Action");
194 println!("--------------");
195 print_card_strat(init, args.num_cards);
196 println!("If Player Two Raised");
197 println!("--------------------");
198 print_card_strat(raised, args.num_cards);
199 println!("Player Two");
200 println!("==========");
201 let mut called = Vec::with_capacity(3);
202 let mut raised = Vec::with_capacity(3);
203 for (&(card, raise), actions) in player_two_strat {
204 if raise { &mut raised } else { &mut called }.push((card, actions));
205 }
206 println!("If Player One Called");
207 println!("--------------------");
208 print_card_strat(called, args.num_cards);
209 println!("If Player One Raised");
210 println!("--------------------");
211 print_card_strat(raised, args.num_cards);
212}Sourcepub fn num_infosets(&self) -> usize
pub fn num_infosets(&self) -> usize
The total number of information sets for each player
Source§impl<I: Hash + Eq + Clone, A: Hash + Eq + Clone> Game<I, A>
impl<I: Hash + Eq + Clone, A: Hash + Eq + Clone> Game<I, A>
Sourcepub fn from_named(
&self,
strats: [impl IntoIterator<Item = (impl Borrow<I>, impl IntoIterator<Item = (impl Borrow<A>, impl Borrow<f64>)>)>; 2],
) -> Result<Strategies<'_, I, A>, StratError>
pub fn from_named( &self, strats: [impl IntoIterator<Item = (impl Borrow<I>, impl IntoIterator<Item = (impl Borrow<A>, impl Borrow<f64>)>)>; 2], ) -> Result<Strategies<'_, I, A>, StratError>
Convert a named strategy into Strategies
The input can be any set of types that vaguelly iterates over pairs of information sets and then actions paired to weights. Weights can be any non-negative f64, which will be normalized in the final strategy. There are no restrictions on iteration order.
§Example
use std::collections::HashMap;
let game = // ...
let one: HashMap<&'static str, HashMap<&'static str, f64>> = [
("info", [("A", 0.2), ("B", 0.8)].into())
].into();
let two: HashMap<&'static str, HashMap<&'static str, f64>> = [
("info", [("1", 0.5), ("2", 0.5)].into())
].into();
let strat = game.from_named([one, two]).unwrap();
let info = strat.get_info();
info.regret();§Errors
This will error if a valid strategy wasn’t specified for every infoset, or it received invalid infosets or actions for the current Game.
Source§impl<I: Eq, A: Eq> Game<I, A>
impl<I: Eq, A: Eq> Game<I, A>
Sourcepub fn from_named_eq(
&self,
strats: [impl IntoIterator<Item = (impl Borrow<I>, impl IntoIterator<Item = (impl Borrow<A>, impl Borrow<f64>)>)>; 2],
) -> Result<Strategies<'_, I, A>, StratError>
pub fn from_named_eq( &self, strats: [impl IntoIterator<Item = (impl Borrow<I>, impl IntoIterator<Item = (impl Borrow<A>, impl Borrow<f64>)>)>; 2], ) -> Result<Strategies<'_, I, A>, StratError>
Convert a named strategy into Strategies
In case cloning is very expensive, this version doesn’t require cloning or hashing, but otherwise runs in time quadratic in the number of infosets and actions, which is almost certaintly going to be worse than the cost of cloning.
Also note that currently constructing the Game requires hashing so that relaxation is meaningless.
This is otherwise the same as Game::from_named, so see that method for examples.
Trait Implementations§
impl<I, A> Eq for Game<I, A>
Auto Trait Implementations§
impl<Infoset, Action> Freeze for Game<Infoset, Action>
impl<Infoset, Action> RefUnwindSafe for Game<Infoset, Action>where
Infoset: RefUnwindSafe,
Action: RefUnwindSafe,
impl<Infoset, Action> Send for Game<Infoset, Action>
impl<Infoset, Action> Sync for Game<Infoset, Action>
impl<Infoset, Action> Unpin for Game<Infoset, Action>
impl<Infoset, Action> UnwindSafe for Game<Infoset, Action>where
Infoset: UnwindSafe,
Action: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more