Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
8c19a37
feat: Add sphinxcontrib.tikz extension to the documentation build con…
edwardchalstrey1 Mar 11, 2026
ce211bd
Add quotes
edwardchalstrey1 Mar 11, 2026
ef0ed7f
Merge branch 'dev-install-fix' into catalog/744
edwardchalstrey1 Mar 11, 2026
668d743
Add visualization column for tikz code
edwardchalstrey1 Mar 11, 2026
2868d5f
use gambit colour scheme
edwardchalstrey1 Mar 11, 2026
4393212
one column for game
edwardchalstrey1 Mar 11, 2026
f3f30ec
remove superfluous game slug line
edwardchalstrey1 Mar 11, 2026
329ce21
change dt settings
edwardchalstrey1 Mar 11, 2026
8f3ab62
fix rendering
edwardchalstrey1 Mar 11, 2026
d5ae404
combine description and download column
edwardchalstrey1 Mar 11, 2026
8ce94ec
move game slug above image
edwardchalstrey1 Mar 11, 2026
71813ac
Add TEX and PDF downloads
edwardchalstrey1 Mar 11, 2026
49dd5d9
make download links consistently named
edwardchalstrey1 Mar 11, 2026
46904af
Move description under download links
edwardchalstrey1 Mar 11, 2026
aabdee6
Add python code and download dropdown
edwardchalstrey1 Mar 11, 2026
862b891
update widths and table headers
edwardchalstrey1 Mar 11, 2026
72bae1f
long desc
edwardchalstrey1 Mar 11, 2026
346c67b
Make sure title displays properly
edwardchalstrey1 Mar 11, 2026
196b6bd
put code under a pygambit dropdown
edwardchalstrey1 Mar 11, 2026
b24208d
make game title bold
edwardchalstrey1 Mar 11, 2026
d953eb3
rename details and description
edwardchalstrey1 Mar 11, 2026
bc17cb0
update gitignore
edwardchalstrey1 Mar 11, 2026
45fb000
Merge branch 'master' into catalog/744
edwardchalstrey1 Mar 11, 2026
463eee3
Merge branch 'catalog/758' into catalog/744
edwardchalstrey1 Mar 11, 2026
b7e58ff
revert changes to update_makefile
edwardchalstrey1 Mar 11, 2026
82096e4
update gitignore
edwardchalstrey1 Mar 11, 2026
6805a4a
add generate_png
edwardchalstrey1 Mar 11, 2026
a7411ae
add imagemagick to rtd build
edwardchalstrey1 Mar 11, 2026
d20d5ae
update draw_tree version
edwardchalstrey1 Mar 11, 2026
d88816e
update comment
edwardchalstrey1 Mar 11, 2026
19cb753
Implement efficient subgame root detection via interval reachability …
d-kad Mar 12, 2026
43ba14c
Bump actions/upload-artifact from 6 to 7 (#790)
dependabot[bot] Mar 12, 2026
3da1207
single column table
edwardchalstrey1 Mar 12, 2026
abcf67f
feat: integrate game titles into open dropdowns and clarify PyGambit …
edwardchalstrey1 Mar 12, 2026
e090dc9
combine the description and pygambit code in one dropdown
edwardchalstrey1 Mar 12, 2026
d479b5c
one dropdown
edwardchalstrey1 Mar 12, 2026
fcd482a
finalise table structure and fix pygambit code
edwardchalstrey1 Mar 12, 2026
d2ca652
Consistent size catalog images
edwardchalstrey1 Mar 12, 2026
d7463ea
Align images and text to centre
edwardchalstrey1 Mar 12, 2026
55cbe39
keep text align left
edwardchalstrey1 Mar 12, 2026
dbd6b80
Refactor catalog update to conditionally generate game visualizations…
edwardchalstrey1 Mar 13, 2026
f556b2b
fix regex to extract tikz correctly
edwardchalstrey1 Mar 13, 2026
1551660
Pass `force_build` argument to `generate_rst_table` and add a print s…
edwardchalstrey1 Mar 13, 2026
ef1f8c8
Commit catalog images
edwardchalstrey1 Mar 13, 2026
66e0646
Allow ef download and commit ef files, update gitignore
edwardchalstrey1 Mar 13, 2026
bfb049c
Add key step to catalog update instructions
edwardchalstrey1 Mar 13, 2026
8fa24f6
Merge branch 'clarify-catalog-build-instructions' into catalog/744
edwardchalstrey1 Mar 13, 2026
c77efe0
Separate build and regenerate-images flags
edwardchalstrey1 Mar 13, 2026
8402abf
remove print statement
edwardchalstrey1 Mar 13, 2026
04e4656
move draw_tree_args out of func and document
edwardchalstrey1 Mar 13, 2026
244cf97
tidy
edwardchalstrey1 Mar 13, 2026
c94e408
use jupyter for rendering normal form games
edwardchalstrey1 Mar 13, 2026
0f84618
fix jupyter code indentation
edwardchalstrey1 Mar 13, 2026
4b7bd42
Separate extensive and strategic form games into different tables
edwardchalstrey1 Mar 13, 2026
4e43389
restore ef to download links
edwardchalstrey1 Mar 13, 2026
6d65ff4
remove title duplication
edwardchalstrey1 Mar 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/osxbinary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- run: make
- run: sudo make install
- run: make osx-dmg
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v7
with:
name: artifact-osx-14
path: "*.dmg"
2 changes: 1 addition & 1 deletion .github/workflows/tools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
cp gambit* installer
"${WIX}bin/candle" build_support/msw/gambit.wxs
"${WIX}bin/light" -ext WixUIExtension gambit.wixobj
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v7
with:
name: artifact-msw
path: "*.msi"
2 changes: 1 addition & 1 deletion .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ jobs:
python -m cibuildwheel --output-dir wheelhouse/
env:
CIBW_SKIP: "pp*"
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v7
with:
path: ./wheelhouse/*.whl
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,10 @@ dist
*.dmg
Gambit.app/*
*.so
doc/tutorials/games/*.nfg
doc/tutorials/games/*.efg
doc/tutorials/*.png
*.dmg
Gambit.app/*
*.ipynb_checkpoints
*.ef
doc/**/*.ef
build_support/msw/gambit.wxs
build_support/osx/Info.plist
src/pygambit/catalog
Expand Down
3 changes: 2 additions & 1 deletion .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ build:
- libgmp-dev
- pandoc
- texlive-full
- imagemagick
jobs:
# Create CSV for catalog table in docs
# Create RST for catalog table in docs
post_install:
- $READTHEDOCS_VIRTUALENV_PATH/bin/python build_support/catalog/update.py

Expand Down
8 changes: 7 additions & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@
### Changed
- `Game.comment` has been renamed to `Game.description`

### Added
- Implement linear-time algorithm to find all root nodes of proper subgames, using an adaptation of
Tarjan's (1974) algorithm for finding bridges in an undirected graph. Subgame roots are cached so
subsequent lookup is constant-time (if the game is unchanged). (#584)

### Fixed
- `enumpoly` would take a very long time on some supports where an equilibrium is located on the
boundary of the projected game. Search is now restricted to the interior of the space ruling
these out; these will always be found by another projection. (#756)
- In the graphical interface, the logit correspondence display would fail and terminate the program
on very small (<10^{-300}) probabilities.
- The new subgame root computation fixes a bug which failed to detect subgames where the subgame
root node is a member of an absent-minded infoset. (#584)

## [16.5.1] - unreleased

### Fixed
- `Game.reveal` raised a null pointer access exception or dumped core in some cases (#749)


## [16.5.0] - 2026-01-05

### Fixed
Expand Down
152 changes: 120 additions & 32 deletions build_support/catalog/update.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,136 @@
import argparse
import re
from pathlib import Path

import pandas as pd
from draw_tree import generate_pdf, generate_png, generate_tex

import pygambit as gbt

CATALOG_RST_TABLE = Path(__file__).parent.parent.parent / "doc" / "catalog_table.rst"
CATALOG_DIR = Path(__file__).parent.parent.parent / "catalog"
MAKEFILE_AM = Path(__file__).parent.parent.parent / "Makefile.am"

# Common arguments for visualization generation
draw_tree_args = {
"color_scheme": "gambit",
"sublevel_scaling": 0,
"shared_terminal_depth": True,
}


def _write_efg_table(df: pd.DataFrame, f, tikz_re, regenerate_images: bool):
"""Write the EFG games list-table to file handle f."""
f.write(".. list-table::\n")
f.write(" :header-rows: 1\n")
f.write(" :widths: 100\n")
f.write(" :class: tight-table\n")
f.write("\n")
f.write(" * - **Extensive form games**\n")

efg_df = df[df["Format"] == "efg"]
for _, row in efg_df.iterrows():
slug = row["Game"]
title = str(row.get("Title", "")).strip()
description = str(row.get("Description", "")).strip()

tex_path = CATALOG_DIR / "img" / f"{slug}.tex"
if regenerate_images or not tex_path.exists():
g = gbt.catalog.load(slug)
viz_path = CATALOG_DIR / "img" / f"{slug}"
viz_path.parent.mkdir(parents=True, exist_ok=True)
for func in [generate_tex, generate_png, generate_pdf]:
func(g, save_to=str(viz_path), **draw_tree_args)

with open(tex_path, encoding="utf-8") as tex_f:
tex_content = tex_f.read()
match = tikz_re.search(tex_content)
tikz = match.group(1).strip() if match else "% Could not extract tikzpicture from tex file"

# Main dropdown
f.write(f" * - .. dropdown:: {title}\n")
f.write(" \n")
if description:
for line in description.splitlines():
f.write(f" {line}\n")
f.write(" \n")
f.write(" **Load in PyGambit:**\n")
f.write(" \n")
f.write(" .. code-block:: python\n")
f.write(" \n")
f.write(f' pygambit.catalog.load("{slug}")\n')
f.write(" \n")

# Download links (inside the dropdown)
download_links = [row["Download"]]
for ext in ["efg", "ef", "tex", "png", "pdf"]:
download_links.append(f":download:`{slug}.{ext} <../catalog/img/{slug}.{ext}>`")
f.write(" **Download game and image files:**\n")
f.write(" \n")
f.write(f" {' '.join(download_links)}\n")
f.write(" \n")

# TiKZ image (outside dropdown)
f.write(" .. tikz::\n")
f.write(" :align: center\n")
f.write(" \n")
for line in tikz.splitlines():
f.write(f" {line}\n")
f.write(" \n")


def _write_nfg_table(df: pd.DataFrame, f):
"""Write the NFG games list-table to file handle f."""
f.write(".. list-table::\n")
f.write(" :header-rows: 1\n")
f.write(" :widths: 100\n")
f.write(" :class: tight-table\n")
f.write("\n")
f.write(" * - **Strategic form games**\n")

nfg_df = df[df["Format"] == "nfg"]
for _, row in nfg_df.iterrows():
slug = row["Game"]

# Title as plain text header
f.write(" * - \n")
f.write(" \n")

# Jupyter-execute block (no dropdown)
f.write(" .. jupyter-execute::\n")
f.write(" \n")
f.write(" import pygambit\n")
f.write(f' pygambit.catalog.load("{slug}")\n')
f.write(" \n")

# Download link (plain, no dropdown)
f.write(f" :download:`{slug}.nfg <../catalog/{slug}.nfg>`\n")
f.write(" \n")


def generate_rst_table(df: pd.DataFrame, rst_path: Path, regnerate_images: bool = False):
"""Generate RST output with two list-tables: one for EFG and one for NFG games."""
tikz_re = re.compile(r"\\begin\{document\}(.*?)\\end\{document\}", re.DOTALL)

def generate_rst_table(df: pd.DataFrame, rst_path: Path):
"""Generate a list-table RST file with dropdowns for long descriptions."""
with open(rst_path, "w", encoding="utf-8") as f:
f.write(".. list-table::\n")
f.write(" :header-rows: 1\n")
f.write(" :widths: 20 80 20\n")
f.write(" :class: tight-table\n")
# TOC linking to both sections
f.write(".. contents::\n")
f.write(" :local:\n")
f.write(" :depth: 1\n")
f.write("\n")

f.write(" * - **Game**\n")
f.write(" - **Description**\n")
f.write(" - **Download**\n")

for _, row in df.iterrows():
f.write(f" * - {row['Game']}\n")

description_cell_lines = []
title = str(row.get("Title", "")).strip()
description = str(row.get("Description", "")).strip()
if description:
description_cell_lines.append(f".. dropdown:: {title}")
description_cell_lines.append(" ") # Indented blank line
for line in description.splitlines():
description_cell_lines.append(f" {line}")
else:
description_cell_lines.append(title)

f.write(f" - {description_cell_lines[0]}\n")
for line in description_cell_lines[1:]:
f.write(f" {line}\n")
# EFG section
f.write("Extensive form games\n")
f.write("--------------------\n")
f.write("\n")
_write_efg_table(df, f, tikz_re, regnerate_images)
f.write("\n")

f.write(f" - {row['Download']}\n")
# NFG section
f.write("Strategic form games\n")
f.write("--------------------\n")
f.write("\n")
_write_nfg_table(df, f)


def update_makefile():
Expand Down Expand Up @@ -96,16 +185,15 @@ def update_makefile():


if __name__ == "__main__":

parser = argparse.ArgumentParser()
parser.add_argument("--build", action="store_true")
parser.add_argument("--regenerate-images", action="store_true")
args = parser.parse_args()

# Create RST list-table used by doc/catalog.rst
df = gbt.catalog.games(include_descriptions=True)
generate_rst_table(df, CATALOG_RST_TABLE)
generate_rst_table(df, CATALOG_RST_TABLE, regnerate_images=args.regenerate_images)
print(f"Generated {CATALOG_RST_TABLE} for use in local docs build. DO NOT COMMIT.")

# Update the Makefile.am with the current list of catalog files
if args.build:
# Update the Makefile.am with the current list of catalog files
update_makefile()
3 changes: 2 additions & 1 deletion catalog/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ def append_record(
record["Description"] = game.description
ext = "efg" if game.is_tree else "nfg"
record["Download"] = f":download:`{slug}.{ext} <../catalog/{slug}.{ext}>`"
record["Format"] = ext
records.append(record)

# Add all the games stored as EFG/NFG files
Expand Down Expand Up @@ -186,7 +187,7 @@ def append_record(

if include_descriptions:
return pd.DataFrame.from_records(
records, columns=["Game", "Title", "Description", "Download"]
records, columns=["Game", "Title", "Description", "Download", "Format"]
)
return pd.DataFrame.from_records(records, columns=["Game", "Title"])

Expand Down
38 changes: 38 additions & 0 deletions catalog/img/2smp.ef
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
player 1 name Player~1
player 2 name Player~2
level -2.0 node 1 player 1
level 2.0 node 1 xshift -8.0 from -2.0,1 move H
level 6.0 node 1 player 1 xshift -4.0 from 2.0,1 move H
level 10.0 node 1 xshift -2.0 from 6.0,1 move H
level 14.0 node 1 xshift -1.0 from 10.0,1 move H payoffs 1 -1
level 14.0 node 2 xshift 1.0 from 10.0,1 move T payoffs -1 1
level 10.0 node 2 xshift 2.0 from 6.0,1 move T
level 14.0 node 3 xshift -1.0 from 10.0,2 move H payoffs -1 1
level 14.0 node 4 xshift 1.0 from 10.0,2 move T payoffs 1 -1
level 6.0 node 2 player 1 xshift 4.0 from 2.0,1 move T
level 10.0 node 3 xshift -2.0 from 6.0,2 move H
level 14.0 node 5 xshift -1.0 from 10.0,3 move H payoffs 1 -1
level 14.0 node 6 xshift 1.0 from 10.0,3 move T payoffs -1 1
level 10.0 node 4 xshift 2.0 from 6.0,2 move T
level 14.0 node 7 xshift -1.0 from 10.0,4 move H payoffs -1 1
level 14.0 node 8 xshift 1.0 from 10.0,4 move T payoffs 1 -1
level 2.0 node 2 xshift 8.0 from -2.0,1 move T
level 6.0 node 3 player 1 xshift -4.0 from 2.0,2 move H
level 10.0 node 5 xshift -2.0 from 6.0,3 move H
level 14.0 node 9 xshift -1.0 from 10.0,5 move H payoffs 1 -1
level 14.0 node 10 xshift 1.0 from 10.0,5 move T payoffs -1 1
level 10.0 node 6 xshift 2.0 from 6.0,3 move T
level 14.0 node 11 xshift -1.0 from 10.0,6 move H payoffs -1 1
level 14.0 node 12 xshift 1.0 from 10.0,6 move T payoffs 1 -1
level 6.0 node 4 player 1 xshift 4.0 from 2.0,2 move T
level 10.0 node 7 xshift -2.0 from 6.0,4 move H
level 14.0 node 13 xshift -1.0 from 10.0,7 move H payoffs 1 -1
level 14.0 node 14 xshift 1.0 from 10.0,7 move T payoffs -1 1
level 10.0 node 8 xshift 2.0 from 6.0,4 move T
level 14.0 node 15 xshift -1.0 from 10.0,8 move H payoffs -1 1
level 14.0 node 16 xshift 1.0 from 10.0,8 move T payoffs 1 -1
iset 2.0,1 2.0,2 player 2
iset 10.0,1 10.0,2 player 2
iset 10.0,3 10.0,4 player 2
iset 10.0,5 10.0,6 player 2
iset 10.0,7 10.0,8 player 2
Binary file added catalog/img/2smp.pdf
Binary file not shown.
Binary file added catalog/img/2smp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading