Trait cfr::IntoGameNode

source ·
pub trait IntoGameNode {
    type PlayerInfo: Eq;
    type Action: Eq;
    type ChanceInfo: Eq;
    type Outcomes: IntoIterator<Item = (f64, Self)>;
    type Actions: IntoIterator<Item = (Self::Action, Self)>;

    // Required method
    fn into_game_node(self) -> GameNode<Self>;
}
Expand description

A trait that defines how to convert game-tree-like data into a Game

Define this trait on your custom data type to allow zero-copy conversion into the internal game tree representation to enable game solving. There are five associated types that define how your game is represented. Here zero-copy means none of the types need to implement Copy or Clone, but the conversion will still allocate memory for the different branches.

The trait ultimately resolves to converting each of your tree nodes into a coresponding GameNode that contains all the information necessary for the internal game structure.

Examples

If you’re constructing your data from scratch and don’t have a custom representation then the easiest way to structure your data is with a custom singleton wrapper. Any data types that fit the required bounds should work in this scenario, but note that information sets are defined by the order of actions, so using a structure without consistent iteration order could cause exceptions when trying to create a full game.

struct Node(GameNode<Node>);

#[derive(Hash, PartialEq, Eq)]
enum Impossible {}

impl IntoGameNode for Node {
    type PlayerInfo = u64;
    type Action = String;
    type ChanceInfo = Impossible;
    type Outcomes = Vec<(f64, Node)>;
    type Actions = Vec<(String, Node)>;

    fn into_game_node(self) -> GameNode<Self> {
        self.0
    }
}

let game = Game::from_root(
    Node(GameNode::Player(PlayerNum::One, 1, vec![
        ("fixed".into(), Node(GameNode::Terminal(0.0))),
        ("random".into(), Node(GameNode::Chance(None, vec![
            (0.5, Node(GameNode::Terminal(1.0))),
            (0.5, Node(GameNode::Terminal(-1.0))),
        ]))),
    ]))
);

However, this can also be used to create more advanced games in a lazy manner. This example illustrates a lazily created game, but note that the game itself is not interesting.

struct Node(u64);

#[derive(Hash, PartialEq, Eq)]
enum Impossible {}

struct ActionIter(u64);

impl Iterator for ActionIter {
    type Item = (u64, Node);

    fn next(&mut self) -> Option<Self::Item> {
        if self.0 > 2 {
            self.0 -= 2;
            Some((self.0, Node(self.0)))
        } else {
            None
        }
    }
}

impl IntoGameNode for Node {
    type PlayerInfo = u64;
    type Action = u64;
    type ChanceInfo = Impossible;
    type Outcomes = [(f64, Node); 0];
    type Actions = ActionIter;

    fn into_game_node(self) -> GameNode<Self> {
        if self.0 == 0 {
            GameNode::Terminal(0.0)
        } else {
            let num = if self.0 % 2 == 0 {
                PlayerNum::One
            } else {
                PlayerNum::Two
            };
            GameNode::Player(num, self.0, ActionIter(self.0 + 1))
        }
    }
}

let game = Game::from_root(Node(6));

Required Associated Types§

source

type PlayerInfo: Eq

The type for player information sets

All nodes that have the same player information set are indistinguishable from the perspective of the acting player. That means that they must have the same actions available in the same order. In addition, this library only works for games with perfect recall, which means that a player can’t forget their own actions. Another way to state this is that all nodes with the same infoset must all have followed the same previous infoset for that player.

source

type Action: Eq

The type of the player action

Player nodes have an iterator of actions attached to future states. The actual action representation isn’t important, but infosets must have the same actions in the same order. When converting a set of strategies back into their named representations, these will be used to represent them.

source

type ChanceInfo: Eq

The information set type for chance nodes

Chance node information sets have the same identical actions restrictions that player infosets do, but don’t require perfect recall. The benefit of specifying chance infosets is that sampling based methods can preserve the correlation in sampling which helps convergence. For example if chance is revealing cards, later draws may be independent of player actions, and so should be in the same infoset.

Since these only help convergence, they are optional. If you know these are unspecified, this should be set to the ! type, or any empty type.

source

type Outcomes: IntoIterator<Item = (f64, Self)>

The type for iterating over the actions in a chance node

For chance nodes in the same information set, these should iterate over outcomes in the same order. The associated float for each outcome is a positive weight associated with that outcome. Outcome occur proportional to their weight. In other words, the weights must all be positive, but they don’t have to sum to one.

source

type Actions: IntoIterator<Item = (Self::Action, Self)>

The type for iterating over the actions in a player nodes

Actions must occur in the same order for the same information sets, so using representations like a std::collections::HashMap is discouraged.

Required Methods§

source

fn into_game_node(self) -> GameNode<Self>

Convert this type into a GameNode

Note that the GameNode is just an intemediary representation meant to convert custom types into a Game.

Object Safety§

This trait is not object safe.

Implementors§