GameTheory.jl
GameTheory.jl is a Julia package about algorithms and data structures for Game Theory.
Installation
To install the package, enter the Pkg mode by pressing ] and run
add GameTheoryUsage
Once installed, the GameTheory package can be used by typing
using GameTheoryCreating a game
The Base type Player can be created by passing a payoff matrix:
player1 = Player([3 1; 0 2])2×2 Player{2, Int64}:
3 1
0 2Here the rows of the payoff matrix correspond to player 1's own actions and the columns to the opponent's actions.
A 2-player NormalFormGame can be created either by passing Player instances,
player2 = Player([2 0; 1 3])
g = NormalFormGame((player1, player2))2×2 NormalFormGame{2, Int64}:
(3, 2) (1, 1)
(0, 0) (2, 3)or by passing an array of tuples representing payoff profiles:
g = NormalFormGame([(3, 2) (1, 1)
(0, 0) (2, 3)])2×2 NormalFormGame{2, Int64}:
(3, 2) (1, 1)
(0, 0) (2, 3)or by passing a payoff matrix directly:
payoff_bimatrix = Array{Int}(undef, 2, 2, 2)
payoff_bimatrix[1, 1, :] = [3, 2]
payoff_bimatrix[1, 2, :] = [1, 1]
payoff_bimatrix[2, 1, :] = [0, 0]
payoff_bimatrix[2, 2, :] = [2, 3]
g = NormalFormGame(payoff_bimatrix)2×2 NormalFormGame{2, Int64}:
(3, 2) (1, 1)
(0, 0) (2, 3)Payoff array conventions
Each player's payoff_array is indexed with the player's own action first: for player i in an N-player game, the first axis corresponds to player i's own actions, and the j-th axis, j = 2, ..., N, to the actions of player i+j-1 (modulo N). In the 2-player game g constructed above,
player1.payoff_array[1, 2] == 1 && player2.payoff_array[2, 1] == 1trueboth give the payoffs under the action profile in which player 1 plays action 1 and player 2 plays action 2 — each player's array is indexed with that player's own action first. Similarly, in a 3-player game, g.players[2].payoff_array[a2, a3, a1] is player 2's payoff under the action profile (a1, a2, a3).
Computing Nash equilibria
After constructing a NormalFormGame, we can find its Nash equilibria by using methods of GameTheory. For example, pure_nash finds all pure-action Nash equilibria by enumeration:
pure_nash(g)2-element Vector{Tuple{Int64, Int64}}:
(1, 1)
(2, 2)The game also has a mixed-action Nash equilibrium: vertex_enumeration finds all Nash equilibria of a two-player nondegenerate game, pure and mixed:
vertex_enumeration(g)3-element Vector{Tuple{Vector{Float64}, Vector{Float64}}}:
([1.0, 0.0], [1.0, 0.0])
([0.0, 1.0], [0.0, 1.0])
([0.75, 0.25], [0.25, 0.75])See Computing Nash Equilibria for the other solvers available.
Defining a game by its payoff function
A game can also be defined by a function that computes the payoffs lazily, rather than by arrays that store them. Define a subtype of AbstractNormalFormGame{N,T}, for N players and payoffs of type T, and implement two functions for it: GameTheory.nums_actions, returning the numbers of actions of the players, and GameTheory.payoff_profile, returning the payoffs at an action profile. For Matching Pennies,
struct MatchingPennies <: AbstractNormalFormGame{2,Int} end
GameTheory.nums_actions(::MatchingPennies) = (2, 2)
GameTheory.payoff_profile(::MatchingPennies, a) = a[1] == a[2] ? (1, -1) : (-1, 1)
mp = MatchingPennies()Such a game is accepted by most functions that take a NormalFormGame:
support_enumeration(mp)1-element Vector{Tuple{Vector{Float64}, Vector{Float64}}}:
([0.5, 0.5], [0.5, 0.5])Its payoff arrays are obtained by tabulation when needed:
NormalFormGame(mp)2×2 NormalFormGame{2, Int64}:
(1, -1) (-1, 1)
(-1, 1) (1, -1)Notebooks
Some notebooks for demonstration are available: