OpenSpiel: A Framework for Reinforcement Learning in Games

Marc Lanctot, Edward Lockhart, Jean-Baptiste Lespiau, Vinicius Zambaldi, Satyaki Upadhyay, Julien Pérolat, Sriram Srinivasan, Finbarr Timbers, Karl Tuyls, Shayegan Omidshafiei, Daniel Hennes, Dustin Morrill, Paul Muller, Timo Ewalds, Ryan Faulkner, János Kramár, Bart De Vylder, Brennan Saeta, James Bradbury, David Ding, Sebastian Borgeaud, Matthew Lai, Julian Schrittwieser, Thomas Anthony, Edward Hughes, Ivo Danihelka, Jonah Ryan-Davis

OpenSpiel Overview

OpenSpiel has been possible due to a team of contributors. For a full list of all the contributors, please see the list of authors on github.

We would also like to thank the following people, who helped and supported the development of OpenSpiel:

2 OpenSpiel At a Glance

We provide an intentionally brief overview here. For details, please see Section 3.

OpenSpiel provides a framework for writing games and algorithms and evaluating them on a variety of benchmark games. OpenSpiel contains implementations of over 20 different games of various sorts (perfect information, simultaneous move, imperfect information, gridworld games, an auction game, and several normal-form / matrix games). Game implementations are in C++ and wrapped in Python. Algorithms are implemented in C++ and/or Python. The API is almost identical in the two languages, so code can easily be translated if needed. A subset of the library has also been ported to Swift. Most of the learning algorithms written in Python use Tensorflow , though we are actively seeking examples and other support for PyTorch and JAXhttps://github.com/google/jax.

OpenSpiel has been tested on Linux and MacOS. There is also limited support on Windows.

Components of OpenSpiel are listed in Tables 1 and 2. As of October 2019, these tables will no longer be updated: Please refer to the Overview of Implemented Games or the Overview of Implemented Algorithms pages on the web site for most current information.

Getting Started

The following commands will clone the repository and build OpenSpiel on Ubuntu or Debian Linux, or MacOS. There is also limited support for Windows. We now show the fastest way to install OpenSpiel. Please see the recommended installation instructions using virtualenv for more detail.

Note that we have tested OpenSpiel Linux and MacOS, and there is limited support on Windows. Also, for the case of Linux, some of the scripts and instructions currently assume Debian-based distributions (i.e. Debian, Ubuntu, etc.). All of the dependencies exist on other distributions, but may have different names, and package managers differ. Please see install.sh for necessary dependencies.

To be able to import the Python code (both the C++ binding pyspiel and the rest) from any location, you will need to add to your PYTHONPATH the root directory and the open_spiel directory. Add the following in your .bashrc or .profile:

2 Running the First Example

After having built OpenSpiel following Sec 2.1, run the example from the build directory without any arguments:

This prints out a list of registered games and the usage. Now, let’s play a game of Tic-Tac-Toe with uniform random players:

Wow – how exhilarating! Now, why not try one of your favorite games?

Note that the structure in the build directory mirrors that of the source, so the example is found in open_spiel/examples/example.cc. At this stage you can run one of many binaries created, such as games/backgammon_test or algorithms/external_sampling_mccfr_test.

Once you have set your PYTHONPATH as explained in Sec 2.1.1, you can similarly run the python examples:

3 Adding a New Game

We describe here only the simplest and fastest way to add a new game. It is ideal to first be aware of the general API, which is described on a high level in Section 3, on github, and via comments in spiel.h.

Choose a game to copy from in games/. Suggested games: Tic-Tac-Toe and Breakthrough for perfect information without chance events, Backgammon or Pig for perfect information games with chance events, Goofspiel and Oshi-Zumo for simultaneous move games, and Leduc poker and Liar’s dice for imperfect information games. For the rest of these steps, we assume Tic-Tac-Toe.

Copy the header and source: tic_tac_toe.h, tic_tac_toe.cc, and tic_tac_toe_test.cc to new_game.h, new_game.cc, and new_game_test.cc.

Add the new game’s source files to games/CMakeLists.txt.

Add the new game’s test target to games/CMakeLists.txt

In new_game.h, rename the header guard at the the top and bottom of the file.

In the new files, rename the inner-most namespace from tic_tac_toe to new_game

In the new files, rename TicTacToeGame and TicTacToeState to NewGameGame and NewGameState

At the top of new_game.cc, change the short name to new_game and include the new game’s header.

Add the short name to the list of expected games in python/tests/pyspiel_test.py.

You should now have a duplicate game of Tic-Tac-Toe under a different name. It should build and the test should run, and can be verified by rebuilding and running the example from Section 2.2.

Now, change the implementations of the functions in NewGameGame and NewGameState to reflect your new game’s logic. Most API functions should be clear from the game you copied from. If not, each API function that is overridden will be fully documented in superclasses in spiel.h. See also the description of extensive-form games in Section 3.1 which closely matches the API.

Once done, rebuild and rerun the tests from Sec 2.1 to ensure everything passes (including your new game’s test!)

4 Adding a New Algorithm

Adding a new algorithm is fairly straight-forward. Like adding a game, it is easiest to copy and start from one of the existing algorithms. If adding a C++ algorithm, choose one from algorithms/. If adding a Python algorithm, choose one from python/algorithms/. For appropriate matches, see Table 2.

Unlike games, there is no specific structure or API that must be followed for an algorithm. If the algorithm is one in a class of existing algorithms, then we advise keeping the style and design similar to the ones in the same class, re-using function or modules where possible.

The algorithms themselves are not binaries, but classes or functions that can be used externally. The best way to show an example of an algorithm’s use is via a test. However, there are also binary executables in examples/ and python/examples/.

Design and API

The purpose of OpenSpiel is to promote general multiagent reinforcement learning across many different game types, in a similar way as general game-playing but with a heavy emphasis on learning and not in competition form. We hope that OpenSpiel could have a similar effect on general RL in games as the Atari Learning Environment has had on single-agent RL.

OpenSpiel provides a general API with a C++ foundation, which is exposed through Python bindings (via pybind11). Games are written in C++. This allows for fast or memory-efficient implementations of basic algorithms that might need the efficiency. Some custom RL environments are also implemented in Python. Most algorithms that require machine learning are implemented in Python.

Above all, OpenSpiel is designed to be easy to install and use, easy to understand, easy to extend (“hackable”), and general/broad. OpenSpiel is built around two major important design criteria:

Keep it simple. Simple choices are preferred to more complex ones. The code should be readable, usable, extendable by non-experts in the programming language(s), and especially to researchers from potentially different fields. OpenSpiel provides reference implementations that are used to learn from and prototype with, rather than fully-optimized / high-performance code that would require additional assumptions (narrowing the scope / breadth) or advanced (or lower-level) language features.

Keep it light. Dependencies can be problematic for long-term compatibility, maintenance, and ease-of-use. Unless there is strong justification, we tend to avoid introducing dependencies to keep things portable and easy to install.

There are several formalisms and corresponding research communities for representing multiagent interactions. It is beyond the scope of this paper to survey the various formalisms, so we describe the ones most relevant to our implementations. There have been recent efforts to harmonize the terminology and make useful associations among algorithms between computational game theory and reinforcement learning , so we base our terminology on classical concepts and these recent papers.

Games in OpenSpiel are represented as procedural extensive-form games , though in some cases can also be cyclic such as in Markov Decision Processes and Markov games . We first give the classical definitions, then describe some extensions, and explain some equivalent notions between the fields of reinforcement learning and games.

An extensive-form game is a tuple ⟨N,A,H,Z,u,τ,S⟩\langle\mathcal{N},\mathcal{A},\mathcal{H},\mathcal{Z},u,\tau,\mathcal{S}\rangle, where

N={1,2,…n}\mathcal{N}=\{1,2,\ldots n\} is a finite set of nn playersNote that the player IDs range from to n−1n-1 in the implementations.. There is also a special player cc, called chance.

A\mathcal{A} is a finite set of actions that players can take. This is a global set of state-independent actions; generally, only a subset of legal actions are available when agents decide.

H\mathcal{H} is a finite set of histories. Each history is a sequence of actions that were taken from the start of the game.

Z⊆H\mathcal{Z}\subseteq\mathcal{H} is a subset of terminal histories that represents a completely played game.

u:Z→Δun⊆ℜnu:\mathcal{Z}\rightarrow\Delta_{u}^{n}\subseteq\Re^{n}, where Δu=[umin⁡,umax⁡]\Delta_{u}=[u_{\min},u_{\max}], is the utility function assigning each player a utility at terminal states, and umin⁡,umax⁡u_{\min},u_{\max} are constants representing the minimum and maximum utility.

τ:H→N\tau:\mathcal{H}\rightarrow\mathcal{N} is a player identity function; τ(h)\tau(h) identifies which player acts at hh.

S\mathcal{S} is a set of states. In general, S\mathcal{S} is a partition of H\mathcal{H} such that each state s∈Ss\in\mathcal{S} contains histories h∈sh\in s that cannot be distinguished by τ(s)=τ(h)\mboxwhereh∈s\tau(s)=\tau(h)\mbox{ where }h\in s. Decisions are made by players at these states. There are several ways to precisely define S\mathcal{S} as described below.

We denote the legal actions available at state ss as A(s)⊆A\mathcal{A}(s)\subseteq\mathcal{A}. Importantly, a history represents the true ground/world state: when agents act, they change this history, but depending on how the partition is chosen, some actions (including chance’s) may be private and not revealed to some players.

We will extend this formalism further on to more easily describe how games are represented in OpenSpiel. However, we can already state some important categories of games:

A constant-sum (kk-sum) game is one where ∀z∈Z,∑i∈Nui(z)=k\forall z\in\mathcal{Z},\sum_{i\in\mathcal{N}}u_{i}(z)=k.

A zero-sum game is a constant-sum game with k=0k=0.

An identical interest game is one where ∀z∈Z,∀i,j∈N,ui(z)=uj(z)\forall z\in\mathcal{Z},\forall i,j\in\mathcal{N},u_{i}(z)=u_{j}(z).

A general-sum game is one without any constraint on the sum of the utilities.

In other words: kk-sum games are strictly competitive, identical interest games are strictly cooperative, and general-sum games are neither or somewhere in between. Also,

A perfect information game is one where there is only one history per state: ∀s∈S,∣s∣=1\forall s\in\mathcal{S},|s|=1.

A imperfect information game is one where there is generally more than one history per state, ∃s∈S:∣s∣>1\exists s\in\mathcal{S}:|s|>1.

Chess, Go, and Breakthrough are examples of perfect information games without events (no chance player). Backgammon and Pig are examples of perfect information games with chance events. Leduc poker, Kuhn poker, Liar’s Dice, and Phantom Tic-Tac-Toe are examples of imperfect information games. Every one of these example games is zero-sum.

A chance node (or chance event) is a history hh such that τ(h)=c\tau(h)=c.

In zero-sum perfect information games, minimax and alpha-beta search are classical search algorithms for making decisions using heuristic value functions . The analogs for perfect information games with chance events are expectiminimax and *-minimax .

We can augment the extensive-form game with a special kind of player, the simultaneous move player: ÷\div. When τ(s)=÷\tau(s)=\div, each player ii has a set of legal actions Ai(s)\mathcal{A}_{i}(s), and all players act simultaneously choosing a joint action a=(ai){i∈N}a=(a_{i})_{\{i\in\mathcal{N}\}}. Histories in these games are then sequences of joint actions, and transitions take the form (h,a,h′)(h,a,h^{\prime}). The rest of the properties from extensive-form games still hold.

A normal-form (or one-shot game) is a simultaneous-move game with a single state, ∣S∣=1|S|=1. A matrix game is a normal-form game where ∣N∣=2|\mathcal{N}|=2.

A simultaneous-move game can be represented as a specific type of extensive-form game with imperfect information.

To see why this is true: consider the game of Rock, Paper, Scissors (A={\textscr,\textscp,\textscs}\mathcal{A}=\{\textsc{r},\textsc{p},\textsc{s}\}) where each player chooses a single action, revealing their choice simultaneously. An equivalent turn-based is the following: the first player writes their action on a piece of paper, and places it face down. Then, the second player does the same. Then, the choices are revealed simultaneously. The players acted at separate times, but the second player did not know the choice made by the first player (and hence could be in one of three histories: h=\textscr,h=\textscp,\mboxorh=\textscsh=\textsc{r},h=\textsc{p},\mbox{ or }h=\textsc{s}), and the game has two states instead of one state. In a game with many states, the same idea can simply be repeated for every state.

Why, then, represent these games differently? There are several reasons:

They have historically been treated as separate in the multiagent RL literature.

They can sometimes be solved using Bellman-style dynamic programming, unlike general imperfect information games.

They are slightly more general. In fact, one can represent a turn-based game using a simultaneous-move game, simply by setting Ai(s)=∅\mathcal{A}_{i}(s)=\emptyset for j≠τ(s)j\not=\tau(s) or by adding a special pass move as the only legal action when it is not a player’s turn.

We elaborate on each of these points in the following section, when we relate simultaneous-move games to existing multiagent RL formalisms.

1.2 Policies, Objectives, and Multiagent Reinforcement Learning

We now add the last necessary ingredients for designing decision-making and learning algorithms, and bring in the remaining standard RL terms.

A policy π:S→Δ(A(s))\pi:\mathcal{S}\rightarrow\Delta(\mathcal{A}(s)), where Δ(X)\Delta(X) represents the set of probability distributions over XX, describes agent behavior. An agent acts by selecting actions from its policy: a∼πa\sim\pi. A deterministic policy is one where at each state the distribution over actions has probability 11 on one action and zero on the others. A policy that is not (necessarily) deterministic is called stochastic.

In games, the chance player is special because it always plays with a fixed (stochastic) policy πc\pi_{c}.

A transition function T:S×A→Δ(S)\mathcal{T}:\mathcal{S}\times\mathcal{A}\rightarrow\Delta(\mathcal{S}) defines a probability distribution over successor states s′s^{\prime} when choosing action aa from state ss.

A transition function can be equivalently represented using intermediate chance nodes between the histories of the predecessor and successor states h∈sh\in s and h′∈s′h^{\prime}\in s^{\prime}. The transition function is then determined by πc\pi_{c} and Pr⁡(h∣s)\Pr(h|s).

In Poker, a player acts from an information state, and the histories corresponding to such an information state only differ in the chance event outcomes that correspond to the opponent’s private cards. In these partially-observable games, a state is normally called an information state to emphasize the fact that the agent’s perception of the state (ss) is different than the true underlying world state (one of h∈sh\in s).

The property of perfect recall turns out to be a very important criterion for determining convergence guarantees for exact tabular algorithms, as we show in Section 3.2.

An observation is a partial view of the information state and contains strictly less information than the information state. To be valid, the sequence of observations and actions of all players should contain at least as much information as the information state. Formally: Let Ω\Omega be a finite set of observations. Let Oi:S→ΩO_{i}:\mathcal{S}\rightarrow\Omega be an observation function for player ii and denote oi(s)o_{i}(s) as the observation. As ss contains histories hh, we will write oi(h)=oi(s)o_{i}(h)=o_{i}(s) if h∈sh\in s. A valid observation is such that the function h→(oi(h′))h′⊏hh\rightarrow(o_{i}(h^{\prime}))_{h^{\prime}\sqsubset h} defines a partition of the history space H\mathcal{H} that is a sub-partition of S\mathcal{S}.

In a multiplayer game, we define a per-step reward to player ii for a transition as ri(s,a,s′)r_{i}(s,a,s^{\prime}), with r(s,a,s′)r(s,a,s^{\prime}) representing the vector of returns to all players. In most OpenSpiel games, these r(s,a,s′)=0r(s,a,s^{\prime})=0 until s′s^{\prime} is terminal, ending the episode, and these values are obtained by State::Rewards and State::PlayerReward function called on s′s^{\prime}. Player interaction over an episode generates a trajectory ρ=(s0,a0,s1,⋯ )\rho=(s_{0},a_{0},s_{1},\cdots) whose length is ∣ρ∣|\rho|. We define a return to player ii as gt,iρ=∑t′≥t∣ρ∣−1ri(st′,at′,st′+1)g^{\rho}_{t,i}=\sum_{t^{\prime}\geq t}^{|\rho|-1}r_{i}(s_{t^{\prime}},a_{t^{\prime}},s_{t^{\prime}+1}) with gtρg^{\rho}_{t} representing a vector of rewards to all players as with per-step rewards. In OpenSpiel, the State::Returns function provides g0ρg^{\rho}_{0} and State::PlayerReturn provides g0,iρg^{\rho}_{0,i}. Note that we do not use a discount factor when defining rewards here because most games are episodic; learning agents are free to discount rewards however they like, if necessary. Note also that the standard (undiscounted) return is the random variable GtG_{t}.

2 Algorithms and Results

The trajectories algorithms run a batch of episodes by following a joint policy π\pi, collecting various data such as the states visited, state policies, actions sampled, returns, episode lengths, etc., which could form the basis of the data collection for various RL algorithms.

There is a simple implementation of value iteration. In single-agent games, it is identical to the standard algorithm . In two-player turn-taking zero-sum games, the values for state ss, i.e. V(s)V(s), is stored in view of the player to play at ss, i.e. Vτ(s)(s)V_{\tau(s)}(s). This can be solved by applying the identities V1(s)=−V2(s)V_{1}(s)=-V_{2}(s) and r1(s,a,s′)=−r2(s,a,s′)r_{1}(s,a,s^{\prime})=-r_{2}(s,a,s^{\prime}).

2.2 Search Algorithms

There are two classical search algorithms for zero-sum turn-taking games of perfect information: minimax (and alpha-beta) search , and Monte Carlo tree search (MCTS) .

Suppose one wants to choose at some root state sroots_{root} : given a heuristic value function for v0,i(s)v_{0,i}(s) (representing the value of state ss to player ii) and some depth dd, minimax search computes a policy π(s)\pi(s) that assigns 1 to an action that maximizes the following depth-limited adversarial multistep value backup:

where here we treat T(s,a)=s′\mathcal{T}(s,a)=s^{\prime} as a deterministic map for the successor state reached from taking action aa in state ss.

The Python implementation of minimax includes expectiminimax as well, which also backs up expected values at chance nodes. Alpha-beta style cut-offs could also be applied using ∗*-minimax , but it is not currently implemented.

The implementations of MCTS are vanilla UCT with random playouts. Chance node are supported and represented explicitly in the tree: at chance nodes, the tree policy is always to sample according to the chance node’s probability distibution.

2.3 Optimization Algorithms

OpenSpiel includes some basic optimization algorithms applied to games, such as solving zero-sum matrix games ([72, Section 4], ) and sequence-form linear programming for two-player zero-sum extensive-form games ( and [72, Section 5]), and an algorithm to check whether an action is dominated by a mixture of other strategies in a normal-form [72, Sec 4.5.2].

2.4 Traditional Single-Agent RL Algorithms

We currently have three algorithms usable for traditional (single-agent) RL: Deep Q-Networks (DQN) , Advantage Actor-Critic (A2C) , and Ephemeral Value Adjustments (EVA) . Each algorithm will operate as the standard one in single-agent environments.

Each of these algorithms can also be run in the multiagent setting, in various ways. The default is that each player is independently running a copy of the algorithm with states and observations that include what other players did. The other way to use these algorithms is to compute an approximate best response to a fixed set of other players’ policies, described in Section 3.2.5.

The main difference between the implementations of these algorithms and other standard ones is that these are aware that only a subset of actions are legal / illegal. So, for example, in Q-learning the value update for a transition (s,a,s′)(s,a,s^{\prime}) and policy updates are:

Note that the actions are in the set of legal actions A(s)\mathcal{A}(s) and A(s′)\mathcal{A}(s^{\prime}) rather than assuming that every action is legal at every state. For policy gradient methods, a masked softmax is used to set the logits of the illegal actions to −∞-\infty to force the policy to sets probability zero to illegal actions.

2.5 Partially-Observable (Imperfect Information) Games

There are many algorithms for reinforcement learning in partially-observable (zero-sum) games, as this is the focus of the core team’s research interests.

Suppose π\pi is a joint policy. A best response policy for player ii is a policy that maximized player ii’s return against the other players’ policies (π−i\pi_{-i}). There may be many best responses, and we denote the set of such best responses,

Let δi(π)\delta_{i}(\pi) be the incentive for player ii to deviate to one of its best responses: δi(π)=ui(πib,π−i)−ui(π),\delta_{i}(\pi)=u_{i}(\pi_{i}^{b},\pi_{-i})-u_{i}(\pi), where πib∈BR(π−i)\pi_{i}^{b}\in BR(\pi_{-i}). An approximate ϵ{\bm{\epsilon}}-Nash equilibrium is a joint policy such that δi(π)≤ϵ\delta_{i}(\pi)\leq\epsilon for all i∈Ni\in\mathcal{N}, where a Nash equilibrium is obtained at ϵ=0\epsilon=0.

A common metric for determining the rates of convergence (to equilibria) of algorithms in practice is:

In two-player constant-sum (i.e. kk-sum) games, a similar metric has been used:

where πib∈BR(π−i)\pi_{i}^{b}\in BR(\pi_{-i}). Nash equilibria are often considered optimal in two-player zero-sum games, because they guarantee maximal worst-case returns against any other opponent policy. This is also true for approximate equilibria, so convergence to equilibra has been a focus in this class of games.

Fictitious Play and Best Response-Based Iterative Algorithms

Fictitious play (FP) is a classic iterative procedure for computing policies in (normal-form) games . Starting with a uniform random policy at time t=0t=0. Then, for t∈{1,2,⋯ }t\in\{1,2,\cdots\}, do:

Each player computes a best response to the opponents’ average policy: πit∈BR(πˉ−it−1)\pi^{t}_{i}\in BR(\bar{\pi}^{t-1}_{-i}).

Each player updates their average policy: πˉit=(t−1)πˉit−1+πitt\bar{\pi}^{t}_{i}=\frac{(t-1)\bar{\pi}^{t-1}_{i}+\pi^{t}_{i}}{t}.

OpenSpiel has an implementation of extensive-form fictitious play (XFP) , which is equivalent to the classical fictitious play. To run it on normal-form games, the game needs to be transformed into a turn-based game using TurnBasedSimultaneousGame in game_transforms/. Fictitious Self-Play is a sampled-based RL version of XFP that uses supervised learning to learn the average policy and reinforcement learning to compute approximate best responses. Neural Fictitious Self-Play (NFSP) scales these ideas using neural networks and a reservoir-sampled buffer to maintain a uniform sample of experience to train the average policy .

The average policy in fictitious play can be described equivalently as a meta-policy that assigns uniform weight over all the previous best response policies, and each iteration computes a best response to the opponents’ meta-policies. Policy-Space Response Oracles (PSRO) generalizes fictitious play and the double-oracle algorithm by analyzing this meta-game using empirical game-theoretic analysis . Exploitabiliy Descent replaces the second step of fictitious play with a policy gradient ascent against the state-action values given the opponents play their best responses . This one change allows convergence of the policies themselves rather than having to maintain an average policy; in addition, it makes the optimization of the polices amenable to RL-style general function approximation.

A convergence curve for XFP and ED are shown in Figure 1. A convergence curve for NFSP in 2-player Leduc is found below (Figure 3), included with the policy gradient methods.

Counterfactual regret (CFR) minimization is a policy iteration algorithm for computing approximate equilibra in two-player zero-sum games . It has revolutionized Poker AI research , leading to the largest variants of poker being solved and competitive polices that have beat top human professionals .

CFR does two main things: (a) define a new notion of state-action value, the counterfactual value, and (b) define a decomposed regret minimization procedure (based on these values) at every information state that, together, leads to minimization of overall average regret. This means that the average policy of two CFR players approaches an approximate equilibrium.

Define Z(s)\mathcal{Z}(s) as the set of terminal histories that pass through ss, paired with the prefix of each terminal h⊏zh\sqsubset z. Define a reach probability ηπ(h)\eta^{\pi}(h) to be the product of all players’ probabilities of state-action pairs along hh (including chance’s), which can be decomposed into player ii’s contribution and their opponents’ contributions: ηπ(h)=ηiπ(h)η−iπ(h)\eta^{\pi}(h)=\eta_{i}^{\pi}(h)\eta_{-i}^{\pi}(h). Similarly define ηπ(h,z)\eta^{\pi}(h,z) similarly from hh to zz and haha as the history hh appended with action aa. The counterfactual state-action value for i=τ(s)i=\tau(s) is:

The state value is then vπ,ic(s)=∑h∈sπ(s,a)qπ,ic(s,a)v_{\pi,i}^{c}(s)=\sum_{h\in s}\pi(s,a)q_{\pi,i}^{c}(s,a).

CFR starts with a uniform random policy π0\pi^{0} and proceeds by applying regret minimization at every information state independently. Define rt(s,a)=qπt,ic(s,a)−vπt,ic(s)r^{t}(s,a)=q^{c}_{\pi^{t},i}(s,a)-v^{c}_{\pi^{t},i}(s) to be the instantaneous counterfactual regret. CFR proceeds by minimizing this regret, typically using regret-matching . A table of cumulative regret is maintained Rt(s,a)=∑trt(s,a)R^{t}(s,a)=\sum_{t}r^{t}(s,a), and the policy at each state is updated using:

In addition to basic CFR, OpenSpiel contains a few variants of Monte Carlo CFR such as outcome sampling and external sampling, and CFR+ .

Regression CFR (RCFR) was the first variant to combine RL-style function approximation with CFR techniques . The main idea is to train a regressor to predict the cumulative or average counterfactual regrets, R^t(s,a)≈Rt(s,a)\hat{R}^{t}(s,a)\approx R^{t}(s,a) or rˉ′t(s,a)≈\nicefracRt(s,a)t\bar{r}^{\prime t}(s,a)\approx\nicefrac{{R^{t}(s,a)}}{{t}}, instead of reading them from a table. The original paper used domain-specific features and regression trees. The implementation in OpenSpiel uses neural networks with raw inputs obtained by each game’s InformationSetAsNormalizedVector bit string.

Figure 2 shows the convergence rate of RCFR compared to a tabular CFR.

Deep CFR applies these ideas to a significantly larger game using convolutional networks, external sampling Monte Carlo CFR, and–like NFSP–a reservoir-sampled buffer.

Value-based RL algorithms, such as temporal-difference learning and Q-learning, evaluate a policy π\pi by computing or estimating state (or state-action) values that represent the expected return conditioned on having reached state ss,

Policies are improved by choosing the actions that lead to higher-valued states or higher-valued returns.

In episodic partially-observable games, when agents have perfect recall (Def 5), there is an important connection between traditional values in value-based RL and counterfactual values [75, Section 3.2]:

where β−i(s)=∑h∈sη−iπ(h)\beta_{-i}(s)=\sum_{h\in s}\eta_{-i}^{\pi}(h) is the Bayes normalization term to ensure that Pr⁡(h∣s)\Pr(h|s) is a probability distribution. CFR is then as a (tabular) all-actions policy gradient algorithm with generalized infinitesimal gradient ascent (GIGA) at each state , inspiring new RL variants for partially observable games.

These variants: Q-based “all-actions” Policy Gradient (QPG), Regret Policy Gradients (RPG), and Regret-Matching Policy Gradients (RMGP) are included in OpenSpiel, along with classic batched A2C. RPG differs from QPG in that the policy is optimized toward a no-regret region, minimizing the loss based on r+(s,a)r^{+}(s,a), the motivation being that a policy with zero regret is, by definition, an equilibrium policy. Convergence results for these algorithms are shown in Figure 3.

One practical benefit is that the NeuRD policy updates are not weighted by the policy like policy gradient is. As a result, in non-stationary domains, NeuRD is also more adaptive to changes in the environment. Results for NeuRD are show in Figures 4 and 5.

3 Tools and Evaluation

OpenSpiel has a few tools for visualization and evaluation, though some would also be considered algorithms (such as α\alpha-Rank). The best response algorithm is also a tool in some sense, but is listed in Section 2 due to its association with partially-observable games.

For now, all the tools and evaluation we mention in this section is contained under the python/egt and python/visualizations subdirectories of the code base.

A game tree can be visualized by using Graphviz. An example is shown in Fig 6.

3.2 Visualization of Evolutionary and Policy Learning Dynamics

One common visualization tool in the multiagent learning literature (especially in games) is a phase portrait that shows a vector field and/or trajectories of particle that depict local changes to the policy under specific update dynamics .

For example, consider the well-known single-population replicator dynamic for symmetric games, where each player follows a learning dynamic described by:

where u(a,πt)u(a,\bm{\pi}_{t}) represents the expected utility of playing action aa against the full policy πt\bm{\pi}_{t}, and uˉ(πt)\bar{u}(\bm{\pi}_{t}) is the expected value over all actions ∑a∈Aπt(a)u(a,πt)\sum_{a\in\mathcal{A}}\pi_{t}(a)u(a,\bm{\pi}_{t}).

Figure 7 shows plots generated from OpenSpiel for replicator dynamics in the game of Rock–Paper–Scissors. Figure 8 shows plots generated from OpenSpiel for four common bimatrix games.

3.3 α𝛼\alpha-Rank

α\alpha-Rank is an algorithm that leverages evolutionary game theory to rank AI agents interacting in multiplayer games. Specifically, α\alpha-Rank defines a Markov transition matrix with states corresponding to the profile of agents being used by the players (i.e., tuples of AI agents), and transitions informed by a specific evolutionary model that ensures correspondence of the rankings to a game-theoretic solution concept known as a Markov-Conley Chain. A key benefit of α\alpha-Rank is that it can rank agents in scenarios involving intransitive agent relations (e.g., the agents Rock, Paper, and Scissors in the eponymous game), unlike the Elo rating system ; an additional practical benefit is that it is also tractable to compute in general games, unlike ranking systems relying on Nash equilibria .

OpenSpiel currently supports using α\alpha-Rank for both single-population (symmetric) and multi-population games. Specifically, users may specify games via payoff tables (or tensors for the >2 players case) as well as Heuristic Payoff Tables (HPTs). Note that here we only include an overiew of the technique and visualizations; for a tour through the usage and code please see the α\alpha-Rank doc on the web site.

Figure 9(a) shows a visualization of the Markov transition matrix of α\alpha-Rank run on the Rock, Paper, Scissors game. The next example demonstrates computing α\alpha-Rank on an asymmetric 3-player meta-game, constructed by computing utilities for Kuhn poker agents from the best response policies generated in the first few rounds of via extensive-form fictitious play (XFP) . The result is shown in Figure 9(b).

One may choose to conduct a sweep over the ranking-intensity parameter, α\alpha (as opposed to choosing a fixed α\alpha). This is useful for general games where bounds on utilities may be unknown, and where the ranking computed by α\alpha-Rank should use a sufficiently high value of α\alpha (to ensure correspondence to the underlying Markov-Conley Chain solution concept). In such cases, the following interface can be used to both visualize the sweep and obtain the final rankings computed. The result is shown in Figure 10.

Guide to Contributing

If you are looking for ideas on potential contributions or want to see a rough road map for the future of OpenSpiel, please visit the Roadmap and Call for Contributions on github.

Before making a contribution to OpenSpiel, please read the design philosophy in Section 3. We also kindly request that you contact us before writing any large piece of code, in case (a) we are already working on it and/or (b) it’s something we have already considered and may have some design advice on its implementation. Please also note that some games may have copyrights which could require legal approval(s). Otherwise, happy hacking!

If you would like to contact us regarding anything related to OpenSpiel, please create an issue on the github site so that the team is notified, and so that the responses are visible to everyone.

References