All games, whether represented in extensive or strategic form, are
handled by the C++ classes Game and
GameRep. The classes which represent games
and parts thereof uses the pointer-to-implementation (pimpl) idiom.
Client code will generally refer to objects of type
Game. This is a smart-pointer class containing
a pointer to the representation class GameRep.
For example, to create a new game with an extensive (tree) representation in C++, set its title, and print out that title:
#include <iostream>
#include "libgambit/libgambit.h"
main()
{
Gambit::Game myGame = Gambit::NewTree();
myGame->SetTitle("Hello, World!");
std::cout << myGame->GetTitle() << std::endl;
}
Note that the object myGame has pointer semantics. If it points to
a null or invalid pointer, attempting to dereference it will throw
a C++ exception of class Gambit::NullException.
Assignment of one Game to another also has
pointer semantics; both objects will refer to the same physical game.
Also, objects of class Game handle reference-counting,
which is decremented when the object goes out of scope. Thus, in the
above example, the game created is automatically deallocated when the
last Game referencing it goes away at the end
of main().The parallel code for Python users is
import gambit
myGame = gambit.NewTree()
myGame.SetTitle("Hello, World!")
print myGame.GetTitle()Constructing a game in strategic form requires specifying an initial
dimension for the game. In C++, this is done using an
Array of type int. For
a two-player game where the first player has 2 strategies and the second
player 3 strategies,
Gambit::Array<int> dim(2); dim[1] = 2; dim[2] = 3; Gambit::Game myGame = Gambit::NewTable(dim);In Python, it's a little simpler, since a Python list can be used directly:
myGame = gambit.NewTable([2,3])
As just noted, Gambit games can be expressed in either extensive or
strategic representation. Games in extensive representation can only
be built or modified using functions appropriate to game trees, and
games in strategic representation can only use functions appropriate
to setting the payoffs associated with each strategy vector.
For games in extensive representation, Gambit
can compute the reduced strategic representation. This is done
by calling BuildComputedValues on the
game object. The computed reduced strategy sets are cached internally
until any structural changes in the game tree are made, at which point
the cached values are cleared and BuildComputedValues
must be called again to compute the revised reduced strategic
representation.
Note that the size of the reduced strategic representation
of a game may increase very rapidly, so think carefully about building
the reduced strategic representation for games of nontrivial size;
it is generally recommended to analyze extensive games directly
in the extensive form where possible.
After building a game, it is generally recommended to call
Canonicalize on the game. This function
renumbers the nodes and information sets in the game in a regular way,
such that two identical games will always have their elements numbered
the same way, irrespective of the order of operations used to construct
them.
Games can also be read from a file. A typical C++ usage might be
Gambit::Game myGame;
try {
std::ifstream f("mygame.efg");
myGame = Gambit::ReadGame(f);
}
except (Gambit::InvalidFileException) {
// report the error condition here
}
In the Python version, ReadGame takes a
string with the file contents, not the file object itself, and (currently)
failure results in an RuntimeError:
try:
myGame = gambit.ReadGame(file("mygame.efg").read())
except RuntimeError:
# handle here
ReadGame reads files in the .efg
and .nfg formats described in Chapter 5.
It does not (yet) handle the XML-based .gbt workbook
format recently introduced in the graphical user interface.
However, note that the game structure itself is stored in the
.gbt file using these formats, so it can be extracted
with a little preprocessing.Writing games to these file formats is accomplished by the members
WriteEfgFile and WriteNfgFile,
respectively. This fragment reads in an extensive game, and writes both
a copy of the game in extensive form, as well as its reduced strategic
form, to separate files.
std::ifstream f1("mygame.efg");
Gambit::Game myGame = Gambit::ReadGame(f1);
std::ofstream f2("mygamecopy.efg");
myGame->WriteEfgFile(f2);
myGame->BuildComputedValues(); /* constructs the reduced strategic form */
std::ofstream f3("mygamecopy.nfg");
myGame->WriteNfgFile(f3);
Use in Python again varies slightly. In Python, the members
AsEfgFile and AsNfgFile
create a string representation of the game, which can be written to a
file:
myGame = gambit.ReadGame(file("mygame.efg").read())
file("mygamecopy.efg", "w").write(myGame.AsEfgFile())
file("mygamecopy.nfg", "w").write(myGame.AsNfgFile())
Table 4-2. Classes representing parts of games
| Class | Description |
|---|---|
| GamePlayer | A player in the game (including the chance player) |
| GameOutcome | An outcome, assigning payoffs to all players |
| GameNode | A node in an extensive game |
| GameInfoset | An information set in an extensive game |
| GameStrategy | A strategy for a player in a strategic game |
Table 4-2 lists the classes which represent
objects defined within a game. As with Game,
each of these classes is a smart-pointer class, which references an
internal representation class ending in Rep;
e.g., GamePlayer is a smart pointer pointing to
an object of class GamePlayerRep.
In addition to serving the usual purposes of a smart pointer, these classes also guard against attempting to access objects within a game which no longer exist. Consider this example in Python:
>>> import gambit
>>> myGame = gambit.NewTree()
>>> outcome = myGame.NewOutcome()
>>> myGame.DeleteOutcome(outcome)
>>> outcome.GetLabel()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/usr/lib/python2.4/site-packages/gambit.py", line 1096, in GetLabel
def GetLabel(*args): return _gambit.GameOutcome_GetLabel(*args)
RuntimeError: operating on null object
Here, we create a variable outcome which refers to
an outcome in the game. This outcome is subsequently deleted from the
game. After that point, further attempts to reference the object
outcome result in an exception. The C++ equivalent code
fragment would be
Gambit::Game myGame = Gambit::NewTree(); Gambit::GameOutcome outcome = myGame->NewOutcome(); myGame->DeleteOutcome(outcome); std::string label = outcome->GetLabel();The last line would raise an exception of type
Gambit::NullException.