Skip to content

Commit f77e4b2

Browse files
pi-games:0.1.0 (#5212)
1 parent a3b19a7 commit f77e4b2

9 files changed

Lines changed: 1244 additions & 0 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Piotr Kuszewski
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# pi-games
2+
3+
Typesetting tools for **game theory** in Typst: draw **normal-form payoff
4+
matrices** and **extensive-form game trees** with consistent per-player
5+
colours across both representations.
6+
7+
`pi-games` is an umbrella module that bundles three libraries — a shared colour
8+
palette, normal-form tables, and extensive-form trees (built on
9+
[CeTZ](https://github.com/cetz-package/cetz)) — behind a single import.
10+
11+
**Normal form — Prisoner's Dilemma**
12+
13+
![Prisoner's Dilemma normal form](assets/prisoners-dilemma.png)
14+
15+
**Extensive form — Beer–Quiche signalling game**
16+
17+
![Beer–Quiche signalling game](assets/beer-quiche.png)
18+
19+
## Usage
20+
21+
```typst
22+
#import "@preview/pi-games:0.1.0": *
23+
```
24+
25+
Extensive-form trees are drawn inside a `cetz.canvas` block, so import CeTZ as
26+
well when you need them:
27+
28+
```typst
29+
#import "@preview/cetz:0.5.2" as cetz
30+
#import "@preview/pi-games:0.1.0": *
31+
```
32+
33+
### Normal form
34+
35+
`game-normal-form` covers the same kind of normal-form games as the
36+
[`game-theoryst`](https://typst.app/universe/package/game-theoryst/) Typst
37+
package by Connor T. Wiegand — players, strategy lists, a payoff matrix and
38+
per-player best-response underlines — but with its own CeTZ-based API: payoffs
39+
are a single nested array and best responses are passed as `(row, col)`
40+
coordinate lists, rather than `game-theoryst`'s table cells with inline
41+
`hul()`/`vul()` markers.
42+
43+
`game-normal-form` returns ready-to-place content (no canvas needed). The
44+
Prisoner's Dilemma above is produced by:
45+
46+
```typst
47+
#game-normal-form(
48+
[Prisoner 1], [Prisoner 2],
49+
([C], [D]),
50+
([C], [D]),
51+
(
52+
(([$3$], [$3$]), ([$0$], [$5$])),
53+
(([$5$], [$0$]), ([$1$], [$1$])),
54+
),
55+
p1-best: ((1, 0), (1, 1)), // underline Player 1 best responses
56+
p2-best: ((0, 1), (1, 1)), // underline Player 2 best responses
57+
nash: ((1, 1),), // outline the Nash equilibrium cell
58+
)
59+
```
60+
61+
Use `game-three-player-normal-form` for three-player games, rendered as one
62+
sub-matrix per Player 3 strategy.
63+
64+
### Extensive form
65+
66+
The tree style replicates the look of the
67+
[`xgames`](https://carlabernard.ch/beni/downloads/xgames.pdf) LaTeX package by
68+
Benjamin Bernard — per-player coloured node labels, action labels and payoff
69+
vectors — reimplemented natively in Typst/CeTZ.
70+
71+
Build trees from nodes, branches, terminals and information sets inside a
72+
`cetz.canvas`. The Beer–Quiche signalling game above uses `game-nature`,
73+
`game-node`, `game-branch`, `game-prob`, `game-terminal` and bracket-style
74+
`game-infoset` calls. A minimal tree:
75+
76+
```typst
77+
#cetz.canvas({
78+
import cetz.draw: *
79+
game-branch((0, 0), (-1.5, -2), action: [Out], player: 1, side: "w")
80+
game-terminal((-1.5, -2), payoffs: ([0], [2]))
81+
game-branch((0, 0), (1.5, -2), action: [In], player: 1, side: "e")
82+
game-terminal((1.5, -2), payoffs: ([1], [1]))
83+
game-node((0, 0), player: 1, label: [Player 1], la: "n")
84+
})
85+
```
86+
87+
## Public API
88+
89+
**Normal form**
90+
91+
| Function | Description |
92+
|---|---|
93+
| `game-normal-form(p1, p2, s1, s2, payoffs, ...)` | N×M payoff matrix with coloured payoffs, best-response underlines and Nash highlights |
94+
| `game-three-player-normal-form(p1, p2, p3, s1, s2, s3, payoffs, ...)` | Three-player game as a row of N×M sub-matrices |
95+
96+
**Extensive form**
97+
98+
| Function | Description |
99+
|---|---|
100+
| `game-node(pos, player, label, ...)` | Decision node (filled, open, or dot style) |
101+
| `game-nature(pos, label, ...)` | Nature / chance node (grey) |
102+
| `game-terminal(pos, payoffs, ...)` | Terminal node with a coloured payoff vector |
103+
| `game-branch(from, to, action, ...)` | Branch with an optional action label |
104+
| `game-prob(from, to, action, ...)` | Nature-branch probability label (wrapper around `game-branch`) |
105+
| `game-infoset(...pts, player, style, ...)` | Information set, `"dashed"` or `"bracket"` style |
106+
| `game-subgame(apex, depth, width, ...)` | Proper-subgame triangle marker |
107+
| `game-highlight(from, to, color, ...)` | Bold overlay marking an equilibrium path |
108+
| `game-payoffs(payoffs, parens)` | Inline coloured payoff vector for body text |
109+
| `game-player(player, label)` / `game-player-default(player)` | Player name in the player's colour |
110+
111+
## Styling
112+
113+
All colours and geometry are plain `let` bindings exposed by the package and can
114+
be overridden after import — e.g. the player palette `game-pal`, the Nash
115+
outline `game-nash-color`, the highlight colour `game-highlight-color`, and tree
116+
geometry such as `game-node-radius`, `game-terminal-radius`, `game-gap`, …
117+
118+
```typst
119+
#import "@preview/pi-games:0.1.0": *
120+
#let game-pal = ( // custom player colours
121+
rgb("#005f73"), rgb("#94d2bd"), rgb("#e9d8a6"),
122+
rgb("#ee9b00"), rgb("#ae2012"),
123+
)
124+
```
125+
126+
## Dependencies
127+
128+
- [`@preview/cetz`](https://typst.app/universe/package/cetz) `0.5.2` (requires Typst ≥ 0.14.0)
129+
130+
## Acknowledgements
131+
132+
- The normal-form games cover the same ground as the
133+
[`game-theoryst`](https://typst.app/universe/package/game-theoryst/) Typst
134+
package by Connor T. Wiegand, reimplemented here with a different,
135+
CeTZ-based API.
136+
- The extensive-form trees replicate the visual style of the
137+
[`xgames`](https://carlabernard.ch/beni/downloads/xgames.pdf) LaTeX package by
138+
Benjamin Bernard. The original colour-coded design is his; `pi-games` only
139+
reimplements that look in Typst/CeTZ. Full credit for the design goes to him.
140+
141+
## License
142+
143+
MIT © Piotr Kuszewski — see [`LICENSE`](LICENSE).
46.9 KB
Loading
15.2 KB
Loading
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// pi-games.typ — Umbrella module bundling all pi-game libraries.
2+
// Piotr Kuszewski · SGH Warsaw School of Economics
3+
//
4+
// Import once to get the shared palette, normal-form tables, and
5+
// extensive-form game trees in a single namespace:
6+
// #import "@preview/pi-games:0.1.0": *
7+
//
8+
// Bundles (in src/):
9+
// · pi-game-palette.typ — shared colour palette (single source of truth)
10+
// · pi-game-normal.typ — normal-form / strategic-form game tables
11+
// · pi-game-trees.typ — extensive-form game trees (CeTZ)
12+
//
13+
// Only the documented public API is re-exported. Each name is imported
14+
// from a single canonical source to avoid duplicate bindings. Internal
15+
// helpers are deliberately NOT re-exported (see the exclusion note below),
16+
// and `cetz` is not re-exported — import it directly where a `cetz.canvas`
17+
// block is needed: #import "@preview/cetz:0.5.2" as cetz
18+
19+
// ── Shared colour palette (canonical source of all colours) ───────────
20+
#import "src/pi-game-palette.typ": (
21+
game-player-colors,
22+
game-nature-color,
23+
game-fg,
24+
game-nash-color,
25+
game-highlight-color,
26+
game-infoset-color,
27+
game-subgame-stroke,
28+
game-subgame-fill,
29+
game-subgame-label,
30+
)
31+
32+
// ── Normal-form games: functions + normal-form style/geometry config ──
33+
#import "src/pi-game-normal.typ": (
34+
game-normal-form,
35+
game-three-player-normal-form,
36+
game-pal,
37+
game-cell-width,
38+
game-cell-height,
39+
game-games-per-row,
40+
)
41+
42+
// ── Extensive-form trees: functions + tree-only geometry/style config ─
43+
#import "src/pi-game-trees.typ": (
44+
// drawing functions
45+
game-node,
46+
game-nature,
47+
game-terminal,
48+
game-branch,
49+
game-prob,
50+
game-infoset,
51+
game-subgame,
52+
game-highlight,
53+
// inline text helpers
54+
game-payoffs,
55+
game-player,
56+
game-player-default,
57+
// geometry constants
58+
game-node-radius,
59+
game-terminal-radius,
60+
game-gap,
61+
game-act,
62+
game-apos,
63+
game-tick,
64+
// stroke widths
65+
game-sw-b,
66+
game-sw-n,
67+
game-sw-ni,
68+
game-sw-i,
69+
game-sw-h,
70+
// font sizes
71+
game-fsl,
72+
game-fsa,
73+
game-fsp,
74+
)
75+
76+
// Deliberately excluded internal helpers (private to pi-game-trees.typ,
77+
// not part of the public API): _gc, _gD, _gA, _vs, _va, _vm, _vn,
78+
// _vpl, _vpr, _gvanch, _gname. They are not listed above, so a wildcard
79+
// `#import "@preview/pi-games:0.1.0": *` will not expose them.

0 commit comments

Comments
 (0)