First examples
One can run an example of a game running (in the build/
folder):
./examples/example --game=tic_tac_toe
Similar examples using the Python API (run from one above build
):
# Similar to the C++ example:
python3 open_spiel/python/examples/example.py --game_string=breakthrough
# Play a game against a random or MCTS bot:
python3 open_spiel/python/examples/mcts.py --game=tic_tac_toe --player1=human --player2=random
python3 open_spiel/python/examples/mcts.py --game=tic_tac_toe --player1=human --player2=mcts
Concepts
The following documentation describes the high-level concepts. Refer to the code comments for specific API descriptions.
Note that, in English, the word “game” is used for both the description of the rules (e.g. the game of chess) and for a specific instance of a playthrough (e.g. “we played a game of chess yesterday”). We will be using “playthrough” or “trajectory” to refer to the second concept.
The methods names are in CamelCase
in C++ and snake_case
in Python without
any other difference (e.g. state.ApplyAction
in C++ will be
state.apply_action
in Python).
The tree representation
There are mainly 2 concepts to know about (defined in open_spiel/spiel.h):
A
Game
object contains the high level description for a game (e.g. whether it is simultaneous or sequential, the number of players, the maximum and minimum scores).A
State
, which describe a specifics point (e.g. a specific board position in chess, a specific set of player cards, public cards and past bets in Poker) within a trajectory.
All possible trajectories in a game are represented as a tree. In this tree, a
node is a State
and is associated to a specific history of moves for all
players. Transitions are actions taken by players (in case of a simultaneous
node, the transition is composed of the actions for all players).
Note that in most games, we deal with chance (i.e. any source of randomness)
using a an explicit player (the “chance” player, which has id
kChancePlayerId
). For example, in Poker, the root state would just be the
players without any cards, and the first transitions will be chance nodes to
deal the cards to the players (in practice once card is dealt per transition).
See spiel.h
for the full API description. For example,
game.NewInitialState()
will return the root State
. Then,
state.LegalActions()
can be used to get the possible legal actions and
state.ApplyAction(action)
can be used to update state
in place to play the
given action
(use state.Child(action)
to create a new state and apply the
action to it).
Loading a game
The games are all implemented in C++ in open_spiel/games.
Available games names can be listed using RegisteredNames()
.
A game can be created from its name and its arguments (which usually have defaults). There are 2 ways to create a game:
Using the game name and a structured
GameParameters
object (which, in Python, is a dictionary from argument name to compatible types (int, bool, str or a further dict). e.g.{"players": 3}
withLoadGame
.Using a string representation such as
kuhn_poker(players=3)
, givingLoadGame(kuhn_poker(players=3))
. Seeopen_spiel/game_parameters.cc
for the exact syntax.
Creating sequential games from simultaneous games
It is possible to apply generic game transformations (see
open_spiel/game_transforms/) such as loading an n
-players
simultaneous games into an equivalent turn-based game where simultaneous moves
are encoded as n
turns.
One can use LoadGameAsTurnBased(game)
, or use the string representation, such
as
turn_based_simultaneous_game(game=goofspiel(imp_info=True,num_cards=4,points_order=descending))
.
Playing a trajectory
Here are for example the Python code to play one trajectory:
import random
import pyspiel
import numpy as np
game = pyspiel.load_game("kuhn_poker")
state = game.new_initial_state()
while not state.is_terminal():
legal_actions = state.legal_actions()
if state.is_chance_node():
# Sample a chance event outcome.
outcomes_with_probs = state.chance_outcomes()
action_list, prob_list = zip(*outcomes_with_probs)
action = np.random.choice(action_list, p=prob_list)
state.apply_action(action)
else:
# The algorithm can pick an action based on an observation (fully observable
# games) or an information state (information available for that player)
# We arbitrarily select the first available action as an example.
action = legal_actions[0]
state.apply_action(action)
See open_spiel/python/examples/example.py for a more thorough example that covers more use of the core API.
See open_spiel/python/examples/playthrough.py (and open_spiel/python/algorithms/generate_playthrough.py) for an richer example generating a playthrough and printing all available information.
In C++, see open_spiel/examples/example.cc which generates random trajectories.