Skip to content

Commit 0b30460

Browse files
Catalog/761 (#793)
* add failing test for games(include_descriptions=True) which gets used in update script * Add description field to df when selected * refactor to reduce code duplication * correctly show descriptions on catalog page * Add biblio links to all games in catalog * fix biblio links * update table row spacing * hide samples page * Add download links * generate rst table instead of CSV for Catalog of games page * Refactor table such that descriptions are nested under titles
1 parent 09f2a33 commit 0b30460

11 files changed

Lines changed: 93 additions & 33 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,4 @@ Gambit.app/*
5151
build_support/msw/gambit.wxs
5252
build_support/osx/Info.plist
5353
src/pygambit/catalog
54-
doc/catalog.csv
54+
doc/catalog_table.rst

build_support/catalog/update.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,49 @@
11
import argparse
22
from pathlib import Path
33

4+
import pandas as pd
5+
46
import pygambit as gbt
57

6-
CATALOG_CSV = Path(__file__).parent.parent.parent / "doc" / "catalog.csv"
8+
CATALOG_RST_TABLE = Path(__file__).parent.parent.parent / "doc" / "catalog_table.rst"
79
CATALOG_DIR = Path(__file__).parent.parent.parent / "catalog"
810
MAKEFILE_AM = Path(__file__).parent.parent.parent / "Makefile.am"
911

1012

13+
def generate_rst_table(df: pd.DataFrame, rst_path: Path):
14+
"""Generate a list-table RST file with dropdowns for long descriptions."""
15+
with open(rst_path, "w", encoding="utf-8") as f:
16+
f.write(".. list-table::\n")
17+
f.write(" :header-rows: 1\n")
18+
f.write(" :widths: 20 80 20\n")
19+
f.write(" :class: tight-table\n")
20+
f.write("\n")
21+
22+
f.write(" * - **Game**\n")
23+
f.write(" - **Description**\n")
24+
f.write(" - **Download**\n")
25+
26+
for _, row in df.iterrows():
27+
f.write(f" * - {row['Game']}\n")
28+
29+
description_cell_lines = []
30+
title = str(row.get("Title", "")).strip()
31+
description = str(row.get("Description", "")).strip()
32+
if description:
33+
description_cell_lines.append(f".. dropdown:: {title}")
34+
description_cell_lines.append(" ") # Indented blank line
35+
for line in description.splitlines():
36+
description_cell_lines.append(f" {line}")
37+
else:
38+
description_cell_lines.append(title)
39+
40+
f.write(f" - {description_cell_lines[0]}\n")
41+
for line in description_cell_lines[1:]:
42+
f.write(f" {line}\n")
43+
44+
f.write(f" - {row['Download']}\n")
45+
46+
1147
def update_makefile():
1248
"""Update the Makefile.am with all games from the catalog."""
1349

@@ -65,9 +101,10 @@ def update_makefile():
65101
parser.add_argument("--build", action="store_true")
66102
args = parser.parse_args()
67103

68-
# Create CSV used by RST docs page
69-
gbt.catalog.games().to_csv(CATALOG_CSV, index=False)
70-
print(f"Generated {CATALOG_CSV} for use in local docs build. DO NOT COMMIT.")
104+
# Create RST list-table used by doc/catalog.rst
105+
df = gbt.catalog.games(include_descriptions=True)
106+
generate_rst_table(df, CATALOG_RST_TABLE)
107+
print(f"Generated {CATALOG_RST_TABLE} for use in local docs build. DO NOT COMMIT.")
71108

72109
# Update the Makefile.am with the current list of catalog files
73110
if args.build:

catalog/__init__.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ def games(
6565
n_outcomes: int | None = None,
6666
n_players: int | None = None,
6767
n_strategies: int | None = None,
68+
include_descriptions: bool = False,
6869
) -> pd.DataFrame:
6970
"""
7071
List games available in the package catalog.
7172
72-
Arguments are treated as filters on the
73+
Most arguments are treated as filters on the
7374
attributes of the Game objects.
7475
7576
Parameters
@@ -98,11 +99,15 @@ def games(
9899
The number of players in the game.
99100
n_strategies: int, optional
100101
The number of pure strategies in the game.
102+
include_descriptions: bool, optional
103+
Whether to include the description of each game in the returned DataFrame.
104+
Defaults to False.
101105
102106
Returns
103107
-------
104108
pd.DataFrame
105109
A DataFrame with columns "Game" and "Title", where "Game" is the slug to load the game.
110+
If `include_descriptions=True`, the DataFrame will also include a "Description" column.
106111
"""
107112
records: list[dict[str, Any]] = []
108113

@@ -140,6 +145,20 @@ def check_filters(game: gbt.Game) -> bool:
140145
return False
141146
return not (n_strategies is not None and len(game.strategies) != n_strategies)
142147

148+
def append_record(
149+
slug: str,
150+
game: gbt.Game,
151+
) -> None:
152+
record = {
153+
"Game": slug,
154+
"Title": game.title,
155+
}
156+
if include_descriptions:
157+
record["Description"] = game.description
158+
ext = "efg" if game.is_tree else "nfg"
159+
record["Download"] = f":download:`{slug}.{ext} <../catalog/{slug}.{ext}>`"
160+
records.append(record)
161+
143162
# Add all the games stored as EFG/NFG files
144163
for resource_path in sorted(_CATALOG_RESOURCE.rglob("*")):
145164
reader = READERS.get(resource_path.suffix)
@@ -153,12 +172,7 @@ def check_filters(game: gbt.Game) -> bool:
153172
with as_file(resource_path) as path:
154173
game = reader(str(path))
155174
if check_filters(game):
156-
records.append(
157-
{
158-
"Game": slug,
159-
"Title": game.title,
160-
}
161-
)
175+
append_record(slug, game)
162176

163177
# Add all the games from families
164178
for slug, game in family_games().items():
@@ -168,13 +182,12 @@ def check_filters(game: gbt.Game) -> bool:
168182
f"Slug collision: {slug} is present in both file-based and family games."
169183
)
170184
if check_filters(game):
171-
records.append(
172-
{
173-
"Game": slug,
174-
"Title": game.title,
175-
}
176-
)
185+
append_record(slug, game)
177186

187+
if include_descriptions:
188+
return pd.DataFrame.from_records(
189+
records, columns=["Game", "Title", "Description", "Download"]
190+
)
178191
return pd.DataFrame.from_records(records, columns=["Game", "Title"])
179192

180193

@@ -218,7 +231,7 @@ def one_shot_trust(unique_NE_variant: bool = False) -> gbt.Game:
218231
The constructed extensive-form game.
219232
"""
220233
g = gbt.Game.new_tree(players=["Buyer", "Seller"])
221-
g.description = "One-shot trust game with binary actions, originally from Kreps (1990)."
234+
g.description = "One-shot trust game with binary actions, originally from [Kre90]_."
222235
g.append_move(g.root, "Buyer", ["Trust", "Not trust"])
223236
g.append_move(g.root.children[0], "Seller", ["Honor", "Abuse"])
224237
g.set_outcome(g.root.children[0].children[0], g.add_outcome([1, 1], label="Trustworthy"))

catalog/myerson1991/fig4_2.efg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
EFG 2 R "Myerson (1991) Figure 4.2" { "Player 1" "Player 2" }
2-
"An example from Myerson [^Mye1991] which illustrates the distinction between
2+
"An example from Myerson [Mye91]_ which illustrates the distinction between
33
an equilibrium of an extensive form game and an equilibrium of its
44
(multi)agent representation. The actions B1, Z1, and W2 form a
55
behavior profile which is an equilibrium in the (multi)agent
@@ -8,7 +8,7 @@ game, because Player 1 would prefer to switch from (B1, Z1) to
88
(A1, Y1); the (multi)agent representation rules out such coordinated
99
deviations across information sets.
1010

11-
[^Mye1991]: Myerson, Roger B. (1991) Game Theory: Analysis of Conflict.
11+
[Mye91]_: Myerson, Roger B. (1991) Game Theory: Analysis of Conflict.
1212
Cambridge: Harvard University Press.
1313
"
1414

catalog/selten1975/fig1.efg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
EFG 2 R "Selten's horse (Selten IJGT 1975, Figure 1)" { "Player 1" "Player 2" "Player 3" }
2-
"This is a three-player game presented in Selten [^Sel1975], commonly referred
2+
"This is a three-player game presented in Selten [Sel75]_, commonly referred
33
to as \"Selten's horse\" owing to the layout in which it can be drawn.
44
It is the motivating example for his definition of (trembling-hand)
55
perfect equilibrium, by showing a game that has an equilibrium which
66
is \"unreasonable\", but which is not ruled out by subgame perfection because
77
this game has no proper subgames.
88

9-
[^Sel1975]: Selten, Reinhard (1975). A reexamination of the perfectness concept
9+
[Sel75]_: Selten, Reinhard (1975). A reexamination of the perfectness concept
1010
for equilibrium points in extensive games. International Journal of Game
1111
Theory 4(1): 25-55.
1212
"

catalog/selten1975/fig2.efg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
EFG 2 R "Selten (IJGT 1975) Figure 2" { "Player 1" "Player 2" }
2-
"This is a counterexample presented in [^Sel1975], to show that extensive and
2+
"This is a counterexample presented in [Sel75]_, to show that extensive and
33
normal form concepts of perfectness do not coincide. This game has one
44
perfect equilibrium in the extensive from, but a distinct (pure) strategy
55
equilibrium is also perfect in the normal form.
66

7-
[^Sel75]: Selten, Reinhard (1975). A reexamination of the perfectness concept
7+
[Sel75]_: Selten, Reinhard (1975). A reexamination of the perfectness concept
88
for equilibrium points in extensive games. International Journal of Game
99
Theory 4(1): 25-55.
1010
"

catalog/selten1975/fig3.efg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
EFG 2 R "Selten (IJGT 1975) Figure 3" { "Player 1" "Player 2" "Player 3" }
2-
"This is a counterexample presented in [^Sel1975], to show that extensive and
2+
"This is a counterexample presented in [Sel75]_, to show that extensive and
33
normal form concepts of perfectness do not coincide. Specifically, there
44
are two equilibria which are perfect in the normal form but not perfect
55
in the extensive form.
66

7-
[^Sel75]: Selten, Reinhard (1975). A reexamination of the perfectness concept
7+
[Sel75]_: Selten, Reinhard (1975). A reexamination of the perfectness concept
88
for equilibrium points in extensive games. International Journal of Game
99
Theory 4(1): 25-55.
1010
"

doc/biblio.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Bibliography
22
============
33

4+
.. note::
5+
6+
To reference an entry in this bibliography, use the format ``[key]_``, for example, ``[Mye91]_`` will link to the Myerson (1991) textbook entry.
7+
8+
49

510
Articles on computation of Nash equilibria
611
------------------------------------------
@@ -89,6 +94,9 @@ General game theory articles and texts
8994
.. [KreWil82] David Kreps and Robert Wilson, "Sequential Equilibria",
9095
863-894, Econometrica , 50, 1982.
9196
97+
.. [Kre90] David Kreps, 1990, A Course in Microeconomic Theory,
98+
Princeton University Press.
99+
92100
.. [McKPal95] Richard McKelvey and Tom Palfrey, "Quantal response
93101
equilibria for normal form games", 6-38, Games and Economic Behavior ,
94102
10, 1995.

doc/catalog.rst

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,4 @@ Catalog of games
44
Below is a complete list of games included in Gambit's catalog.
55
Check out the :ref:`pygambit API reference <pygambit-catalog>` for instructions on how to search and load these games in Python, and the :ref:`Updating the games catalog <updating-catalog>` guide for instructions on how to contribute new games to the catalog.
66

7-
.. csv-table::
8-
:file: catalog.csv
9-
:header-rows: 1
10-
:widths: 20, 80
11-
:class: tight-table
7+
.. include:: catalog_table.rst

doc/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ We recommended most new users install the PyGambit Python package and read the a
8787
tools
8888
gui
8989
catalog
90-
samples
9190
developer
9291
formats
9392
biblio

0 commit comments

Comments
 (0)