From 90cb55f16f528a023d040bd21cce312b6f726a55 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 13:23:03 +0000 Subject: [PATCH 01/76] Add generated build support files to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d2f3fe579..9d37e0d3b 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ doc/tutorials/*.png Gambit.app/* *.ipynb_checkpoints *.ef +build_support/msw/gambit.wxs +build_support/osx/Info.plist From 6a70eeb743f82500bf3f7a9f6666ac6b90ff39cf Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 13:30:56 +0000 Subject: [PATCH 02/76] fix differing path --- doc/tutorials/01_quickstart.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index 06ec6f91e..4c1d85f7b 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -467,13 +467,13 @@ } ], "source": [ - "# gbt.read_nfg(\"test_games/prisoners_dilemma.nfg\")" + "# gbt.read_nfg(\"prisoners_dilemma.nfg\")" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "gambitvenv313", "language": "python", "name": "python3" }, From 7d03a2c8a1a2a987a5643c76498588c31b866749 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 13:36:36 +0000 Subject: [PATCH 03/76] mock up catalog section of tutorial --- doc/tutorials/01_quickstart.ipynb | 43 ++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index 4c1d85f7b..e7c6ee954 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -417,11 +417,52 @@ }, { "cell_type": "markdown", - "id": "24f36b0d", + "id": "15ab8d84", "metadata": {}, "source": [ "The equilibrium shows that both players are playing their dominant strategy, which is to defect. This is because defecting is the best response to the other player's strategy, regardless of what that strategy is.\n", "\n", + "Loading games from the catalog\n", + "------------------------------\n", + "\n", + "Gambit includes a catalog of standard games that can be loaded directly by name.\n", + "You can list all the available games in the catalog like so:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "701aa52a", + "metadata": {}, + "outputs": [], + "source": [ + "# gambit.catalog.games()" + ] + }, + { + "cell_type": "markdown", + "id": "a919ddf7", + "metadata": {}, + "source": [ + "You can then load a specific game by its name. For example, to load the \"Prisoner's Dilemma\" game from the catalog, you would do the following:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6db7a29a", + "metadata": {}, + "outputs": [], + "source": [ + "# g = gbt.catalog.PrisonersDilemma()\n", + "# g" + ] + }, + { + "cell_type": "markdown", + "id": "24f36b0d", + "metadata": {}, + "source": [ "Saving and reading strategic form games to and from file\n", "--------------------\n", "\n", From b65bf9c0e63ac517ce6b8082c3bbb215432b972c Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 13:47:54 +0000 Subject: [PATCH 04/76] move game files into catalog --- {contrib/games => catalog/games/files}/2s2x2x2.efg | 0 {contrib/games => catalog/games/files}/2smp.efg | 0 {contrib/games => catalog/games/files}/2x2.agg | 0 {contrib/games => catalog/games/files}/2x2.nfg | 0 {contrib/games => catalog/games/files}/2x2a.nfg | 0 {contrib/games => catalog/games/files}/2x2const.nfg | 0 {contrib/games => catalog/games/files}/2x2x2-nau.nfg | 0 {contrib/games => catalog/games/files}/2x2x2.efg | 0 {contrib/games => catalog/games/files}/2x2x2.nfg | 0 {contrib/games => catalog/games/files}/2x2x2x2.nfg | 0 {contrib/games => catalog/games/files}/2x2x2x2x2.nfg | 0 {contrib/games => catalog/games/files}/3x3x3.nfg | 0 {contrib/games => catalog/games/files}/4cards.efg | 0 {contrib/games => catalog/games/files}/5x4x3.nfg | 0 {contrib/games => catalog/games/files}/8x2x2.nfg | 0 {contrib/games => catalog/games/files}/8x8.nfg | 0 {contrib/games => catalog/games/files}/BSS_S_085.Weighted.agg | 0 .../games => catalog/games/files}/Bayesian-Coffee-3-2-2-3.bagg | 0 {contrib/games => catalog/games/files}/GenRPS5.agg | 0 {contrib/games => catalog/games/files}/artist1.efg | 0 {contrib/games => catalog/games/files}/artist2.efg | 0 {contrib/games => catalog/games/files}/badgame1.efg | 0 {contrib/games => catalog/games/files}/badgame2.efg | 0 {contrib/games => catalog/games/files}/bagwell.efg | 0 {contrib/games => catalog/games/files}/bayes1a.efg | 0 {contrib/games => catalog/games/files}/bayes2a.efg | 0 {contrib/games => catalog/games/files}/bcp2.efg | 0 {contrib/games => catalog/games/files}/bcp3.efg | 0 {contrib/games => catalog/games/files}/bcp4.efg | 0 {contrib/games => catalog/games/files}/bhg1.efg | 0 {contrib/games => catalog/games/files}/bhg2.efg | 0 {contrib/games => catalog/games/files}/bhg3.efg | 0 {contrib/games => catalog/games/files}/bhg4.efg | 0 {contrib/games => catalog/games/files}/bhg5.efg | 0 {contrib/games => catalog/games/files}/caro2.efg | 0 {contrib/games => catalog/games/files}/cent2.efg | 0 {contrib/games => catalog/games/files}/cent2.nfg | 0 {contrib/games => catalog/games/files}/cent3.efg | 0 {contrib/games => catalog/games/files}/cent4.efg | 0 {contrib/games => catalog/games/files}/cent6.efg | 0 {contrib/games => catalog/games/files}/centcs10.efg | 0 {contrib/games => catalog/games/files}/centcs6.efg | 0 {contrib/games => catalog/games/files}/condjury.efg | 0 {contrib/games => catalog/games/files}/coord2.efg | 0 {contrib/games => catalog/games/files}/coord2.nfg | 0 {contrib/games => catalog/games/files}/coord2ts.efg | 0 {contrib/games => catalog/games/files}/coord3.efg | 0 {contrib/games => catalog/games/files}/coord3.nfg | 0 {contrib/games => catalog/games/files}/coord333.nfg | 0 {contrib/games => catalog/games/files}/coord4.efg | 0 {contrib/games => catalog/games/files}/coord4.nfg | 0 {contrib/games => catalog/games/files}/cross.efg | 0 {contrib/games => catalog/games/files}/cs.efg | 0 {contrib/games => catalog/games/files}/csg1.nfg | 0 {contrib/games => catalog/games/files}/csg2.nfg | 0 {contrib/games => catalog/games/files}/csg3.nfg | 0 {contrib/games => catalog/games/files}/csg4.nfg | 0 {contrib/games => catalog/games/files}/deg1.nfg | 0 {contrib/games => catalog/games/files}/deg2.nfg | 0 {contrib/games => catalog/games/files}/e01.efg | 0 {contrib/games => catalog/games/files}/e01.nfg | 0 {contrib/games => catalog/games/files}/e02.efg | 0 {contrib/games => catalog/games/files}/e02.nfg | 0 {contrib/games => catalog/games/files}/e03.efg | 0 {contrib/games => catalog/games/files}/e04.efg | 0 {contrib/games => catalog/games/files}/e04.nfg | 0 {contrib/games => catalog/games/files}/e05.efg | 0 {contrib/games => catalog/games/files}/e06.efg | 0 {contrib/games => catalog/games/files}/e07.efg | 0 {contrib/games => catalog/games/files}/e07.nfg | 0 {contrib/games => catalog/games/files}/e08.efg | 0 {contrib/games => catalog/games/files}/e09.efg | 0 {contrib/games => catalog/games/files}/e10.efg | 0 {contrib/games => catalog/games/files}/e10a.efg | 0 {contrib/games => catalog/games/files}/e13.efg | 0 {contrib/games => catalog/games/files}/e16.efg | 0 {contrib/games => catalog/games/files}/e17.efg | 0 {contrib/games => catalog/games/files}/e18.efg | 0 {contrib/games => catalog/games/files}/g1.efg | 0 {contrib/games => catalog/games/files}/g1.nfg | 0 {contrib/games => catalog/games/files}/g2.efg | 0 {contrib/games => catalog/games/files}/g2.nfg | 0 {contrib/games => catalog/games/files}/g3.efg | 0 {contrib/games => catalog/games/files}/g3.nfg | 0 {contrib/games => catalog/games/files}/holdout.efg | 0 {contrib/games => catalog/games/files}/holdout7.efg | 0 {contrib/games => catalog/games/files}/hs1.efg | 0 {contrib/games => catalog/games/files}/jury_mr.efg | 0 {contrib/games => catalog/games/files}/jury_un.efg | 0 {contrib/games => catalog/games/files}/km1.efg | 0 {contrib/games => catalog/games/files}/km2.efg | 0 {contrib/games => catalog/games/files}/km3.efg | 0 {contrib/games => catalog/games/files}/km6.efg | 0 {contrib/games => catalog/games/files}/loopback.nfg | 0 {contrib/games => catalog/games/files}/mixdom.nfg | 0 {contrib/games => catalog/games/files}/mixdom2.nfg | 0 {contrib/games => catalog/games/files}/montyhal.efg | 0 {contrib/games => catalog/games/files}/my_2-1.efg | 0 {contrib/games => catalog/games/files}/my_2-4.efg | 0 {contrib/games => catalog/games/files}/my_2-8.efg | 0 {contrib/games => catalog/games/files}/my_3-3a.efg | 0 {contrib/games => catalog/games/files}/my_3-3b.efg | 0 {contrib/games => catalog/games/files}/my_3-3c.efg | 0 {contrib/games => catalog/games/files}/my_3-3d.efg | 0 {contrib/games => catalog/games/files}/my_3-3e.efg | 0 {contrib/games => catalog/games/files}/my_3-4.efg | 0 {contrib/games => catalog/games/files}/myerson.efg | 0 {contrib/games => catalog/games/files}/myerson_fig_4_2.efg | 0 {contrib/games => catalog/games/files}/nim.efg | 0 {contrib/games => catalog/games/files}/nim7.efg | 0 {contrib/games => catalog/games/files}/oneill.nfg | 0 {contrib/games => catalog/games/files}/palf.efg | 0 {contrib/games => catalog/games/files}/palf2.efg | 0 {contrib/games => catalog/games/files}/palf3.efg | 0 {contrib/games => catalog/games/files}/pbride.efg | 0 {contrib/games => catalog/games/files}/pd.nfg | 0 {contrib/games => catalog/games/files}/perfect1.nfg | 0 {contrib/games => catalog/games/files}/perfect2.nfg | 0 {contrib/games => catalog/games/files}/perfect3.nfg | 0 {contrib/games => catalog/games/files}/poker.efg | 0 {contrib/games => catalog/games/files}/poker.nfg | 0 {contrib/games => catalog/games/files}/poker2.efg | 0 {contrib/games => catalog/games/files}/pvw.efg | 0 {contrib/games => catalog/games/files}/pvw2.efg | 0 {contrib/games => catalog/games/files}/sh3.efg | 0 {contrib/games => catalog/games/files}/sh3.nfg | 0 {contrib/games => catalog/games/files}/spence.efg | 0 {contrib/games => catalog/games/files}/stengel.nfg | 0 {contrib/games => catalog/games/files}/sww1.efg | 0 {contrib/games => catalog/games/files}/sww1.nfg | 0 {contrib/games => catalog/games/files}/sww2.efg | 0 {contrib/games => catalog/games/files}/sww3.efg | 0 {contrib/games => catalog/games/files}/tim.efg | 0 {contrib/games => catalog/games/files}/todd1.nfg | 0 {contrib/games => catalog/games/files}/todd2.nfg | 0 {contrib/games => catalog/games/files}/todd3.nfg | 0 {contrib/games => catalog/games/files}/ttt.efg | 0 {contrib/games => catalog/games/files}/vd.efg | 0 {contrib/games => catalog/games/files}/vd.nfg | 0 {contrib/games => catalog/games/files}/w_ex1.efg | 0 {contrib/games => catalog/games/files}/w_ex2.efg | 0 {contrib/games => catalog/games/files}/wilson1.efg | 0 {contrib/games => catalog/games/files}/wink3.nfg | 0 {contrib/games => catalog/games/files}/winkels.nfg | 0 {contrib/games => catalog/games/files}/work1.efg | 0 {contrib/games => catalog/games/files}/work2.efg | 0 {contrib/games => catalog/games/files}/work3.efg | 0 {contrib/games => catalog/games/files}/yamamoto.nfg | 0 {contrib/games => catalog/games/files}/zero.nfg | 0 149 files changed, 0 insertions(+), 0 deletions(-) rename {contrib/games => catalog/games/files}/2s2x2x2.efg (100%) rename {contrib/games => catalog/games/files}/2smp.efg (100%) rename {contrib/games => catalog/games/files}/2x2.agg (100%) rename {contrib/games => catalog/games/files}/2x2.nfg (100%) rename {contrib/games => catalog/games/files}/2x2a.nfg (100%) rename {contrib/games => catalog/games/files}/2x2const.nfg (100%) rename {contrib/games => catalog/games/files}/2x2x2-nau.nfg (100%) rename {contrib/games => catalog/games/files}/2x2x2.efg (100%) rename {contrib/games => catalog/games/files}/2x2x2.nfg (100%) rename {contrib/games => catalog/games/files}/2x2x2x2.nfg (100%) rename {contrib/games => catalog/games/files}/2x2x2x2x2.nfg (100%) rename {contrib/games => catalog/games/files}/3x3x3.nfg (100%) rename {contrib/games => catalog/games/files}/4cards.efg (100%) rename {contrib/games => catalog/games/files}/5x4x3.nfg (100%) rename {contrib/games => catalog/games/files}/8x2x2.nfg (100%) rename {contrib/games => catalog/games/files}/8x8.nfg (100%) rename {contrib/games => catalog/games/files}/BSS_S_085.Weighted.agg (100%) rename {contrib/games => catalog/games/files}/Bayesian-Coffee-3-2-2-3.bagg (100%) rename {contrib/games => catalog/games/files}/GenRPS5.agg (100%) rename {contrib/games => catalog/games/files}/artist1.efg (100%) rename {contrib/games => catalog/games/files}/artist2.efg (100%) rename {contrib/games => catalog/games/files}/badgame1.efg (100%) rename {contrib/games => catalog/games/files}/badgame2.efg (100%) rename {contrib/games => catalog/games/files}/bagwell.efg (100%) rename {contrib/games => catalog/games/files}/bayes1a.efg (100%) rename {contrib/games => catalog/games/files}/bayes2a.efg (100%) rename {contrib/games => catalog/games/files}/bcp2.efg (100%) rename {contrib/games => catalog/games/files}/bcp3.efg (100%) rename {contrib/games => catalog/games/files}/bcp4.efg (100%) rename {contrib/games => catalog/games/files}/bhg1.efg (100%) rename {contrib/games => catalog/games/files}/bhg2.efg (100%) rename {contrib/games => catalog/games/files}/bhg3.efg (100%) rename {contrib/games => catalog/games/files}/bhg4.efg (100%) rename {contrib/games => catalog/games/files}/bhg5.efg (100%) rename {contrib/games => catalog/games/files}/caro2.efg (100%) rename {contrib/games => catalog/games/files}/cent2.efg (100%) rename {contrib/games => catalog/games/files}/cent2.nfg (100%) rename {contrib/games => catalog/games/files}/cent3.efg (100%) rename {contrib/games => catalog/games/files}/cent4.efg (100%) rename {contrib/games => catalog/games/files}/cent6.efg (100%) rename {contrib/games => catalog/games/files}/centcs10.efg (100%) rename {contrib/games => catalog/games/files}/centcs6.efg (100%) rename {contrib/games => catalog/games/files}/condjury.efg (100%) rename {contrib/games => catalog/games/files}/coord2.efg (100%) rename {contrib/games => catalog/games/files}/coord2.nfg (100%) rename {contrib/games => catalog/games/files}/coord2ts.efg (100%) rename {contrib/games => catalog/games/files}/coord3.efg (100%) rename {contrib/games => catalog/games/files}/coord3.nfg (100%) rename {contrib/games => catalog/games/files}/coord333.nfg (100%) rename {contrib/games => catalog/games/files}/coord4.efg (100%) rename {contrib/games => catalog/games/files}/coord4.nfg (100%) rename {contrib/games => catalog/games/files}/cross.efg (100%) rename {contrib/games => catalog/games/files}/cs.efg (100%) rename {contrib/games => catalog/games/files}/csg1.nfg (100%) rename {contrib/games => catalog/games/files}/csg2.nfg (100%) rename {contrib/games => catalog/games/files}/csg3.nfg (100%) rename {contrib/games => catalog/games/files}/csg4.nfg (100%) rename {contrib/games => catalog/games/files}/deg1.nfg (100%) rename {contrib/games => catalog/games/files}/deg2.nfg (100%) rename {contrib/games => catalog/games/files}/e01.efg (100%) rename {contrib/games => catalog/games/files}/e01.nfg (100%) rename {contrib/games => catalog/games/files}/e02.efg (100%) rename {contrib/games => catalog/games/files}/e02.nfg (100%) rename {contrib/games => catalog/games/files}/e03.efg (100%) rename {contrib/games => catalog/games/files}/e04.efg (100%) rename {contrib/games => catalog/games/files}/e04.nfg (100%) rename {contrib/games => catalog/games/files}/e05.efg (100%) rename {contrib/games => catalog/games/files}/e06.efg (100%) rename {contrib/games => catalog/games/files}/e07.efg (100%) rename {contrib/games => catalog/games/files}/e07.nfg (100%) rename {contrib/games => catalog/games/files}/e08.efg (100%) rename {contrib/games => catalog/games/files}/e09.efg (100%) rename {contrib/games => catalog/games/files}/e10.efg (100%) rename {contrib/games => catalog/games/files}/e10a.efg (100%) rename {contrib/games => catalog/games/files}/e13.efg (100%) rename {contrib/games => catalog/games/files}/e16.efg (100%) rename {contrib/games => catalog/games/files}/e17.efg (100%) rename {contrib/games => catalog/games/files}/e18.efg (100%) rename {contrib/games => catalog/games/files}/g1.efg (100%) rename {contrib/games => catalog/games/files}/g1.nfg (100%) rename {contrib/games => catalog/games/files}/g2.efg (100%) rename {contrib/games => catalog/games/files}/g2.nfg (100%) rename {contrib/games => catalog/games/files}/g3.efg (100%) rename {contrib/games => catalog/games/files}/g3.nfg (100%) rename {contrib/games => catalog/games/files}/holdout.efg (100%) rename {contrib/games => catalog/games/files}/holdout7.efg (100%) rename {contrib/games => catalog/games/files}/hs1.efg (100%) rename {contrib/games => catalog/games/files}/jury_mr.efg (100%) rename {contrib/games => catalog/games/files}/jury_un.efg (100%) rename {contrib/games => catalog/games/files}/km1.efg (100%) rename {contrib/games => catalog/games/files}/km2.efg (100%) rename {contrib/games => catalog/games/files}/km3.efg (100%) rename {contrib/games => catalog/games/files}/km6.efg (100%) rename {contrib/games => catalog/games/files}/loopback.nfg (100%) rename {contrib/games => catalog/games/files}/mixdom.nfg (100%) rename {contrib/games => catalog/games/files}/mixdom2.nfg (100%) rename {contrib/games => catalog/games/files}/montyhal.efg (100%) rename {contrib/games => catalog/games/files}/my_2-1.efg (100%) rename {contrib/games => catalog/games/files}/my_2-4.efg (100%) rename {contrib/games => catalog/games/files}/my_2-8.efg (100%) rename {contrib/games => catalog/games/files}/my_3-3a.efg (100%) rename {contrib/games => catalog/games/files}/my_3-3b.efg (100%) rename {contrib/games => catalog/games/files}/my_3-3c.efg (100%) rename {contrib/games => catalog/games/files}/my_3-3d.efg (100%) rename {contrib/games => catalog/games/files}/my_3-3e.efg (100%) rename {contrib/games => catalog/games/files}/my_3-4.efg (100%) rename {contrib/games => catalog/games/files}/myerson.efg (100%) rename {contrib/games => catalog/games/files}/myerson_fig_4_2.efg (100%) rename {contrib/games => catalog/games/files}/nim.efg (100%) rename {contrib/games => catalog/games/files}/nim7.efg (100%) rename {contrib/games => catalog/games/files}/oneill.nfg (100%) rename {contrib/games => catalog/games/files}/palf.efg (100%) rename {contrib/games => catalog/games/files}/palf2.efg (100%) rename {contrib/games => catalog/games/files}/palf3.efg (100%) rename {contrib/games => catalog/games/files}/pbride.efg (100%) rename {contrib/games => catalog/games/files}/pd.nfg (100%) rename {contrib/games => catalog/games/files}/perfect1.nfg (100%) rename {contrib/games => catalog/games/files}/perfect2.nfg (100%) rename {contrib/games => catalog/games/files}/perfect3.nfg (100%) rename {contrib/games => catalog/games/files}/poker.efg (100%) rename {contrib/games => catalog/games/files}/poker.nfg (100%) rename {contrib/games => catalog/games/files}/poker2.efg (100%) rename {contrib/games => catalog/games/files}/pvw.efg (100%) rename {contrib/games => catalog/games/files}/pvw2.efg (100%) rename {contrib/games => catalog/games/files}/sh3.efg (100%) rename {contrib/games => catalog/games/files}/sh3.nfg (100%) rename {contrib/games => catalog/games/files}/spence.efg (100%) rename {contrib/games => catalog/games/files}/stengel.nfg (100%) rename {contrib/games => catalog/games/files}/sww1.efg (100%) rename {contrib/games => catalog/games/files}/sww1.nfg (100%) rename {contrib/games => catalog/games/files}/sww2.efg (100%) rename {contrib/games => catalog/games/files}/sww3.efg (100%) rename {contrib/games => catalog/games/files}/tim.efg (100%) rename {contrib/games => catalog/games/files}/todd1.nfg (100%) rename {contrib/games => catalog/games/files}/todd2.nfg (100%) rename {contrib/games => catalog/games/files}/todd3.nfg (100%) rename {contrib/games => catalog/games/files}/ttt.efg (100%) rename {contrib/games => catalog/games/files}/vd.efg (100%) rename {contrib/games => catalog/games/files}/vd.nfg (100%) rename {contrib/games => catalog/games/files}/w_ex1.efg (100%) rename {contrib/games => catalog/games/files}/w_ex2.efg (100%) rename {contrib/games => catalog/games/files}/wilson1.efg (100%) rename {contrib/games => catalog/games/files}/wink3.nfg (100%) rename {contrib/games => catalog/games/files}/winkels.nfg (100%) rename {contrib/games => catalog/games/files}/work1.efg (100%) rename {contrib/games => catalog/games/files}/work2.efg (100%) rename {contrib/games => catalog/games/files}/work3.efg (100%) rename {contrib/games => catalog/games/files}/yamamoto.nfg (100%) rename {contrib/games => catalog/games/files}/zero.nfg (100%) diff --git a/contrib/games/2s2x2x2.efg b/catalog/games/files/2s2x2x2.efg similarity index 100% rename from contrib/games/2s2x2x2.efg rename to catalog/games/files/2s2x2x2.efg diff --git a/contrib/games/2smp.efg b/catalog/games/files/2smp.efg similarity index 100% rename from contrib/games/2smp.efg rename to catalog/games/files/2smp.efg diff --git a/contrib/games/2x2.agg b/catalog/games/files/2x2.agg similarity index 100% rename from contrib/games/2x2.agg rename to catalog/games/files/2x2.agg diff --git a/contrib/games/2x2.nfg b/catalog/games/files/2x2.nfg similarity index 100% rename from contrib/games/2x2.nfg rename to catalog/games/files/2x2.nfg diff --git a/contrib/games/2x2a.nfg b/catalog/games/files/2x2a.nfg similarity index 100% rename from contrib/games/2x2a.nfg rename to catalog/games/files/2x2a.nfg diff --git a/contrib/games/2x2const.nfg b/catalog/games/files/2x2const.nfg similarity index 100% rename from contrib/games/2x2const.nfg rename to catalog/games/files/2x2const.nfg diff --git a/contrib/games/2x2x2-nau.nfg b/catalog/games/files/2x2x2-nau.nfg similarity index 100% rename from contrib/games/2x2x2-nau.nfg rename to catalog/games/files/2x2x2-nau.nfg diff --git a/contrib/games/2x2x2.efg b/catalog/games/files/2x2x2.efg similarity index 100% rename from contrib/games/2x2x2.efg rename to catalog/games/files/2x2x2.efg diff --git a/contrib/games/2x2x2.nfg b/catalog/games/files/2x2x2.nfg similarity index 100% rename from contrib/games/2x2x2.nfg rename to catalog/games/files/2x2x2.nfg diff --git a/contrib/games/2x2x2x2.nfg b/catalog/games/files/2x2x2x2.nfg similarity index 100% rename from contrib/games/2x2x2x2.nfg rename to catalog/games/files/2x2x2x2.nfg diff --git a/contrib/games/2x2x2x2x2.nfg b/catalog/games/files/2x2x2x2x2.nfg similarity index 100% rename from contrib/games/2x2x2x2x2.nfg rename to catalog/games/files/2x2x2x2x2.nfg diff --git a/contrib/games/3x3x3.nfg b/catalog/games/files/3x3x3.nfg similarity index 100% rename from contrib/games/3x3x3.nfg rename to catalog/games/files/3x3x3.nfg diff --git a/contrib/games/4cards.efg b/catalog/games/files/4cards.efg similarity index 100% rename from contrib/games/4cards.efg rename to catalog/games/files/4cards.efg diff --git a/contrib/games/5x4x3.nfg b/catalog/games/files/5x4x3.nfg similarity index 100% rename from contrib/games/5x4x3.nfg rename to catalog/games/files/5x4x3.nfg diff --git a/contrib/games/8x2x2.nfg b/catalog/games/files/8x2x2.nfg similarity index 100% rename from contrib/games/8x2x2.nfg rename to catalog/games/files/8x2x2.nfg diff --git a/contrib/games/8x8.nfg b/catalog/games/files/8x8.nfg similarity index 100% rename from contrib/games/8x8.nfg rename to catalog/games/files/8x8.nfg diff --git a/contrib/games/BSS_S_085.Weighted.agg b/catalog/games/files/BSS_S_085.Weighted.agg similarity index 100% rename from contrib/games/BSS_S_085.Weighted.agg rename to catalog/games/files/BSS_S_085.Weighted.agg diff --git a/contrib/games/Bayesian-Coffee-3-2-2-3.bagg b/catalog/games/files/Bayesian-Coffee-3-2-2-3.bagg similarity index 100% rename from contrib/games/Bayesian-Coffee-3-2-2-3.bagg rename to catalog/games/files/Bayesian-Coffee-3-2-2-3.bagg diff --git a/contrib/games/GenRPS5.agg b/catalog/games/files/GenRPS5.agg similarity index 100% rename from contrib/games/GenRPS5.agg rename to catalog/games/files/GenRPS5.agg diff --git a/contrib/games/artist1.efg b/catalog/games/files/artist1.efg similarity index 100% rename from contrib/games/artist1.efg rename to catalog/games/files/artist1.efg diff --git a/contrib/games/artist2.efg b/catalog/games/files/artist2.efg similarity index 100% rename from contrib/games/artist2.efg rename to catalog/games/files/artist2.efg diff --git a/contrib/games/badgame1.efg b/catalog/games/files/badgame1.efg similarity index 100% rename from contrib/games/badgame1.efg rename to catalog/games/files/badgame1.efg diff --git a/contrib/games/badgame2.efg b/catalog/games/files/badgame2.efg similarity index 100% rename from contrib/games/badgame2.efg rename to catalog/games/files/badgame2.efg diff --git a/contrib/games/bagwell.efg b/catalog/games/files/bagwell.efg similarity index 100% rename from contrib/games/bagwell.efg rename to catalog/games/files/bagwell.efg diff --git a/contrib/games/bayes1a.efg b/catalog/games/files/bayes1a.efg similarity index 100% rename from contrib/games/bayes1a.efg rename to catalog/games/files/bayes1a.efg diff --git a/contrib/games/bayes2a.efg b/catalog/games/files/bayes2a.efg similarity index 100% rename from contrib/games/bayes2a.efg rename to catalog/games/files/bayes2a.efg diff --git a/contrib/games/bcp2.efg b/catalog/games/files/bcp2.efg similarity index 100% rename from contrib/games/bcp2.efg rename to catalog/games/files/bcp2.efg diff --git a/contrib/games/bcp3.efg b/catalog/games/files/bcp3.efg similarity index 100% rename from contrib/games/bcp3.efg rename to catalog/games/files/bcp3.efg diff --git a/contrib/games/bcp4.efg b/catalog/games/files/bcp4.efg similarity index 100% rename from contrib/games/bcp4.efg rename to catalog/games/files/bcp4.efg diff --git a/contrib/games/bhg1.efg b/catalog/games/files/bhg1.efg similarity index 100% rename from contrib/games/bhg1.efg rename to catalog/games/files/bhg1.efg diff --git a/contrib/games/bhg2.efg b/catalog/games/files/bhg2.efg similarity index 100% rename from contrib/games/bhg2.efg rename to catalog/games/files/bhg2.efg diff --git a/contrib/games/bhg3.efg b/catalog/games/files/bhg3.efg similarity index 100% rename from contrib/games/bhg3.efg rename to catalog/games/files/bhg3.efg diff --git a/contrib/games/bhg4.efg b/catalog/games/files/bhg4.efg similarity index 100% rename from contrib/games/bhg4.efg rename to catalog/games/files/bhg4.efg diff --git a/contrib/games/bhg5.efg b/catalog/games/files/bhg5.efg similarity index 100% rename from contrib/games/bhg5.efg rename to catalog/games/files/bhg5.efg diff --git a/contrib/games/caro2.efg b/catalog/games/files/caro2.efg similarity index 100% rename from contrib/games/caro2.efg rename to catalog/games/files/caro2.efg diff --git a/contrib/games/cent2.efg b/catalog/games/files/cent2.efg similarity index 100% rename from contrib/games/cent2.efg rename to catalog/games/files/cent2.efg diff --git a/contrib/games/cent2.nfg b/catalog/games/files/cent2.nfg similarity index 100% rename from contrib/games/cent2.nfg rename to catalog/games/files/cent2.nfg diff --git a/contrib/games/cent3.efg b/catalog/games/files/cent3.efg similarity index 100% rename from contrib/games/cent3.efg rename to catalog/games/files/cent3.efg diff --git a/contrib/games/cent4.efg b/catalog/games/files/cent4.efg similarity index 100% rename from contrib/games/cent4.efg rename to catalog/games/files/cent4.efg diff --git a/contrib/games/cent6.efg b/catalog/games/files/cent6.efg similarity index 100% rename from contrib/games/cent6.efg rename to catalog/games/files/cent6.efg diff --git a/contrib/games/centcs10.efg b/catalog/games/files/centcs10.efg similarity index 100% rename from contrib/games/centcs10.efg rename to catalog/games/files/centcs10.efg diff --git a/contrib/games/centcs6.efg b/catalog/games/files/centcs6.efg similarity index 100% rename from contrib/games/centcs6.efg rename to catalog/games/files/centcs6.efg diff --git a/contrib/games/condjury.efg b/catalog/games/files/condjury.efg similarity index 100% rename from contrib/games/condjury.efg rename to catalog/games/files/condjury.efg diff --git a/contrib/games/coord2.efg b/catalog/games/files/coord2.efg similarity index 100% rename from contrib/games/coord2.efg rename to catalog/games/files/coord2.efg diff --git a/contrib/games/coord2.nfg b/catalog/games/files/coord2.nfg similarity index 100% rename from contrib/games/coord2.nfg rename to catalog/games/files/coord2.nfg diff --git a/contrib/games/coord2ts.efg b/catalog/games/files/coord2ts.efg similarity index 100% rename from contrib/games/coord2ts.efg rename to catalog/games/files/coord2ts.efg diff --git a/contrib/games/coord3.efg b/catalog/games/files/coord3.efg similarity index 100% rename from contrib/games/coord3.efg rename to catalog/games/files/coord3.efg diff --git a/contrib/games/coord3.nfg b/catalog/games/files/coord3.nfg similarity index 100% rename from contrib/games/coord3.nfg rename to catalog/games/files/coord3.nfg diff --git a/contrib/games/coord333.nfg b/catalog/games/files/coord333.nfg similarity index 100% rename from contrib/games/coord333.nfg rename to catalog/games/files/coord333.nfg diff --git a/contrib/games/coord4.efg b/catalog/games/files/coord4.efg similarity index 100% rename from contrib/games/coord4.efg rename to catalog/games/files/coord4.efg diff --git a/contrib/games/coord4.nfg b/catalog/games/files/coord4.nfg similarity index 100% rename from contrib/games/coord4.nfg rename to catalog/games/files/coord4.nfg diff --git a/contrib/games/cross.efg b/catalog/games/files/cross.efg similarity index 100% rename from contrib/games/cross.efg rename to catalog/games/files/cross.efg diff --git a/contrib/games/cs.efg b/catalog/games/files/cs.efg similarity index 100% rename from contrib/games/cs.efg rename to catalog/games/files/cs.efg diff --git a/contrib/games/csg1.nfg b/catalog/games/files/csg1.nfg similarity index 100% rename from contrib/games/csg1.nfg rename to catalog/games/files/csg1.nfg diff --git a/contrib/games/csg2.nfg b/catalog/games/files/csg2.nfg similarity index 100% rename from contrib/games/csg2.nfg rename to catalog/games/files/csg2.nfg diff --git a/contrib/games/csg3.nfg b/catalog/games/files/csg3.nfg similarity index 100% rename from contrib/games/csg3.nfg rename to catalog/games/files/csg3.nfg diff --git a/contrib/games/csg4.nfg b/catalog/games/files/csg4.nfg similarity index 100% rename from contrib/games/csg4.nfg rename to catalog/games/files/csg4.nfg diff --git a/contrib/games/deg1.nfg b/catalog/games/files/deg1.nfg similarity index 100% rename from contrib/games/deg1.nfg rename to catalog/games/files/deg1.nfg diff --git a/contrib/games/deg2.nfg b/catalog/games/files/deg2.nfg similarity index 100% rename from contrib/games/deg2.nfg rename to catalog/games/files/deg2.nfg diff --git a/contrib/games/e01.efg b/catalog/games/files/e01.efg similarity index 100% rename from contrib/games/e01.efg rename to catalog/games/files/e01.efg diff --git a/contrib/games/e01.nfg b/catalog/games/files/e01.nfg similarity index 100% rename from contrib/games/e01.nfg rename to catalog/games/files/e01.nfg diff --git a/contrib/games/e02.efg b/catalog/games/files/e02.efg similarity index 100% rename from contrib/games/e02.efg rename to catalog/games/files/e02.efg diff --git a/contrib/games/e02.nfg b/catalog/games/files/e02.nfg similarity index 100% rename from contrib/games/e02.nfg rename to catalog/games/files/e02.nfg diff --git a/contrib/games/e03.efg b/catalog/games/files/e03.efg similarity index 100% rename from contrib/games/e03.efg rename to catalog/games/files/e03.efg diff --git a/contrib/games/e04.efg b/catalog/games/files/e04.efg similarity index 100% rename from contrib/games/e04.efg rename to catalog/games/files/e04.efg diff --git a/contrib/games/e04.nfg b/catalog/games/files/e04.nfg similarity index 100% rename from contrib/games/e04.nfg rename to catalog/games/files/e04.nfg diff --git a/contrib/games/e05.efg b/catalog/games/files/e05.efg similarity index 100% rename from contrib/games/e05.efg rename to catalog/games/files/e05.efg diff --git a/contrib/games/e06.efg b/catalog/games/files/e06.efg similarity index 100% rename from contrib/games/e06.efg rename to catalog/games/files/e06.efg diff --git a/contrib/games/e07.efg b/catalog/games/files/e07.efg similarity index 100% rename from contrib/games/e07.efg rename to catalog/games/files/e07.efg diff --git a/contrib/games/e07.nfg b/catalog/games/files/e07.nfg similarity index 100% rename from contrib/games/e07.nfg rename to catalog/games/files/e07.nfg diff --git a/contrib/games/e08.efg b/catalog/games/files/e08.efg similarity index 100% rename from contrib/games/e08.efg rename to catalog/games/files/e08.efg diff --git a/contrib/games/e09.efg b/catalog/games/files/e09.efg similarity index 100% rename from contrib/games/e09.efg rename to catalog/games/files/e09.efg diff --git a/contrib/games/e10.efg b/catalog/games/files/e10.efg similarity index 100% rename from contrib/games/e10.efg rename to catalog/games/files/e10.efg diff --git a/contrib/games/e10a.efg b/catalog/games/files/e10a.efg similarity index 100% rename from contrib/games/e10a.efg rename to catalog/games/files/e10a.efg diff --git a/contrib/games/e13.efg b/catalog/games/files/e13.efg similarity index 100% rename from contrib/games/e13.efg rename to catalog/games/files/e13.efg diff --git a/contrib/games/e16.efg b/catalog/games/files/e16.efg similarity index 100% rename from contrib/games/e16.efg rename to catalog/games/files/e16.efg diff --git a/contrib/games/e17.efg b/catalog/games/files/e17.efg similarity index 100% rename from contrib/games/e17.efg rename to catalog/games/files/e17.efg diff --git a/contrib/games/e18.efg b/catalog/games/files/e18.efg similarity index 100% rename from contrib/games/e18.efg rename to catalog/games/files/e18.efg diff --git a/contrib/games/g1.efg b/catalog/games/files/g1.efg similarity index 100% rename from contrib/games/g1.efg rename to catalog/games/files/g1.efg diff --git a/contrib/games/g1.nfg b/catalog/games/files/g1.nfg similarity index 100% rename from contrib/games/g1.nfg rename to catalog/games/files/g1.nfg diff --git a/contrib/games/g2.efg b/catalog/games/files/g2.efg similarity index 100% rename from contrib/games/g2.efg rename to catalog/games/files/g2.efg diff --git a/contrib/games/g2.nfg b/catalog/games/files/g2.nfg similarity index 100% rename from contrib/games/g2.nfg rename to catalog/games/files/g2.nfg diff --git a/contrib/games/g3.efg b/catalog/games/files/g3.efg similarity index 100% rename from contrib/games/g3.efg rename to catalog/games/files/g3.efg diff --git a/contrib/games/g3.nfg b/catalog/games/files/g3.nfg similarity index 100% rename from contrib/games/g3.nfg rename to catalog/games/files/g3.nfg diff --git a/contrib/games/holdout.efg b/catalog/games/files/holdout.efg similarity index 100% rename from contrib/games/holdout.efg rename to catalog/games/files/holdout.efg diff --git a/contrib/games/holdout7.efg b/catalog/games/files/holdout7.efg similarity index 100% rename from contrib/games/holdout7.efg rename to catalog/games/files/holdout7.efg diff --git a/contrib/games/hs1.efg b/catalog/games/files/hs1.efg similarity index 100% rename from contrib/games/hs1.efg rename to catalog/games/files/hs1.efg diff --git a/contrib/games/jury_mr.efg b/catalog/games/files/jury_mr.efg similarity index 100% rename from contrib/games/jury_mr.efg rename to catalog/games/files/jury_mr.efg diff --git a/contrib/games/jury_un.efg b/catalog/games/files/jury_un.efg similarity index 100% rename from contrib/games/jury_un.efg rename to catalog/games/files/jury_un.efg diff --git a/contrib/games/km1.efg b/catalog/games/files/km1.efg similarity index 100% rename from contrib/games/km1.efg rename to catalog/games/files/km1.efg diff --git a/contrib/games/km2.efg b/catalog/games/files/km2.efg similarity index 100% rename from contrib/games/km2.efg rename to catalog/games/files/km2.efg diff --git a/contrib/games/km3.efg b/catalog/games/files/km3.efg similarity index 100% rename from contrib/games/km3.efg rename to catalog/games/files/km3.efg diff --git a/contrib/games/km6.efg b/catalog/games/files/km6.efg similarity index 100% rename from contrib/games/km6.efg rename to catalog/games/files/km6.efg diff --git a/contrib/games/loopback.nfg b/catalog/games/files/loopback.nfg similarity index 100% rename from contrib/games/loopback.nfg rename to catalog/games/files/loopback.nfg diff --git a/contrib/games/mixdom.nfg b/catalog/games/files/mixdom.nfg similarity index 100% rename from contrib/games/mixdom.nfg rename to catalog/games/files/mixdom.nfg diff --git a/contrib/games/mixdom2.nfg b/catalog/games/files/mixdom2.nfg similarity index 100% rename from contrib/games/mixdom2.nfg rename to catalog/games/files/mixdom2.nfg diff --git a/contrib/games/montyhal.efg b/catalog/games/files/montyhal.efg similarity index 100% rename from contrib/games/montyhal.efg rename to catalog/games/files/montyhal.efg diff --git a/contrib/games/my_2-1.efg b/catalog/games/files/my_2-1.efg similarity index 100% rename from contrib/games/my_2-1.efg rename to catalog/games/files/my_2-1.efg diff --git a/contrib/games/my_2-4.efg b/catalog/games/files/my_2-4.efg similarity index 100% rename from contrib/games/my_2-4.efg rename to catalog/games/files/my_2-4.efg diff --git a/contrib/games/my_2-8.efg b/catalog/games/files/my_2-8.efg similarity index 100% rename from contrib/games/my_2-8.efg rename to catalog/games/files/my_2-8.efg diff --git a/contrib/games/my_3-3a.efg b/catalog/games/files/my_3-3a.efg similarity index 100% rename from contrib/games/my_3-3a.efg rename to catalog/games/files/my_3-3a.efg diff --git a/contrib/games/my_3-3b.efg b/catalog/games/files/my_3-3b.efg similarity index 100% rename from contrib/games/my_3-3b.efg rename to catalog/games/files/my_3-3b.efg diff --git a/contrib/games/my_3-3c.efg b/catalog/games/files/my_3-3c.efg similarity index 100% rename from contrib/games/my_3-3c.efg rename to catalog/games/files/my_3-3c.efg diff --git a/contrib/games/my_3-3d.efg b/catalog/games/files/my_3-3d.efg similarity index 100% rename from contrib/games/my_3-3d.efg rename to catalog/games/files/my_3-3d.efg diff --git a/contrib/games/my_3-3e.efg b/catalog/games/files/my_3-3e.efg similarity index 100% rename from contrib/games/my_3-3e.efg rename to catalog/games/files/my_3-3e.efg diff --git a/contrib/games/my_3-4.efg b/catalog/games/files/my_3-4.efg similarity index 100% rename from contrib/games/my_3-4.efg rename to catalog/games/files/my_3-4.efg diff --git a/contrib/games/myerson.efg b/catalog/games/files/myerson.efg similarity index 100% rename from contrib/games/myerson.efg rename to catalog/games/files/myerson.efg diff --git a/contrib/games/myerson_fig_4_2.efg b/catalog/games/files/myerson_fig_4_2.efg similarity index 100% rename from contrib/games/myerson_fig_4_2.efg rename to catalog/games/files/myerson_fig_4_2.efg diff --git a/contrib/games/nim.efg b/catalog/games/files/nim.efg similarity index 100% rename from contrib/games/nim.efg rename to catalog/games/files/nim.efg diff --git a/contrib/games/nim7.efg b/catalog/games/files/nim7.efg similarity index 100% rename from contrib/games/nim7.efg rename to catalog/games/files/nim7.efg diff --git a/contrib/games/oneill.nfg b/catalog/games/files/oneill.nfg similarity index 100% rename from contrib/games/oneill.nfg rename to catalog/games/files/oneill.nfg diff --git a/contrib/games/palf.efg b/catalog/games/files/palf.efg similarity index 100% rename from contrib/games/palf.efg rename to catalog/games/files/palf.efg diff --git a/contrib/games/palf2.efg b/catalog/games/files/palf2.efg similarity index 100% rename from contrib/games/palf2.efg rename to catalog/games/files/palf2.efg diff --git a/contrib/games/palf3.efg b/catalog/games/files/palf3.efg similarity index 100% rename from contrib/games/palf3.efg rename to catalog/games/files/palf3.efg diff --git a/contrib/games/pbride.efg b/catalog/games/files/pbride.efg similarity index 100% rename from contrib/games/pbride.efg rename to catalog/games/files/pbride.efg diff --git a/contrib/games/pd.nfg b/catalog/games/files/pd.nfg similarity index 100% rename from contrib/games/pd.nfg rename to catalog/games/files/pd.nfg diff --git a/contrib/games/perfect1.nfg b/catalog/games/files/perfect1.nfg similarity index 100% rename from contrib/games/perfect1.nfg rename to catalog/games/files/perfect1.nfg diff --git a/contrib/games/perfect2.nfg b/catalog/games/files/perfect2.nfg similarity index 100% rename from contrib/games/perfect2.nfg rename to catalog/games/files/perfect2.nfg diff --git a/contrib/games/perfect3.nfg b/catalog/games/files/perfect3.nfg similarity index 100% rename from contrib/games/perfect3.nfg rename to catalog/games/files/perfect3.nfg diff --git a/contrib/games/poker.efg b/catalog/games/files/poker.efg similarity index 100% rename from contrib/games/poker.efg rename to catalog/games/files/poker.efg diff --git a/contrib/games/poker.nfg b/catalog/games/files/poker.nfg similarity index 100% rename from contrib/games/poker.nfg rename to catalog/games/files/poker.nfg diff --git a/contrib/games/poker2.efg b/catalog/games/files/poker2.efg similarity index 100% rename from contrib/games/poker2.efg rename to catalog/games/files/poker2.efg diff --git a/contrib/games/pvw.efg b/catalog/games/files/pvw.efg similarity index 100% rename from contrib/games/pvw.efg rename to catalog/games/files/pvw.efg diff --git a/contrib/games/pvw2.efg b/catalog/games/files/pvw2.efg similarity index 100% rename from contrib/games/pvw2.efg rename to catalog/games/files/pvw2.efg diff --git a/contrib/games/sh3.efg b/catalog/games/files/sh3.efg similarity index 100% rename from contrib/games/sh3.efg rename to catalog/games/files/sh3.efg diff --git a/contrib/games/sh3.nfg b/catalog/games/files/sh3.nfg similarity index 100% rename from contrib/games/sh3.nfg rename to catalog/games/files/sh3.nfg diff --git a/contrib/games/spence.efg b/catalog/games/files/spence.efg similarity index 100% rename from contrib/games/spence.efg rename to catalog/games/files/spence.efg diff --git a/contrib/games/stengel.nfg b/catalog/games/files/stengel.nfg similarity index 100% rename from contrib/games/stengel.nfg rename to catalog/games/files/stengel.nfg diff --git a/contrib/games/sww1.efg b/catalog/games/files/sww1.efg similarity index 100% rename from contrib/games/sww1.efg rename to catalog/games/files/sww1.efg diff --git a/contrib/games/sww1.nfg b/catalog/games/files/sww1.nfg similarity index 100% rename from contrib/games/sww1.nfg rename to catalog/games/files/sww1.nfg diff --git a/contrib/games/sww2.efg b/catalog/games/files/sww2.efg similarity index 100% rename from contrib/games/sww2.efg rename to catalog/games/files/sww2.efg diff --git a/contrib/games/sww3.efg b/catalog/games/files/sww3.efg similarity index 100% rename from contrib/games/sww3.efg rename to catalog/games/files/sww3.efg diff --git a/contrib/games/tim.efg b/catalog/games/files/tim.efg similarity index 100% rename from contrib/games/tim.efg rename to catalog/games/files/tim.efg diff --git a/contrib/games/todd1.nfg b/catalog/games/files/todd1.nfg similarity index 100% rename from contrib/games/todd1.nfg rename to catalog/games/files/todd1.nfg diff --git a/contrib/games/todd2.nfg b/catalog/games/files/todd2.nfg similarity index 100% rename from contrib/games/todd2.nfg rename to catalog/games/files/todd2.nfg diff --git a/contrib/games/todd3.nfg b/catalog/games/files/todd3.nfg similarity index 100% rename from contrib/games/todd3.nfg rename to catalog/games/files/todd3.nfg diff --git a/contrib/games/ttt.efg b/catalog/games/files/ttt.efg similarity index 100% rename from contrib/games/ttt.efg rename to catalog/games/files/ttt.efg diff --git a/contrib/games/vd.efg b/catalog/games/files/vd.efg similarity index 100% rename from contrib/games/vd.efg rename to catalog/games/files/vd.efg diff --git a/contrib/games/vd.nfg b/catalog/games/files/vd.nfg similarity index 100% rename from contrib/games/vd.nfg rename to catalog/games/files/vd.nfg diff --git a/contrib/games/w_ex1.efg b/catalog/games/files/w_ex1.efg similarity index 100% rename from contrib/games/w_ex1.efg rename to catalog/games/files/w_ex1.efg diff --git a/contrib/games/w_ex2.efg b/catalog/games/files/w_ex2.efg similarity index 100% rename from contrib/games/w_ex2.efg rename to catalog/games/files/w_ex2.efg diff --git a/contrib/games/wilson1.efg b/catalog/games/files/wilson1.efg similarity index 100% rename from contrib/games/wilson1.efg rename to catalog/games/files/wilson1.efg diff --git a/contrib/games/wink3.nfg b/catalog/games/files/wink3.nfg similarity index 100% rename from contrib/games/wink3.nfg rename to catalog/games/files/wink3.nfg diff --git a/contrib/games/winkels.nfg b/catalog/games/files/winkels.nfg similarity index 100% rename from contrib/games/winkels.nfg rename to catalog/games/files/winkels.nfg diff --git a/contrib/games/work1.efg b/catalog/games/files/work1.efg similarity index 100% rename from contrib/games/work1.efg rename to catalog/games/files/work1.efg diff --git a/contrib/games/work2.efg b/catalog/games/files/work2.efg similarity index 100% rename from contrib/games/work2.efg rename to catalog/games/files/work2.efg diff --git a/contrib/games/work3.efg b/catalog/games/files/work3.efg similarity index 100% rename from contrib/games/work3.efg rename to catalog/games/files/work3.efg diff --git a/contrib/games/yamamoto.nfg b/catalog/games/files/yamamoto.nfg similarity index 100% rename from contrib/games/yamamoto.nfg rename to catalog/games/files/yamamoto.nfg diff --git a/contrib/games/zero.nfg b/catalog/games/files/zero.nfg similarity index 100% rename from contrib/games/zero.nfg rename to catalog/games/files/zero.nfg From 4a66d92c1adb38e721e9cae6723ae17133277a38 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 13:51:34 +0000 Subject: [PATCH 05/76] rename gamefiles dir --- catalog/catalog.py | 0 catalog/{games/files => gamefiles}/2s2x2x2.efg | 0 catalog/{games/files => gamefiles}/2smp.efg | 0 catalog/{games/files => gamefiles}/2x2.agg | 0 catalog/{games/files => gamefiles}/2x2.nfg | 0 catalog/{games/files => gamefiles}/2x2a.nfg | 0 catalog/{games/files => gamefiles}/2x2const.nfg | 0 catalog/{games/files => gamefiles}/2x2x2-nau.nfg | 0 catalog/{games/files => gamefiles}/2x2x2.efg | 0 catalog/{games/files => gamefiles}/2x2x2.nfg | 0 catalog/{games/files => gamefiles}/2x2x2x2.nfg | 0 catalog/{games/files => gamefiles}/2x2x2x2x2.nfg | 0 catalog/{games/files => gamefiles}/3x3x3.nfg | 0 catalog/{games/files => gamefiles}/4cards.efg | 0 catalog/{games/files => gamefiles}/5x4x3.nfg | 0 catalog/{games/files => gamefiles}/8x2x2.nfg | 0 catalog/{games/files => gamefiles}/8x8.nfg | 0 catalog/{games/files => gamefiles}/BSS_S_085.Weighted.agg | 0 catalog/{games/files => gamefiles}/Bayesian-Coffee-3-2-2-3.bagg | 0 catalog/{games/files => gamefiles}/GenRPS5.agg | 0 catalog/{games/files => gamefiles}/artist1.efg | 0 catalog/{games/files => gamefiles}/artist2.efg | 0 catalog/{games/files => gamefiles}/badgame1.efg | 0 catalog/{games/files => gamefiles}/badgame2.efg | 0 catalog/{games/files => gamefiles}/bagwell.efg | 0 catalog/{games/files => gamefiles}/bayes1a.efg | 0 catalog/{games/files => gamefiles}/bayes2a.efg | 0 catalog/{games/files => gamefiles}/bcp2.efg | 0 catalog/{games/files => gamefiles}/bcp3.efg | 0 catalog/{games/files => gamefiles}/bcp4.efg | 0 catalog/{games/files => gamefiles}/bhg1.efg | 0 catalog/{games/files => gamefiles}/bhg2.efg | 0 catalog/{games/files => gamefiles}/bhg3.efg | 0 catalog/{games/files => gamefiles}/bhg4.efg | 0 catalog/{games/files => gamefiles}/bhg5.efg | 0 catalog/{games/files => gamefiles}/caro2.efg | 0 catalog/{games/files => gamefiles}/cent2.efg | 0 catalog/{games/files => gamefiles}/cent2.nfg | 0 catalog/{games/files => gamefiles}/cent3.efg | 0 catalog/{games/files => gamefiles}/cent4.efg | 0 catalog/{games/files => gamefiles}/cent6.efg | 0 catalog/{games/files => gamefiles}/centcs10.efg | 0 catalog/{games/files => gamefiles}/centcs6.efg | 0 catalog/{games/files => gamefiles}/condjury.efg | 0 catalog/{games/files => gamefiles}/coord2.efg | 0 catalog/{games/files => gamefiles}/coord2.nfg | 0 catalog/{games/files => gamefiles}/coord2ts.efg | 0 catalog/{games/files => gamefiles}/coord3.efg | 0 catalog/{games/files => gamefiles}/coord3.nfg | 0 catalog/{games/files => gamefiles}/coord333.nfg | 0 catalog/{games/files => gamefiles}/coord4.efg | 0 catalog/{games/files => gamefiles}/coord4.nfg | 0 catalog/{games/files => gamefiles}/cross.efg | 0 catalog/{games/files => gamefiles}/cs.efg | 0 catalog/{games/files => gamefiles}/csg1.nfg | 0 catalog/{games/files => gamefiles}/csg2.nfg | 0 catalog/{games/files => gamefiles}/csg3.nfg | 0 catalog/{games/files => gamefiles}/csg4.nfg | 0 catalog/{games/files => gamefiles}/deg1.nfg | 0 catalog/{games/files => gamefiles}/deg2.nfg | 0 catalog/{games/files => gamefiles}/e01.efg | 0 catalog/{games/files => gamefiles}/e01.nfg | 0 catalog/{games/files => gamefiles}/e02.efg | 0 catalog/{games/files => gamefiles}/e02.nfg | 0 catalog/{games/files => gamefiles}/e03.efg | 0 catalog/{games/files => gamefiles}/e04.efg | 0 catalog/{games/files => gamefiles}/e04.nfg | 0 catalog/{games/files => gamefiles}/e05.efg | 0 catalog/{games/files => gamefiles}/e06.efg | 0 catalog/{games/files => gamefiles}/e07.efg | 0 catalog/{games/files => gamefiles}/e07.nfg | 0 catalog/{games/files => gamefiles}/e08.efg | 0 catalog/{games/files => gamefiles}/e09.efg | 0 catalog/{games/files => gamefiles}/e10.efg | 0 catalog/{games/files => gamefiles}/e10a.efg | 0 catalog/{games/files => gamefiles}/e13.efg | 0 catalog/{games/files => gamefiles}/e16.efg | 0 catalog/{games/files => gamefiles}/e17.efg | 0 catalog/{games/files => gamefiles}/e18.efg | 0 catalog/{games/files => gamefiles}/g1.efg | 0 catalog/{games/files => gamefiles}/g1.nfg | 0 catalog/{games/files => gamefiles}/g2.efg | 0 catalog/{games/files => gamefiles}/g2.nfg | 0 catalog/{games/files => gamefiles}/g3.efg | 0 catalog/{games/files => gamefiles}/g3.nfg | 0 catalog/{games/files => gamefiles}/holdout.efg | 0 catalog/{games/files => gamefiles}/holdout7.efg | 0 catalog/{games/files => gamefiles}/hs1.efg | 0 catalog/{games/files => gamefiles}/jury_mr.efg | 0 catalog/{games/files => gamefiles}/jury_un.efg | 0 catalog/{games/files => gamefiles}/km1.efg | 0 catalog/{games/files => gamefiles}/km2.efg | 0 catalog/{games/files => gamefiles}/km3.efg | 0 catalog/{games/files => gamefiles}/km6.efg | 0 catalog/{games/files => gamefiles}/loopback.nfg | 0 catalog/{games/files => gamefiles}/mixdom.nfg | 0 catalog/{games/files => gamefiles}/mixdom2.nfg | 0 catalog/{games/files => gamefiles}/montyhal.efg | 0 catalog/{games/files => gamefiles}/my_2-1.efg | 0 catalog/{games/files => gamefiles}/my_2-4.efg | 0 catalog/{games/files => gamefiles}/my_2-8.efg | 0 catalog/{games/files => gamefiles}/my_3-3a.efg | 0 catalog/{games/files => gamefiles}/my_3-3b.efg | 0 catalog/{games/files => gamefiles}/my_3-3c.efg | 0 catalog/{games/files => gamefiles}/my_3-3d.efg | 0 catalog/{games/files => gamefiles}/my_3-3e.efg | 0 catalog/{games/files => gamefiles}/my_3-4.efg | 0 catalog/{games/files => gamefiles}/myerson.efg | 0 catalog/{games/files => gamefiles}/myerson_fig_4_2.efg | 0 catalog/{games/files => gamefiles}/nim.efg | 0 catalog/{games/files => gamefiles}/nim7.efg | 0 catalog/{games/files => gamefiles}/oneill.nfg | 0 catalog/{games/files => gamefiles}/palf.efg | 0 catalog/{games/files => gamefiles}/palf2.efg | 0 catalog/{games/files => gamefiles}/palf3.efg | 0 catalog/{games/files => gamefiles}/pbride.efg | 0 catalog/{games/files => gamefiles}/pd.nfg | 0 catalog/{games/files => gamefiles}/perfect1.nfg | 0 catalog/{games/files => gamefiles}/perfect2.nfg | 0 catalog/{games/files => gamefiles}/perfect3.nfg | 0 catalog/{games/files => gamefiles}/poker.efg | 0 catalog/{games/files => gamefiles}/poker.nfg | 0 catalog/{games/files => gamefiles}/poker2.efg | 0 catalog/{games/files => gamefiles}/pvw.efg | 0 catalog/{games/files => gamefiles}/pvw2.efg | 0 catalog/{games/files => gamefiles}/sh3.efg | 0 catalog/{games/files => gamefiles}/sh3.nfg | 0 catalog/{games/files => gamefiles}/spence.efg | 0 catalog/{games/files => gamefiles}/stengel.nfg | 0 catalog/{games/files => gamefiles}/sww1.efg | 0 catalog/{games/files => gamefiles}/sww1.nfg | 0 catalog/{games/files => gamefiles}/sww2.efg | 0 catalog/{games/files => gamefiles}/sww3.efg | 0 catalog/{games/files => gamefiles}/tim.efg | 0 catalog/{games/files => gamefiles}/todd1.nfg | 0 catalog/{games/files => gamefiles}/todd2.nfg | 0 catalog/{games/files => gamefiles}/todd3.nfg | 0 catalog/{games/files => gamefiles}/ttt.efg | 0 catalog/{games/files => gamefiles}/vd.efg | 0 catalog/{games/files => gamefiles}/vd.nfg | 0 catalog/{games/files => gamefiles}/w_ex1.efg | 0 catalog/{games/files => gamefiles}/w_ex2.efg | 0 catalog/{games/files => gamefiles}/wilson1.efg | 0 catalog/{games/files => gamefiles}/wink3.nfg | 0 catalog/{games/files => gamefiles}/winkels.nfg | 0 catalog/{games/files => gamefiles}/work1.efg | 0 catalog/{games/files => gamefiles}/work2.efg | 0 catalog/{games/files => gamefiles}/work3.efg | 0 catalog/{games/files => gamefiles}/yamamoto.nfg | 0 catalog/{games/files => gamefiles}/zero.nfg | 0 150 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 catalog/catalog.py rename catalog/{games/files => gamefiles}/2s2x2x2.efg (100%) rename catalog/{games/files => gamefiles}/2smp.efg (100%) rename catalog/{games/files => gamefiles}/2x2.agg (100%) rename catalog/{games/files => gamefiles}/2x2.nfg (100%) rename catalog/{games/files => gamefiles}/2x2a.nfg (100%) rename catalog/{games/files => gamefiles}/2x2const.nfg (100%) rename catalog/{games/files => gamefiles}/2x2x2-nau.nfg (100%) rename catalog/{games/files => gamefiles}/2x2x2.efg (100%) rename catalog/{games/files => gamefiles}/2x2x2.nfg (100%) rename catalog/{games/files => gamefiles}/2x2x2x2.nfg (100%) rename catalog/{games/files => gamefiles}/2x2x2x2x2.nfg (100%) rename catalog/{games/files => gamefiles}/3x3x3.nfg (100%) rename catalog/{games/files => gamefiles}/4cards.efg (100%) rename catalog/{games/files => gamefiles}/5x4x3.nfg (100%) rename catalog/{games/files => gamefiles}/8x2x2.nfg (100%) rename catalog/{games/files => gamefiles}/8x8.nfg (100%) rename catalog/{games/files => gamefiles}/BSS_S_085.Weighted.agg (100%) rename catalog/{games/files => gamefiles}/Bayesian-Coffee-3-2-2-3.bagg (100%) rename catalog/{games/files => gamefiles}/GenRPS5.agg (100%) rename catalog/{games/files => gamefiles}/artist1.efg (100%) rename catalog/{games/files => gamefiles}/artist2.efg (100%) rename catalog/{games/files => gamefiles}/badgame1.efg (100%) rename catalog/{games/files => gamefiles}/badgame2.efg (100%) rename catalog/{games/files => gamefiles}/bagwell.efg (100%) rename catalog/{games/files => gamefiles}/bayes1a.efg (100%) rename catalog/{games/files => gamefiles}/bayes2a.efg (100%) rename catalog/{games/files => gamefiles}/bcp2.efg (100%) rename catalog/{games/files => gamefiles}/bcp3.efg (100%) rename catalog/{games/files => gamefiles}/bcp4.efg (100%) rename catalog/{games/files => gamefiles}/bhg1.efg (100%) rename catalog/{games/files => gamefiles}/bhg2.efg (100%) rename catalog/{games/files => gamefiles}/bhg3.efg (100%) rename catalog/{games/files => gamefiles}/bhg4.efg (100%) rename catalog/{games/files => gamefiles}/bhg5.efg (100%) rename catalog/{games/files => gamefiles}/caro2.efg (100%) rename catalog/{games/files => gamefiles}/cent2.efg (100%) rename catalog/{games/files => gamefiles}/cent2.nfg (100%) rename catalog/{games/files => gamefiles}/cent3.efg (100%) rename catalog/{games/files => gamefiles}/cent4.efg (100%) rename catalog/{games/files => gamefiles}/cent6.efg (100%) rename catalog/{games/files => gamefiles}/centcs10.efg (100%) rename catalog/{games/files => gamefiles}/centcs6.efg (100%) rename catalog/{games/files => gamefiles}/condjury.efg (100%) rename catalog/{games/files => gamefiles}/coord2.efg (100%) rename catalog/{games/files => gamefiles}/coord2.nfg (100%) rename catalog/{games/files => gamefiles}/coord2ts.efg (100%) rename catalog/{games/files => gamefiles}/coord3.efg (100%) rename catalog/{games/files => gamefiles}/coord3.nfg (100%) rename catalog/{games/files => gamefiles}/coord333.nfg (100%) rename catalog/{games/files => gamefiles}/coord4.efg (100%) rename catalog/{games/files => gamefiles}/coord4.nfg (100%) rename catalog/{games/files => gamefiles}/cross.efg (100%) rename catalog/{games/files => gamefiles}/cs.efg (100%) rename catalog/{games/files => gamefiles}/csg1.nfg (100%) rename catalog/{games/files => gamefiles}/csg2.nfg (100%) rename catalog/{games/files => gamefiles}/csg3.nfg (100%) rename catalog/{games/files => gamefiles}/csg4.nfg (100%) rename catalog/{games/files => gamefiles}/deg1.nfg (100%) rename catalog/{games/files => gamefiles}/deg2.nfg (100%) rename catalog/{games/files => gamefiles}/e01.efg (100%) rename catalog/{games/files => gamefiles}/e01.nfg (100%) rename catalog/{games/files => gamefiles}/e02.efg (100%) rename catalog/{games/files => gamefiles}/e02.nfg (100%) rename catalog/{games/files => gamefiles}/e03.efg (100%) rename catalog/{games/files => gamefiles}/e04.efg (100%) rename catalog/{games/files => gamefiles}/e04.nfg (100%) rename catalog/{games/files => gamefiles}/e05.efg (100%) rename catalog/{games/files => gamefiles}/e06.efg (100%) rename catalog/{games/files => gamefiles}/e07.efg (100%) rename catalog/{games/files => gamefiles}/e07.nfg (100%) rename catalog/{games/files => gamefiles}/e08.efg (100%) rename catalog/{games/files => gamefiles}/e09.efg (100%) rename catalog/{games/files => gamefiles}/e10.efg (100%) rename catalog/{games/files => gamefiles}/e10a.efg (100%) rename catalog/{games/files => gamefiles}/e13.efg (100%) rename catalog/{games/files => gamefiles}/e16.efg (100%) rename catalog/{games/files => gamefiles}/e17.efg (100%) rename catalog/{games/files => gamefiles}/e18.efg (100%) rename catalog/{games/files => gamefiles}/g1.efg (100%) rename catalog/{games/files => gamefiles}/g1.nfg (100%) rename catalog/{games/files => gamefiles}/g2.efg (100%) rename catalog/{games/files => gamefiles}/g2.nfg (100%) rename catalog/{games/files => gamefiles}/g3.efg (100%) rename catalog/{games/files => gamefiles}/g3.nfg (100%) rename catalog/{games/files => gamefiles}/holdout.efg (100%) rename catalog/{games/files => gamefiles}/holdout7.efg (100%) rename catalog/{games/files => gamefiles}/hs1.efg (100%) rename catalog/{games/files => gamefiles}/jury_mr.efg (100%) rename catalog/{games/files => gamefiles}/jury_un.efg (100%) rename catalog/{games/files => gamefiles}/km1.efg (100%) rename catalog/{games/files => gamefiles}/km2.efg (100%) rename catalog/{games/files => gamefiles}/km3.efg (100%) rename catalog/{games/files => gamefiles}/km6.efg (100%) rename catalog/{games/files => gamefiles}/loopback.nfg (100%) rename catalog/{games/files => gamefiles}/mixdom.nfg (100%) rename catalog/{games/files => gamefiles}/mixdom2.nfg (100%) rename catalog/{games/files => gamefiles}/montyhal.efg (100%) rename catalog/{games/files => gamefiles}/my_2-1.efg (100%) rename catalog/{games/files => gamefiles}/my_2-4.efg (100%) rename catalog/{games/files => gamefiles}/my_2-8.efg (100%) rename catalog/{games/files => gamefiles}/my_3-3a.efg (100%) rename catalog/{games/files => gamefiles}/my_3-3b.efg (100%) rename catalog/{games/files => gamefiles}/my_3-3c.efg (100%) rename catalog/{games/files => gamefiles}/my_3-3d.efg (100%) rename catalog/{games/files => gamefiles}/my_3-3e.efg (100%) rename catalog/{games/files => gamefiles}/my_3-4.efg (100%) rename catalog/{games/files => gamefiles}/myerson.efg (100%) rename catalog/{games/files => gamefiles}/myerson_fig_4_2.efg (100%) rename catalog/{games/files => gamefiles}/nim.efg (100%) rename catalog/{games/files => gamefiles}/nim7.efg (100%) rename catalog/{games/files => gamefiles}/oneill.nfg (100%) rename catalog/{games/files => gamefiles}/palf.efg (100%) rename catalog/{games/files => gamefiles}/palf2.efg (100%) rename catalog/{games/files => gamefiles}/palf3.efg (100%) rename catalog/{games/files => gamefiles}/pbride.efg (100%) rename catalog/{games/files => gamefiles}/pd.nfg (100%) rename catalog/{games/files => gamefiles}/perfect1.nfg (100%) rename catalog/{games/files => gamefiles}/perfect2.nfg (100%) rename catalog/{games/files => gamefiles}/perfect3.nfg (100%) rename catalog/{games/files => gamefiles}/poker.efg (100%) rename catalog/{games/files => gamefiles}/poker.nfg (100%) rename catalog/{games/files => gamefiles}/poker2.efg (100%) rename catalog/{games/files => gamefiles}/pvw.efg (100%) rename catalog/{games/files => gamefiles}/pvw2.efg (100%) rename catalog/{games/files => gamefiles}/sh3.efg (100%) rename catalog/{games/files => gamefiles}/sh3.nfg (100%) rename catalog/{games/files => gamefiles}/spence.efg (100%) rename catalog/{games/files => gamefiles}/stengel.nfg (100%) rename catalog/{games/files => gamefiles}/sww1.efg (100%) rename catalog/{games/files => gamefiles}/sww1.nfg (100%) rename catalog/{games/files => gamefiles}/sww2.efg (100%) rename catalog/{games/files => gamefiles}/sww3.efg (100%) rename catalog/{games/files => gamefiles}/tim.efg (100%) rename catalog/{games/files => gamefiles}/todd1.nfg (100%) rename catalog/{games/files => gamefiles}/todd2.nfg (100%) rename catalog/{games/files => gamefiles}/todd3.nfg (100%) rename catalog/{games/files => gamefiles}/ttt.efg (100%) rename catalog/{games/files => gamefiles}/vd.efg (100%) rename catalog/{games/files => gamefiles}/vd.nfg (100%) rename catalog/{games/files => gamefiles}/w_ex1.efg (100%) rename catalog/{games/files => gamefiles}/w_ex2.efg (100%) rename catalog/{games/files => gamefiles}/wilson1.efg (100%) rename catalog/{games/files => gamefiles}/wink3.nfg (100%) rename catalog/{games/files => gamefiles}/winkels.nfg (100%) rename catalog/{games/files => gamefiles}/work1.efg (100%) rename catalog/{games/files => gamefiles}/work2.efg (100%) rename catalog/{games/files => gamefiles}/work3.efg (100%) rename catalog/{games/files => gamefiles}/yamamoto.nfg (100%) rename catalog/{games/files => gamefiles}/zero.nfg (100%) diff --git a/catalog/catalog.py b/catalog/catalog.py new file mode 100644 index 000000000..e69de29bb diff --git a/catalog/games/files/2s2x2x2.efg b/catalog/gamefiles/2s2x2x2.efg similarity index 100% rename from catalog/games/files/2s2x2x2.efg rename to catalog/gamefiles/2s2x2x2.efg diff --git a/catalog/games/files/2smp.efg b/catalog/gamefiles/2smp.efg similarity index 100% rename from catalog/games/files/2smp.efg rename to catalog/gamefiles/2smp.efg diff --git a/catalog/games/files/2x2.agg b/catalog/gamefiles/2x2.agg similarity index 100% rename from catalog/games/files/2x2.agg rename to catalog/gamefiles/2x2.agg diff --git a/catalog/games/files/2x2.nfg b/catalog/gamefiles/2x2.nfg similarity index 100% rename from catalog/games/files/2x2.nfg rename to catalog/gamefiles/2x2.nfg diff --git a/catalog/games/files/2x2a.nfg b/catalog/gamefiles/2x2a.nfg similarity index 100% rename from catalog/games/files/2x2a.nfg rename to catalog/gamefiles/2x2a.nfg diff --git a/catalog/games/files/2x2const.nfg b/catalog/gamefiles/2x2const.nfg similarity index 100% rename from catalog/games/files/2x2const.nfg rename to catalog/gamefiles/2x2const.nfg diff --git a/catalog/games/files/2x2x2-nau.nfg b/catalog/gamefiles/2x2x2-nau.nfg similarity index 100% rename from catalog/games/files/2x2x2-nau.nfg rename to catalog/gamefiles/2x2x2-nau.nfg diff --git a/catalog/games/files/2x2x2.efg b/catalog/gamefiles/2x2x2.efg similarity index 100% rename from catalog/games/files/2x2x2.efg rename to catalog/gamefiles/2x2x2.efg diff --git a/catalog/games/files/2x2x2.nfg b/catalog/gamefiles/2x2x2.nfg similarity index 100% rename from catalog/games/files/2x2x2.nfg rename to catalog/gamefiles/2x2x2.nfg diff --git a/catalog/games/files/2x2x2x2.nfg b/catalog/gamefiles/2x2x2x2.nfg similarity index 100% rename from catalog/games/files/2x2x2x2.nfg rename to catalog/gamefiles/2x2x2x2.nfg diff --git a/catalog/games/files/2x2x2x2x2.nfg b/catalog/gamefiles/2x2x2x2x2.nfg similarity index 100% rename from catalog/games/files/2x2x2x2x2.nfg rename to catalog/gamefiles/2x2x2x2x2.nfg diff --git a/catalog/games/files/3x3x3.nfg b/catalog/gamefiles/3x3x3.nfg similarity index 100% rename from catalog/games/files/3x3x3.nfg rename to catalog/gamefiles/3x3x3.nfg diff --git a/catalog/games/files/4cards.efg b/catalog/gamefiles/4cards.efg similarity index 100% rename from catalog/games/files/4cards.efg rename to catalog/gamefiles/4cards.efg diff --git a/catalog/games/files/5x4x3.nfg b/catalog/gamefiles/5x4x3.nfg similarity index 100% rename from catalog/games/files/5x4x3.nfg rename to catalog/gamefiles/5x4x3.nfg diff --git a/catalog/games/files/8x2x2.nfg b/catalog/gamefiles/8x2x2.nfg similarity index 100% rename from catalog/games/files/8x2x2.nfg rename to catalog/gamefiles/8x2x2.nfg diff --git a/catalog/games/files/8x8.nfg b/catalog/gamefiles/8x8.nfg similarity index 100% rename from catalog/games/files/8x8.nfg rename to catalog/gamefiles/8x8.nfg diff --git a/catalog/games/files/BSS_S_085.Weighted.agg b/catalog/gamefiles/BSS_S_085.Weighted.agg similarity index 100% rename from catalog/games/files/BSS_S_085.Weighted.agg rename to catalog/gamefiles/BSS_S_085.Weighted.agg diff --git a/catalog/games/files/Bayesian-Coffee-3-2-2-3.bagg b/catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg similarity index 100% rename from catalog/games/files/Bayesian-Coffee-3-2-2-3.bagg rename to catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg diff --git a/catalog/games/files/GenRPS5.agg b/catalog/gamefiles/GenRPS5.agg similarity index 100% rename from catalog/games/files/GenRPS5.agg rename to catalog/gamefiles/GenRPS5.agg diff --git a/catalog/games/files/artist1.efg b/catalog/gamefiles/artist1.efg similarity index 100% rename from catalog/games/files/artist1.efg rename to catalog/gamefiles/artist1.efg diff --git a/catalog/games/files/artist2.efg b/catalog/gamefiles/artist2.efg similarity index 100% rename from catalog/games/files/artist2.efg rename to catalog/gamefiles/artist2.efg diff --git a/catalog/games/files/badgame1.efg b/catalog/gamefiles/badgame1.efg similarity index 100% rename from catalog/games/files/badgame1.efg rename to catalog/gamefiles/badgame1.efg diff --git a/catalog/games/files/badgame2.efg b/catalog/gamefiles/badgame2.efg similarity index 100% rename from catalog/games/files/badgame2.efg rename to catalog/gamefiles/badgame2.efg diff --git a/catalog/games/files/bagwell.efg b/catalog/gamefiles/bagwell.efg similarity index 100% rename from catalog/games/files/bagwell.efg rename to catalog/gamefiles/bagwell.efg diff --git a/catalog/games/files/bayes1a.efg b/catalog/gamefiles/bayes1a.efg similarity index 100% rename from catalog/games/files/bayes1a.efg rename to catalog/gamefiles/bayes1a.efg diff --git a/catalog/games/files/bayes2a.efg b/catalog/gamefiles/bayes2a.efg similarity index 100% rename from catalog/games/files/bayes2a.efg rename to catalog/gamefiles/bayes2a.efg diff --git a/catalog/games/files/bcp2.efg b/catalog/gamefiles/bcp2.efg similarity index 100% rename from catalog/games/files/bcp2.efg rename to catalog/gamefiles/bcp2.efg diff --git a/catalog/games/files/bcp3.efg b/catalog/gamefiles/bcp3.efg similarity index 100% rename from catalog/games/files/bcp3.efg rename to catalog/gamefiles/bcp3.efg diff --git a/catalog/games/files/bcp4.efg b/catalog/gamefiles/bcp4.efg similarity index 100% rename from catalog/games/files/bcp4.efg rename to catalog/gamefiles/bcp4.efg diff --git a/catalog/games/files/bhg1.efg b/catalog/gamefiles/bhg1.efg similarity index 100% rename from catalog/games/files/bhg1.efg rename to catalog/gamefiles/bhg1.efg diff --git a/catalog/games/files/bhg2.efg b/catalog/gamefiles/bhg2.efg similarity index 100% rename from catalog/games/files/bhg2.efg rename to catalog/gamefiles/bhg2.efg diff --git a/catalog/games/files/bhg3.efg b/catalog/gamefiles/bhg3.efg similarity index 100% rename from catalog/games/files/bhg3.efg rename to catalog/gamefiles/bhg3.efg diff --git a/catalog/games/files/bhg4.efg b/catalog/gamefiles/bhg4.efg similarity index 100% rename from catalog/games/files/bhg4.efg rename to catalog/gamefiles/bhg4.efg diff --git a/catalog/games/files/bhg5.efg b/catalog/gamefiles/bhg5.efg similarity index 100% rename from catalog/games/files/bhg5.efg rename to catalog/gamefiles/bhg5.efg diff --git a/catalog/games/files/caro2.efg b/catalog/gamefiles/caro2.efg similarity index 100% rename from catalog/games/files/caro2.efg rename to catalog/gamefiles/caro2.efg diff --git a/catalog/games/files/cent2.efg b/catalog/gamefiles/cent2.efg similarity index 100% rename from catalog/games/files/cent2.efg rename to catalog/gamefiles/cent2.efg diff --git a/catalog/games/files/cent2.nfg b/catalog/gamefiles/cent2.nfg similarity index 100% rename from catalog/games/files/cent2.nfg rename to catalog/gamefiles/cent2.nfg diff --git a/catalog/games/files/cent3.efg b/catalog/gamefiles/cent3.efg similarity index 100% rename from catalog/games/files/cent3.efg rename to catalog/gamefiles/cent3.efg diff --git a/catalog/games/files/cent4.efg b/catalog/gamefiles/cent4.efg similarity index 100% rename from catalog/games/files/cent4.efg rename to catalog/gamefiles/cent4.efg diff --git a/catalog/games/files/cent6.efg b/catalog/gamefiles/cent6.efg similarity index 100% rename from catalog/games/files/cent6.efg rename to catalog/gamefiles/cent6.efg diff --git a/catalog/games/files/centcs10.efg b/catalog/gamefiles/centcs10.efg similarity index 100% rename from catalog/games/files/centcs10.efg rename to catalog/gamefiles/centcs10.efg diff --git a/catalog/games/files/centcs6.efg b/catalog/gamefiles/centcs6.efg similarity index 100% rename from catalog/games/files/centcs6.efg rename to catalog/gamefiles/centcs6.efg diff --git a/catalog/games/files/condjury.efg b/catalog/gamefiles/condjury.efg similarity index 100% rename from catalog/games/files/condjury.efg rename to catalog/gamefiles/condjury.efg diff --git a/catalog/games/files/coord2.efg b/catalog/gamefiles/coord2.efg similarity index 100% rename from catalog/games/files/coord2.efg rename to catalog/gamefiles/coord2.efg diff --git a/catalog/games/files/coord2.nfg b/catalog/gamefiles/coord2.nfg similarity index 100% rename from catalog/games/files/coord2.nfg rename to catalog/gamefiles/coord2.nfg diff --git a/catalog/games/files/coord2ts.efg b/catalog/gamefiles/coord2ts.efg similarity index 100% rename from catalog/games/files/coord2ts.efg rename to catalog/gamefiles/coord2ts.efg diff --git a/catalog/games/files/coord3.efg b/catalog/gamefiles/coord3.efg similarity index 100% rename from catalog/games/files/coord3.efg rename to catalog/gamefiles/coord3.efg diff --git a/catalog/games/files/coord3.nfg b/catalog/gamefiles/coord3.nfg similarity index 100% rename from catalog/games/files/coord3.nfg rename to catalog/gamefiles/coord3.nfg diff --git a/catalog/games/files/coord333.nfg b/catalog/gamefiles/coord333.nfg similarity index 100% rename from catalog/games/files/coord333.nfg rename to catalog/gamefiles/coord333.nfg diff --git a/catalog/games/files/coord4.efg b/catalog/gamefiles/coord4.efg similarity index 100% rename from catalog/games/files/coord4.efg rename to catalog/gamefiles/coord4.efg diff --git a/catalog/games/files/coord4.nfg b/catalog/gamefiles/coord4.nfg similarity index 100% rename from catalog/games/files/coord4.nfg rename to catalog/gamefiles/coord4.nfg diff --git a/catalog/games/files/cross.efg b/catalog/gamefiles/cross.efg similarity index 100% rename from catalog/games/files/cross.efg rename to catalog/gamefiles/cross.efg diff --git a/catalog/games/files/cs.efg b/catalog/gamefiles/cs.efg similarity index 100% rename from catalog/games/files/cs.efg rename to catalog/gamefiles/cs.efg diff --git a/catalog/games/files/csg1.nfg b/catalog/gamefiles/csg1.nfg similarity index 100% rename from catalog/games/files/csg1.nfg rename to catalog/gamefiles/csg1.nfg diff --git a/catalog/games/files/csg2.nfg b/catalog/gamefiles/csg2.nfg similarity index 100% rename from catalog/games/files/csg2.nfg rename to catalog/gamefiles/csg2.nfg diff --git a/catalog/games/files/csg3.nfg b/catalog/gamefiles/csg3.nfg similarity index 100% rename from catalog/games/files/csg3.nfg rename to catalog/gamefiles/csg3.nfg diff --git a/catalog/games/files/csg4.nfg b/catalog/gamefiles/csg4.nfg similarity index 100% rename from catalog/games/files/csg4.nfg rename to catalog/gamefiles/csg4.nfg diff --git a/catalog/games/files/deg1.nfg b/catalog/gamefiles/deg1.nfg similarity index 100% rename from catalog/games/files/deg1.nfg rename to catalog/gamefiles/deg1.nfg diff --git a/catalog/games/files/deg2.nfg b/catalog/gamefiles/deg2.nfg similarity index 100% rename from catalog/games/files/deg2.nfg rename to catalog/gamefiles/deg2.nfg diff --git a/catalog/games/files/e01.efg b/catalog/gamefiles/e01.efg similarity index 100% rename from catalog/games/files/e01.efg rename to catalog/gamefiles/e01.efg diff --git a/catalog/games/files/e01.nfg b/catalog/gamefiles/e01.nfg similarity index 100% rename from catalog/games/files/e01.nfg rename to catalog/gamefiles/e01.nfg diff --git a/catalog/games/files/e02.efg b/catalog/gamefiles/e02.efg similarity index 100% rename from catalog/games/files/e02.efg rename to catalog/gamefiles/e02.efg diff --git a/catalog/games/files/e02.nfg b/catalog/gamefiles/e02.nfg similarity index 100% rename from catalog/games/files/e02.nfg rename to catalog/gamefiles/e02.nfg diff --git a/catalog/games/files/e03.efg b/catalog/gamefiles/e03.efg similarity index 100% rename from catalog/games/files/e03.efg rename to catalog/gamefiles/e03.efg diff --git a/catalog/games/files/e04.efg b/catalog/gamefiles/e04.efg similarity index 100% rename from catalog/games/files/e04.efg rename to catalog/gamefiles/e04.efg diff --git a/catalog/games/files/e04.nfg b/catalog/gamefiles/e04.nfg similarity index 100% rename from catalog/games/files/e04.nfg rename to catalog/gamefiles/e04.nfg diff --git a/catalog/games/files/e05.efg b/catalog/gamefiles/e05.efg similarity index 100% rename from catalog/games/files/e05.efg rename to catalog/gamefiles/e05.efg diff --git a/catalog/games/files/e06.efg b/catalog/gamefiles/e06.efg similarity index 100% rename from catalog/games/files/e06.efg rename to catalog/gamefiles/e06.efg diff --git a/catalog/games/files/e07.efg b/catalog/gamefiles/e07.efg similarity index 100% rename from catalog/games/files/e07.efg rename to catalog/gamefiles/e07.efg diff --git a/catalog/games/files/e07.nfg b/catalog/gamefiles/e07.nfg similarity index 100% rename from catalog/games/files/e07.nfg rename to catalog/gamefiles/e07.nfg diff --git a/catalog/games/files/e08.efg b/catalog/gamefiles/e08.efg similarity index 100% rename from catalog/games/files/e08.efg rename to catalog/gamefiles/e08.efg diff --git a/catalog/games/files/e09.efg b/catalog/gamefiles/e09.efg similarity index 100% rename from catalog/games/files/e09.efg rename to catalog/gamefiles/e09.efg diff --git a/catalog/games/files/e10.efg b/catalog/gamefiles/e10.efg similarity index 100% rename from catalog/games/files/e10.efg rename to catalog/gamefiles/e10.efg diff --git a/catalog/games/files/e10a.efg b/catalog/gamefiles/e10a.efg similarity index 100% rename from catalog/games/files/e10a.efg rename to catalog/gamefiles/e10a.efg diff --git a/catalog/games/files/e13.efg b/catalog/gamefiles/e13.efg similarity index 100% rename from catalog/games/files/e13.efg rename to catalog/gamefiles/e13.efg diff --git a/catalog/games/files/e16.efg b/catalog/gamefiles/e16.efg similarity index 100% rename from catalog/games/files/e16.efg rename to catalog/gamefiles/e16.efg diff --git a/catalog/games/files/e17.efg b/catalog/gamefiles/e17.efg similarity index 100% rename from catalog/games/files/e17.efg rename to catalog/gamefiles/e17.efg diff --git a/catalog/games/files/e18.efg b/catalog/gamefiles/e18.efg similarity index 100% rename from catalog/games/files/e18.efg rename to catalog/gamefiles/e18.efg diff --git a/catalog/games/files/g1.efg b/catalog/gamefiles/g1.efg similarity index 100% rename from catalog/games/files/g1.efg rename to catalog/gamefiles/g1.efg diff --git a/catalog/games/files/g1.nfg b/catalog/gamefiles/g1.nfg similarity index 100% rename from catalog/games/files/g1.nfg rename to catalog/gamefiles/g1.nfg diff --git a/catalog/games/files/g2.efg b/catalog/gamefiles/g2.efg similarity index 100% rename from catalog/games/files/g2.efg rename to catalog/gamefiles/g2.efg diff --git a/catalog/games/files/g2.nfg b/catalog/gamefiles/g2.nfg similarity index 100% rename from catalog/games/files/g2.nfg rename to catalog/gamefiles/g2.nfg diff --git a/catalog/games/files/g3.efg b/catalog/gamefiles/g3.efg similarity index 100% rename from catalog/games/files/g3.efg rename to catalog/gamefiles/g3.efg diff --git a/catalog/games/files/g3.nfg b/catalog/gamefiles/g3.nfg similarity index 100% rename from catalog/games/files/g3.nfg rename to catalog/gamefiles/g3.nfg diff --git a/catalog/games/files/holdout.efg b/catalog/gamefiles/holdout.efg similarity index 100% rename from catalog/games/files/holdout.efg rename to catalog/gamefiles/holdout.efg diff --git a/catalog/games/files/holdout7.efg b/catalog/gamefiles/holdout7.efg similarity index 100% rename from catalog/games/files/holdout7.efg rename to catalog/gamefiles/holdout7.efg diff --git a/catalog/games/files/hs1.efg b/catalog/gamefiles/hs1.efg similarity index 100% rename from catalog/games/files/hs1.efg rename to catalog/gamefiles/hs1.efg diff --git a/catalog/games/files/jury_mr.efg b/catalog/gamefiles/jury_mr.efg similarity index 100% rename from catalog/games/files/jury_mr.efg rename to catalog/gamefiles/jury_mr.efg diff --git a/catalog/games/files/jury_un.efg b/catalog/gamefiles/jury_un.efg similarity index 100% rename from catalog/games/files/jury_un.efg rename to catalog/gamefiles/jury_un.efg diff --git a/catalog/games/files/km1.efg b/catalog/gamefiles/km1.efg similarity index 100% rename from catalog/games/files/km1.efg rename to catalog/gamefiles/km1.efg diff --git a/catalog/games/files/km2.efg b/catalog/gamefiles/km2.efg similarity index 100% rename from catalog/games/files/km2.efg rename to catalog/gamefiles/km2.efg diff --git a/catalog/games/files/km3.efg b/catalog/gamefiles/km3.efg similarity index 100% rename from catalog/games/files/km3.efg rename to catalog/gamefiles/km3.efg diff --git a/catalog/games/files/km6.efg b/catalog/gamefiles/km6.efg similarity index 100% rename from catalog/games/files/km6.efg rename to catalog/gamefiles/km6.efg diff --git a/catalog/games/files/loopback.nfg b/catalog/gamefiles/loopback.nfg similarity index 100% rename from catalog/games/files/loopback.nfg rename to catalog/gamefiles/loopback.nfg diff --git a/catalog/games/files/mixdom.nfg b/catalog/gamefiles/mixdom.nfg similarity index 100% rename from catalog/games/files/mixdom.nfg rename to catalog/gamefiles/mixdom.nfg diff --git a/catalog/games/files/mixdom2.nfg b/catalog/gamefiles/mixdom2.nfg similarity index 100% rename from catalog/games/files/mixdom2.nfg rename to catalog/gamefiles/mixdom2.nfg diff --git a/catalog/games/files/montyhal.efg b/catalog/gamefiles/montyhal.efg similarity index 100% rename from catalog/games/files/montyhal.efg rename to catalog/gamefiles/montyhal.efg diff --git a/catalog/games/files/my_2-1.efg b/catalog/gamefiles/my_2-1.efg similarity index 100% rename from catalog/games/files/my_2-1.efg rename to catalog/gamefiles/my_2-1.efg diff --git a/catalog/games/files/my_2-4.efg b/catalog/gamefiles/my_2-4.efg similarity index 100% rename from catalog/games/files/my_2-4.efg rename to catalog/gamefiles/my_2-4.efg diff --git a/catalog/games/files/my_2-8.efg b/catalog/gamefiles/my_2-8.efg similarity index 100% rename from catalog/games/files/my_2-8.efg rename to catalog/gamefiles/my_2-8.efg diff --git a/catalog/games/files/my_3-3a.efg b/catalog/gamefiles/my_3-3a.efg similarity index 100% rename from catalog/games/files/my_3-3a.efg rename to catalog/gamefiles/my_3-3a.efg diff --git a/catalog/games/files/my_3-3b.efg b/catalog/gamefiles/my_3-3b.efg similarity index 100% rename from catalog/games/files/my_3-3b.efg rename to catalog/gamefiles/my_3-3b.efg diff --git a/catalog/games/files/my_3-3c.efg b/catalog/gamefiles/my_3-3c.efg similarity index 100% rename from catalog/games/files/my_3-3c.efg rename to catalog/gamefiles/my_3-3c.efg diff --git a/catalog/games/files/my_3-3d.efg b/catalog/gamefiles/my_3-3d.efg similarity index 100% rename from catalog/games/files/my_3-3d.efg rename to catalog/gamefiles/my_3-3d.efg diff --git a/catalog/games/files/my_3-3e.efg b/catalog/gamefiles/my_3-3e.efg similarity index 100% rename from catalog/games/files/my_3-3e.efg rename to catalog/gamefiles/my_3-3e.efg diff --git a/catalog/games/files/my_3-4.efg b/catalog/gamefiles/my_3-4.efg similarity index 100% rename from catalog/games/files/my_3-4.efg rename to catalog/gamefiles/my_3-4.efg diff --git a/catalog/games/files/myerson.efg b/catalog/gamefiles/myerson.efg similarity index 100% rename from catalog/games/files/myerson.efg rename to catalog/gamefiles/myerson.efg diff --git a/catalog/games/files/myerson_fig_4_2.efg b/catalog/gamefiles/myerson_fig_4_2.efg similarity index 100% rename from catalog/games/files/myerson_fig_4_2.efg rename to catalog/gamefiles/myerson_fig_4_2.efg diff --git a/catalog/games/files/nim.efg b/catalog/gamefiles/nim.efg similarity index 100% rename from catalog/games/files/nim.efg rename to catalog/gamefiles/nim.efg diff --git a/catalog/games/files/nim7.efg b/catalog/gamefiles/nim7.efg similarity index 100% rename from catalog/games/files/nim7.efg rename to catalog/gamefiles/nim7.efg diff --git a/catalog/games/files/oneill.nfg b/catalog/gamefiles/oneill.nfg similarity index 100% rename from catalog/games/files/oneill.nfg rename to catalog/gamefiles/oneill.nfg diff --git a/catalog/games/files/palf.efg b/catalog/gamefiles/palf.efg similarity index 100% rename from catalog/games/files/palf.efg rename to catalog/gamefiles/palf.efg diff --git a/catalog/games/files/palf2.efg b/catalog/gamefiles/palf2.efg similarity index 100% rename from catalog/games/files/palf2.efg rename to catalog/gamefiles/palf2.efg diff --git a/catalog/games/files/palf3.efg b/catalog/gamefiles/palf3.efg similarity index 100% rename from catalog/games/files/palf3.efg rename to catalog/gamefiles/palf3.efg diff --git a/catalog/games/files/pbride.efg b/catalog/gamefiles/pbride.efg similarity index 100% rename from catalog/games/files/pbride.efg rename to catalog/gamefiles/pbride.efg diff --git a/catalog/games/files/pd.nfg b/catalog/gamefiles/pd.nfg similarity index 100% rename from catalog/games/files/pd.nfg rename to catalog/gamefiles/pd.nfg diff --git a/catalog/games/files/perfect1.nfg b/catalog/gamefiles/perfect1.nfg similarity index 100% rename from catalog/games/files/perfect1.nfg rename to catalog/gamefiles/perfect1.nfg diff --git a/catalog/games/files/perfect2.nfg b/catalog/gamefiles/perfect2.nfg similarity index 100% rename from catalog/games/files/perfect2.nfg rename to catalog/gamefiles/perfect2.nfg diff --git a/catalog/games/files/perfect3.nfg b/catalog/gamefiles/perfect3.nfg similarity index 100% rename from catalog/games/files/perfect3.nfg rename to catalog/gamefiles/perfect3.nfg diff --git a/catalog/games/files/poker.efg b/catalog/gamefiles/poker.efg similarity index 100% rename from catalog/games/files/poker.efg rename to catalog/gamefiles/poker.efg diff --git a/catalog/games/files/poker.nfg b/catalog/gamefiles/poker.nfg similarity index 100% rename from catalog/games/files/poker.nfg rename to catalog/gamefiles/poker.nfg diff --git a/catalog/games/files/poker2.efg b/catalog/gamefiles/poker2.efg similarity index 100% rename from catalog/games/files/poker2.efg rename to catalog/gamefiles/poker2.efg diff --git a/catalog/games/files/pvw.efg b/catalog/gamefiles/pvw.efg similarity index 100% rename from catalog/games/files/pvw.efg rename to catalog/gamefiles/pvw.efg diff --git a/catalog/games/files/pvw2.efg b/catalog/gamefiles/pvw2.efg similarity index 100% rename from catalog/games/files/pvw2.efg rename to catalog/gamefiles/pvw2.efg diff --git a/catalog/games/files/sh3.efg b/catalog/gamefiles/sh3.efg similarity index 100% rename from catalog/games/files/sh3.efg rename to catalog/gamefiles/sh3.efg diff --git a/catalog/games/files/sh3.nfg b/catalog/gamefiles/sh3.nfg similarity index 100% rename from catalog/games/files/sh3.nfg rename to catalog/gamefiles/sh3.nfg diff --git a/catalog/games/files/spence.efg b/catalog/gamefiles/spence.efg similarity index 100% rename from catalog/games/files/spence.efg rename to catalog/gamefiles/spence.efg diff --git a/catalog/games/files/stengel.nfg b/catalog/gamefiles/stengel.nfg similarity index 100% rename from catalog/games/files/stengel.nfg rename to catalog/gamefiles/stengel.nfg diff --git a/catalog/games/files/sww1.efg b/catalog/gamefiles/sww1.efg similarity index 100% rename from catalog/games/files/sww1.efg rename to catalog/gamefiles/sww1.efg diff --git a/catalog/games/files/sww1.nfg b/catalog/gamefiles/sww1.nfg similarity index 100% rename from catalog/games/files/sww1.nfg rename to catalog/gamefiles/sww1.nfg diff --git a/catalog/games/files/sww2.efg b/catalog/gamefiles/sww2.efg similarity index 100% rename from catalog/games/files/sww2.efg rename to catalog/gamefiles/sww2.efg diff --git a/catalog/games/files/sww3.efg b/catalog/gamefiles/sww3.efg similarity index 100% rename from catalog/games/files/sww3.efg rename to catalog/gamefiles/sww3.efg diff --git a/catalog/games/files/tim.efg b/catalog/gamefiles/tim.efg similarity index 100% rename from catalog/games/files/tim.efg rename to catalog/gamefiles/tim.efg diff --git a/catalog/games/files/todd1.nfg b/catalog/gamefiles/todd1.nfg similarity index 100% rename from catalog/games/files/todd1.nfg rename to catalog/gamefiles/todd1.nfg diff --git a/catalog/games/files/todd2.nfg b/catalog/gamefiles/todd2.nfg similarity index 100% rename from catalog/games/files/todd2.nfg rename to catalog/gamefiles/todd2.nfg diff --git a/catalog/games/files/todd3.nfg b/catalog/gamefiles/todd3.nfg similarity index 100% rename from catalog/games/files/todd3.nfg rename to catalog/gamefiles/todd3.nfg diff --git a/catalog/games/files/ttt.efg b/catalog/gamefiles/ttt.efg similarity index 100% rename from catalog/games/files/ttt.efg rename to catalog/gamefiles/ttt.efg diff --git a/catalog/games/files/vd.efg b/catalog/gamefiles/vd.efg similarity index 100% rename from catalog/games/files/vd.efg rename to catalog/gamefiles/vd.efg diff --git a/catalog/games/files/vd.nfg b/catalog/gamefiles/vd.nfg similarity index 100% rename from catalog/games/files/vd.nfg rename to catalog/gamefiles/vd.nfg diff --git a/catalog/games/files/w_ex1.efg b/catalog/gamefiles/w_ex1.efg similarity index 100% rename from catalog/games/files/w_ex1.efg rename to catalog/gamefiles/w_ex1.efg diff --git a/catalog/games/files/w_ex2.efg b/catalog/gamefiles/w_ex2.efg similarity index 100% rename from catalog/games/files/w_ex2.efg rename to catalog/gamefiles/w_ex2.efg diff --git a/catalog/games/files/wilson1.efg b/catalog/gamefiles/wilson1.efg similarity index 100% rename from catalog/games/files/wilson1.efg rename to catalog/gamefiles/wilson1.efg diff --git a/catalog/games/files/wink3.nfg b/catalog/gamefiles/wink3.nfg similarity index 100% rename from catalog/games/files/wink3.nfg rename to catalog/gamefiles/wink3.nfg diff --git a/catalog/games/files/winkels.nfg b/catalog/gamefiles/winkels.nfg similarity index 100% rename from catalog/games/files/winkels.nfg rename to catalog/gamefiles/winkels.nfg diff --git a/catalog/games/files/work1.efg b/catalog/gamefiles/work1.efg similarity index 100% rename from catalog/games/files/work1.efg rename to catalog/gamefiles/work1.efg diff --git a/catalog/games/files/work2.efg b/catalog/gamefiles/work2.efg similarity index 100% rename from catalog/games/files/work2.efg rename to catalog/gamefiles/work2.efg diff --git a/catalog/games/files/work3.efg b/catalog/gamefiles/work3.efg similarity index 100% rename from catalog/games/files/work3.efg rename to catalog/gamefiles/work3.efg diff --git a/catalog/games/files/yamamoto.nfg b/catalog/gamefiles/yamamoto.nfg similarity index 100% rename from catalog/games/files/yamamoto.nfg rename to catalog/gamefiles/yamamoto.nfg diff --git a/catalog/games/files/zero.nfg b/catalog/gamefiles/zero.nfg similarity index 100% rename from catalog/games/files/zero.nfg rename to catalog/gamefiles/zero.nfg From c4fc3a86596712dfb08871c010f4c83a2c8e0227 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 14:24:37 +0000 Subject: [PATCH 06/76] initial catalog.py --- catalog/__init__.py | 3 +++ catalog/catalog.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 catalog/__init__.py diff --git a/catalog/__init__.py b/catalog/__init__.py new file mode 100644 index 000000000..a57c5a20f --- /dev/null +++ b/catalog/__init__.py @@ -0,0 +1,3 @@ +from .catalog import PrisonersDilemma, TwoStageMatchingPennies + +__all__ = ["PrisonersDilemma", "TwoStageMatchingPennies"] diff --git a/catalog/catalog.py b/catalog/catalog.py index e69de29bb..fef3c339c 100644 --- a/catalog/catalog.py +++ b/catalog/catalog.py @@ -0,0 +1,28 @@ +from pathlib import Path + +from pygambit import read_efg, read_nfg + +_CATALOG_DIR = Path(__file__).parent +_GAMEFILES_DIR = _CATALOG_DIR / "gamefiles" + + +class CatalogGame: + """ + Base class for catalog games. + This class serves as a template for specific games in the catalog. + Calling any subclass will return an instance of the corresponding game. + """ + def __new__(cls): + raise NotImplementedError("This method should be implemented by subclasses.") + + +class PrisonersDilemma(CatalogGame): + """Prisoner's Dilemma game.""" + def __new__(cls): + return read_nfg(str(_GAMEFILES_DIR / "pd.nfg")) + + +class TwoStageMatchingPennies(CatalogGame): + """Two-Stage Matching Pennies game.""" + def __new__(cls): + return read_efg(str(_GAMEFILES_DIR / "2smp.efg")) From c7deefc8981ca60e74b1ccd814c7b0cc3aa7f36b Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 14:26:44 +0000 Subject: [PATCH 07/76] move catalog into pygambit --- doc/tutorials/01_quickstart.ipynb | 25 +++++++++++++++---- src/pygambit/__init__.py | 1 + {catalog => src/pygambit/catalog}/__init__.py | 0 {catalog => src/pygambit/catalog}/catalog.py | 0 .../pygambit/catalog}/gamefiles/2s2x2x2.efg | 0 .../pygambit/catalog}/gamefiles/2smp.efg | 0 .../pygambit/catalog}/gamefiles/2x2.agg | 0 .../pygambit/catalog}/gamefiles/2x2.nfg | 0 .../pygambit/catalog}/gamefiles/2x2a.nfg | 0 .../pygambit/catalog}/gamefiles/2x2const.nfg | 0 .../pygambit/catalog}/gamefiles/2x2x2-nau.nfg | 0 .../pygambit/catalog}/gamefiles/2x2x2.efg | 0 .../pygambit/catalog}/gamefiles/2x2x2.nfg | 0 .../pygambit/catalog}/gamefiles/2x2x2x2.nfg | 0 .../pygambit/catalog}/gamefiles/2x2x2x2x2.nfg | 0 .../pygambit/catalog}/gamefiles/3x3x3.nfg | 0 .../pygambit/catalog}/gamefiles/4cards.efg | 0 .../pygambit/catalog}/gamefiles/5x4x3.nfg | 0 .../pygambit/catalog}/gamefiles/8x2x2.nfg | 0 .../pygambit/catalog}/gamefiles/8x8.nfg | 0 .../catalog}/gamefiles/BSS_S_085.Weighted.agg | 0 .../gamefiles/Bayesian-Coffee-3-2-2-3.bagg | 0 .../pygambit/catalog}/gamefiles/GenRPS5.agg | 0 .../pygambit/catalog}/gamefiles/artist1.efg | 0 .../pygambit/catalog}/gamefiles/artist2.efg | 0 .../pygambit/catalog}/gamefiles/badgame1.efg | 0 .../pygambit/catalog}/gamefiles/badgame2.efg | 0 .../pygambit/catalog}/gamefiles/bagwell.efg | 0 .../pygambit/catalog}/gamefiles/bayes1a.efg | 0 .../pygambit/catalog}/gamefiles/bayes2a.efg | 0 .../pygambit/catalog}/gamefiles/bcp2.efg | 0 .../pygambit/catalog}/gamefiles/bcp3.efg | 0 .../pygambit/catalog}/gamefiles/bcp4.efg | 0 .../pygambit/catalog}/gamefiles/bhg1.efg | 0 .../pygambit/catalog}/gamefiles/bhg2.efg | 0 .../pygambit/catalog}/gamefiles/bhg3.efg | 0 .../pygambit/catalog}/gamefiles/bhg4.efg | 0 .../pygambit/catalog}/gamefiles/bhg5.efg | 0 .../pygambit/catalog}/gamefiles/caro2.efg | 0 .../pygambit/catalog}/gamefiles/cent2.efg | 0 .../pygambit/catalog}/gamefiles/cent2.nfg | 0 .../pygambit/catalog}/gamefiles/cent3.efg | 0 .../pygambit/catalog}/gamefiles/cent4.efg | 0 .../pygambit/catalog}/gamefiles/cent6.efg | 0 .../pygambit/catalog}/gamefiles/centcs10.efg | 0 .../pygambit/catalog}/gamefiles/centcs6.efg | 0 .../pygambit/catalog}/gamefiles/condjury.efg | 0 .../pygambit/catalog}/gamefiles/coord2.efg | 0 .../pygambit/catalog}/gamefiles/coord2.nfg | 0 .../pygambit/catalog}/gamefiles/coord2ts.efg | 0 .../pygambit/catalog}/gamefiles/coord3.efg | 0 .../pygambit/catalog}/gamefiles/coord3.nfg | 0 .../pygambit/catalog}/gamefiles/coord333.nfg | 0 .../pygambit/catalog}/gamefiles/coord4.efg | 0 .../pygambit/catalog}/gamefiles/coord4.nfg | 0 .../pygambit/catalog}/gamefiles/cross.efg | 0 .../pygambit/catalog}/gamefiles/cs.efg | 0 .../pygambit/catalog}/gamefiles/csg1.nfg | 0 .../pygambit/catalog}/gamefiles/csg2.nfg | 0 .../pygambit/catalog}/gamefiles/csg3.nfg | 0 .../pygambit/catalog}/gamefiles/csg4.nfg | 0 .../pygambit/catalog}/gamefiles/deg1.nfg | 0 .../pygambit/catalog}/gamefiles/deg2.nfg | 0 .../pygambit/catalog}/gamefiles/e01.efg | 0 .../pygambit/catalog}/gamefiles/e01.nfg | 0 .../pygambit/catalog}/gamefiles/e02.efg | 0 .../pygambit/catalog}/gamefiles/e02.nfg | 0 .../pygambit/catalog}/gamefiles/e03.efg | 0 .../pygambit/catalog}/gamefiles/e04.efg | 0 .../pygambit/catalog}/gamefiles/e04.nfg | 0 .../pygambit/catalog}/gamefiles/e05.efg | 0 .../pygambit/catalog}/gamefiles/e06.efg | 0 .../pygambit/catalog}/gamefiles/e07.efg | 0 .../pygambit/catalog}/gamefiles/e07.nfg | 0 .../pygambit/catalog}/gamefiles/e08.efg | 0 .../pygambit/catalog}/gamefiles/e09.efg | 0 .../pygambit/catalog}/gamefiles/e10.efg | 0 .../pygambit/catalog}/gamefiles/e10a.efg | 0 .../pygambit/catalog}/gamefiles/e13.efg | 0 .../pygambit/catalog}/gamefiles/e16.efg | 0 .../pygambit/catalog}/gamefiles/e17.efg | 0 .../pygambit/catalog}/gamefiles/e18.efg | 0 .../pygambit/catalog}/gamefiles/g1.efg | 0 .../pygambit/catalog}/gamefiles/g1.nfg | 0 .../pygambit/catalog}/gamefiles/g2.efg | 0 .../pygambit/catalog}/gamefiles/g2.nfg | 0 .../pygambit/catalog}/gamefiles/g3.efg | 0 .../pygambit/catalog}/gamefiles/g3.nfg | 0 .../pygambit/catalog}/gamefiles/holdout.efg | 0 .../pygambit/catalog}/gamefiles/holdout7.efg | 0 .../pygambit/catalog}/gamefiles/hs1.efg | 0 .../pygambit/catalog}/gamefiles/jury_mr.efg | 0 .../pygambit/catalog}/gamefiles/jury_un.efg | 0 .../pygambit/catalog}/gamefiles/km1.efg | 0 .../pygambit/catalog}/gamefiles/km2.efg | 0 .../pygambit/catalog}/gamefiles/km3.efg | 0 .../pygambit/catalog}/gamefiles/km6.efg | 0 .../pygambit/catalog}/gamefiles/loopback.nfg | 0 .../pygambit/catalog}/gamefiles/mixdom.nfg | 0 .../pygambit/catalog}/gamefiles/mixdom2.nfg | 0 .../pygambit/catalog}/gamefiles/montyhal.efg | 0 .../pygambit/catalog}/gamefiles/my_2-1.efg | 0 .../pygambit/catalog}/gamefiles/my_2-4.efg | 0 .../pygambit/catalog}/gamefiles/my_2-8.efg | 0 .../pygambit/catalog}/gamefiles/my_3-3a.efg | 0 .../pygambit/catalog}/gamefiles/my_3-3b.efg | 0 .../pygambit/catalog}/gamefiles/my_3-3c.efg | 0 .../pygambit/catalog}/gamefiles/my_3-3d.efg | 0 .../pygambit/catalog}/gamefiles/my_3-3e.efg | 0 .../pygambit/catalog}/gamefiles/my_3-4.efg | 0 .../pygambit/catalog}/gamefiles/myerson.efg | 0 .../catalog}/gamefiles/myerson_fig_4_2.efg | 0 .../pygambit/catalog}/gamefiles/nim.efg | 0 .../pygambit/catalog}/gamefiles/nim7.efg | 0 .../pygambit/catalog}/gamefiles/oneill.nfg | 0 .../pygambit/catalog}/gamefiles/palf.efg | 0 .../pygambit/catalog}/gamefiles/palf2.efg | 0 .../pygambit/catalog}/gamefiles/palf3.efg | 0 .../pygambit/catalog}/gamefiles/pbride.efg | 0 .../pygambit/catalog}/gamefiles/pd.nfg | 0 .../pygambit/catalog}/gamefiles/perfect1.nfg | 0 .../pygambit/catalog}/gamefiles/perfect2.nfg | 0 .../pygambit/catalog}/gamefiles/perfect3.nfg | 0 .../pygambit/catalog}/gamefiles/poker.efg | 0 .../pygambit/catalog}/gamefiles/poker.nfg | 0 .../pygambit/catalog}/gamefiles/poker2.efg | 0 .../pygambit/catalog}/gamefiles/pvw.efg | 0 .../pygambit/catalog}/gamefiles/pvw2.efg | 0 .../pygambit/catalog}/gamefiles/sh3.efg | 0 .../pygambit/catalog}/gamefiles/sh3.nfg | 0 .../pygambit/catalog}/gamefiles/spence.efg | 0 .../pygambit/catalog}/gamefiles/stengel.nfg | 0 .../pygambit/catalog}/gamefiles/sww1.efg | 0 .../pygambit/catalog}/gamefiles/sww1.nfg | 0 .../pygambit/catalog}/gamefiles/sww2.efg | 0 .../pygambit/catalog}/gamefiles/sww3.efg | 0 .../pygambit/catalog}/gamefiles/tim.efg | 0 .../pygambit/catalog}/gamefiles/todd1.nfg | 0 .../pygambit/catalog}/gamefiles/todd2.nfg | 0 .../pygambit/catalog}/gamefiles/todd3.nfg | 0 .../pygambit/catalog}/gamefiles/ttt.efg | 0 .../pygambit/catalog}/gamefiles/vd.efg | 0 .../pygambit/catalog}/gamefiles/vd.nfg | 0 .../pygambit/catalog}/gamefiles/w_ex1.efg | 0 .../pygambit/catalog}/gamefiles/w_ex2.efg | 0 .../pygambit/catalog}/gamefiles/wilson1.efg | 0 .../pygambit/catalog}/gamefiles/wink3.nfg | 0 .../pygambit/catalog}/gamefiles/winkels.nfg | 0 .../pygambit/catalog}/gamefiles/work1.efg | 0 .../pygambit/catalog}/gamefiles/work2.efg | 0 .../pygambit/catalog}/gamefiles/work3.efg | 0 .../pygambit/catalog}/gamefiles/yamamoto.nfg | 0 .../pygambit/catalog}/gamefiles/zero.nfg | 0 153 files changed, 21 insertions(+), 5 deletions(-) rename {catalog => src/pygambit/catalog}/__init__.py (100%) rename {catalog => src/pygambit/catalog}/catalog.py (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2s2x2x2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2smp.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2x2.agg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2x2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2x2a.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2x2const.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2x2x2-nau.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2x2x2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2x2x2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2x2x2x2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/2x2x2x2x2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/3x3x3.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/4cards.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/5x4x3.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/8x2x2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/8x8.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/BSS_S_085.Weighted.agg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/Bayesian-Coffee-3-2-2-3.bagg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/GenRPS5.agg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/artist1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/artist2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/badgame1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/badgame2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bagwell.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bayes1a.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bayes2a.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bcp2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bcp3.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bcp4.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bhg1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bhg2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bhg3.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bhg4.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/bhg5.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/caro2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/cent2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/cent2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/cent3.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/cent4.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/cent6.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/centcs10.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/centcs6.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/condjury.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/coord2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/coord2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/coord2ts.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/coord3.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/coord3.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/coord333.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/coord4.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/coord4.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/cross.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/cs.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/csg1.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/csg2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/csg3.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/csg4.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/deg1.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/deg2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e01.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e01.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e02.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e02.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e03.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e04.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e04.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e05.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e06.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e07.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e07.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e08.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e09.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e10.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e10a.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e13.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e16.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e17.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/e18.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/g1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/g1.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/g2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/g2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/g3.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/g3.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/holdout.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/holdout7.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/hs1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/jury_mr.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/jury_un.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/km1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/km2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/km3.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/km6.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/loopback.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/mixdom.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/mixdom2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/montyhal.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/my_2-1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/my_2-4.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/my_2-8.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/my_3-3a.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/my_3-3b.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/my_3-3c.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/my_3-3d.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/my_3-3e.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/my_3-4.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/myerson.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/myerson_fig_4_2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/nim.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/nim7.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/oneill.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/palf.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/palf2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/palf3.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/pbride.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/pd.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/perfect1.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/perfect2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/perfect3.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/poker.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/poker.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/poker2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/pvw.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/pvw2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/sh3.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/sh3.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/spence.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/stengel.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/sww1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/sww1.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/sww2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/sww3.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/tim.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/todd1.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/todd2.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/todd3.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/ttt.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/vd.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/vd.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/w_ex1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/w_ex2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/wilson1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/wink3.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/winkels.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/work1.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/work2.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/work3.efg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/yamamoto.nfg (100%) rename {catalog => src/pygambit/catalog}/gamefiles/zero.nfg (100%) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index e7c6ee954..c6a9a6dc6 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "c58d382d", "metadata": {}, "outputs": [], @@ -449,13 +449,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "6db7a29a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "

Two person Prisoner's Dilemma game

\n", + "
12
19,90,10
210,01,1
\n" + ], + "text/plain": [ + "Game(title='Two person Prisoner's Dilemma game')" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# g = gbt.catalog.PrisonersDilemma()\n", - "# g" + "g = gbt.catalog.PrisonersDilemma()\n", + "g" ] }, { diff --git a/src/pygambit/__init__.py b/src/pygambit/__init__.py index 1d6f730bf..5f9fb08cc 100644 --- a/src/pygambit/__init__.py +++ b/src/pygambit/__init__.py @@ -26,6 +26,7 @@ nash, # noqa: F401 qre, # noqa: F401 supports, # noqa: F401 + catalog, # noqa: F401 ) import importlib.metadata diff --git a/catalog/__init__.py b/src/pygambit/catalog/__init__.py similarity index 100% rename from catalog/__init__.py rename to src/pygambit/catalog/__init__.py diff --git a/catalog/catalog.py b/src/pygambit/catalog/catalog.py similarity index 100% rename from catalog/catalog.py rename to src/pygambit/catalog/catalog.py diff --git a/catalog/gamefiles/2s2x2x2.efg b/src/pygambit/catalog/gamefiles/2s2x2x2.efg similarity index 100% rename from catalog/gamefiles/2s2x2x2.efg rename to src/pygambit/catalog/gamefiles/2s2x2x2.efg diff --git a/catalog/gamefiles/2smp.efg b/src/pygambit/catalog/gamefiles/2smp.efg similarity index 100% rename from catalog/gamefiles/2smp.efg rename to src/pygambit/catalog/gamefiles/2smp.efg diff --git a/catalog/gamefiles/2x2.agg b/src/pygambit/catalog/gamefiles/2x2.agg similarity index 100% rename from catalog/gamefiles/2x2.agg rename to src/pygambit/catalog/gamefiles/2x2.agg diff --git a/catalog/gamefiles/2x2.nfg b/src/pygambit/catalog/gamefiles/2x2.nfg similarity index 100% rename from catalog/gamefiles/2x2.nfg rename to src/pygambit/catalog/gamefiles/2x2.nfg diff --git a/catalog/gamefiles/2x2a.nfg b/src/pygambit/catalog/gamefiles/2x2a.nfg similarity index 100% rename from catalog/gamefiles/2x2a.nfg rename to src/pygambit/catalog/gamefiles/2x2a.nfg diff --git a/catalog/gamefiles/2x2const.nfg b/src/pygambit/catalog/gamefiles/2x2const.nfg similarity index 100% rename from catalog/gamefiles/2x2const.nfg rename to src/pygambit/catalog/gamefiles/2x2const.nfg diff --git a/catalog/gamefiles/2x2x2-nau.nfg b/src/pygambit/catalog/gamefiles/2x2x2-nau.nfg similarity index 100% rename from catalog/gamefiles/2x2x2-nau.nfg rename to src/pygambit/catalog/gamefiles/2x2x2-nau.nfg diff --git a/catalog/gamefiles/2x2x2.efg b/src/pygambit/catalog/gamefiles/2x2x2.efg similarity index 100% rename from catalog/gamefiles/2x2x2.efg rename to src/pygambit/catalog/gamefiles/2x2x2.efg diff --git a/catalog/gamefiles/2x2x2.nfg b/src/pygambit/catalog/gamefiles/2x2x2.nfg similarity index 100% rename from catalog/gamefiles/2x2x2.nfg rename to src/pygambit/catalog/gamefiles/2x2x2.nfg diff --git a/catalog/gamefiles/2x2x2x2.nfg b/src/pygambit/catalog/gamefiles/2x2x2x2.nfg similarity index 100% rename from catalog/gamefiles/2x2x2x2.nfg rename to src/pygambit/catalog/gamefiles/2x2x2x2.nfg diff --git a/catalog/gamefiles/2x2x2x2x2.nfg b/src/pygambit/catalog/gamefiles/2x2x2x2x2.nfg similarity index 100% rename from catalog/gamefiles/2x2x2x2x2.nfg rename to src/pygambit/catalog/gamefiles/2x2x2x2x2.nfg diff --git a/catalog/gamefiles/3x3x3.nfg b/src/pygambit/catalog/gamefiles/3x3x3.nfg similarity index 100% rename from catalog/gamefiles/3x3x3.nfg rename to src/pygambit/catalog/gamefiles/3x3x3.nfg diff --git a/catalog/gamefiles/4cards.efg b/src/pygambit/catalog/gamefiles/4cards.efg similarity index 100% rename from catalog/gamefiles/4cards.efg rename to src/pygambit/catalog/gamefiles/4cards.efg diff --git a/catalog/gamefiles/5x4x3.nfg b/src/pygambit/catalog/gamefiles/5x4x3.nfg similarity index 100% rename from catalog/gamefiles/5x4x3.nfg rename to src/pygambit/catalog/gamefiles/5x4x3.nfg diff --git a/catalog/gamefiles/8x2x2.nfg b/src/pygambit/catalog/gamefiles/8x2x2.nfg similarity index 100% rename from catalog/gamefiles/8x2x2.nfg rename to src/pygambit/catalog/gamefiles/8x2x2.nfg diff --git a/catalog/gamefiles/8x8.nfg b/src/pygambit/catalog/gamefiles/8x8.nfg similarity index 100% rename from catalog/gamefiles/8x8.nfg rename to src/pygambit/catalog/gamefiles/8x8.nfg diff --git a/catalog/gamefiles/BSS_S_085.Weighted.agg b/src/pygambit/catalog/gamefiles/BSS_S_085.Weighted.agg similarity index 100% rename from catalog/gamefiles/BSS_S_085.Weighted.agg rename to src/pygambit/catalog/gamefiles/BSS_S_085.Weighted.agg diff --git a/catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg b/src/pygambit/catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg similarity index 100% rename from catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg rename to src/pygambit/catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg diff --git a/catalog/gamefiles/GenRPS5.agg b/src/pygambit/catalog/gamefiles/GenRPS5.agg similarity index 100% rename from catalog/gamefiles/GenRPS5.agg rename to src/pygambit/catalog/gamefiles/GenRPS5.agg diff --git a/catalog/gamefiles/artist1.efg b/src/pygambit/catalog/gamefiles/artist1.efg similarity index 100% rename from catalog/gamefiles/artist1.efg rename to src/pygambit/catalog/gamefiles/artist1.efg diff --git a/catalog/gamefiles/artist2.efg b/src/pygambit/catalog/gamefiles/artist2.efg similarity index 100% rename from catalog/gamefiles/artist2.efg rename to src/pygambit/catalog/gamefiles/artist2.efg diff --git a/catalog/gamefiles/badgame1.efg b/src/pygambit/catalog/gamefiles/badgame1.efg similarity index 100% rename from catalog/gamefiles/badgame1.efg rename to src/pygambit/catalog/gamefiles/badgame1.efg diff --git a/catalog/gamefiles/badgame2.efg b/src/pygambit/catalog/gamefiles/badgame2.efg similarity index 100% rename from catalog/gamefiles/badgame2.efg rename to src/pygambit/catalog/gamefiles/badgame2.efg diff --git a/catalog/gamefiles/bagwell.efg b/src/pygambit/catalog/gamefiles/bagwell.efg similarity index 100% rename from catalog/gamefiles/bagwell.efg rename to src/pygambit/catalog/gamefiles/bagwell.efg diff --git a/catalog/gamefiles/bayes1a.efg b/src/pygambit/catalog/gamefiles/bayes1a.efg similarity index 100% rename from catalog/gamefiles/bayes1a.efg rename to src/pygambit/catalog/gamefiles/bayes1a.efg diff --git a/catalog/gamefiles/bayes2a.efg b/src/pygambit/catalog/gamefiles/bayes2a.efg similarity index 100% rename from catalog/gamefiles/bayes2a.efg rename to src/pygambit/catalog/gamefiles/bayes2a.efg diff --git a/catalog/gamefiles/bcp2.efg b/src/pygambit/catalog/gamefiles/bcp2.efg similarity index 100% rename from catalog/gamefiles/bcp2.efg rename to src/pygambit/catalog/gamefiles/bcp2.efg diff --git a/catalog/gamefiles/bcp3.efg b/src/pygambit/catalog/gamefiles/bcp3.efg similarity index 100% rename from catalog/gamefiles/bcp3.efg rename to src/pygambit/catalog/gamefiles/bcp3.efg diff --git a/catalog/gamefiles/bcp4.efg b/src/pygambit/catalog/gamefiles/bcp4.efg similarity index 100% rename from catalog/gamefiles/bcp4.efg rename to src/pygambit/catalog/gamefiles/bcp4.efg diff --git a/catalog/gamefiles/bhg1.efg b/src/pygambit/catalog/gamefiles/bhg1.efg similarity index 100% rename from catalog/gamefiles/bhg1.efg rename to src/pygambit/catalog/gamefiles/bhg1.efg diff --git a/catalog/gamefiles/bhg2.efg b/src/pygambit/catalog/gamefiles/bhg2.efg similarity index 100% rename from catalog/gamefiles/bhg2.efg rename to src/pygambit/catalog/gamefiles/bhg2.efg diff --git a/catalog/gamefiles/bhg3.efg b/src/pygambit/catalog/gamefiles/bhg3.efg similarity index 100% rename from catalog/gamefiles/bhg3.efg rename to src/pygambit/catalog/gamefiles/bhg3.efg diff --git a/catalog/gamefiles/bhg4.efg b/src/pygambit/catalog/gamefiles/bhg4.efg similarity index 100% rename from catalog/gamefiles/bhg4.efg rename to src/pygambit/catalog/gamefiles/bhg4.efg diff --git a/catalog/gamefiles/bhg5.efg b/src/pygambit/catalog/gamefiles/bhg5.efg similarity index 100% rename from catalog/gamefiles/bhg5.efg rename to src/pygambit/catalog/gamefiles/bhg5.efg diff --git a/catalog/gamefiles/caro2.efg b/src/pygambit/catalog/gamefiles/caro2.efg similarity index 100% rename from catalog/gamefiles/caro2.efg rename to src/pygambit/catalog/gamefiles/caro2.efg diff --git a/catalog/gamefiles/cent2.efg b/src/pygambit/catalog/gamefiles/cent2.efg similarity index 100% rename from catalog/gamefiles/cent2.efg rename to src/pygambit/catalog/gamefiles/cent2.efg diff --git a/catalog/gamefiles/cent2.nfg b/src/pygambit/catalog/gamefiles/cent2.nfg similarity index 100% rename from catalog/gamefiles/cent2.nfg rename to src/pygambit/catalog/gamefiles/cent2.nfg diff --git a/catalog/gamefiles/cent3.efg b/src/pygambit/catalog/gamefiles/cent3.efg similarity index 100% rename from catalog/gamefiles/cent3.efg rename to src/pygambit/catalog/gamefiles/cent3.efg diff --git a/catalog/gamefiles/cent4.efg b/src/pygambit/catalog/gamefiles/cent4.efg similarity index 100% rename from catalog/gamefiles/cent4.efg rename to src/pygambit/catalog/gamefiles/cent4.efg diff --git a/catalog/gamefiles/cent6.efg b/src/pygambit/catalog/gamefiles/cent6.efg similarity index 100% rename from catalog/gamefiles/cent6.efg rename to src/pygambit/catalog/gamefiles/cent6.efg diff --git a/catalog/gamefiles/centcs10.efg b/src/pygambit/catalog/gamefiles/centcs10.efg similarity index 100% rename from catalog/gamefiles/centcs10.efg rename to src/pygambit/catalog/gamefiles/centcs10.efg diff --git a/catalog/gamefiles/centcs6.efg b/src/pygambit/catalog/gamefiles/centcs6.efg similarity index 100% rename from catalog/gamefiles/centcs6.efg rename to src/pygambit/catalog/gamefiles/centcs6.efg diff --git a/catalog/gamefiles/condjury.efg b/src/pygambit/catalog/gamefiles/condjury.efg similarity index 100% rename from catalog/gamefiles/condjury.efg rename to src/pygambit/catalog/gamefiles/condjury.efg diff --git a/catalog/gamefiles/coord2.efg b/src/pygambit/catalog/gamefiles/coord2.efg similarity index 100% rename from catalog/gamefiles/coord2.efg rename to src/pygambit/catalog/gamefiles/coord2.efg diff --git a/catalog/gamefiles/coord2.nfg b/src/pygambit/catalog/gamefiles/coord2.nfg similarity index 100% rename from catalog/gamefiles/coord2.nfg rename to src/pygambit/catalog/gamefiles/coord2.nfg diff --git a/catalog/gamefiles/coord2ts.efg b/src/pygambit/catalog/gamefiles/coord2ts.efg similarity index 100% rename from catalog/gamefiles/coord2ts.efg rename to src/pygambit/catalog/gamefiles/coord2ts.efg diff --git a/catalog/gamefiles/coord3.efg b/src/pygambit/catalog/gamefiles/coord3.efg similarity index 100% rename from catalog/gamefiles/coord3.efg rename to src/pygambit/catalog/gamefiles/coord3.efg diff --git a/catalog/gamefiles/coord3.nfg b/src/pygambit/catalog/gamefiles/coord3.nfg similarity index 100% rename from catalog/gamefiles/coord3.nfg rename to src/pygambit/catalog/gamefiles/coord3.nfg diff --git a/catalog/gamefiles/coord333.nfg b/src/pygambit/catalog/gamefiles/coord333.nfg similarity index 100% rename from catalog/gamefiles/coord333.nfg rename to src/pygambit/catalog/gamefiles/coord333.nfg diff --git a/catalog/gamefiles/coord4.efg b/src/pygambit/catalog/gamefiles/coord4.efg similarity index 100% rename from catalog/gamefiles/coord4.efg rename to src/pygambit/catalog/gamefiles/coord4.efg diff --git a/catalog/gamefiles/coord4.nfg b/src/pygambit/catalog/gamefiles/coord4.nfg similarity index 100% rename from catalog/gamefiles/coord4.nfg rename to src/pygambit/catalog/gamefiles/coord4.nfg diff --git a/catalog/gamefiles/cross.efg b/src/pygambit/catalog/gamefiles/cross.efg similarity index 100% rename from catalog/gamefiles/cross.efg rename to src/pygambit/catalog/gamefiles/cross.efg diff --git a/catalog/gamefiles/cs.efg b/src/pygambit/catalog/gamefiles/cs.efg similarity index 100% rename from catalog/gamefiles/cs.efg rename to src/pygambit/catalog/gamefiles/cs.efg diff --git a/catalog/gamefiles/csg1.nfg b/src/pygambit/catalog/gamefiles/csg1.nfg similarity index 100% rename from catalog/gamefiles/csg1.nfg rename to src/pygambit/catalog/gamefiles/csg1.nfg diff --git a/catalog/gamefiles/csg2.nfg b/src/pygambit/catalog/gamefiles/csg2.nfg similarity index 100% rename from catalog/gamefiles/csg2.nfg rename to src/pygambit/catalog/gamefiles/csg2.nfg diff --git a/catalog/gamefiles/csg3.nfg b/src/pygambit/catalog/gamefiles/csg3.nfg similarity index 100% rename from catalog/gamefiles/csg3.nfg rename to src/pygambit/catalog/gamefiles/csg3.nfg diff --git a/catalog/gamefiles/csg4.nfg b/src/pygambit/catalog/gamefiles/csg4.nfg similarity index 100% rename from catalog/gamefiles/csg4.nfg rename to src/pygambit/catalog/gamefiles/csg4.nfg diff --git a/catalog/gamefiles/deg1.nfg b/src/pygambit/catalog/gamefiles/deg1.nfg similarity index 100% rename from catalog/gamefiles/deg1.nfg rename to src/pygambit/catalog/gamefiles/deg1.nfg diff --git a/catalog/gamefiles/deg2.nfg b/src/pygambit/catalog/gamefiles/deg2.nfg similarity index 100% rename from catalog/gamefiles/deg2.nfg rename to src/pygambit/catalog/gamefiles/deg2.nfg diff --git a/catalog/gamefiles/e01.efg b/src/pygambit/catalog/gamefiles/e01.efg similarity index 100% rename from catalog/gamefiles/e01.efg rename to src/pygambit/catalog/gamefiles/e01.efg diff --git a/catalog/gamefiles/e01.nfg b/src/pygambit/catalog/gamefiles/e01.nfg similarity index 100% rename from catalog/gamefiles/e01.nfg rename to src/pygambit/catalog/gamefiles/e01.nfg diff --git a/catalog/gamefiles/e02.efg b/src/pygambit/catalog/gamefiles/e02.efg similarity index 100% rename from catalog/gamefiles/e02.efg rename to src/pygambit/catalog/gamefiles/e02.efg diff --git a/catalog/gamefiles/e02.nfg b/src/pygambit/catalog/gamefiles/e02.nfg similarity index 100% rename from catalog/gamefiles/e02.nfg rename to src/pygambit/catalog/gamefiles/e02.nfg diff --git a/catalog/gamefiles/e03.efg b/src/pygambit/catalog/gamefiles/e03.efg similarity index 100% rename from catalog/gamefiles/e03.efg rename to src/pygambit/catalog/gamefiles/e03.efg diff --git a/catalog/gamefiles/e04.efg b/src/pygambit/catalog/gamefiles/e04.efg similarity index 100% rename from catalog/gamefiles/e04.efg rename to src/pygambit/catalog/gamefiles/e04.efg diff --git a/catalog/gamefiles/e04.nfg b/src/pygambit/catalog/gamefiles/e04.nfg similarity index 100% rename from catalog/gamefiles/e04.nfg rename to src/pygambit/catalog/gamefiles/e04.nfg diff --git a/catalog/gamefiles/e05.efg b/src/pygambit/catalog/gamefiles/e05.efg similarity index 100% rename from catalog/gamefiles/e05.efg rename to src/pygambit/catalog/gamefiles/e05.efg diff --git a/catalog/gamefiles/e06.efg b/src/pygambit/catalog/gamefiles/e06.efg similarity index 100% rename from catalog/gamefiles/e06.efg rename to src/pygambit/catalog/gamefiles/e06.efg diff --git a/catalog/gamefiles/e07.efg b/src/pygambit/catalog/gamefiles/e07.efg similarity index 100% rename from catalog/gamefiles/e07.efg rename to src/pygambit/catalog/gamefiles/e07.efg diff --git a/catalog/gamefiles/e07.nfg b/src/pygambit/catalog/gamefiles/e07.nfg similarity index 100% rename from catalog/gamefiles/e07.nfg rename to src/pygambit/catalog/gamefiles/e07.nfg diff --git a/catalog/gamefiles/e08.efg b/src/pygambit/catalog/gamefiles/e08.efg similarity index 100% rename from catalog/gamefiles/e08.efg rename to src/pygambit/catalog/gamefiles/e08.efg diff --git a/catalog/gamefiles/e09.efg b/src/pygambit/catalog/gamefiles/e09.efg similarity index 100% rename from catalog/gamefiles/e09.efg rename to src/pygambit/catalog/gamefiles/e09.efg diff --git a/catalog/gamefiles/e10.efg b/src/pygambit/catalog/gamefiles/e10.efg similarity index 100% rename from catalog/gamefiles/e10.efg rename to src/pygambit/catalog/gamefiles/e10.efg diff --git a/catalog/gamefiles/e10a.efg b/src/pygambit/catalog/gamefiles/e10a.efg similarity index 100% rename from catalog/gamefiles/e10a.efg rename to src/pygambit/catalog/gamefiles/e10a.efg diff --git a/catalog/gamefiles/e13.efg b/src/pygambit/catalog/gamefiles/e13.efg similarity index 100% rename from catalog/gamefiles/e13.efg rename to src/pygambit/catalog/gamefiles/e13.efg diff --git a/catalog/gamefiles/e16.efg b/src/pygambit/catalog/gamefiles/e16.efg similarity index 100% rename from catalog/gamefiles/e16.efg rename to src/pygambit/catalog/gamefiles/e16.efg diff --git a/catalog/gamefiles/e17.efg b/src/pygambit/catalog/gamefiles/e17.efg similarity index 100% rename from catalog/gamefiles/e17.efg rename to src/pygambit/catalog/gamefiles/e17.efg diff --git a/catalog/gamefiles/e18.efg b/src/pygambit/catalog/gamefiles/e18.efg similarity index 100% rename from catalog/gamefiles/e18.efg rename to src/pygambit/catalog/gamefiles/e18.efg diff --git a/catalog/gamefiles/g1.efg b/src/pygambit/catalog/gamefiles/g1.efg similarity index 100% rename from catalog/gamefiles/g1.efg rename to src/pygambit/catalog/gamefiles/g1.efg diff --git a/catalog/gamefiles/g1.nfg b/src/pygambit/catalog/gamefiles/g1.nfg similarity index 100% rename from catalog/gamefiles/g1.nfg rename to src/pygambit/catalog/gamefiles/g1.nfg diff --git a/catalog/gamefiles/g2.efg b/src/pygambit/catalog/gamefiles/g2.efg similarity index 100% rename from catalog/gamefiles/g2.efg rename to src/pygambit/catalog/gamefiles/g2.efg diff --git a/catalog/gamefiles/g2.nfg b/src/pygambit/catalog/gamefiles/g2.nfg similarity index 100% rename from catalog/gamefiles/g2.nfg rename to src/pygambit/catalog/gamefiles/g2.nfg diff --git a/catalog/gamefiles/g3.efg b/src/pygambit/catalog/gamefiles/g3.efg similarity index 100% rename from catalog/gamefiles/g3.efg rename to src/pygambit/catalog/gamefiles/g3.efg diff --git a/catalog/gamefiles/g3.nfg b/src/pygambit/catalog/gamefiles/g3.nfg similarity index 100% rename from catalog/gamefiles/g3.nfg rename to src/pygambit/catalog/gamefiles/g3.nfg diff --git a/catalog/gamefiles/holdout.efg b/src/pygambit/catalog/gamefiles/holdout.efg similarity index 100% rename from catalog/gamefiles/holdout.efg rename to src/pygambit/catalog/gamefiles/holdout.efg diff --git a/catalog/gamefiles/holdout7.efg b/src/pygambit/catalog/gamefiles/holdout7.efg similarity index 100% rename from catalog/gamefiles/holdout7.efg rename to src/pygambit/catalog/gamefiles/holdout7.efg diff --git a/catalog/gamefiles/hs1.efg b/src/pygambit/catalog/gamefiles/hs1.efg similarity index 100% rename from catalog/gamefiles/hs1.efg rename to src/pygambit/catalog/gamefiles/hs1.efg diff --git a/catalog/gamefiles/jury_mr.efg b/src/pygambit/catalog/gamefiles/jury_mr.efg similarity index 100% rename from catalog/gamefiles/jury_mr.efg rename to src/pygambit/catalog/gamefiles/jury_mr.efg diff --git a/catalog/gamefiles/jury_un.efg b/src/pygambit/catalog/gamefiles/jury_un.efg similarity index 100% rename from catalog/gamefiles/jury_un.efg rename to src/pygambit/catalog/gamefiles/jury_un.efg diff --git a/catalog/gamefiles/km1.efg b/src/pygambit/catalog/gamefiles/km1.efg similarity index 100% rename from catalog/gamefiles/km1.efg rename to src/pygambit/catalog/gamefiles/km1.efg diff --git a/catalog/gamefiles/km2.efg b/src/pygambit/catalog/gamefiles/km2.efg similarity index 100% rename from catalog/gamefiles/km2.efg rename to src/pygambit/catalog/gamefiles/km2.efg diff --git a/catalog/gamefiles/km3.efg b/src/pygambit/catalog/gamefiles/km3.efg similarity index 100% rename from catalog/gamefiles/km3.efg rename to src/pygambit/catalog/gamefiles/km3.efg diff --git a/catalog/gamefiles/km6.efg b/src/pygambit/catalog/gamefiles/km6.efg similarity index 100% rename from catalog/gamefiles/km6.efg rename to src/pygambit/catalog/gamefiles/km6.efg diff --git a/catalog/gamefiles/loopback.nfg b/src/pygambit/catalog/gamefiles/loopback.nfg similarity index 100% rename from catalog/gamefiles/loopback.nfg rename to src/pygambit/catalog/gamefiles/loopback.nfg diff --git a/catalog/gamefiles/mixdom.nfg b/src/pygambit/catalog/gamefiles/mixdom.nfg similarity index 100% rename from catalog/gamefiles/mixdom.nfg rename to src/pygambit/catalog/gamefiles/mixdom.nfg diff --git a/catalog/gamefiles/mixdom2.nfg b/src/pygambit/catalog/gamefiles/mixdom2.nfg similarity index 100% rename from catalog/gamefiles/mixdom2.nfg rename to src/pygambit/catalog/gamefiles/mixdom2.nfg diff --git a/catalog/gamefiles/montyhal.efg b/src/pygambit/catalog/gamefiles/montyhal.efg similarity index 100% rename from catalog/gamefiles/montyhal.efg rename to src/pygambit/catalog/gamefiles/montyhal.efg diff --git a/catalog/gamefiles/my_2-1.efg b/src/pygambit/catalog/gamefiles/my_2-1.efg similarity index 100% rename from catalog/gamefiles/my_2-1.efg rename to src/pygambit/catalog/gamefiles/my_2-1.efg diff --git a/catalog/gamefiles/my_2-4.efg b/src/pygambit/catalog/gamefiles/my_2-4.efg similarity index 100% rename from catalog/gamefiles/my_2-4.efg rename to src/pygambit/catalog/gamefiles/my_2-4.efg diff --git a/catalog/gamefiles/my_2-8.efg b/src/pygambit/catalog/gamefiles/my_2-8.efg similarity index 100% rename from catalog/gamefiles/my_2-8.efg rename to src/pygambit/catalog/gamefiles/my_2-8.efg diff --git a/catalog/gamefiles/my_3-3a.efg b/src/pygambit/catalog/gamefiles/my_3-3a.efg similarity index 100% rename from catalog/gamefiles/my_3-3a.efg rename to src/pygambit/catalog/gamefiles/my_3-3a.efg diff --git a/catalog/gamefiles/my_3-3b.efg b/src/pygambit/catalog/gamefiles/my_3-3b.efg similarity index 100% rename from catalog/gamefiles/my_3-3b.efg rename to src/pygambit/catalog/gamefiles/my_3-3b.efg diff --git a/catalog/gamefiles/my_3-3c.efg b/src/pygambit/catalog/gamefiles/my_3-3c.efg similarity index 100% rename from catalog/gamefiles/my_3-3c.efg rename to src/pygambit/catalog/gamefiles/my_3-3c.efg diff --git a/catalog/gamefiles/my_3-3d.efg b/src/pygambit/catalog/gamefiles/my_3-3d.efg similarity index 100% rename from catalog/gamefiles/my_3-3d.efg rename to src/pygambit/catalog/gamefiles/my_3-3d.efg diff --git a/catalog/gamefiles/my_3-3e.efg b/src/pygambit/catalog/gamefiles/my_3-3e.efg similarity index 100% rename from catalog/gamefiles/my_3-3e.efg rename to src/pygambit/catalog/gamefiles/my_3-3e.efg diff --git a/catalog/gamefiles/my_3-4.efg b/src/pygambit/catalog/gamefiles/my_3-4.efg similarity index 100% rename from catalog/gamefiles/my_3-4.efg rename to src/pygambit/catalog/gamefiles/my_3-4.efg diff --git a/catalog/gamefiles/myerson.efg b/src/pygambit/catalog/gamefiles/myerson.efg similarity index 100% rename from catalog/gamefiles/myerson.efg rename to src/pygambit/catalog/gamefiles/myerson.efg diff --git a/catalog/gamefiles/myerson_fig_4_2.efg b/src/pygambit/catalog/gamefiles/myerson_fig_4_2.efg similarity index 100% rename from catalog/gamefiles/myerson_fig_4_2.efg rename to src/pygambit/catalog/gamefiles/myerson_fig_4_2.efg diff --git a/catalog/gamefiles/nim.efg b/src/pygambit/catalog/gamefiles/nim.efg similarity index 100% rename from catalog/gamefiles/nim.efg rename to src/pygambit/catalog/gamefiles/nim.efg diff --git a/catalog/gamefiles/nim7.efg b/src/pygambit/catalog/gamefiles/nim7.efg similarity index 100% rename from catalog/gamefiles/nim7.efg rename to src/pygambit/catalog/gamefiles/nim7.efg diff --git a/catalog/gamefiles/oneill.nfg b/src/pygambit/catalog/gamefiles/oneill.nfg similarity index 100% rename from catalog/gamefiles/oneill.nfg rename to src/pygambit/catalog/gamefiles/oneill.nfg diff --git a/catalog/gamefiles/palf.efg b/src/pygambit/catalog/gamefiles/palf.efg similarity index 100% rename from catalog/gamefiles/palf.efg rename to src/pygambit/catalog/gamefiles/palf.efg diff --git a/catalog/gamefiles/palf2.efg b/src/pygambit/catalog/gamefiles/palf2.efg similarity index 100% rename from catalog/gamefiles/palf2.efg rename to src/pygambit/catalog/gamefiles/palf2.efg diff --git a/catalog/gamefiles/palf3.efg b/src/pygambit/catalog/gamefiles/palf3.efg similarity index 100% rename from catalog/gamefiles/palf3.efg rename to src/pygambit/catalog/gamefiles/palf3.efg diff --git a/catalog/gamefiles/pbride.efg b/src/pygambit/catalog/gamefiles/pbride.efg similarity index 100% rename from catalog/gamefiles/pbride.efg rename to src/pygambit/catalog/gamefiles/pbride.efg diff --git a/catalog/gamefiles/pd.nfg b/src/pygambit/catalog/gamefiles/pd.nfg similarity index 100% rename from catalog/gamefiles/pd.nfg rename to src/pygambit/catalog/gamefiles/pd.nfg diff --git a/catalog/gamefiles/perfect1.nfg b/src/pygambit/catalog/gamefiles/perfect1.nfg similarity index 100% rename from catalog/gamefiles/perfect1.nfg rename to src/pygambit/catalog/gamefiles/perfect1.nfg diff --git a/catalog/gamefiles/perfect2.nfg b/src/pygambit/catalog/gamefiles/perfect2.nfg similarity index 100% rename from catalog/gamefiles/perfect2.nfg rename to src/pygambit/catalog/gamefiles/perfect2.nfg diff --git a/catalog/gamefiles/perfect3.nfg b/src/pygambit/catalog/gamefiles/perfect3.nfg similarity index 100% rename from catalog/gamefiles/perfect3.nfg rename to src/pygambit/catalog/gamefiles/perfect3.nfg diff --git a/catalog/gamefiles/poker.efg b/src/pygambit/catalog/gamefiles/poker.efg similarity index 100% rename from catalog/gamefiles/poker.efg rename to src/pygambit/catalog/gamefiles/poker.efg diff --git a/catalog/gamefiles/poker.nfg b/src/pygambit/catalog/gamefiles/poker.nfg similarity index 100% rename from catalog/gamefiles/poker.nfg rename to src/pygambit/catalog/gamefiles/poker.nfg diff --git a/catalog/gamefiles/poker2.efg b/src/pygambit/catalog/gamefiles/poker2.efg similarity index 100% rename from catalog/gamefiles/poker2.efg rename to src/pygambit/catalog/gamefiles/poker2.efg diff --git a/catalog/gamefiles/pvw.efg b/src/pygambit/catalog/gamefiles/pvw.efg similarity index 100% rename from catalog/gamefiles/pvw.efg rename to src/pygambit/catalog/gamefiles/pvw.efg diff --git a/catalog/gamefiles/pvw2.efg b/src/pygambit/catalog/gamefiles/pvw2.efg similarity index 100% rename from catalog/gamefiles/pvw2.efg rename to src/pygambit/catalog/gamefiles/pvw2.efg diff --git a/catalog/gamefiles/sh3.efg b/src/pygambit/catalog/gamefiles/sh3.efg similarity index 100% rename from catalog/gamefiles/sh3.efg rename to src/pygambit/catalog/gamefiles/sh3.efg diff --git a/catalog/gamefiles/sh3.nfg b/src/pygambit/catalog/gamefiles/sh3.nfg similarity index 100% rename from catalog/gamefiles/sh3.nfg rename to src/pygambit/catalog/gamefiles/sh3.nfg diff --git a/catalog/gamefiles/spence.efg b/src/pygambit/catalog/gamefiles/spence.efg similarity index 100% rename from catalog/gamefiles/spence.efg rename to src/pygambit/catalog/gamefiles/spence.efg diff --git a/catalog/gamefiles/stengel.nfg b/src/pygambit/catalog/gamefiles/stengel.nfg similarity index 100% rename from catalog/gamefiles/stengel.nfg rename to src/pygambit/catalog/gamefiles/stengel.nfg diff --git a/catalog/gamefiles/sww1.efg b/src/pygambit/catalog/gamefiles/sww1.efg similarity index 100% rename from catalog/gamefiles/sww1.efg rename to src/pygambit/catalog/gamefiles/sww1.efg diff --git a/catalog/gamefiles/sww1.nfg b/src/pygambit/catalog/gamefiles/sww1.nfg similarity index 100% rename from catalog/gamefiles/sww1.nfg rename to src/pygambit/catalog/gamefiles/sww1.nfg diff --git a/catalog/gamefiles/sww2.efg b/src/pygambit/catalog/gamefiles/sww2.efg similarity index 100% rename from catalog/gamefiles/sww2.efg rename to src/pygambit/catalog/gamefiles/sww2.efg diff --git a/catalog/gamefiles/sww3.efg b/src/pygambit/catalog/gamefiles/sww3.efg similarity index 100% rename from catalog/gamefiles/sww3.efg rename to src/pygambit/catalog/gamefiles/sww3.efg diff --git a/catalog/gamefiles/tim.efg b/src/pygambit/catalog/gamefiles/tim.efg similarity index 100% rename from catalog/gamefiles/tim.efg rename to src/pygambit/catalog/gamefiles/tim.efg diff --git a/catalog/gamefiles/todd1.nfg b/src/pygambit/catalog/gamefiles/todd1.nfg similarity index 100% rename from catalog/gamefiles/todd1.nfg rename to src/pygambit/catalog/gamefiles/todd1.nfg diff --git a/catalog/gamefiles/todd2.nfg b/src/pygambit/catalog/gamefiles/todd2.nfg similarity index 100% rename from catalog/gamefiles/todd2.nfg rename to src/pygambit/catalog/gamefiles/todd2.nfg diff --git a/catalog/gamefiles/todd3.nfg b/src/pygambit/catalog/gamefiles/todd3.nfg similarity index 100% rename from catalog/gamefiles/todd3.nfg rename to src/pygambit/catalog/gamefiles/todd3.nfg diff --git a/catalog/gamefiles/ttt.efg b/src/pygambit/catalog/gamefiles/ttt.efg similarity index 100% rename from catalog/gamefiles/ttt.efg rename to src/pygambit/catalog/gamefiles/ttt.efg diff --git a/catalog/gamefiles/vd.efg b/src/pygambit/catalog/gamefiles/vd.efg similarity index 100% rename from catalog/gamefiles/vd.efg rename to src/pygambit/catalog/gamefiles/vd.efg diff --git a/catalog/gamefiles/vd.nfg b/src/pygambit/catalog/gamefiles/vd.nfg similarity index 100% rename from catalog/gamefiles/vd.nfg rename to src/pygambit/catalog/gamefiles/vd.nfg diff --git a/catalog/gamefiles/w_ex1.efg b/src/pygambit/catalog/gamefiles/w_ex1.efg similarity index 100% rename from catalog/gamefiles/w_ex1.efg rename to src/pygambit/catalog/gamefiles/w_ex1.efg diff --git a/catalog/gamefiles/w_ex2.efg b/src/pygambit/catalog/gamefiles/w_ex2.efg similarity index 100% rename from catalog/gamefiles/w_ex2.efg rename to src/pygambit/catalog/gamefiles/w_ex2.efg diff --git a/catalog/gamefiles/wilson1.efg b/src/pygambit/catalog/gamefiles/wilson1.efg similarity index 100% rename from catalog/gamefiles/wilson1.efg rename to src/pygambit/catalog/gamefiles/wilson1.efg diff --git a/catalog/gamefiles/wink3.nfg b/src/pygambit/catalog/gamefiles/wink3.nfg similarity index 100% rename from catalog/gamefiles/wink3.nfg rename to src/pygambit/catalog/gamefiles/wink3.nfg diff --git a/catalog/gamefiles/winkels.nfg b/src/pygambit/catalog/gamefiles/winkels.nfg similarity index 100% rename from catalog/gamefiles/winkels.nfg rename to src/pygambit/catalog/gamefiles/winkels.nfg diff --git a/catalog/gamefiles/work1.efg b/src/pygambit/catalog/gamefiles/work1.efg similarity index 100% rename from catalog/gamefiles/work1.efg rename to src/pygambit/catalog/gamefiles/work1.efg diff --git a/catalog/gamefiles/work2.efg b/src/pygambit/catalog/gamefiles/work2.efg similarity index 100% rename from catalog/gamefiles/work2.efg rename to src/pygambit/catalog/gamefiles/work2.efg diff --git a/catalog/gamefiles/work3.efg b/src/pygambit/catalog/gamefiles/work3.efg similarity index 100% rename from catalog/gamefiles/work3.efg rename to src/pygambit/catalog/gamefiles/work3.efg diff --git a/catalog/gamefiles/yamamoto.nfg b/src/pygambit/catalog/gamefiles/yamamoto.nfg similarity index 100% rename from catalog/gamefiles/yamamoto.nfg rename to src/pygambit/catalog/gamefiles/yamamoto.nfg diff --git a/catalog/gamefiles/zero.nfg b/src/pygambit/catalog/gamefiles/zero.nfg similarity index 100% rename from catalog/gamefiles/zero.nfg rename to src/pygambit/catalog/gamefiles/zero.nfg From 07b1beea68c8d1f2e12502503728d8fa637cb5cb Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 14:55:10 +0000 Subject: [PATCH 08/76] enforce gamefile and gametype on subclasses --- src/pygambit/catalog/catalog.py | 41 +++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index fef3c339c..58198d1d5 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -1,6 +1,6 @@ from pathlib import Path -from pygambit import read_efg, read_nfg +from pygambit import Game, read_efg, read_nfg _CATALOG_DIR = Path(__file__).parent _GAMEFILES_DIR = _CATALOG_DIR / "gamefiles" @@ -12,17 +12,44 @@ class CatalogGame: This class serves as a template for specific games in the catalog. Calling any subclass will return an instance of the corresponding game. """ - def __new__(cls): - raise NotImplementedError("This method should be implemented by subclasses.") + # Subclasses must define these + game_file: str | None = None + game_type: str | None = None # 'nfg' or 'efg' + + def __new__(cls) -> Game: + if cls.game_file is None: + raise NotImplementedError(f"{cls.__name__} must define 'game_file'") + if cls.game_type is None: + raise NotImplementedError(f"{cls.__name__} must define 'game_type'") + + # Load the appropriate game type + file_path = _GAMEFILES_DIR / cls.game_file + if cls.game_type == "nfg": + game = read_nfg(str(file_path)) + elif cls.game_type == "efg": + game = read_efg(str(file_path)) + else: + raise ValueError(f"game_type must be 'nfg' or 'efg', got '{cls.game_type}'") + + return game + + def __init_subclass__(cls, **kwargs): + """Validate that subclasses define required attributes.""" + super().__init_subclass__(**kwargs) + # This runs when a subclass is defined + if not hasattr(cls, "game_file") or cls.game_file is None: + raise TypeError(f"{cls.__name__} must define 'game_file' class attribute") + if not hasattr(cls, "game_type") or cls.game_type is None: + raise TypeError(f"{cls.__name__} must define 'game_type' class attribute") class PrisonersDilemma(CatalogGame): """Prisoner's Dilemma game.""" - def __new__(cls): - return read_nfg(str(_GAMEFILES_DIR / "pd.nfg")) + game_file = "pd.nfg" + game_type = "nfg" class TwoStageMatchingPennies(CatalogGame): """Two-Stage Matching Pennies game.""" - def __new__(cls): - return read_efg(str(_GAMEFILES_DIR / "2smp.efg")) + game_file = "2smp.efg" + game_type = "efg" From d11ada75d5d050dae464cf1a0189c2b3e0a4090d Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 14:58:55 +0000 Subject: [PATCH 09/76] don't enforce game_type as this can be inferred from the file extension --- src/pygambit/catalog/catalog.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 58198d1d5..e27b6e2e5 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -14,13 +14,11 @@ class CatalogGame: """ # Subclasses must define these game_file: str | None = None - game_type: str | None = None # 'nfg' or 'efg' def __new__(cls) -> Game: if cls.game_file is None: raise NotImplementedError(f"{cls.__name__} must define 'game_file'") - if cls.game_type is None: - raise NotImplementedError(f"{cls.__name__} must define 'game_type'") + cls.game_type = cls.game_file.split(".")[-1] # infer game type from file extension # Load the appropriate game type file_path = _GAMEFILES_DIR / cls.game_file @@ -29,7 +27,7 @@ def __new__(cls) -> Game: elif cls.game_type == "efg": game = read_efg(str(file_path)) else: - raise ValueError(f"game_type must be 'nfg' or 'efg', got '{cls.game_type}'") + raise ValueError(f"Game file extension must be 'nfg' or 'efg', got '{cls.game_type}'") return game @@ -39,17 +37,13 @@ def __init_subclass__(cls, **kwargs): # This runs when a subclass is defined if not hasattr(cls, "game_file") or cls.game_file is None: raise TypeError(f"{cls.__name__} must define 'game_file' class attribute") - if not hasattr(cls, "game_type") or cls.game_type is None: - raise TypeError(f"{cls.__name__} must define 'game_type' class attribute") class PrisonersDilemma(CatalogGame): """Prisoner's Dilemma game.""" game_file = "pd.nfg" - game_type = "nfg" class TwoStageMatchingPennies(CatalogGame): """Two-Stage Matching Pennies game.""" game_file = "2smp.efg" - game_type = "efg" From 828eabda64385b4790f616e0298b09cd3921ed60 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 15:13:04 +0000 Subject: [PATCH 10/76] add a test game to the catalog defined directly in code --- src/pygambit/catalog/__init__.py | 4 ++-- src/pygambit/catalog/catalog.py | 31 +++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/pygambit/catalog/__init__.py b/src/pygambit/catalog/__init__.py index a57c5a20f..ce49d8546 100644 --- a/src/pygambit/catalog/__init__.py +++ b/src/pygambit/catalog/__init__.py @@ -1,3 +1,3 @@ -from .catalog import PrisonersDilemma, TwoStageMatchingPennies +from .catalog import PrisonersDilemma, PrisonersDilemmaTestgame, TwoStageMatchingPennies -__all__ = ["PrisonersDilemma", "TwoStageMatchingPennies"] +__all__ = ["PrisonersDilemma", "TwoStageMatchingPennies", "PrisonersDilemmaTestgame"] diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index e27b6e2e5..96206089e 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -1,5 +1,7 @@ from pathlib import Path +import numpy as np + from pygambit import Game, read_efg, read_nfg _CATALOG_DIR = Path(__file__).parent @@ -12,6 +14,17 @@ class CatalogGame: This class serves as a template for specific games in the catalog. Calling any subclass will return an instance of the corresponding game. """ + + def __new__(cls) -> Game: + raise NotImplementedError("Subclasses must implement __new__ method") + + +class CatalogGameFromFile(CatalogGame): + """ + Base class for catalog games loaded from files. + This class serves as a template for specific games in the catalog. + Calling any subclass will return an instance of the corresponding game. + """ # Subclasses must define these game_file: str | None = None @@ -39,11 +52,25 @@ def __init_subclass__(cls, **kwargs): raise TypeError(f"{cls.__name__} must define 'game_file' class attribute") -class PrisonersDilemma(CatalogGame): +class PrisonersDilemma(CatalogGameFromFile): """Prisoner's Dilemma game.""" game_file = "pd.nfg" -class TwoStageMatchingPennies(CatalogGame): +class TwoStageMatchingPennies(CatalogGameFromFile): """Two-Stage Matching Pennies game.""" game_file = "2smp.efg" + + +class PrisonersDilemmaTestgame(CatalogGame): + """A simple test game based on the Prisoner's Dilemma.""" + def __new__(cls) -> Game: + player1_payoffs = np.array([[-1, -3], [0, -2]]) + player2_payoffs = np.transpose(player1_payoffs) + + g1 = Game.from_arrays( + player1_payoffs, + player2_payoffs, + title="Test Prisoner's Dilemma" + ) + return g1 From faeef279358ed6e2a4ba97a54610954b95884256 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 15:31:56 +0000 Subject: [PATCH 11/76] enhance CatalogGame and CatalogGameFromFile to extract metadata without needing to instantiate the full game object --- src/pygambit/catalog/catalog.py | 39 ++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 96206089e..fd0322ee1 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -18,6 +18,12 @@ class CatalogGame: def __new__(cls) -> Game: raise NotImplementedError("Subclasses must implement __new__ method") + @classmethod + def _extract_metadata_from_game(cls, game: Game) -> None: + """Extract metadata from the game and set as class attributes.""" + cls.title = game.title + cls.num_players = len(game.players) + class CatalogGameFromFile(CatalogGame): """ @@ -27,30 +33,42 @@ class CatalogGameFromFile(CatalogGame): """ # Subclasses must define these game_file: str | None = None + _cached_game: Game | None = None def __new__(cls) -> Game: + # Return cached game if available, otherwise load it + if cls._cached_game is None: + cls._cached_game = cls._load_game() + # Return a fresh instance (not the cached one) + return cls._load_game() + + @classmethod + def _load_game(cls) -> Game: + """Load the game from file.""" if cls.game_file is None: raise NotImplementedError(f"{cls.__name__} must define 'game_file'") - cls.game_type = cls.game_file.split(".")[-1] # infer game type from file extension - # Load the appropriate game type + cls.game_type = cls.game_file.split(".")[-1] file_path = _GAMEFILES_DIR / cls.game_file + if cls.game_type == "nfg": - game = read_nfg(str(file_path)) + return read_nfg(str(file_path)) elif cls.game_type == "efg": - game = read_efg(str(file_path)) + return read_efg(str(file_path)) else: raise ValueError(f"Game file extension must be 'nfg' or 'efg', got '{cls.game_type}'") - return game - def __init_subclass__(cls, **kwargs): - """Validate that subclasses define required attributes.""" + """Validate and extract metadata when subclass is defined.""" super().__init_subclass__(**kwargs) - # This runs when a subclass is defined + if not hasattr(cls, "game_file") or cls.game_file is None: raise TypeError(f"{cls.__name__} must define 'game_file' class attribute") + # Load game and extract metadata immediately when class is defined + cls._cached_game = cls._load_game() + cls._extract_metadata_from_game(cls._cached_game) + class PrisonersDilemma(CatalogGameFromFile): """Prisoner's Dilemma game.""" @@ -64,6 +82,7 @@ class TwoStageMatchingPennies(CatalogGameFromFile): class PrisonersDilemmaTestgame(CatalogGame): """A simple test game based on the Prisoner's Dilemma.""" + def __new__(cls) -> Game: player1_payoffs = np.array([[-1, -3], [0, -2]]) player2_payoffs = np.transpose(player1_payoffs) @@ -73,4 +92,8 @@ def __new__(cls) -> Game: player2_payoffs, title="Test Prisoner's Dilemma" ) + + # Extract metadata from game and set as class attributes + cls._extract_metadata_from_game(g1) + return g1 From 8c49b31c84a25b46397a9b5225e36d10bb729997 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 15:36:10 +0000 Subject: [PATCH 12/76] refactor CatalogGame to extract metadata during subclass initialization --- src/pygambit/catalog/catalog.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index fd0322ee1..7acfe647a 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -24,6 +24,22 @@ def _extract_metadata_from_game(cls, game: Game) -> None: cls.title = game.title cls.num_players = len(game.players) + def __init_subclass__(cls, **kwargs): + """Extract metadata when subclass is defined (if not a file-based game).""" + super().__init_subclass__(**kwargs) + + # Skip if this is CatalogGameFromFile or its subclasses + if cls.__name__ == "CatalogGameFromFile" or issubclass(cls, CatalogGameFromFile): + return + + # For non-file-based games, create a temporary instance to extract metadata + try: + temp_game = cls.__new__(cls) + cls._extract_metadata_from_game(temp_game) + except NotImplementedError: + # Base class, skip + pass + class CatalogGameFromFile(CatalogGame): """ @@ -93,7 +109,4 @@ def __new__(cls) -> Game: title="Test Prisoner's Dilemma" ) - # Extract metadata from game and set as class attributes - cls._extract_metadata_from_game(g1) - return g1 From 8cc6b5b4a2b4ef8f46009bb8506d6f1eb587fc42 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 16:19:03 +0000 Subject: [PATCH 13/76] include metadata attributes for games --- src/pygambit/catalog/catalog.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 7acfe647a..6e9913e9e 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -15,6 +15,12 @@ class CatalogGame: Calling any subclass will return an instance of the corresponding game. """ + title: str | None = None + num_players: int | None = None + game_type: str | None = None + description: str | None = None + citation: str | None = None + def __new__(cls) -> Game: raise NotImplementedError("Subclasses must implement __new__ method") @@ -47,7 +53,7 @@ class CatalogGameFromFile(CatalogGame): This class serves as a template for specific games in the catalog. Calling any subclass will return an instance of the corresponding game. """ - # Subclasses must define these + game_file: str | None = None _cached_game: Game | None = None @@ -87,17 +93,22 @@ def __init_subclass__(cls, **kwargs): class PrisonersDilemma(CatalogGameFromFile): - """Prisoner's Dilemma game.""" game_file = "pd.nfg" + description = "Prisoner's Dilemma game." + citation = "Example citation for Prisoner's Dilemma." class TwoStageMatchingPennies(CatalogGameFromFile): - """Two-Stage Matching Pennies game.""" game_file = "2smp.efg" + description = "Two-Stage Matching Pennies game." + citation = "Example citation for Two-Stage Matching Pennies." class PrisonersDilemmaTestgame(CatalogGame): - """A simple test game based on the Prisoner's Dilemma.""" + title = "Test Prisoner's Dilemma" + game_type = "nfg" + description = "A simple test game based on the Prisoner's Dilemma." + citation = "Example citation for Test Prisoner's Dilemma." def __new__(cls) -> Game: player1_payoffs = np.array([[-1, -3], [0, -2]]) @@ -106,7 +117,7 @@ def __new__(cls) -> Game: g1 = Game.from_arrays( player1_payoffs, player2_payoffs, - title="Test Prisoner's Dilemma" + title=cls.title, ) return g1 From 70f176ff4fbf1b07340c66ec32e446926f751245 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 16:22:50 +0000 Subject: [PATCH 14/76] don't allow None for CatalogGame attributes --- src/pygambit/catalog/catalog.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 6e9913e9e..3d6b6c72a 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -15,11 +15,11 @@ class CatalogGame: Calling any subclass will return an instance of the corresponding game. """ - title: str | None = None - num_players: int | None = None - game_type: str | None = None - description: str | None = None - citation: str | None = None + title: str + num_players: int + game_type: str + description: str + citation: str def __new__(cls) -> Game: raise NotImplementedError("Subclasses must implement __new__ method") @@ -54,7 +54,7 @@ class CatalogGameFromFile(CatalogGame): Calling any subclass will return an instance of the corresponding game. """ - game_file: str | None = None + game_file: str _cached_game: Game | None = None def __new__(cls) -> Game: From f5b7fcb651f39f7df7597decf2c012b7949ca050 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 16:28:27 +0000 Subject: [PATCH 15/76] organize catalog game classes with section headers for better readability --- src/pygambit/catalog/catalog.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 3d6b6c72a..7b53eb1a5 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -92,6 +92,11 @@ def __init_subclass__(cls, **kwargs): cls._extract_metadata_from_game(cls._cached_game) +############################ +# Catalog games from files # +############################ + + class PrisonersDilemma(CatalogGameFromFile): game_file = "pd.nfg" description = "Prisoner's Dilemma game." @@ -104,6 +109,11 @@ class TwoStageMatchingPennies(CatalogGameFromFile): citation = "Example citation for Two-Stage Matching Pennies." +########################################## +# Catalog games defined programmatically # +########################################## + + class PrisonersDilemmaTestgame(CatalogGame): title = "Test Prisoner's Dilemma" game_type = "nfg" From 37b1e2ba39ea03bb799e7fba736743b36345cc39 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 16:38:45 +0000 Subject: [PATCH 16/76] implement gbt.catalog.games() --- doc/tutorials/01_quickstart.ipynb | 21 ++++++++++++++++----- src/pygambit/catalog/__init__.py | 14 ++++++++++++-- src/pygambit/catalog/catalog.py | 14 ++++++++++++++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index c6a9a6dc6..499f221b1 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -431,12 +431,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "701aa52a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "['PrisonersDilemma', 'TwoStageMatchingPennies', 'PrisonersDilemmaTestgame']" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# gambit.catalog.games()" + "gbt.catalog.games()" ] }, { @@ -449,7 +460,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "6db7a29a", "metadata": {}, "outputs": [ @@ -463,7 +474,7 @@ "Game(title='Two person Prisoner's Dilemma game')" ] }, - "execution_count": 2, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } diff --git a/src/pygambit/catalog/__init__.py b/src/pygambit/catalog/__init__.py index ce49d8546..dd758f759 100644 --- a/src/pygambit/catalog/__init__.py +++ b/src/pygambit/catalog/__init__.py @@ -1,3 +1,13 @@ -from .catalog import PrisonersDilemma, PrisonersDilemmaTestgame, TwoStageMatchingPennies +from .catalog import ( + PrisonersDilemma, + PrisonersDilemmaTestgame, + TwoStageMatchingPennies, + games, +) -__all__ = ["PrisonersDilemma", "TwoStageMatchingPennies", "PrisonersDilemmaTestgame"] +__all__ = [ + "games", + "PrisonersDilemma", + "TwoStageMatchingPennies", + "PrisonersDilemmaTestgame" +] diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 7b53eb1a5..3f2201807 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -92,6 +92,20 @@ def __init_subclass__(cls, **kwargs): cls._extract_metadata_from_game(cls._cached_game) +def games() -> list[str]: + """Return a list of all catalog game names.""" + def get_all_subclasses(cls): + """Recursively get all subclasses.""" + all_subclasses = [] + for subclass in cls.__subclasses__(): + if subclass.__name__ not in ["CatalogGameFromFile"]: + all_subclasses.append(subclass.__name__) + all_subclasses.extend(get_all_subclasses(subclass)) + return all_subclasses + + return get_all_subclasses(CatalogGame) + + ############################ # Catalog games from files # ############################ From a9dc597dfecd9f2d7058ac96387eb463a2908cad Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 16:44:22 +0000 Subject: [PATCH 17/76] dynamically import all catalog games and update __all__ accordingly --- src/pygambit/catalog/__init__.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/pygambit/catalog/__init__.py b/src/pygambit/catalog/__init__.py index dd758f759..619e73f41 100644 --- a/src/pygambit/catalog/__init__.py +++ b/src/pygambit/catalog/__init__.py @@ -1,13 +1,16 @@ -from .catalog import ( - PrisonersDilemma, - PrisonersDilemmaTestgame, - TwoStageMatchingPennies, - games, -) - -__all__ = [ - "games", - "PrisonersDilemma", - "TwoStageMatchingPennies", - "PrisonersDilemmaTestgame" -] +from .catalog import games + +# Dynamically import all catalog games +_all_games = games() +_game_classes = {} + +for game_name in _all_games: + # Import each game class from catalog module + from . import catalog + _game_classes[game_name] = getattr(catalog, game_name) + +# Add to module namespace +globals().update(_game_classes) + +# Build __all__ dynamically +__all__ = ["games"] + list(_all_games) # type: ignore[assignment] From a314e30d10743f4f9335691a174dcf09b19456e7 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 7 Jan 2026 17:03:56 +0000 Subject: [PATCH 18/76] add filter for game type in catalog.games() --- doc/tutorials/01_quickstart.ipynb | 8 ++++---- src/pygambit/catalog/catalog.py | 26 ++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index 499f221b1..8fdf00b0d 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -431,23 +431,23 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "701aa52a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['PrisonersDilemma', 'TwoStageMatchingPennies', 'PrisonersDilemmaTestgame']" + "['PrisonersDilemma', 'PrisonersDilemmaTestgame']" ] }, - "execution_count": 2, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "gbt.catalog.games()" + "gbt.catalog.games(game_type=\"nfg\")" ] }, { diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 3f2201807..37ba8049e 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -1,4 +1,5 @@ from pathlib import Path +from typing import Literal import numpy as np @@ -17,7 +18,7 @@ class CatalogGame: title: str num_players: int - game_type: str + game_type: Literal["nfg", "efg"] description: str citation: str @@ -92,13 +93,30 @@ def __init_subclass__(cls, **kwargs): cls._extract_metadata_from_game(cls._cached_game) -def games() -> list[str]: - """Return a list of all catalog game names.""" +def games(game_type: Literal["all", "nfg", "efg"] = "all") -> list[str]: + """ + Return a list of catalog game names. + + Parameters + ---------- + game_type : {"all", "nfg", "efg"}, default "all" + Filter games by type: + - "all": return all games + - "nfg": return only normal-form (strategic) games + - "efg": return only extensive-form games + + Returns + ------- + list[str] + List of game class names matching the specified type. + """ def get_all_subclasses(cls): """Recursively get all subclasses.""" all_subclasses = [] for subclass in cls.__subclasses__(): - if subclass.__name__ not in ["CatalogGameFromFile"]: + if subclass.__name__ not in ["CatalogGameFromFile"] and ( + game_type == "all" or subclass.game_type == game_type + ): all_subclasses.append(subclass.__name__) all_subclasses.extend(get_all_subclasses(subclass)) return all_subclasses From e17687744ea8126e1a6d2fd5e2a5851a8d06f466 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 09:58:07 +0000 Subject: [PATCH 19/76] add filter for number of players --- doc/tutorials/01_quickstart.ipynb | 63 +++++++++++++------------------ src/pygambit/catalog/catalog.py | 9 ++++- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index 8fdf00b0d..3407adf0c 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -50,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "2060c1ed", "metadata": {}, "outputs": [ @@ -60,7 +60,7 @@ "pygambit.gambit.Game" ] }, - "execution_count": 1, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -83,7 +83,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "9d8203e8", "metadata": {}, "outputs": [], @@ -111,7 +111,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "id": "61030607", "metadata": {}, "outputs": [], @@ -135,7 +135,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "id": "caecc334", "metadata": {}, "outputs": [ @@ -149,7 +149,7 @@ "Game(title='Prisoner's Dilemma')" ] }, - "execution_count": 4, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -189,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "843ba7f3", "metadata": {}, "outputs": [ @@ -203,7 +203,7 @@ "Game(title='Another Prisoner's Dilemma')" ] }, - "execution_count": 5, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -233,7 +233,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "id": "5ee752c4", "metadata": {}, "outputs": [ @@ -270,7 +270,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "id": "a81c06c7", "metadata": {}, "outputs": [ @@ -280,7 +280,7 @@ "pygambit.nash.NashComputationResult" ] }, - "execution_count": 7, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -300,7 +300,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "id": "bd395180", "metadata": {}, "outputs": [ @@ -310,7 +310,7 @@ "1" ] }, - "execution_count": 8, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -329,7 +329,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "76570ebc", "metadata": {}, "outputs": [ @@ -342,7 +342,7 @@ "[[Rational(0, 1), Rational(1, 1)], [Rational(0, 1), Rational(1, 1)]]" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -354,7 +354,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "id": "6e8cfcde", "metadata": {}, "outputs": [ @@ -364,7 +364,7 @@ "pygambit.gambit.MixedStrategyProfileRational" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -385,7 +385,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "id": "980bf6b1", "metadata": {}, "outputs": [ @@ -431,7 +431,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "id": "701aa52a", "metadata": {}, "outputs": [ @@ -441,13 +441,13 @@ "['PrisonersDilemma', 'PrisonersDilemmaTestgame']" ] }, - "execution_count": 3, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "gbt.catalog.games(game_type=\"nfg\")" + "gbt.catalog.games(game_type=\"nfg\", num_players=2)" ] }, { @@ -460,7 +460,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 14, "id": "6db7a29a", "metadata": {}, "outputs": [ @@ -474,7 +474,7 @@ "Game(title='Two person Prisoner's Dilemma game')" ] }, - "execution_count": 3, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -500,7 +500,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "f58eaa77", "metadata": {}, "outputs": [], @@ -518,21 +518,10 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "4119a2ac", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "pygambit.gambit.Game" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# gbt.read_nfg(\"prisoners_dilemma.nfg\")" ] diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 37ba8049e..016775bb0 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -93,7 +93,10 @@ def __init_subclass__(cls, **kwargs): cls._extract_metadata_from_game(cls._cached_game) -def games(game_type: Literal["all", "nfg", "efg"] = "all") -> list[str]: +def games( + game_type: Literal["all", "nfg", "efg"] = "all", + num_players: int | None = None, +) -> list[str]: """ Return a list of catalog game names. @@ -104,6 +107,8 @@ def games(game_type: Literal["all", "nfg", "efg"] = "all") -> list[str]: - "all": return all games - "nfg": return only normal-form (strategic) games - "efg": return only extensive-form games + num_players : int | None, default None + If specified, only return games with the given number of players. Returns ------- @@ -116,6 +121,8 @@ def get_all_subclasses(cls): for subclass in cls.__subclasses__(): if subclass.__name__ not in ["CatalogGameFromFile"] and ( game_type == "all" or subclass.game_type == game_type + ) and ( + num_players is None or subclass.num_players == num_players ): all_subclasses.append(subclass.__name__) all_subclasses.extend(get_all_subclasses(subclass)) From 292a55d0feb7e867c9aa62e5ee199b00a76fae07 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 10:15:24 +0000 Subject: [PATCH 20/76] move catalog gamefiles into top dir for now --- {src/pygambit/catalog => catalog}/gamefiles/2s2x2x2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/2smp.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/2x2.agg | 0 {src/pygambit/catalog => catalog}/gamefiles/2x2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/2x2a.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/2x2const.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/2x2x2-nau.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/2x2x2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/2x2x2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/2x2x2x2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/2x2x2x2x2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/3x3x3.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/4cards.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/5x4x3.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/8x2x2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/8x8.nfg | 0 .../catalog => catalog}/gamefiles/BSS_S_085.Weighted.agg | 0 .../catalog => catalog}/gamefiles/Bayesian-Coffee-3-2-2-3.bagg | 0 {src/pygambit/catalog => catalog}/gamefiles/GenRPS5.agg | 0 {src/pygambit/catalog => catalog}/gamefiles/artist1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/artist2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/badgame1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/badgame2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bagwell.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bayes1a.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bayes2a.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bcp2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bcp3.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bcp4.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bhg1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bhg2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bhg3.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bhg4.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/bhg5.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/caro2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/cent2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/cent2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/cent3.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/cent4.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/cent6.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/centcs10.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/centcs6.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/condjury.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/coord2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/coord2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/coord2ts.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/coord3.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/coord3.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/coord333.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/coord4.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/coord4.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/cross.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/cs.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/csg1.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/csg2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/csg3.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/csg4.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/deg1.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/deg2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/e01.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e01.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/e02.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e02.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/e03.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e04.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e04.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/e05.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e06.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e07.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e07.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/e08.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e09.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e10.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e10a.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e13.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e16.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e17.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/e18.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/g1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/g1.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/g2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/g2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/g3.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/g3.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/holdout.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/holdout7.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/hs1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/jury_mr.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/jury_un.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/km1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/km2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/km3.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/km6.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/loopback.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/mixdom.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/mixdom2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/montyhal.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/my_2-1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/my_2-4.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/my_2-8.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/my_3-3a.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/my_3-3b.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/my_3-3c.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/my_3-3d.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/my_3-3e.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/my_3-4.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/myerson.efg | 0 .../pygambit/catalog => catalog}/gamefiles/myerson_fig_4_2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/nim.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/nim7.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/oneill.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/palf.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/palf2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/palf3.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/pbride.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/pd.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/perfect1.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/perfect2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/perfect3.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/poker.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/poker.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/poker2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/pvw.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/pvw2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/sh3.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/sh3.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/spence.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/stengel.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/sww1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/sww1.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/sww2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/sww3.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/tim.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/todd1.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/todd2.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/todd3.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/ttt.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/vd.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/vd.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/w_ex1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/w_ex2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/wilson1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/wink3.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/winkels.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/work1.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/work2.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/work3.efg | 0 {src/pygambit/catalog => catalog}/gamefiles/yamamoto.nfg | 0 {src/pygambit/catalog => catalog}/gamefiles/zero.nfg | 0 src/pygambit/catalog/catalog.py | 3 +-- 150 files changed, 1 insertion(+), 2 deletions(-) rename {src/pygambit/catalog => catalog}/gamefiles/2s2x2x2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/2smp.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/2x2.agg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/2x2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/2x2a.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/2x2const.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/2x2x2-nau.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/2x2x2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/2x2x2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/2x2x2x2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/2x2x2x2x2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/3x3x3.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/4cards.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/5x4x3.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/8x2x2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/8x8.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/BSS_S_085.Weighted.agg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/Bayesian-Coffee-3-2-2-3.bagg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/GenRPS5.agg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/artist1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/artist2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/badgame1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/badgame2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bagwell.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bayes1a.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bayes2a.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bcp2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bcp3.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bcp4.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bhg1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bhg2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bhg3.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bhg4.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/bhg5.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/caro2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/cent2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/cent2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/cent3.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/cent4.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/cent6.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/centcs10.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/centcs6.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/condjury.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/coord2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/coord2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/coord2ts.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/coord3.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/coord3.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/coord333.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/coord4.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/coord4.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/cross.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/cs.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/csg1.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/csg2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/csg3.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/csg4.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/deg1.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/deg2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e01.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e01.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e02.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e02.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e03.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e04.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e04.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e05.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e06.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e07.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e07.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e08.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e09.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e10.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e10a.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e13.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e16.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e17.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/e18.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/g1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/g1.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/g2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/g2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/g3.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/g3.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/holdout.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/holdout7.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/hs1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/jury_mr.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/jury_un.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/km1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/km2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/km3.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/km6.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/loopback.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/mixdom.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/mixdom2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/montyhal.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/my_2-1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/my_2-4.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/my_2-8.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/my_3-3a.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/my_3-3b.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/my_3-3c.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/my_3-3d.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/my_3-3e.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/my_3-4.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/myerson.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/myerson_fig_4_2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/nim.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/nim7.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/oneill.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/palf.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/palf2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/palf3.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/pbride.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/pd.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/perfect1.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/perfect2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/perfect3.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/poker.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/poker.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/poker2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/pvw.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/pvw2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/sh3.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/sh3.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/spence.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/stengel.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/sww1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/sww1.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/sww2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/sww3.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/tim.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/todd1.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/todd2.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/todd3.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/ttt.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/vd.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/vd.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/w_ex1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/w_ex2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/wilson1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/wink3.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/winkels.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/work1.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/work2.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/work3.efg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/yamamoto.nfg (100%) rename {src/pygambit/catalog => catalog}/gamefiles/zero.nfg (100%) diff --git a/src/pygambit/catalog/gamefiles/2s2x2x2.efg b/catalog/gamefiles/2s2x2x2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/2s2x2x2.efg rename to catalog/gamefiles/2s2x2x2.efg diff --git a/src/pygambit/catalog/gamefiles/2smp.efg b/catalog/gamefiles/2smp.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/2smp.efg rename to catalog/gamefiles/2smp.efg diff --git a/src/pygambit/catalog/gamefiles/2x2.agg b/catalog/gamefiles/2x2.agg similarity index 100% rename from src/pygambit/catalog/gamefiles/2x2.agg rename to catalog/gamefiles/2x2.agg diff --git a/src/pygambit/catalog/gamefiles/2x2.nfg b/catalog/gamefiles/2x2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/2x2.nfg rename to catalog/gamefiles/2x2.nfg diff --git a/src/pygambit/catalog/gamefiles/2x2a.nfg b/catalog/gamefiles/2x2a.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/2x2a.nfg rename to catalog/gamefiles/2x2a.nfg diff --git a/src/pygambit/catalog/gamefiles/2x2const.nfg b/catalog/gamefiles/2x2const.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/2x2const.nfg rename to catalog/gamefiles/2x2const.nfg diff --git a/src/pygambit/catalog/gamefiles/2x2x2-nau.nfg b/catalog/gamefiles/2x2x2-nau.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/2x2x2-nau.nfg rename to catalog/gamefiles/2x2x2-nau.nfg diff --git a/src/pygambit/catalog/gamefiles/2x2x2.efg b/catalog/gamefiles/2x2x2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/2x2x2.efg rename to catalog/gamefiles/2x2x2.efg diff --git a/src/pygambit/catalog/gamefiles/2x2x2.nfg b/catalog/gamefiles/2x2x2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/2x2x2.nfg rename to catalog/gamefiles/2x2x2.nfg diff --git a/src/pygambit/catalog/gamefiles/2x2x2x2.nfg b/catalog/gamefiles/2x2x2x2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/2x2x2x2.nfg rename to catalog/gamefiles/2x2x2x2.nfg diff --git a/src/pygambit/catalog/gamefiles/2x2x2x2x2.nfg b/catalog/gamefiles/2x2x2x2x2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/2x2x2x2x2.nfg rename to catalog/gamefiles/2x2x2x2x2.nfg diff --git a/src/pygambit/catalog/gamefiles/3x3x3.nfg b/catalog/gamefiles/3x3x3.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/3x3x3.nfg rename to catalog/gamefiles/3x3x3.nfg diff --git a/src/pygambit/catalog/gamefiles/4cards.efg b/catalog/gamefiles/4cards.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/4cards.efg rename to catalog/gamefiles/4cards.efg diff --git a/src/pygambit/catalog/gamefiles/5x4x3.nfg b/catalog/gamefiles/5x4x3.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/5x4x3.nfg rename to catalog/gamefiles/5x4x3.nfg diff --git a/src/pygambit/catalog/gamefiles/8x2x2.nfg b/catalog/gamefiles/8x2x2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/8x2x2.nfg rename to catalog/gamefiles/8x2x2.nfg diff --git a/src/pygambit/catalog/gamefiles/8x8.nfg b/catalog/gamefiles/8x8.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/8x8.nfg rename to catalog/gamefiles/8x8.nfg diff --git a/src/pygambit/catalog/gamefiles/BSS_S_085.Weighted.agg b/catalog/gamefiles/BSS_S_085.Weighted.agg similarity index 100% rename from src/pygambit/catalog/gamefiles/BSS_S_085.Weighted.agg rename to catalog/gamefiles/BSS_S_085.Weighted.agg diff --git a/src/pygambit/catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg b/catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg similarity index 100% rename from src/pygambit/catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg rename to catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg diff --git a/src/pygambit/catalog/gamefiles/GenRPS5.agg b/catalog/gamefiles/GenRPS5.agg similarity index 100% rename from src/pygambit/catalog/gamefiles/GenRPS5.agg rename to catalog/gamefiles/GenRPS5.agg diff --git a/src/pygambit/catalog/gamefiles/artist1.efg b/catalog/gamefiles/artist1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/artist1.efg rename to catalog/gamefiles/artist1.efg diff --git a/src/pygambit/catalog/gamefiles/artist2.efg b/catalog/gamefiles/artist2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/artist2.efg rename to catalog/gamefiles/artist2.efg diff --git a/src/pygambit/catalog/gamefiles/badgame1.efg b/catalog/gamefiles/badgame1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/badgame1.efg rename to catalog/gamefiles/badgame1.efg diff --git a/src/pygambit/catalog/gamefiles/badgame2.efg b/catalog/gamefiles/badgame2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/badgame2.efg rename to catalog/gamefiles/badgame2.efg diff --git a/src/pygambit/catalog/gamefiles/bagwell.efg b/catalog/gamefiles/bagwell.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bagwell.efg rename to catalog/gamefiles/bagwell.efg diff --git a/src/pygambit/catalog/gamefiles/bayes1a.efg b/catalog/gamefiles/bayes1a.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bayes1a.efg rename to catalog/gamefiles/bayes1a.efg diff --git a/src/pygambit/catalog/gamefiles/bayes2a.efg b/catalog/gamefiles/bayes2a.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bayes2a.efg rename to catalog/gamefiles/bayes2a.efg diff --git a/src/pygambit/catalog/gamefiles/bcp2.efg b/catalog/gamefiles/bcp2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bcp2.efg rename to catalog/gamefiles/bcp2.efg diff --git a/src/pygambit/catalog/gamefiles/bcp3.efg b/catalog/gamefiles/bcp3.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bcp3.efg rename to catalog/gamefiles/bcp3.efg diff --git a/src/pygambit/catalog/gamefiles/bcp4.efg b/catalog/gamefiles/bcp4.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bcp4.efg rename to catalog/gamefiles/bcp4.efg diff --git a/src/pygambit/catalog/gamefiles/bhg1.efg b/catalog/gamefiles/bhg1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bhg1.efg rename to catalog/gamefiles/bhg1.efg diff --git a/src/pygambit/catalog/gamefiles/bhg2.efg b/catalog/gamefiles/bhg2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bhg2.efg rename to catalog/gamefiles/bhg2.efg diff --git a/src/pygambit/catalog/gamefiles/bhg3.efg b/catalog/gamefiles/bhg3.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bhg3.efg rename to catalog/gamefiles/bhg3.efg diff --git a/src/pygambit/catalog/gamefiles/bhg4.efg b/catalog/gamefiles/bhg4.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bhg4.efg rename to catalog/gamefiles/bhg4.efg diff --git a/src/pygambit/catalog/gamefiles/bhg5.efg b/catalog/gamefiles/bhg5.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/bhg5.efg rename to catalog/gamefiles/bhg5.efg diff --git a/src/pygambit/catalog/gamefiles/caro2.efg b/catalog/gamefiles/caro2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/caro2.efg rename to catalog/gamefiles/caro2.efg diff --git a/src/pygambit/catalog/gamefiles/cent2.efg b/catalog/gamefiles/cent2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/cent2.efg rename to catalog/gamefiles/cent2.efg diff --git a/src/pygambit/catalog/gamefiles/cent2.nfg b/catalog/gamefiles/cent2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/cent2.nfg rename to catalog/gamefiles/cent2.nfg diff --git a/src/pygambit/catalog/gamefiles/cent3.efg b/catalog/gamefiles/cent3.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/cent3.efg rename to catalog/gamefiles/cent3.efg diff --git a/src/pygambit/catalog/gamefiles/cent4.efg b/catalog/gamefiles/cent4.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/cent4.efg rename to catalog/gamefiles/cent4.efg diff --git a/src/pygambit/catalog/gamefiles/cent6.efg b/catalog/gamefiles/cent6.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/cent6.efg rename to catalog/gamefiles/cent6.efg diff --git a/src/pygambit/catalog/gamefiles/centcs10.efg b/catalog/gamefiles/centcs10.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/centcs10.efg rename to catalog/gamefiles/centcs10.efg diff --git a/src/pygambit/catalog/gamefiles/centcs6.efg b/catalog/gamefiles/centcs6.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/centcs6.efg rename to catalog/gamefiles/centcs6.efg diff --git a/src/pygambit/catalog/gamefiles/condjury.efg b/catalog/gamefiles/condjury.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/condjury.efg rename to catalog/gamefiles/condjury.efg diff --git a/src/pygambit/catalog/gamefiles/coord2.efg b/catalog/gamefiles/coord2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/coord2.efg rename to catalog/gamefiles/coord2.efg diff --git a/src/pygambit/catalog/gamefiles/coord2.nfg b/catalog/gamefiles/coord2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/coord2.nfg rename to catalog/gamefiles/coord2.nfg diff --git a/src/pygambit/catalog/gamefiles/coord2ts.efg b/catalog/gamefiles/coord2ts.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/coord2ts.efg rename to catalog/gamefiles/coord2ts.efg diff --git a/src/pygambit/catalog/gamefiles/coord3.efg b/catalog/gamefiles/coord3.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/coord3.efg rename to catalog/gamefiles/coord3.efg diff --git a/src/pygambit/catalog/gamefiles/coord3.nfg b/catalog/gamefiles/coord3.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/coord3.nfg rename to catalog/gamefiles/coord3.nfg diff --git a/src/pygambit/catalog/gamefiles/coord333.nfg b/catalog/gamefiles/coord333.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/coord333.nfg rename to catalog/gamefiles/coord333.nfg diff --git a/src/pygambit/catalog/gamefiles/coord4.efg b/catalog/gamefiles/coord4.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/coord4.efg rename to catalog/gamefiles/coord4.efg diff --git a/src/pygambit/catalog/gamefiles/coord4.nfg b/catalog/gamefiles/coord4.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/coord4.nfg rename to catalog/gamefiles/coord4.nfg diff --git a/src/pygambit/catalog/gamefiles/cross.efg b/catalog/gamefiles/cross.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/cross.efg rename to catalog/gamefiles/cross.efg diff --git a/src/pygambit/catalog/gamefiles/cs.efg b/catalog/gamefiles/cs.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/cs.efg rename to catalog/gamefiles/cs.efg diff --git a/src/pygambit/catalog/gamefiles/csg1.nfg b/catalog/gamefiles/csg1.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/csg1.nfg rename to catalog/gamefiles/csg1.nfg diff --git a/src/pygambit/catalog/gamefiles/csg2.nfg b/catalog/gamefiles/csg2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/csg2.nfg rename to catalog/gamefiles/csg2.nfg diff --git a/src/pygambit/catalog/gamefiles/csg3.nfg b/catalog/gamefiles/csg3.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/csg3.nfg rename to catalog/gamefiles/csg3.nfg diff --git a/src/pygambit/catalog/gamefiles/csg4.nfg b/catalog/gamefiles/csg4.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/csg4.nfg rename to catalog/gamefiles/csg4.nfg diff --git a/src/pygambit/catalog/gamefiles/deg1.nfg b/catalog/gamefiles/deg1.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/deg1.nfg rename to catalog/gamefiles/deg1.nfg diff --git a/src/pygambit/catalog/gamefiles/deg2.nfg b/catalog/gamefiles/deg2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/deg2.nfg rename to catalog/gamefiles/deg2.nfg diff --git a/src/pygambit/catalog/gamefiles/e01.efg b/catalog/gamefiles/e01.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e01.efg rename to catalog/gamefiles/e01.efg diff --git a/src/pygambit/catalog/gamefiles/e01.nfg b/catalog/gamefiles/e01.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/e01.nfg rename to catalog/gamefiles/e01.nfg diff --git a/src/pygambit/catalog/gamefiles/e02.efg b/catalog/gamefiles/e02.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e02.efg rename to catalog/gamefiles/e02.efg diff --git a/src/pygambit/catalog/gamefiles/e02.nfg b/catalog/gamefiles/e02.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/e02.nfg rename to catalog/gamefiles/e02.nfg diff --git a/src/pygambit/catalog/gamefiles/e03.efg b/catalog/gamefiles/e03.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e03.efg rename to catalog/gamefiles/e03.efg diff --git a/src/pygambit/catalog/gamefiles/e04.efg b/catalog/gamefiles/e04.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e04.efg rename to catalog/gamefiles/e04.efg diff --git a/src/pygambit/catalog/gamefiles/e04.nfg b/catalog/gamefiles/e04.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/e04.nfg rename to catalog/gamefiles/e04.nfg diff --git a/src/pygambit/catalog/gamefiles/e05.efg b/catalog/gamefiles/e05.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e05.efg rename to catalog/gamefiles/e05.efg diff --git a/src/pygambit/catalog/gamefiles/e06.efg b/catalog/gamefiles/e06.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e06.efg rename to catalog/gamefiles/e06.efg diff --git a/src/pygambit/catalog/gamefiles/e07.efg b/catalog/gamefiles/e07.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e07.efg rename to catalog/gamefiles/e07.efg diff --git a/src/pygambit/catalog/gamefiles/e07.nfg b/catalog/gamefiles/e07.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/e07.nfg rename to catalog/gamefiles/e07.nfg diff --git a/src/pygambit/catalog/gamefiles/e08.efg b/catalog/gamefiles/e08.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e08.efg rename to catalog/gamefiles/e08.efg diff --git a/src/pygambit/catalog/gamefiles/e09.efg b/catalog/gamefiles/e09.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e09.efg rename to catalog/gamefiles/e09.efg diff --git a/src/pygambit/catalog/gamefiles/e10.efg b/catalog/gamefiles/e10.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e10.efg rename to catalog/gamefiles/e10.efg diff --git a/src/pygambit/catalog/gamefiles/e10a.efg b/catalog/gamefiles/e10a.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e10a.efg rename to catalog/gamefiles/e10a.efg diff --git a/src/pygambit/catalog/gamefiles/e13.efg b/catalog/gamefiles/e13.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e13.efg rename to catalog/gamefiles/e13.efg diff --git a/src/pygambit/catalog/gamefiles/e16.efg b/catalog/gamefiles/e16.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e16.efg rename to catalog/gamefiles/e16.efg diff --git a/src/pygambit/catalog/gamefiles/e17.efg b/catalog/gamefiles/e17.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e17.efg rename to catalog/gamefiles/e17.efg diff --git a/src/pygambit/catalog/gamefiles/e18.efg b/catalog/gamefiles/e18.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/e18.efg rename to catalog/gamefiles/e18.efg diff --git a/src/pygambit/catalog/gamefiles/g1.efg b/catalog/gamefiles/g1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/g1.efg rename to catalog/gamefiles/g1.efg diff --git a/src/pygambit/catalog/gamefiles/g1.nfg b/catalog/gamefiles/g1.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/g1.nfg rename to catalog/gamefiles/g1.nfg diff --git a/src/pygambit/catalog/gamefiles/g2.efg b/catalog/gamefiles/g2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/g2.efg rename to catalog/gamefiles/g2.efg diff --git a/src/pygambit/catalog/gamefiles/g2.nfg b/catalog/gamefiles/g2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/g2.nfg rename to catalog/gamefiles/g2.nfg diff --git a/src/pygambit/catalog/gamefiles/g3.efg b/catalog/gamefiles/g3.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/g3.efg rename to catalog/gamefiles/g3.efg diff --git a/src/pygambit/catalog/gamefiles/g3.nfg b/catalog/gamefiles/g3.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/g3.nfg rename to catalog/gamefiles/g3.nfg diff --git a/src/pygambit/catalog/gamefiles/holdout.efg b/catalog/gamefiles/holdout.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/holdout.efg rename to catalog/gamefiles/holdout.efg diff --git a/src/pygambit/catalog/gamefiles/holdout7.efg b/catalog/gamefiles/holdout7.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/holdout7.efg rename to catalog/gamefiles/holdout7.efg diff --git a/src/pygambit/catalog/gamefiles/hs1.efg b/catalog/gamefiles/hs1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/hs1.efg rename to catalog/gamefiles/hs1.efg diff --git a/src/pygambit/catalog/gamefiles/jury_mr.efg b/catalog/gamefiles/jury_mr.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/jury_mr.efg rename to catalog/gamefiles/jury_mr.efg diff --git a/src/pygambit/catalog/gamefiles/jury_un.efg b/catalog/gamefiles/jury_un.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/jury_un.efg rename to catalog/gamefiles/jury_un.efg diff --git a/src/pygambit/catalog/gamefiles/km1.efg b/catalog/gamefiles/km1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/km1.efg rename to catalog/gamefiles/km1.efg diff --git a/src/pygambit/catalog/gamefiles/km2.efg b/catalog/gamefiles/km2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/km2.efg rename to catalog/gamefiles/km2.efg diff --git a/src/pygambit/catalog/gamefiles/km3.efg b/catalog/gamefiles/km3.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/km3.efg rename to catalog/gamefiles/km3.efg diff --git a/src/pygambit/catalog/gamefiles/km6.efg b/catalog/gamefiles/km6.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/km6.efg rename to catalog/gamefiles/km6.efg diff --git a/src/pygambit/catalog/gamefiles/loopback.nfg b/catalog/gamefiles/loopback.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/loopback.nfg rename to catalog/gamefiles/loopback.nfg diff --git a/src/pygambit/catalog/gamefiles/mixdom.nfg b/catalog/gamefiles/mixdom.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/mixdom.nfg rename to catalog/gamefiles/mixdom.nfg diff --git a/src/pygambit/catalog/gamefiles/mixdom2.nfg b/catalog/gamefiles/mixdom2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/mixdom2.nfg rename to catalog/gamefiles/mixdom2.nfg diff --git a/src/pygambit/catalog/gamefiles/montyhal.efg b/catalog/gamefiles/montyhal.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/montyhal.efg rename to catalog/gamefiles/montyhal.efg diff --git a/src/pygambit/catalog/gamefiles/my_2-1.efg b/catalog/gamefiles/my_2-1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/my_2-1.efg rename to catalog/gamefiles/my_2-1.efg diff --git a/src/pygambit/catalog/gamefiles/my_2-4.efg b/catalog/gamefiles/my_2-4.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/my_2-4.efg rename to catalog/gamefiles/my_2-4.efg diff --git a/src/pygambit/catalog/gamefiles/my_2-8.efg b/catalog/gamefiles/my_2-8.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/my_2-8.efg rename to catalog/gamefiles/my_2-8.efg diff --git a/src/pygambit/catalog/gamefiles/my_3-3a.efg b/catalog/gamefiles/my_3-3a.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/my_3-3a.efg rename to catalog/gamefiles/my_3-3a.efg diff --git a/src/pygambit/catalog/gamefiles/my_3-3b.efg b/catalog/gamefiles/my_3-3b.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/my_3-3b.efg rename to catalog/gamefiles/my_3-3b.efg diff --git a/src/pygambit/catalog/gamefiles/my_3-3c.efg b/catalog/gamefiles/my_3-3c.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/my_3-3c.efg rename to catalog/gamefiles/my_3-3c.efg diff --git a/src/pygambit/catalog/gamefiles/my_3-3d.efg b/catalog/gamefiles/my_3-3d.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/my_3-3d.efg rename to catalog/gamefiles/my_3-3d.efg diff --git a/src/pygambit/catalog/gamefiles/my_3-3e.efg b/catalog/gamefiles/my_3-3e.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/my_3-3e.efg rename to catalog/gamefiles/my_3-3e.efg diff --git a/src/pygambit/catalog/gamefiles/my_3-4.efg b/catalog/gamefiles/my_3-4.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/my_3-4.efg rename to catalog/gamefiles/my_3-4.efg diff --git a/src/pygambit/catalog/gamefiles/myerson.efg b/catalog/gamefiles/myerson.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/myerson.efg rename to catalog/gamefiles/myerson.efg diff --git a/src/pygambit/catalog/gamefiles/myerson_fig_4_2.efg b/catalog/gamefiles/myerson_fig_4_2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/myerson_fig_4_2.efg rename to catalog/gamefiles/myerson_fig_4_2.efg diff --git a/src/pygambit/catalog/gamefiles/nim.efg b/catalog/gamefiles/nim.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/nim.efg rename to catalog/gamefiles/nim.efg diff --git a/src/pygambit/catalog/gamefiles/nim7.efg b/catalog/gamefiles/nim7.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/nim7.efg rename to catalog/gamefiles/nim7.efg diff --git a/src/pygambit/catalog/gamefiles/oneill.nfg b/catalog/gamefiles/oneill.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/oneill.nfg rename to catalog/gamefiles/oneill.nfg diff --git a/src/pygambit/catalog/gamefiles/palf.efg b/catalog/gamefiles/palf.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/palf.efg rename to catalog/gamefiles/palf.efg diff --git a/src/pygambit/catalog/gamefiles/palf2.efg b/catalog/gamefiles/palf2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/palf2.efg rename to catalog/gamefiles/palf2.efg diff --git a/src/pygambit/catalog/gamefiles/palf3.efg b/catalog/gamefiles/palf3.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/palf3.efg rename to catalog/gamefiles/palf3.efg diff --git a/src/pygambit/catalog/gamefiles/pbride.efg b/catalog/gamefiles/pbride.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/pbride.efg rename to catalog/gamefiles/pbride.efg diff --git a/src/pygambit/catalog/gamefiles/pd.nfg b/catalog/gamefiles/pd.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/pd.nfg rename to catalog/gamefiles/pd.nfg diff --git a/src/pygambit/catalog/gamefiles/perfect1.nfg b/catalog/gamefiles/perfect1.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/perfect1.nfg rename to catalog/gamefiles/perfect1.nfg diff --git a/src/pygambit/catalog/gamefiles/perfect2.nfg b/catalog/gamefiles/perfect2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/perfect2.nfg rename to catalog/gamefiles/perfect2.nfg diff --git a/src/pygambit/catalog/gamefiles/perfect3.nfg b/catalog/gamefiles/perfect3.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/perfect3.nfg rename to catalog/gamefiles/perfect3.nfg diff --git a/src/pygambit/catalog/gamefiles/poker.efg b/catalog/gamefiles/poker.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/poker.efg rename to catalog/gamefiles/poker.efg diff --git a/src/pygambit/catalog/gamefiles/poker.nfg b/catalog/gamefiles/poker.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/poker.nfg rename to catalog/gamefiles/poker.nfg diff --git a/src/pygambit/catalog/gamefiles/poker2.efg b/catalog/gamefiles/poker2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/poker2.efg rename to catalog/gamefiles/poker2.efg diff --git a/src/pygambit/catalog/gamefiles/pvw.efg b/catalog/gamefiles/pvw.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/pvw.efg rename to catalog/gamefiles/pvw.efg diff --git a/src/pygambit/catalog/gamefiles/pvw2.efg b/catalog/gamefiles/pvw2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/pvw2.efg rename to catalog/gamefiles/pvw2.efg diff --git a/src/pygambit/catalog/gamefiles/sh3.efg b/catalog/gamefiles/sh3.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/sh3.efg rename to catalog/gamefiles/sh3.efg diff --git a/src/pygambit/catalog/gamefiles/sh3.nfg b/catalog/gamefiles/sh3.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/sh3.nfg rename to catalog/gamefiles/sh3.nfg diff --git a/src/pygambit/catalog/gamefiles/spence.efg b/catalog/gamefiles/spence.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/spence.efg rename to catalog/gamefiles/spence.efg diff --git a/src/pygambit/catalog/gamefiles/stengel.nfg b/catalog/gamefiles/stengel.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/stengel.nfg rename to catalog/gamefiles/stengel.nfg diff --git a/src/pygambit/catalog/gamefiles/sww1.efg b/catalog/gamefiles/sww1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/sww1.efg rename to catalog/gamefiles/sww1.efg diff --git a/src/pygambit/catalog/gamefiles/sww1.nfg b/catalog/gamefiles/sww1.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/sww1.nfg rename to catalog/gamefiles/sww1.nfg diff --git a/src/pygambit/catalog/gamefiles/sww2.efg b/catalog/gamefiles/sww2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/sww2.efg rename to catalog/gamefiles/sww2.efg diff --git a/src/pygambit/catalog/gamefiles/sww3.efg b/catalog/gamefiles/sww3.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/sww3.efg rename to catalog/gamefiles/sww3.efg diff --git a/src/pygambit/catalog/gamefiles/tim.efg b/catalog/gamefiles/tim.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/tim.efg rename to catalog/gamefiles/tim.efg diff --git a/src/pygambit/catalog/gamefiles/todd1.nfg b/catalog/gamefiles/todd1.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/todd1.nfg rename to catalog/gamefiles/todd1.nfg diff --git a/src/pygambit/catalog/gamefiles/todd2.nfg b/catalog/gamefiles/todd2.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/todd2.nfg rename to catalog/gamefiles/todd2.nfg diff --git a/src/pygambit/catalog/gamefiles/todd3.nfg b/catalog/gamefiles/todd3.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/todd3.nfg rename to catalog/gamefiles/todd3.nfg diff --git a/src/pygambit/catalog/gamefiles/ttt.efg b/catalog/gamefiles/ttt.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/ttt.efg rename to catalog/gamefiles/ttt.efg diff --git a/src/pygambit/catalog/gamefiles/vd.efg b/catalog/gamefiles/vd.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/vd.efg rename to catalog/gamefiles/vd.efg diff --git a/src/pygambit/catalog/gamefiles/vd.nfg b/catalog/gamefiles/vd.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/vd.nfg rename to catalog/gamefiles/vd.nfg diff --git a/src/pygambit/catalog/gamefiles/w_ex1.efg b/catalog/gamefiles/w_ex1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/w_ex1.efg rename to catalog/gamefiles/w_ex1.efg diff --git a/src/pygambit/catalog/gamefiles/w_ex2.efg b/catalog/gamefiles/w_ex2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/w_ex2.efg rename to catalog/gamefiles/w_ex2.efg diff --git a/src/pygambit/catalog/gamefiles/wilson1.efg b/catalog/gamefiles/wilson1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/wilson1.efg rename to catalog/gamefiles/wilson1.efg diff --git a/src/pygambit/catalog/gamefiles/wink3.nfg b/catalog/gamefiles/wink3.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/wink3.nfg rename to catalog/gamefiles/wink3.nfg diff --git a/src/pygambit/catalog/gamefiles/winkels.nfg b/catalog/gamefiles/winkels.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/winkels.nfg rename to catalog/gamefiles/winkels.nfg diff --git a/src/pygambit/catalog/gamefiles/work1.efg b/catalog/gamefiles/work1.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/work1.efg rename to catalog/gamefiles/work1.efg diff --git a/src/pygambit/catalog/gamefiles/work2.efg b/catalog/gamefiles/work2.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/work2.efg rename to catalog/gamefiles/work2.efg diff --git a/src/pygambit/catalog/gamefiles/work3.efg b/catalog/gamefiles/work3.efg similarity index 100% rename from src/pygambit/catalog/gamefiles/work3.efg rename to catalog/gamefiles/work3.efg diff --git a/src/pygambit/catalog/gamefiles/yamamoto.nfg b/catalog/gamefiles/yamamoto.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/yamamoto.nfg rename to catalog/gamefiles/yamamoto.nfg diff --git a/src/pygambit/catalog/gamefiles/zero.nfg b/catalog/gamefiles/zero.nfg similarity index 100% rename from src/pygambit/catalog/gamefiles/zero.nfg rename to catalog/gamefiles/zero.nfg diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 016775bb0..36386e98f 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -5,8 +5,7 @@ from pygambit import Game, read_efg, read_nfg -_CATALOG_DIR = Path(__file__).parent -_GAMEFILES_DIR = _CATALOG_DIR / "gamefiles" +_GAMEFILES_DIR = Path(__file__).parent.parent.parent.parent / "catalog/gamefiles" class CatalogGame: From 6a2677a215b139b7f3022a20eb435426ec5ea111 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 10:18:40 +0000 Subject: [PATCH 21/76] move games back to contrib/games --- {catalog/gamefiles => contrib/games}/2s2x2x2.efg | 0 {catalog/gamefiles => contrib/games}/2smp.efg | 0 {catalog/gamefiles => contrib/games}/2x2.agg | 0 {catalog/gamefiles => contrib/games}/2x2.nfg | 0 {catalog/gamefiles => contrib/games}/2x2a.nfg | 0 {catalog/gamefiles => contrib/games}/2x2const.nfg | 0 {catalog/gamefiles => contrib/games}/2x2x2-nau.nfg | 0 {catalog/gamefiles => contrib/games}/2x2x2.efg | 0 {catalog/gamefiles => contrib/games}/2x2x2.nfg | 0 {catalog/gamefiles => contrib/games}/2x2x2x2.nfg | 0 {catalog/gamefiles => contrib/games}/2x2x2x2x2.nfg | 0 {catalog/gamefiles => contrib/games}/3x3x3.nfg | 0 {catalog/gamefiles => contrib/games}/4cards.efg | 0 {catalog/gamefiles => contrib/games}/5x4x3.nfg | 0 {catalog/gamefiles => contrib/games}/8x2x2.nfg | 0 {catalog/gamefiles => contrib/games}/8x8.nfg | 0 .../gamefiles => contrib/games}/BSS_S_085.Weighted.agg | 0 .../games}/Bayesian-Coffee-3-2-2-3.bagg | 0 {catalog/gamefiles => contrib/games}/GenRPS5.agg | 0 {catalog/gamefiles => contrib/games}/artist1.efg | 0 {catalog/gamefiles => contrib/games}/artist2.efg | 0 {catalog/gamefiles => contrib/games}/badgame1.efg | 0 {catalog/gamefiles => contrib/games}/badgame2.efg | 0 {catalog/gamefiles => contrib/games}/bagwell.efg | 0 {catalog/gamefiles => contrib/games}/bayes1a.efg | 0 {catalog/gamefiles => contrib/games}/bayes2a.efg | 0 {catalog/gamefiles => contrib/games}/bcp2.efg | 0 {catalog/gamefiles => contrib/games}/bcp3.efg | 0 {catalog/gamefiles => contrib/games}/bcp4.efg | 0 {catalog/gamefiles => contrib/games}/bhg1.efg | 0 {catalog/gamefiles => contrib/games}/bhg2.efg | 0 {catalog/gamefiles => contrib/games}/bhg3.efg | 0 {catalog/gamefiles => contrib/games}/bhg4.efg | 0 {catalog/gamefiles => contrib/games}/bhg5.efg | 0 {catalog/gamefiles => contrib/games}/caro2.efg | 0 {catalog/gamefiles => contrib/games}/cent2.efg | 0 {catalog/gamefiles => contrib/games}/cent2.nfg | 0 {catalog/gamefiles => contrib/games}/cent3.efg | 0 {catalog/gamefiles => contrib/games}/cent4.efg | 0 {catalog/gamefiles => contrib/games}/cent6.efg | 0 {catalog/gamefiles => contrib/games}/centcs10.efg | 0 {catalog/gamefiles => contrib/games}/centcs6.efg | 0 {catalog/gamefiles => contrib/games}/condjury.efg | 0 {catalog/gamefiles => contrib/games}/coord2.efg | 0 {catalog/gamefiles => contrib/games}/coord2.nfg | 0 {catalog/gamefiles => contrib/games}/coord2ts.efg | 0 {catalog/gamefiles => contrib/games}/coord3.efg | 0 {catalog/gamefiles => contrib/games}/coord3.nfg | 0 {catalog/gamefiles => contrib/games}/coord333.nfg | 0 {catalog/gamefiles => contrib/games}/coord4.efg | 0 {catalog/gamefiles => contrib/games}/coord4.nfg | 0 {catalog/gamefiles => contrib/games}/cross.efg | 0 {catalog/gamefiles => contrib/games}/cs.efg | 0 {catalog/gamefiles => contrib/games}/csg1.nfg | 0 {catalog/gamefiles => contrib/games}/csg2.nfg | 0 {catalog/gamefiles => contrib/games}/csg3.nfg | 0 {catalog/gamefiles => contrib/games}/csg4.nfg | 0 {catalog/gamefiles => contrib/games}/deg1.nfg | 0 {catalog/gamefiles => contrib/games}/deg2.nfg | 0 {catalog/gamefiles => contrib/games}/e01.efg | 0 {catalog/gamefiles => contrib/games}/e01.nfg | 0 {catalog/gamefiles => contrib/games}/e02.efg | 0 {catalog/gamefiles => contrib/games}/e02.nfg | 0 {catalog/gamefiles => contrib/games}/e03.efg | 0 {catalog/gamefiles => contrib/games}/e04.efg | 0 {catalog/gamefiles => contrib/games}/e04.nfg | 0 {catalog/gamefiles => contrib/games}/e05.efg | 0 {catalog/gamefiles => contrib/games}/e06.efg | 0 {catalog/gamefiles => contrib/games}/e07.efg | 0 {catalog/gamefiles => contrib/games}/e07.nfg | 0 {catalog/gamefiles => contrib/games}/e08.efg | 0 {catalog/gamefiles => contrib/games}/e09.efg | 0 {catalog/gamefiles => contrib/games}/e10.efg | 0 {catalog/gamefiles => contrib/games}/e10a.efg | 0 {catalog/gamefiles => contrib/games}/e13.efg | 0 {catalog/gamefiles => contrib/games}/e16.efg | 0 {catalog/gamefiles => contrib/games}/e17.efg | 0 {catalog/gamefiles => contrib/games}/e18.efg | 0 {catalog/gamefiles => contrib/games}/g1.efg | 0 {catalog/gamefiles => contrib/games}/g1.nfg | 0 {catalog/gamefiles => contrib/games}/g2.efg | 0 {catalog/gamefiles => contrib/games}/g2.nfg | 0 {catalog/gamefiles => contrib/games}/g3.efg | 0 {catalog/gamefiles => contrib/games}/g3.nfg | 0 {catalog/gamefiles => contrib/games}/holdout.efg | 0 {catalog/gamefiles => contrib/games}/holdout7.efg | 0 {catalog/gamefiles => contrib/games}/hs1.efg | 0 {catalog/gamefiles => contrib/games}/jury_mr.efg | 0 {catalog/gamefiles => contrib/games}/jury_un.efg | 0 {catalog/gamefiles => contrib/games}/km1.efg | 0 {catalog/gamefiles => contrib/games}/km2.efg | 0 {catalog/gamefiles => contrib/games}/km3.efg | 0 {catalog/gamefiles => contrib/games}/km6.efg | 0 {catalog/gamefiles => contrib/games}/loopback.nfg | 0 {catalog/gamefiles => contrib/games}/mixdom.nfg | 0 {catalog/gamefiles => contrib/games}/mixdom2.nfg | 0 {catalog/gamefiles => contrib/games}/montyhal.efg | 0 {catalog/gamefiles => contrib/games}/my_2-1.efg | 0 {catalog/gamefiles => contrib/games}/my_2-4.efg | 0 {catalog/gamefiles => contrib/games}/my_2-8.efg | 0 {catalog/gamefiles => contrib/games}/my_3-3a.efg | 0 {catalog/gamefiles => contrib/games}/my_3-3b.efg | 0 {catalog/gamefiles => contrib/games}/my_3-3c.efg | 0 {catalog/gamefiles => contrib/games}/my_3-3d.efg | 0 {catalog/gamefiles => contrib/games}/my_3-3e.efg | 0 {catalog/gamefiles => contrib/games}/my_3-4.efg | 0 {catalog/gamefiles => contrib/games}/myerson.efg | 0 .../gamefiles => contrib/games}/myerson_fig_4_2.efg | 0 {catalog/gamefiles => contrib/games}/nim.efg | 0 {catalog/gamefiles => contrib/games}/nim7.efg | 0 {catalog/gamefiles => contrib/games}/oneill.nfg | 0 {catalog/gamefiles => contrib/games}/palf.efg | 0 {catalog/gamefiles => contrib/games}/palf2.efg | 0 {catalog/gamefiles => contrib/games}/palf3.efg | 0 {catalog/gamefiles => contrib/games}/pbride.efg | 0 {catalog/gamefiles => contrib/games}/pd.nfg | 0 {catalog/gamefiles => contrib/games}/perfect1.nfg | 0 {catalog/gamefiles => contrib/games}/perfect2.nfg | 0 {catalog/gamefiles => contrib/games}/perfect3.nfg | 0 {catalog/gamefiles => contrib/games}/poker.efg | 0 {catalog/gamefiles => contrib/games}/poker.nfg | 0 {catalog/gamefiles => contrib/games}/poker2.efg | 0 {catalog/gamefiles => contrib/games}/pvw.efg | 0 {catalog/gamefiles => contrib/games}/pvw2.efg | 0 {catalog/gamefiles => contrib/games}/sh3.efg | 0 {catalog/gamefiles => contrib/games}/sh3.nfg | 0 {catalog/gamefiles => contrib/games}/spence.efg | 0 {catalog/gamefiles => contrib/games}/stengel.nfg | 0 {catalog/gamefiles => contrib/games}/sww1.efg | 0 {catalog/gamefiles => contrib/games}/sww1.nfg | 0 {catalog/gamefiles => contrib/games}/sww2.efg | 0 {catalog/gamefiles => contrib/games}/sww3.efg | 0 {catalog/gamefiles => contrib/games}/tim.efg | 0 {catalog/gamefiles => contrib/games}/todd1.nfg | 0 {catalog/gamefiles => contrib/games}/todd2.nfg | 0 {catalog/gamefiles => contrib/games}/todd3.nfg | 0 {catalog/gamefiles => contrib/games}/ttt.efg | 0 {catalog/gamefiles => contrib/games}/vd.efg | 0 {catalog/gamefiles => contrib/games}/vd.nfg | 0 {catalog/gamefiles => contrib/games}/w_ex1.efg | 0 {catalog/gamefiles => contrib/games}/w_ex2.efg | 0 {catalog/gamefiles => contrib/games}/wilson1.efg | 0 {catalog/gamefiles => contrib/games}/wink3.nfg | 0 {catalog/gamefiles => contrib/games}/winkels.nfg | 0 {catalog/gamefiles => contrib/games}/work1.efg | 0 {catalog/gamefiles => contrib/games}/work2.efg | 0 {catalog/gamefiles => contrib/games}/work3.efg | 0 {catalog/gamefiles => contrib/games}/yamamoto.nfg | 0 {catalog/gamefiles => contrib/games}/zero.nfg | 0 doc/tutorials/01_quickstart.ipynb | 10 +++++----- src/pygambit/catalog/catalog.py | 2 +- 151 files changed, 6 insertions(+), 6 deletions(-) rename {catalog/gamefiles => contrib/games}/2s2x2x2.efg (100%) rename {catalog/gamefiles => contrib/games}/2smp.efg (100%) rename {catalog/gamefiles => contrib/games}/2x2.agg (100%) rename {catalog/gamefiles => contrib/games}/2x2.nfg (100%) rename {catalog/gamefiles => contrib/games}/2x2a.nfg (100%) rename {catalog/gamefiles => contrib/games}/2x2const.nfg (100%) rename {catalog/gamefiles => contrib/games}/2x2x2-nau.nfg (100%) rename {catalog/gamefiles => contrib/games}/2x2x2.efg (100%) rename {catalog/gamefiles => contrib/games}/2x2x2.nfg (100%) rename {catalog/gamefiles => contrib/games}/2x2x2x2.nfg (100%) rename {catalog/gamefiles => contrib/games}/2x2x2x2x2.nfg (100%) rename {catalog/gamefiles => contrib/games}/3x3x3.nfg (100%) rename {catalog/gamefiles => contrib/games}/4cards.efg (100%) rename {catalog/gamefiles => contrib/games}/5x4x3.nfg (100%) rename {catalog/gamefiles => contrib/games}/8x2x2.nfg (100%) rename {catalog/gamefiles => contrib/games}/8x8.nfg (100%) rename {catalog/gamefiles => contrib/games}/BSS_S_085.Weighted.agg (100%) rename {catalog/gamefiles => contrib/games}/Bayesian-Coffee-3-2-2-3.bagg (100%) rename {catalog/gamefiles => contrib/games}/GenRPS5.agg (100%) rename {catalog/gamefiles => contrib/games}/artist1.efg (100%) rename {catalog/gamefiles => contrib/games}/artist2.efg (100%) rename {catalog/gamefiles => contrib/games}/badgame1.efg (100%) rename {catalog/gamefiles => contrib/games}/badgame2.efg (100%) rename {catalog/gamefiles => contrib/games}/bagwell.efg (100%) rename {catalog/gamefiles => contrib/games}/bayes1a.efg (100%) rename {catalog/gamefiles => contrib/games}/bayes2a.efg (100%) rename {catalog/gamefiles => contrib/games}/bcp2.efg (100%) rename {catalog/gamefiles => contrib/games}/bcp3.efg (100%) rename {catalog/gamefiles => contrib/games}/bcp4.efg (100%) rename {catalog/gamefiles => contrib/games}/bhg1.efg (100%) rename {catalog/gamefiles => contrib/games}/bhg2.efg (100%) rename {catalog/gamefiles => contrib/games}/bhg3.efg (100%) rename {catalog/gamefiles => contrib/games}/bhg4.efg (100%) rename {catalog/gamefiles => contrib/games}/bhg5.efg (100%) rename {catalog/gamefiles => contrib/games}/caro2.efg (100%) rename {catalog/gamefiles => contrib/games}/cent2.efg (100%) rename {catalog/gamefiles => contrib/games}/cent2.nfg (100%) rename {catalog/gamefiles => contrib/games}/cent3.efg (100%) rename {catalog/gamefiles => contrib/games}/cent4.efg (100%) rename {catalog/gamefiles => contrib/games}/cent6.efg (100%) rename {catalog/gamefiles => contrib/games}/centcs10.efg (100%) rename {catalog/gamefiles => contrib/games}/centcs6.efg (100%) rename {catalog/gamefiles => contrib/games}/condjury.efg (100%) rename {catalog/gamefiles => contrib/games}/coord2.efg (100%) rename {catalog/gamefiles => contrib/games}/coord2.nfg (100%) rename {catalog/gamefiles => contrib/games}/coord2ts.efg (100%) rename {catalog/gamefiles => contrib/games}/coord3.efg (100%) rename {catalog/gamefiles => contrib/games}/coord3.nfg (100%) rename {catalog/gamefiles => contrib/games}/coord333.nfg (100%) rename {catalog/gamefiles => contrib/games}/coord4.efg (100%) rename {catalog/gamefiles => contrib/games}/coord4.nfg (100%) rename {catalog/gamefiles => contrib/games}/cross.efg (100%) rename {catalog/gamefiles => contrib/games}/cs.efg (100%) rename {catalog/gamefiles => contrib/games}/csg1.nfg (100%) rename {catalog/gamefiles => contrib/games}/csg2.nfg (100%) rename {catalog/gamefiles => contrib/games}/csg3.nfg (100%) rename {catalog/gamefiles => contrib/games}/csg4.nfg (100%) rename {catalog/gamefiles => contrib/games}/deg1.nfg (100%) rename {catalog/gamefiles => contrib/games}/deg2.nfg (100%) rename {catalog/gamefiles => contrib/games}/e01.efg (100%) rename {catalog/gamefiles => contrib/games}/e01.nfg (100%) rename {catalog/gamefiles => contrib/games}/e02.efg (100%) rename {catalog/gamefiles => contrib/games}/e02.nfg (100%) rename {catalog/gamefiles => contrib/games}/e03.efg (100%) rename {catalog/gamefiles => contrib/games}/e04.efg (100%) rename {catalog/gamefiles => contrib/games}/e04.nfg (100%) rename {catalog/gamefiles => contrib/games}/e05.efg (100%) rename {catalog/gamefiles => contrib/games}/e06.efg (100%) rename {catalog/gamefiles => contrib/games}/e07.efg (100%) rename {catalog/gamefiles => contrib/games}/e07.nfg (100%) rename {catalog/gamefiles => contrib/games}/e08.efg (100%) rename {catalog/gamefiles => contrib/games}/e09.efg (100%) rename {catalog/gamefiles => contrib/games}/e10.efg (100%) rename {catalog/gamefiles => contrib/games}/e10a.efg (100%) rename {catalog/gamefiles => contrib/games}/e13.efg (100%) rename {catalog/gamefiles => contrib/games}/e16.efg (100%) rename {catalog/gamefiles => contrib/games}/e17.efg (100%) rename {catalog/gamefiles => contrib/games}/e18.efg (100%) rename {catalog/gamefiles => contrib/games}/g1.efg (100%) rename {catalog/gamefiles => contrib/games}/g1.nfg (100%) rename {catalog/gamefiles => contrib/games}/g2.efg (100%) rename {catalog/gamefiles => contrib/games}/g2.nfg (100%) rename {catalog/gamefiles => contrib/games}/g3.efg (100%) rename {catalog/gamefiles => contrib/games}/g3.nfg (100%) rename {catalog/gamefiles => contrib/games}/holdout.efg (100%) rename {catalog/gamefiles => contrib/games}/holdout7.efg (100%) rename {catalog/gamefiles => contrib/games}/hs1.efg (100%) rename {catalog/gamefiles => contrib/games}/jury_mr.efg (100%) rename {catalog/gamefiles => contrib/games}/jury_un.efg (100%) rename {catalog/gamefiles => contrib/games}/km1.efg (100%) rename {catalog/gamefiles => contrib/games}/km2.efg (100%) rename {catalog/gamefiles => contrib/games}/km3.efg (100%) rename {catalog/gamefiles => contrib/games}/km6.efg (100%) rename {catalog/gamefiles => contrib/games}/loopback.nfg (100%) rename {catalog/gamefiles => contrib/games}/mixdom.nfg (100%) rename {catalog/gamefiles => contrib/games}/mixdom2.nfg (100%) rename {catalog/gamefiles => contrib/games}/montyhal.efg (100%) rename {catalog/gamefiles => contrib/games}/my_2-1.efg (100%) rename {catalog/gamefiles => contrib/games}/my_2-4.efg (100%) rename {catalog/gamefiles => contrib/games}/my_2-8.efg (100%) rename {catalog/gamefiles => contrib/games}/my_3-3a.efg (100%) rename {catalog/gamefiles => contrib/games}/my_3-3b.efg (100%) rename {catalog/gamefiles => contrib/games}/my_3-3c.efg (100%) rename {catalog/gamefiles => contrib/games}/my_3-3d.efg (100%) rename {catalog/gamefiles => contrib/games}/my_3-3e.efg (100%) rename {catalog/gamefiles => contrib/games}/my_3-4.efg (100%) rename {catalog/gamefiles => contrib/games}/myerson.efg (100%) rename {catalog/gamefiles => contrib/games}/myerson_fig_4_2.efg (100%) rename {catalog/gamefiles => contrib/games}/nim.efg (100%) rename {catalog/gamefiles => contrib/games}/nim7.efg (100%) rename {catalog/gamefiles => contrib/games}/oneill.nfg (100%) rename {catalog/gamefiles => contrib/games}/palf.efg (100%) rename {catalog/gamefiles => contrib/games}/palf2.efg (100%) rename {catalog/gamefiles => contrib/games}/palf3.efg (100%) rename {catalog/gamefiles => contrib/games}/pbride.efg (100%) rename {catalog/gamefiles => contrib/games}/pd.nfg (100%) rename {catalog/gamefiles => contrib/games}/perfect1.nfg (100%) rename {catalog/gamefiles => contrib/games}/perfect2.nfg (100%) rename {catalog/gamefiles => contrib/games}/perfect3.nfg (100%) rename {catalog/gamefiles => contrib/games}/poker.efg (100%) rename {catalog/gamefiles => contrib/games}/poker.nfg (100%) rename {catalog/gamefiles => contrib/games}/poker2.efg (100%) rename {catalog/gamefiles => contrib/games}/pvw.efg (100%) rename {catalog/gamefiles => contrib/games}/pvw2.efg (100%) rename {catalog/gamefiles => contrib/games}/sh3.efg (100%) rename {catalog/gamefiles => contrib/games}/sh3.nfg (100%) rename {catalog/gamefiles => contrib/games}/spence.efg (100%) rename {catalog/gamefiles => contrib/games}/stengel.nfg (100%) rename {catalog/gamefiles => contrib/games}/sww1.efg (100%) rename {catalog/gamefiles => contrib/games}/sww1.nfg (100%) rename {catalog/gamefiles => contrib/games}/sww2.efg (100%) rename {catalog/gamefiles => contrib/games}/sww3.efg (100%) rename {catalog/gamefiles => contrib/games}/tim.efg (100%) rename {catalog/gamefiles => contrib/games}/todd1.nfg (100%) rename {catalog/gamefiles => contrib/games}/todd2.nfg (100%) rename {catalog/gamefiles => contrib/games}/todd3.nfg (100%) rename {catalog/gamefiles => contrib/games}/ttt.efg (100%) rename {catalog/gamefiles => contrib/games}/vd.efg (100%) rename {catalog/gamefiles => contrib/games}/vd.nfg (100%) rename {catalog/gamefiles => contrib/games}/w_ex1.efg (100%) rename {catalog/gamefiles => contrib/games}/w_ex2.efg (100%) rename {catalog/gamefiles => contrib/games}/wilson1.efg (100%) rename {catalog/gamefiles => contrib/games}/wink3.nfg (100%) rename {catalog/gamefiles => contrib/games}/winkels.nfg (100%) rename {catalog/gamefiles => contrib/games}/work1.efg (100%) rename {catalog/gamefiles => contrib/games}/work2.efg (100%) rename {catalog/gamefiles => contrib/games}/work3.efg (100%) rename {catalog/gamefiles => contrib/games}/yamamoto.nfg (100%) rename {catalog/gamefiles => contrib/games}/zero.nfg (100%) diff --git a/catalog/gamefiles/2s2x2x2.efg b/contrib/games/2s2x2x2.efg similarity index 100% rename from catalog/gamefiles/2s2x2x2.efg rename to contrib/games/2s2x2x2.efg diff --git a/catalog/gamefiles/2smp.efg b/contrib/games/2smp.efg similarity index 100% rename from catalog/gamefiles/2smp.efg rename to contrib/games/2smp.efg diff --git a/catalog/gamefiles/2x2.agg b/contrib/games/2x2.agg similarity index 100% rename from catalog/gamefiles/2x2.agg rename to contrib/games/2x2.agg diff --git a/catalog/gamefiles/2x2.nfg b/contrib/games/2x2.nfg similarity index 100% rename from catalog/gamefiles/2x2.nfg rename to contrib/games/2x2.nfg diff --git a/catalog/gamefiles/2x2a.nfg b/contrib/games/2x2a.nfg similarity index 100% rename from catalog/gamefiles/2x2a.nfg rename to contrib/games/2x2a.nfg diff --git a/catalog/gamefiles/2x2const.nfg b/contrib/games/2x2const.nfg similarity index 100% rename from catalog/gamefiles/2x2const.nfg rename to contrib/games/2x2const.nfg diff --git a/catalog/gamefiles/2x2x2-nau.nfg b/contrib/games/2x2x2-nau.nfg similarity index 100% rename from catalog/gamefiles/2x2x2-nau.nfg rename to contrib/games/2x2x2-nau.nfg diff --git a/catalog/gamefiles/2x2x2.efg b/contrib/games/2x2x2.efg similarity index 100% rename from catalog/gamefiles/2x2x2.efg rename to contrib/games/2x2x2.efg diff --git a/catalog/gamefiles/2x2x2.nfg b/contrib/games/2x2x2.nfg similarity index 100% rename from catalog/gamefiles/2x2x2.nfg rename to contrib/games/2x2x2.nfg diff --git a/catalog/gamefiles/2x2x2x2.nfg b/contrib/games/2x2x2x2.nfg similarity index 100% rename from catalog/gamefiles/2x2x2x2.nfg rename to contrib/games/2x2x2x2.nfg diff --git a/catalog/gamefiles/2x2x2x2x2.nfg b/contrib/games/2x2x2x2x2.nfg similarity index 100% rename from catalog/gamefiles/2x2x2x2x2.nfg rename to contrib/games/2x2x2x2x2.nfg diff --git a/catalog/gamefiles/3x3x3.nfg b/contrib/games/3x3x3.nfg similarity index 100% rename from catalog/gamefiles/3x3x3.nfg rename to contrib/games/3x3x3.nfg diff --git a/catalog/gamefiles/4cards.efg b/contrib/games/4cards.efg similarity index 100% rename from catalog/gamefiles/4cards.efg rename to contrib/games/4cards.efg diff --git a/catalog/gamefiles/5x4x3.nfg b/contrib/games/5x4x3.nfg similarity index 100% rename from catalog/gamefiles/5x4x3.nfg rename to contrib/games/5x4x3.nfg diff --git a/catalog/gamefiles/8x2x2.nfg b/contrib/games/8x2x2.nfg similarity index 100% rename from catalog/gamefiles/8x2x2.nfg rename to contrib/games/8x2x2.nfg diff --git a/catalog/gamefiles/8x8.nfg b/contrib/games/8x8.nfg similarity index 100% rename from catalog/gamefiles/8x8.nfg rename to contrib/games/8x8.nfg diff --git a/catalog/gamefiles/BSS_S_085.Weighted.agg b/contrib/games/BSS_S_085.Weighted.agg similarity index 100% rename from catalog/gamefiles/BSS_S_085.Weighted.agg rename to contrib/games/BSS_S_085.Weighted.agg diff --git a/catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg b/contrib/games/Bayesian-Coffee-3-2-2-3.bagg similarity index 100% rename from catalog/gamefiles/Bayesian-Coffee-3-2-2-3.bagg rename to contrib/games/Bayesian-Coffee-3-2-2-3.bagg diff --git a/catalog/gamefiles/GenRPS5.agg b/contrib/games/GenRPS5.agg similarity index 100% rename from catalog/gamefiles/GenRPS5.agg rename to contrib/games/GenRPS5.agg diff --git a/catalog/gamefiles/artist1.efg b/contrib/games/artist1.efg similarity index 100% rename from catalog/gamefiles/artist1.efg rename to contrib/games/artist1.efg diff --git a/catalog/gamefiles/artist2.efg b/contrib/games/artist2.efg similarity index 100% rename from catalog/gamefiles/artist2.efg rename to contrib/games/artist2.efg diff --git a/catalog/gamefiles/badgame1.efg b/contrib/games/badgame1.efg similarity index 100% rename from catalog/gamefiles/badgame1.efg rename to contrib/games/badgame1.efg diff --git a/catalog/gamefiles/badgame2.efg b/contrib/games/badgame2.efg similarity index 100% rename from catalog/gamefiles/badgame2.efg rename to contrib/games/badgame2.efg diff --git a/catalog/gamefiles/bagwell.efg b/contrib/games/bagwell.efg similarity index 100% rename from catalog/gamefiles/bagwell.efg rename to contrib/games/bagwell.efg diff --git a/catalog/gamefiles/bayes1a.efg b/contrib/games/bayes1a.efg similarity index 100% rename from catalog/gamefiles/bayes1a.efg rename to contrib/games/bayes1a.efg diff --git a/catalog/gamefiles/bayes2a.efg b/contrib/games/bayes2a.efg similarity index 100% rename from catalog/gamefiles/bayes2a.efg rename to contrib/games/bayes2a.efg diff --git a/catalog/gamefiles/bcp2.efg b/contrib/games/bcp2.efg similarity index 100% rename from catalog/gamefiles/bcp2.efg rename to contrib/games/bcp2.efg diff --git a/catalog/gamefiles/bcp3.efg b/contrib/games/bcp3.efg similarity index 100% rename from catalog/gamefiles/bcp3.efg rename to contrib/games/bcp3.efg diff --git a/catalog/gamefiles/bcp4.efg b/contrib/games/bcp4.efg similarity index 100% rename from catalog/gamefiles/bcp4.efg rename to contrib/games/bcp4.efg diff --git a/catalog/gamefiles/bhg1.efg b/contrib/games/bhg1.efg similarity index 100% rename from catalog/gamefiles/bhg1.efg rename to contrib/games/bhg1.efg diff --git a/catalog/gamefiles/bhg2.efg b/contrib/games/bhg2.efg similarity index 100% rename from catalog/gamefiles/bhg2.efg rename to contrib/games/bhg2.efg diff --git a/catalog/gamefiles/bhg3.efg b/contrib/games/bhg3.efg similarity index 100% rename from catalog/gamefiles/bhg3.efg rename to contrib/games/bhg3.efg diff --git a/catalog/gamefiles/bhg4.efg b/contrib/games/bhg4.efg similarity index 100% rename from catalog/gamefiles/bhg4.efg rename to contrib/games/bhg4.efg diff --git a/catalog/gamefiles/bhg5.efg b/contrib/games/bhg5.efg similarity index 100% rename from catalog/gamefiles/bhg5.efg rename to contrib/games/bhg5.efg diff --git a/catalog/gamefiles/caro2.efg b/contrib/games/caro2.efg similarity index 100% rename from catalog/gamefiles/caro2.efg rename to contrib/games/caro2.efg diff --git a/catalog/gamefiles/cent2.efg b/contrib/games/cent2.efg similarity index 100% rename from catalog/gamefiles/cent2.efg rename to contrib/games/cent2.efg diff --git a/catalog/gamefiles/cent2.nfg b/contrib/games/cent2.nfg similarity index 100% rename from catalog/gamefiles/cent2.nfg rename to contrib/games/cent2.nfg diff --git a/catalog/gamefiles/cent3.efg b/contrib/games/cent3.efg similarity index 100% rename from catalog/gamefiles/cent3.efg rename to contrib/games/cent3.efg diff --git a/catalog/gamefiles/cent4.efg b/contrib/games/cent4.efg similarity index 100% rename from catalog/gamefiles/cent4.efg rename to contrib/games/cent4.efg diff --git a/catalog/gamefiles/cent6.efg b/contrib/games/cent6.efg similarity index 100% rename from catalog/gamefiles/cent6.efg rename to contrib/games/cent6.efg diff --git a/catalog/gamefiles/centcs10.efg b/contrib/games/centcs10.efg similarity index 100% rename from catalog/gamefiles/centcs10.efg rename to contrib/games/centcs10.efg diff --git a/catalog/gamefiles/centcs6.efg b/contrib/games/centcs6.efg similarity index 100% rename from catalog/gamefiles/centcs6.efg rename to contrib/games/centcs6.efg diff --git a/catalog/gamefiles/condjury.efg b/contrib/games/condjury.efg similarity index 100% rename from catalog/gamefiles/condjury.efg rename to contrib/games/condjury.efg diff --git a/catalog/gamefiles/coord2.efg b/contrib/games/coord2.efg similarity index 100% rename from catalog/gamefiles/coord2.efg rename to contrib/games/coord2.efg diff --git a/catalog/gamefiles/coord2.nfg b/contrib/games/coord2.nfg similarity index 100% rename from catalog/gamefiles/coord2.nfg rename to contrib/games/coord2.nfg diff --git a/catalog/gamefiles/coord2ts.efg b/contrib/games/coord2ts.efg similarity index 100% rename from catalog/gamefiles/coord2ts.efg rename to contrib/games/coord2ts.efg diff --git a/catalog/gamefiles/coord3.efg b/contrib/games/coord3.efg similarity index 100% rename from catalog/gamefiles/coord3.efg rename to contrib/games/coord3.efg diff --git a/catalog/gamefiles/coord3.nfg b/contrib/games/coord3.nfg similarity index 100% rename from catalog/gamefiles/coord3.nfg rename to contrib/games/coord3.nfg diff --git a/catalog/gamefiles/coord333.nfg b/contrib/games/coord333.nfg similarity index 100% rename from catalog/gamefiles/coord333.nfg rename to contrib/games/coord333.nfg diff --git a/catalog/gamefiles/coord4.efg b/contrib/games/coord4.efg similarity index 100% rename from catalog/gamefiles/coord4.efg rename to contrib/games/coord4.efg diff --git a/catalog/gamefiles/coord4.nfg b/contrib/games/coord4.nfg similarity index 100% rename from catalog/gamefiles/coord4.nfg rename to contrib/games/coord4.nfg diff --git a/catalog/gamefiles/cross.efg b/contrib/games/cross.efg similarity index 100% rename from catalog/gamefiles/cross.efg rename to contrib/games/cross.efg diff --git a/catalog/gamefiles/cs.efg b/contrib/games/cs.efg similarity index 100% rename from catalog/gamefiles/cs.efg rename to contrib/games/cs.efg diff --git a/catalog/gamefiles/csg1.nfg b/contrib/games/csg1.nfg similarity index 100% rename from catalog/gamefiles/csg1.nfg rename to contrib/games/csg1.nfg diff --git a/catalog/gamefiles/csg2.nfg b/contrib/games/csg2.nfg similarity index 100% rename from catalog/gamefiles/csg2.nfg rename to contrib/games/csg2.nfg diff --git a/catalog/gamefiles/csg3.nfg b/contrib/games/csg3.nfg similarity index 100% rename from catalog/gamefiles/csg3.nfg rename to contrib/games/csg3.nfg diff --git a/catalog/gamefiles/csg4.nfg b/contrib/games/csg4.nfg similarity index 100% rename from catalog/gamefiles/csg4.nfg rename to contrib/games/csg4.nfg diff --git a/catalog/gamefiles/deg1.nfg b/contrib/games/deg1.nfg similarity index 100% rename from catalog/gamefiles/deg1.nfg rename to contrib/games/deg1.nfg diff --git a/catalog/gamefiles/deg2.nfg b/contrib/games/deg2.nfg similarity index 100% rename from catalog/gamefiles/deg2.nfg rename to contrib/games/deg2.nfg diff --git a/catalog/gamefiles/e01.efg b/contrib/games/e01.efg similarity index 100% rename from catalog/gamefiles/e01.efg rename to contrib/games/e01.efg diff --git a/catalog/gamefiles/e01.nfg b/contrib/games/e01.nfg similarity index 100% rename from catalog/gamefiles/e01.nfg rename to contrib/games/e01.nfg diff --git a/catalog/gamefiles/e02.efg b/contrib/games/e02.efg similarity index 100% rename from catalog/gamefiles/e02.efg rename to contrib/games/e02.efg diff --git a/catalog/gamefiles/e02.nfg b/contrib/games/e02.nfg similarity index 100% rename from catalog/gamefiles/e02.nfg rename to contrib/games/e02.nfg diff --git a/catalog/gamefiles/e03.efg b/contrib/games/e03.efg similarity index 100% rename from catalog/gamefiles/e03.efg rename to contrib/games/e03.efg diff --git a/catalog/gamefiles/e04.efg b/contrib/games/e04.efg similarity index 100% rename from catalog/gamefiles/e04.efg rename to contrib/games/e04.efg diff --git a/catalog/gamefiles/e04.nfg b/contrib/games/e04.nfg similarity index 100% rename from catalog/gamefiles/e04.nfg rename to contrib/games/e04.nfg diff --git a/catalog/gamefiles/e05.efg b/contrib/games/e05.efg similarity index 100% rename from catalog/gamefiles/e05.efg rename to contrib/games/e05.efg diff --git a/catalog/gamefiles/e06.efg b/contrib/games/e06.efg similarity index 100% rename from catalog/gamefiles/e06.efg rename to contrib/games/e06.efg diff --git a/catalog/gamefiles/e07.efg b/contrib/games/e07.efg similarity index 100% rename from catalog/gamefiles/e07.efg rename to contrib/games/e07.efg diff --git a/catalog/gamefiles/e07.nfg b/contrib/games/e07.nfg similarity index 100% rename from catalog/gamefiles/e07.nfg rename to contrib/games/e07.nfg diff --git a/catalog/gamefiles/e08.efg b/contrib/games/e08.efg similarity index 100% rename from catalog/gamefiles/e08.efg rename to contrib/games/e08.efg diff --git a/catalog/gamefiles/e09.efg b/contrib/games/e09.efg similarity index 100% rename from catalog/gamefiles/e09.efg rename to contrib/games/e09.efg diff --git a/catalog/gamefiles/e10.efg b/contrib/games/e10.efg similarity index 100% rename from catalog/gamefiles/e10.efg rename to contrib/games/e10.efg diff --git a/catalog/gamefiles/e10a.efg b/contrib/games/e10a.efg similarity index 100% rename from catalog/gamefiles/e10a.efg rename to contrib/games/e10a.efg diff --git a/catalog/gamefiles/e13.efg b/contrib/games/e13.efg similarity index 100% rename from catalog/gamefiles/e13.efg rename to contrib/games/e13.efg diff --git a/catalog/gamefiles/e16.efg b/contrib/games/e16.efg similarity index 100% rename from catalog/gamefiles/e16.efg rename to contrib/games/e16.efg diff --git a/catalog/gamefiles/e17.efg b/contrib/games/e17.efg similarity index 100% rename from catalog/gamefiles/e17.efg rename to contrib/games/e17.efg diff --git a/catalog/gamefiles/e18.efg b/contrib/games/e18.efg similarity index 100% rename from catalog/gamefiles/e18.efg rename to contrib/games/e18.efg diff --git a/catalog/gamefiles/g1.efg b/contrib/games/g1.efg similarity index 100% rename from catalog/gamefiles/g1.efg rename to contrib/games/g1.efg diff --git a/catalog/gamefiles/g1.nfg b/contrib/games/g1.nfg similarity index 100% rename from catalog/gamefiles/g1.nfg rename to contrib/games/g1.nfg diff --git a/catalog/gamefiles/g2.efg b/contrib/games/g2.efg similarity index 100% rename from catalog/gamefiles/g2.efg rename to contrib/games/g2.efg diff --git a/catalog/gamefiles/g2.nfg b/contrib/games/g2.nfg similarity index 100% rename from catalog/gamefiles/g2.nfg rename to contrib/games/g2.nfg diff --git a/catalog/gamefiles/g3.efg b/contrib/games/g3.efg similarity index 100% rename from catalog/gamefiles/g3.efg rename to contrib/games/g3.efg diff --git a/catalog/gamefiles/g3.nfg b/contrib/games/g3.nfg similarity index 100% rename from catalog/gamefiles/g3.nfg rename to contrib/games/g3.nfg diff --git a/catalog/gamefiles/holdout.efg b/contrib/games/holdout.efg similarity index 100% rename from catalog/gamefiles/holdout.efg rename to contrib/games/holdout.efg diff --git a/catalog/gamefiles/holdout7.efg b/contrib/games/holdout7.efg similarity index 100% rename from catalog/gamefiles/holdout7.efg rename to contrib/games/holdout7.efg diff --git a/catalog/gamefiles/hs1.efg b/contrib/games/hs1.efg similarity index 100% rename from catalog/gamefiles/hs1.efg rename to contrib/games/hs1.efg diff --git a/catalog/gamefiles/jury_mr.efg b/contrib/games/jury_mr.efg similarity index 100% rename from catalog/gamefiles/jury_mr.efg rename to contrib/games/jury_mr.efg diff --git a/catalog/gamefiles/jury_un.efg b/contrib/games/jury_un.efg similarity index 100% rename from catalog/gamefiles/jury_un.efg rename to contrib/games/jury_un.efg diff --git a/catalog/gamefiles/km1.efg b/contrib/games/km1.efg similarity index 100% rename from catalog/gamefiles/km1.efg rename to contrib/games/km1.efg diff --git a/catalog/gamefiles/km2.efg b/contrib/games/km2.efg similarity index 100% rename from catalog/gamefiles/km2.efg rename to contrib/games/km2.efg diff --git a/catalog/gamefiles/km3.efg b/contrib/games/km3.efg similarity index 100% rename from catalog/gamefiles/km3.efg rename to contrib/games/km3.efg diff --git a/catalog/gamefiles/km6.efg b/contrib/games/km6.efg similarity index 100% rename from catalog/gamefiles/km6.efg rename to contrib/games/km6.efg diff --git a/catalog/gamefiles/loopback.nfg b/contrib/games/loopback.nfg similarity index 100% rename from catalog/gamefiles/loopback.nfg rename to contrib/games/loopback.nfg diff --git a/catalog/gamefiles/mixdom.nfg b/contrib/games/mixdom.nfg similarity index 100% rename from catalog/gamefiles/mixdom.nfg rename to contrib/games/mixdom.nfg diff --git a/catalog/gamefiles/mixdom2.nfg b/contrib/games/mixdom2.nfg similarity index 100% rename from catalog/gamefiles/mixdom2.nfg rename to contrib/games/mixdom2.nfg diff --git a/catalog/gamefiles/montyhal.efg b/contrib/games/montyhal.efg similarity index 100% rename from catalog/gamefiles/montyhal.efg rename to contrib/games/montyhal.efg diff --git a/catalog/gamefiles/my_2-1.efg b/contrib/games/my_2-1.efg similarity index 100% rename from catalog/gamefiles/my_2-1.efg rename to contrib/games/my_2-1.efg diff --git a/catalog/gamefiles/my_2-4.efg b/contrib/games/my_2-4.efg similarity index 100% rename from catalog/gamefiles/my_2-4.efg rename to contrib/games/my_2-4.efg diff --git a/catalog/gamefiles/my_2-8.efg b/contrib/games/my_2-8.efg similarity index 100% rename from catalog/gamefiles/my_2-8.efg rename to contrib/games/my_2-8.efg diff --git a/catalog/gamefiles/my_3-3a.efg b/contrib/games/my_3-3a.efg similarity index 100% rename from catalog/gamefiles/my_3-3a.efg rename to contrib/games/my_3-3a.efg diff --git a/catalog/gamefiles/my_3-3b.efg b/contrib/games/my_3-3b.efg similarity index 100% rename from catalog/gamefiles/my_3-3b.efg rename to contrib/games/my_3-3b.efg diff --git a/catalog/gamefiles/my_3-3c.efg b/contrib/games/my_3-3c.efg similarity index 100% rename from catalog/gamefiles/my_3-3c.efg rename to contrib/games/my_3-3c.efg diff --git a/catalog/gamefiles/my_3-3d.efg b/contrib/games/my_3-3d.efg similarity index 100% rename from catalog/gamefiles/my_3-3d.efg rename to contrib/games/my_3-3d.efg diff --git a/catalog/gamefiles/my_3-3e.efg b/contrib/games/my_3-3e.efg similarity index 100% rename from catalog/gamefiles/my_3-3e.efg rename to contrib/games/my_3-3e.efg diff --git a/catalog/gamefiles/my_3-4.efg b/contrib/games/my_3-4.efg similarity index 100% rename from catalog/gamefiles/my_3-4.efg rename to contrib/games/my_3-4.efg diff --git a/catalog/gamefiles/myerson.efg b/contrib/games/myerson.efg similarity index 100% rename from catalog/gamefiles/myerson.efg rename to contrib/games/myerson.efg diff --git a/catalog/gamefiles/myerson_fig_4_2.efg b/contrib/games/myerson_fig_4_2.efg similarity index 100% rename from catalog/gamefiles/myerson_fig_4_2.efg rename to contrib/games/myerson_fig_4_2.efg diff --git a/catalog/gamefiles/nim.efg b/contrib/games/nim.efg similarity index 100% rename from catalog/gamefiles/nim.efg rename to contrib/games/nim.efg diff --git a/catalog/gamefiles/nim7.efg b/contrib/games/nim7.efg similarity index 100% rename from catalog/gamefiles/nim7.efg rename to contrib/games/nim7.efg diff --git a/catalog/gamefiles/oneill.nfg b/contrib/games/oneill.nfg similarity index 100% rename from catalog/gamefiles/oneill.nfg rename to contrib/games/oneill.nfg diff --git a/catalog/gamefiles/palf.efg b/contrib/games/palf.efg similarity index 100% rename from catalog/gamefiles/palf.efg rename to contrib/games/palf.efg diff --git a/catalog/gamefiles/palf2.efg b/contrib/games/palf2.efg similarity index 100% rename from catalog/gamefiles/palf2.efg rename to contrib/games/palf2.efg diff --git a/catalog/gamefiles/palf3.efg b/contrib/games/palf3.efg similarity index 100% rename from catalog/gamefiles/palf3.efg rename to contrib/games/palf3.efg diff --git a/catalog/gamefiles/pbride.efg b/contrib/games/pbride.efg similarity index 100% rename from catalog/gamefiles/pbride.efg rename to contrib/games/pbride.efg diff --git a/catalog/gamefiles/pd.nfg b/contrib/games/pd.nfg similarity index 100% rename from catalog/gamefiles/pd.nfg rename to contrib/games/pd.nfg diff --git a/catalog/gamefiles/perfect1.nfg b/contrib/games/perfect1.nfg similarity index 100% rename from catalog/gamefiles/perfect1.nfg rename to contrib/games/perfect1.nfg diff --git a/catalog/gamefiles/perfect2.nfg b/contrib/games/perfect2.nfg similarity index 100% rename from catalog/gamefiles/perfect2.nfg rename to contrib/games/perfect2.nfg diff --git a/catalog/gamefiles/perfect3.nfg b/contrib/games/perfect3.nfg similarity index 100% rename from catalog/gamefiles/perfect3.nfg rename to contrib/games/perfect3.nfg diff --git a/catalog/gamefiles/poker.efg b/contrib/games/poker.efg similarity index 100% rename from catalog/gamefiles/poker.efg rename to contrib/games/poker.efg diff --git a/catalog/gamefiles/poker.nfg b/contrib/games/poker.nfg similarity index 100% rename from catalog/gamefiles/poker.nfg rename to contrib/games/poker.nfg diff --git a/catalog/gamefiles/poker2.efg b/contrib/games/poker2.efg similarity index 100% rename from catalog/gamefiles/poker2.efg rename to contrib/games/poker2.efg diff --git a/catalog/gamefiles/pvw.efg b/contrib/games/pvw.efg similarity index 100% rename from catalog/gamefiles/pvw.efg rename to contrib/games/pvw.efg diff --git a/catalog/gamefiles/pvw2.efg b/contrib/games/pvw2.efg similarity index 100% rename from catalog/gamefiles/pvw2.efg rename to contrib/games/pvw2.efg diff --git a/catalog/gamefiles/sh3.efg b/contrib/games/sh3.efg similarity index 100% rename from catalog/gamefiles/sh3.efg rename to contrib/games/sh3.efg diff --git a/catalog/gamefiles/sh3.nfg b/contrib/games/sh3.nfg similarity index 100% rename from catalog/gamefiles/sh3.nfg rename to contrib/games/sh3.nfg diff --git a/catalog/gamefiles/spence.efg b/contrib/games/spence.efg similarity index 100% rename from catalog/gamefiles/spence.efg rename to contrib/games/spence.efg diff --git a/catalog/gamefiles/stengel.nfg b/contrib/games/stengel.nfg similarity index 100% rename from catalog/gamefiles/stengel.nfg rename to contrib/games/stengel.nfg diff --git a/catalog/gamefiles/sww1.efg b/contrib/games/sww1.efg similarity index 100% rename from catalog/gamefiles/sww1.efg rename to contrib/games/sww1.efg diff --git a/catalog/gamefiles/sww1.nfg b/contrib/games/sww1.nfg similarity index 100% rename from catalog/gamefiles/sww1.nfg rename to contrib/games/sww1.nfg diff --git a/catalog/gamefiles/sww2.efg b/contrib/games/sww2.efg similarity index 100% rename from catalog/gamefiles/sww2.efg rename to contrib/games/sww2.efg diff --git a/catalog/gamefiles/sww3.efg b/contrib/games/sww3.efg similarity index 100% rename from catalog/gamefiles/sww3.efg rename to contrib/games/sww3.efg diff --git a/catalog/gamefiles/tim.efg b/contrib/games/tim.efg similarity index 100% rename from catalog/gamefiles/tim.efg rename to contrib/games/tim.efg diff --git a/catalog/gamefiles/todd1.nfg b/contrib/games/todd1.nfg similarity index 100% rename from catalog/gamefiles/todd1.nfg rename to contrib/games/todd1.nfg diff --git a/catalog/gamefiles/todd2.nfg b/contrib/games/todd2.nfg similarity index 100% rename from catalog/gamefiles/todd2.nfg rename to contrib/games/todd2.nfg diff --git a/catalog/gamefiles/todd3.nfg b/contrib/games/todd3.nfg similarity index 100% rename from catalog/gamefiles/todd3.nfg rename to contrib/games/todd3.nfg diff --git a/catalog/gamefiles/ttt.efg b/contrib/games/ttt.efg similarity index 100% rename from catalog/gamefiles/ttt.efg rename to contrib/games/ttt.efg diff --git a/catalog/gamefiles/vd.efg b/contrib/games/vd.efg similarity index 100% rename from catalog/gamefiles/vd.efg rename to contrib/games/vd.efg diff --git a/catalog/gamefiles/vd.nfg b/contrib/games/vd.nfg similarity index 100% rename from catalog/gamefiles/vd.nfg rename to contrib/games/vd.nfg diff --git a/catalog/gamefiles/w_ex1.efg b/contrib/games/w_ex1.efg similarity index 100% rename from catalog/gamefiles/w_ex1.efg rename to contrib/games/w_ex1.efg diff --git a/catalog/gamefiles/w_ex2.efg b/contrib/games/w_ex2.efg similarity index 100% rename from catalog/gamefiles/w_ex2.efg rename to contrib/games/w_ex2.efg diff --git a/catalog/gamefiles/wilson1.efg b/contrib/games/wilson1.efg similarity index 100% rename from catalog/gamefiles/wilson1.efg rename to contrib/games/wilson1.efg diff --git a/catalog/gamefiles/wink3.nfg b/contrib/games/wink3.nfg similarity index 100% rename from catalog/gamefiles/wink3.nfg rename to contrib/games/wink3.nfg diff --git a/catalog/gamefiles/winkels.nfg b/contrib/games/winkels.nfg similarity index 100% rename from catalog/gamefiles/winkels.nfg rename to contrib/games/winkels.nfg diff --git a/catalog/gamefiles/work1.efg b/contrib/games/work1.efg similarity index 100% rename from catalog/gamefiles/work1.efg rename to contrib/games/work1.efg diff --git a/catalog/gamefiles/work2.efg b/contrib/games/work2.efg similarity index 100% rename from catalog/gamefiles/work2.efg rename to contrib/games/work2.efg diff --git a/catalog/gamefiles/work3.efg b/contrib/games/work3.efg similarity index 100% rename from catalog/gamefiles/work3.efg rename to contrib/games/work3.efg diff --git a/catalog/gamefiles/yamamoto.nfg b/contrib/games/yamamoto.nfg similarity index 100% rename from catalog/gamefiles/yamamoto.nfg rename to contrib/games/yamamoto.nfg diff --git a/catalog/gamefiles/zero.nfg b/contrib/games/zero.nfg similarity index 100% rename from catalog/gamefiles/zero.nfg rename to contrib/games/zero.nfg diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index 3407adf0c..c12a5fac6 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -431,17 +431,17 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "701aa52a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['PrisonersDilemma', 'PrisonersDilemmaTestgame']" + "['PrisonersDilemma', 'TwoStageMatchingPennies', 'PrisonersDilemmaTestgame']" ] }, - "execution_count": 5, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -460,7 +460,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 3, "id": "6db7a29a", "metadata": {}, "outputs": [ @@ -474,7 +474,7 @@ "Game(title='Two person Prisoner's Dilemma game')" ] }, - "execution_count": 14, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 36386e98f..38fdd015a 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -5,7 +5,7 @@ from pygambit import Game, read_efg, read_nfg -_GAMEFILES_DIR = Path(__file__).parent.parent.parent.parent / "catalog/gamefiles" +_GAMEFILES_DIR = Path(__file__).parent.parent.parent.parent / "contrib/games" class CatalogGame: From a0533e53453887c7bd305a0af8279466c12d6337 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 11:02:51 +0000 Subject: [PATCH 22/76] create example fom test suite --- src/pygambit/catalog/catalog.py | 44 +++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 38fdd015a..3e90f0a22 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -1,8 +1,7 @@ from pathlib import Path from typing import Literal -import numpy as np - +# import numpy as np from pygambit import Game, read_efg, read_nfg _GAMEFILES_DIR = Path(__file__).parent.parent.parent.parent / "contrib/games" @@ -152,20 +151,33 @@ class TwoStageMatchingPennies(CatalogGameFromFile): ########################################## -class PrisonersDilemmaTestgame(CatalogGame): - title = "Test Prisoner's Dilemma" - game_type = "nfg" - description = "A simple test game based on the Prisoner's Dilemma." - citation = "Example citation for Test Prisoner's Dilemma." +class OneShotTrust(CatalogGame): - def __new__(cls) -> Game: - player1_payoffs = np.array([[-1, -3], [0, -2]]) - player2_payoffs = np.transpose(player1_payoffs) + title = "One-shot trust game, after Kreps (1990)" + game_type = "efg" + description = """ + The unique_NE_variant makes Trust a dominant strategy, replacing the + non-singleton equilibrium component from the standard version of the game + where the Buyer plays "Not Trust" and the seller can play any mixture with + < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and + the Seller plays Abuse. + """ + citation = "Kreps (1990)" - g1 = Game.from_arrays( - player1_payoffs, - player2_payoffs, - title=cls.title, + def __new__(cls, unique_NE_variant: bool = False) -> Game: + g = Game.new_tree( + players=["Buyer", "Seller"], title=cls.title ) - - return g1 + g.append_move(g.root, "Buyer", ["Trust", "Not trust"]) + g.append_move(g.root.children[0], "Seller", ["Honor", "Abuse"]) + g.set_outcome(g.root.children[0].children[0], g.add_outcome([1, 1], label="Trustworthy")) + if unique_NE_variant: + g.set_outcome( + g.root.children[0].children[1], g.add_outcome(["1/2", 2], label="Untrustworthy") + ) + else: + g.set_outcome( + g.root.children[0].children[1], g.add_outcome([-1, 2], label="Untrustworthy") + ) + g.set_outcome(g.root.children[1], g.add_outcome([0, 0], label="Opt-out")) + return g From 5a9dce2b5cd8621900e3ea3129c05d0452003b9f Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 11:13:17 +0000 Subject: [PATCH 23/76] make it so each CatalogGame subclass implements _game() instead of __new__() --- src/pygambit/catalog/catalog.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 3e90f0a22..80a713df0 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -20,8 +20,16 @@ class CatalogGame: description: str citation: str - def __new__(cls) -> Game: - raise NotImplementedError("Subclasses must implement __new__ method") + def __new__(cls, *args, **kwargs) -> Game: + """Create a game instance by calling the _game() method.""" + if hasattr(cls, "_game") and cls._game is not CatalogGame._game: + return cls._game(*args, **kwargs) + raise NotImplementedError("Subclasses must implement _game() method") + + @staticmethod + def _game() -> Game: + """Override this method in subclasses to define the game.""" + raise NotImplementedError("Subclasses must implement _game() method") @classmethod def _extract_metadata_from_game(cls, game: Game) -> None: @@ -39,7 +47,7 @@ def __init_subclass__(cls, **kwargs): # For non-file-based games, create a temporary instance to extract metadata try: - temp_game = cls.__new__(cls) + temp_game = cls._game() cls._extract_metadata_from_game(temp_game) except NotImplementedError: # Base class, skip @@ -164,9 +172,10 @@ class OneShotTrust(CatalogGame): """ citation = "Kreps (1990)" - def __new__(cls, unique_NE_variant: bool = False) -> Game: + @staticmethod + def _game(unique_NE_variant: bool = False): g = Game.new_tree( - players=["Buyer", "Seller"], title=cls.title + players=["Buyer", "Seller"], title="One-shot trust game, after Kreps (1990)" ) g.append_move(g.root, "Buyer", ["Trust", "Not trust"]) g.append_move(g.root.children[0], "Seller", ["Honor", "Abuse"]) From 621242fed38ee4b3585659f43ce8107ddfe073d8 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 11:16:35 +0000 Subject: [PATCH 24/76] avoid redefining title --- src/pygambit/catalog/catalog.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 80a713df0..c975408c0 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -160,8 +160,6 @@ class TwoStageMatchingPennies(CatalogGameFromFile): class OneShotTrust(CatalogGame): - - title = "One-shot trust game, after Kreps (1990)" game_type = "efg" description = """ The unique_NE_variant makes Trust a dominant strategy, replacing the From dbe751c1be709d2c97993d711107b8615553a495 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 11:21:33 +0000 Subject: [PATCH 25/76] rename CatalogGameFromFile to CatalogGameFromContrib and update references --- src/pygambit/catalog/catalog.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index c975408c0..762393932 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -41,8 +41,8 @@ def __init_subclass__(cls, **kwargs): """Extract metadata when subclass is defined (if not a file-based game).""" super().__init_subclass__(**kwargs) - # Skip if this is CatalogGameFromFile or its subclasses - if cls.__name__ == "CatalogGameFromFile" or issubclass(cls, CatalogGameFromFile): + # Skip if this is CatalogGameFromContrib or its subclasses + if cls.__name__ == "CatalogGameFromContrib" or issubclass(cls, CatalogGameFromContrib): return # For non-file-based games, create a temporary instance to extract metadata @@ -54,7 +54,7 @@ def __init_subclass__(cls, **kwargs): pass -class CatalogGameFromFile(CatalogGame): +class CatalogGameFromContrib(CatalogGame): """ Base class for catalog games loaded from files. This class serves as a template for specific games in the catalog. @@ -125,7 +125,7 @@ def get_all_subclasses(cls): """Recursively get all subclasses.""" all_subclasses = [] for subclass in cls.__subclasses__(): - if subclass.__name__ not in ["CatalogGameFromFile"] and ( + if subclass.__name__ not in ["CatalogGameFromContrib"] and ( game_type == "all" or subclass.game_type == game_type ) and ( num_players is None or subclass.num_players == num_players @@ -142,13 +142,13 @@ def get_all_subclasses(cls): ############################ -class PrisonersDilemma(CatalogGameFromFile): +class PrisonersDilemma(CatalogGameFromContrib): game_file = "pd.nfg" description = "Prisoner's Dilemma game." citation = "Example citation for Prisoner's Dilemma." -class TwoStageMatchingPennies(CatalogGameFromFile): +class TwoStageMatchingPennies(CatalogGameFromContrib): game_file = "2smp.efg" description = "Two-Stage Matching Pennies game." citation = "Example citation for Two-Stage Matching Pennies." From 7ddd64b9d15c9ac1af859775a98e60b08ca044b8 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 11:28:40 +0000 Subject: [PATCH 26/76] loading from the catalog examples in tutorials --- doc/tutorials/01_quickstart.ipynb | 54 +- doc/tutorials/02_extensive_form.ipynb | 942 ++++++++++++++++---------- 2 files changed, 593 insertions(+), 403 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index c12a5fac6..53836406f 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 19, "id": "c58d382d", "metadata": {}, "outputs": [], @@ -50,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 20, "id": "2060c1ed", "metadata": {}, "outputs": [ @@ -60,7 +60,7 @@ "pygambit.gambit.Game" ] }, - "execution_count": 2, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -83,7 +83,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 21, "id": "9d8203e8", "metadata": {}, "outputs": [], @@ -111,7 +111,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 22, "id": "61030607", "metadata": {}, "outputs": [], @@ -135,7 +135,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 23, "id": "caecc334", "metadata": {}, "outputs": [ @@ -149,7 +149,7 @@ "Game(title='Prisoner's Dilemma')" ] }, - "execution_count": 5, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -189,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 24, "id": "843ba7f3", "metadata": {}, "outputs": [ @@ -203,7 +203,7 @@ "Game(title='Another Prisoner's Dilemma')" ] }, - "execution_count": 6, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -233,7 +233,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 25, "id": "5ee752c4", "metadata": {}, "outputs": [ @@ -270,7 +270,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 26, "id": "a81c06c7", "metadata": {}, "outputs": [ @@ -280,7 +280,7 @@ "pygambit.nash.NashComputationResult" ] }, - "execution_count": 8, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -300,7 +300,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 27, "id": "bd395180", "metadata": {}, "outputs": [ @@ -310,7 +310,7 @@ "1" ] }, - "execution_count": 9, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -329,7 +329,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 28, "id": "76570ebc", "metadata": {}, "outputs": [ @@ -342,7 +342,7 @@ "[[Rational(0, 1), Rational(1, 1)], [Rational(0, 1), Rational(1, 1)]]" ] }, - "execution_count": 10, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -354,7 +354,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 29, "id": "6e8cfcde", "metadata": {}, "outputs": [ @@ -364,7 +364,7 @@ "pygambit.gambit.MixedStrategyProfileRational" ] }, - "execution_count": 11, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -385,7 +385,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 30, "id": "980bf6b1", "metadata": {}, "outputs": [ @@ -426,22 +426,22 @@ "------------------------------\n", "\n", "Gambit includes a catalog of standard games that can be loaded directly by name.\n", - "You can list all the available games in the catalog like so:" + "You can list all the available games and filtering on the game type and number of players in the catalog like so:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "id": "701aa52a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['PrisonersDilemma', 'TwoStageMatchingPennies', 'PrisonersDilemmaTestgame']" + "['PrisonersDilemma']" ] }, - "execution_count": 4, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -460,7 +460,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 32, "id": "6db7a29a", "metadata": {}, "outputs": [ @@ -474,7 +474,7 @@ "Game(title='Two person Prisoner's Dilemma game')" ] }, - "execution_count": 3, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -500,7 +500,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 33, "id": "f58eaa77", "metadata": {}, "outputs": [], @@ -518,7 +518,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 34, "id": "4119a2ac", "metadata": {}, "outputs": [], diff --git a/doc/tutorials/02_extensive_form.ipynb b/doc/tutorials/02_extensive_form.ipynb index 528970c61..d8b7e108e 100644 --- a/doc/tutorials/02_extensive_form.ipynb +++ b/doc/tutorials/02_extensive_form.ipynb @@ -79,20 +79,20 @@ { "data": { "image/svg+xml": [ - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "" ], @@ -142,119 +142,119 @@ { "data": { "image/svg+xml": [ - "\n", + "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "" ], @@ -302,181 +302,171 @@ { "data": { "image/svg+xml": [ - "\n", + "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "\n", + "\n", "" ], "text/plain": [ @@ -529,157 +519,147 @@ { "data": { "image/svg+xml": [ - "\n", + "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", "" ], "text/plain": [ @@ -728,171 +708,161 @@ { "data": { "image/svg+xml": [ - "\n", + "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "" ], "text/plain": [ @@ -941,179 +911,169 @@ { "data": { "image/svg+xml": [ - "\n", + "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", + "\n", + "\n", "" ], "text/plain": [ @@ -1136,7 +1096,237 @@ "source": [ "Nodes without an outcome attached are assumed to have payoffs of zero for all players.\n", "\n", - "Therefore, adding the outcome to this latter terminal node is not strictly necessary in Gambit, but it is useful to be explicit for readability." + "Therefore, adding the outcome to this latter terminal node is not strictly necessary in Gambit, but it is useful to be explicit for readability.\n", + "\n", + "Loading games from the catalog\n", + "------------------------------\n", + "\n", + "Gambit includes a catalog of standard games that can be loaded directly by name.\n", + "You can list all the available games and filtering on the game type and number of players in the catalog like so:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "3207441f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['TwoStageMatchingPennies', 'OneShotTrust']" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gbt.catalog.games(game_type=\"efg\", num_players=2)" + ] + }, + { + "cell_type": "markdown", + "id": "af175f99", + "metadata": {}, + "source": [ + "Some games in the catalog have configurable parameters.\n", + "For example, the `OneShotTrust` game has the `unique_NE_variant` parameter, which makes Trust a dominant strategy, replacing the\n", + " non-singleton equilibrium component from the standard version of the game\n", + " where the Buyer plays \"Not Trust\" and the seller can play any mixture with\n", + " < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and\n", + " the Seller plays Abuse." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4556c0bf", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "draw_tree(gbt.catalog.OneShotTrust(unique_NE_variant=True))" ] }, { @@ -1156,7 +1346,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "id": "37c51152", "metadata": {}, "outputs": [], @@ -1174,7 +1364,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "id": "0d86a750", "metadata": {}, "outputs": [], @@ -1193,7 +1383,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 18, "id": "1bab777f-8a0b-4f1e-9c0c-270690288243", "metadata": {}, "outputs": [], @@ -1205,7 +1395,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "id": "2b715221-e427-4092-ad2f-9f4f2b548fa4", "metadata": {}, "outputs": [], From d84c3044e736c4a08e2dd77e98b540af068410f5 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 15:52:50 +0000 Subject: [PATCH 27/76] fix: correct import path for Game, read_efg, and read_nfg to avoid circular import --- src/pygambit/catalog/catalog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 762393932..d61776eb1 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -2,7 +2,7 @@ from typing import Literal # import numpy as np -from pygambit import Game, read_efg, read_nfg +from pygambit.gambit import Game, read_efg, read_nfg _GAMEFILES_DIR = Path(__file__).parent.parent.parent.parent / "contrib/games" From 4d46dd596fece304c2d7d28951cbb86994f715e0 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 16:08:39 +0000 Subject: [PATCH 28/76] refactor: implement lazy loading for game classes in catalog to avoid circular imports issue --- src/pygambit/catalog/__init__.py | 33 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/pygambit/catalog/__init__.py b/src/pygambit/catalog/__init__.py index 619e73f41..1e54dc415 100644 --- a/src/pygambit/catalog/__init__.py +++ b/src/pygambit/catalog/__init__.py @@ -1,16 +1,25 @@ -from .catalog import games +def __getattr__(name: str): + """Lazily load game classes and the games function.""" + if name == "games": + from .catalog import games + return games -# Dynamically import all catalog games -_all_games = games() -_game_classes = {} + # Try to import the game class + try: + from . import catalog + if hasattr(catalog, name): + return getattr(catalog, name) + except ImportError: + pass -for game_name in _all_games: - # Import each game class from catalog module - from . import catalog - _game_classes[game_name] = getattr(catalog, game_name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") -# Add to module namespace -globals().update(_game_classes) -# Build __all__ dynamically -__all__ = ["games"] + list(_all_games) # type: ignore[assignment] +def __dir__(): + """Support dir() by lazily loading all game names.""" + from .catalog import games + _all_games = games() + return ["games"] + _all_games + + +__all__ = ["games"] # type: ignore[assignment] From c5b8dc56a918bf21a568ee209560ba0cfe90ef15 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 16:44:57 +0000 Subject: [PATCH 29/76] Revert "refactor: implement lazy loading for game classes in catalog to avoid circular imports issue" This reverts commit 4d46dd596fece304c2d7d28951cbb86994f715e0. --- src/pygambit/catalog/__init__.py | 33 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/pygambit/catalog/__init__.py b/src/pygambit/catalog/__init__.py index 1e54dc415..619e73f41 100644 --- a/src/pygambit/catalog/__init__.py +++ b/src/pygambit/catalog/__init__.py @@ -1,25 +1,16 @@ -def __getattr__(name: str): - """Lazily load game classes and the games function.""" - if name == "games": - from .catalog import games - return games +from .catalog import games - # Try to import the game class - try: - from . import catalog - if hasattr(catalog, name): - return getattr(catalog, name) - except ImportError: - pass +# Dynamically import all catalog games +_all_games = games() +_game_classes = {} - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") +for game_name in _all_games: + # Import each game class from catalog module + from . import catalog + _game_classes[game_name] = getattr(catalog, game_name) +# Add to module namespace +globals().update(_game_classes) -def __dir__(): - """Support dir() by lazily loading all game names.""" - from .catalog import games - _all_games = games() - return ["games"] + _all_games - - -__all__ = ["games"] # type: ignore[assignment] +# Build __all__ dynamically +__all__ = ["games"] + list(_all_games) # type: ignore[assignment] From 8fbe66f74310086b8150908511941b22ee5268a2 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 8 Jan 2026 16:49:10 +0000 Subject: [PATCH 30/76] Don't import catalog when importing pygambit to avoid circular imports --- doc/tutorials/01_quickstart.ipynb | 58 ++++++++++++++------------- doc/tutorials/02_extensive_form.ipynb | 6 ++- src/pygambit/__init__.py | 1 - 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index 53836406f..c2b4d900d 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 1, "id": "c58d382d", "metadata": {}, "outputs": [], @@ -50,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 2, "id": "2060c1ed", "metadata": {}, "outputs": [ @@ -60,7 +60,7 @@ "pygambit.gambit.Game" ] }, - "execution_count": 20, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -83,7 +83,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 3, "id": "9d8203e8", "metadata": {}, "outputs": [], @@ -111,7 +111,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 4, "id": "61030607", "metadata": {}, "outputs": [], @@ -135,7 +135,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 5, "id": "caecc334", "metadata": {}, "outputs": [ @@ -149,7 +149,7 @@ "Game(title='Prisoner's Dilemma')" ] }, - "execution_count": 23, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -189,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 6, "id": "843ba7f3", "metadata": {}, "outputs": [ @@ -203,7 +203,7 @@ "Game(title='Another Prisoner's Dilemma')" ] }, - "execution_count": 24, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -233,7 +233,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 7, "id": "5ee752c4", "metadata": {}, "outputs": [ @@ -270,7 +270,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 8, "id": "a81c06c7", "metadata": {}, "outputs": [ @@ -280,7 +280,7 @@ "pygambit.nash.NashComputationResult" ] }, - "execution_count": 26, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -300,7 +300,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 9, "id": "bd395180", "metadata": {}, "outputs": [ @@ -310,7 +310,7 @@ "1" ] }, - "execution_count": 27, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -329,7 +329,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 10, "id": "76570ebc", "metadata": {}, "outputs": [ @@ -342,7 +342,7 @@ "[[Rational(0, 1), Rational(1, 1)], [Rational(0, 1), Rational(1, 1)]]" ] }, - "execution_count": 28, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -354,7 +354,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 11, "id": "6e8cfcde", "metadata": {}, "outputs": [ @@ -364,7 +364,7 @@ "pygambit.gambit.MixedStrategyProfileRational" ] }, - "execution_count": 29, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -385,7 +385,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 12, "id": "980bf6b1", "metadata": {}, "outputs": [ @@ -431,7 +431,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 14, "id": "701aa52a", "metadata": {}, "outputs": [ @@ -441,13 +441,15 @@ "['PrisonersDilemma']" ] }, - "execution_count": 31, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "gbt.catalog.games(game_type=\"nfg\", num_players=2)" + "from pygambit import catalog\n", + "\n", + "catalog.games(game_type=\"nfg\", num_players=2)" ] }, { @@ -460,7 +462,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 15, "id": "6db7a29a", "metadata": {}, "outputs": [ @@ -474,13 +476,13 @@ "Game(title='Two person Prisoner's Dilemma game')" ] }, - "execution_count": 32, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "g = gbt.catalog.PrisonersDilemma()\n", + "g = catalog.PrisonersDilemma()\n", "g" ] }, @@ -500,7 +502,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "id": "f58eaa77", "metadata": {}, "outputs": [], @@ -518,7 +520,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "id": "4119a2ac", "metadata": {}, "outputs": [], @@ -529,7 +531,7 @@ ], "metadata": { "kernelspec": { - "display_name": "gambitvenv313", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, diff --git a/doc/tutorials/02_extensive_form.ipynb b/doc/tutorials/02_extensive_form.ipynb index d8b7e108e..ff7ba5659 100644 --- a/doc/tutorials/02_extensive_form.ipynb +++ b/doc/tutorials/02_extensive_form.ipynb @@ -1123,7 +1123,9 @@ } ], "source": [ - "gbt.catalog.games(game_type=\"efg\", num_players=2)" + "from pygambit import catalog\n", + "\n", + "catalog.games(game_type=\"efg\", num_players=2)" ] }, { @@ -1326,7 +1328,7 @@ } ], "source": [ - "draw_tree(gbt.catalog.OneShotTrust(unique_NE_variant=True))" + "draw_tree(catalog.OneShotTrust(unique_NE_variant=True))" ] }, { diff --git a/src/pygambit/__init__.py b/src/pygambit/__init__.py index 5f9fb08cc..1d6f730bf 100644 --- a/src/pygambit/__init__.py +++ b/src/pygambit/__init__.py @@ -26,7 +26,6 @@ nash, # noqa: F401 qre, # noqa: F401 supports, # noqa: F401 - catalog, # noqa: F401 ) import importlib.metadata From a871e3c32343c78ba1a641cf8aa4d103b4138763 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 13 Jan 2026 10:23:11 +0000 Subject: [PATCH 31/76] print description directly from catalog game --- doc/tutorials/02_extensive_form.ipynb | 30 ++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/doc/tutorials/02_extensive_form.ipynb b/doc/tutorials/02_extensive_form.ipynb index ff7ba5659..a2c1f0eca 100644 --- a/doc/tutorials/02_extensive_form.ipynb +++ b/doc/tutorials/02_extensive_form.ipynb @@ -1134,11 +1134,31 @@ "metadata": {}, "source": [ "Some games in the catalog have configurable parameters.\n", - "For example, the `OneShotTrust` game has the `unique_NE_variant` parameter, which makes Trust a dominant strategy, replacing the\n", - " non-singleton equilibrium component from the standard version of the game\n", - " where the Buyer plays \"Not Trust\" and the seller can play any mixture with\n", - " < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and\n", - " the Seller plays Abuse." + "For example, the `OneShotTrust` game has the `unique_NE_variant` parameter, which is explained in the game's description:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "2933061a-a2eb-4ca7-8ce3-f350bc0678ce", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " The unique_NE_variant makes Trust a dominant strategy, replacing the\n", + " non-singleton equilibrium component from the standard version of the game\n", + " where the Buyer plays \"Not Trust\" and the seller can play any mixture with\n", + " < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and\n", + " the Seller plays Abuse.\n", + " \n" + ] + } + ], + "source": [ + "print(catalog.OneShotTrust.description)" ] }, { From 3e0e9408c385bc302bcc6b470dbbef1cabef2967 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 13 Jan 2026 11:29:01 +0000 Subject: [PATCH 32/76] use docstrings as description fields --- src/pygambit/catalog/catalog.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index d61776eb1..cdf379e73 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -1,3 +1,4 @@ +import inspect from pathlib import Path from typing import Literal @@ -37,6 +38,14 @@ def _extract_metadata_from_game(cls, game: Game) -> None: cls.title = game.title cls.num_players = len(game.players) + @classmethod + def _extract_description_from_docstring(cls) -> None: + """Populate description from the class docstring.""" + if cls.__doc__: + cls.description = inspect.cleandoc(cls.__doc__) + else: + cls.description = "" + def __init_subclass__(cls, **kwargs): """Extract metadata when subclass is defined (if not a file-based game).""" super().__init_subclass__(**kwargs) @@ -45,6 +54,9 @@ def __init_subclass__(cls, **kwargs): if cls.__name__ == "CatalogGameFromContrib" or issubclass(cls, CatalogGameFromContrib): return + # Pull description from docstring + cls._extract_description_from_docstring() + # For non-file-based games, create a temporary instance to extract metadata try: temp_game = cls._game() @@ -94,6 +106,9 @@ def __init_subclass__(cls, **kwargs): if not hasattr(cls, "game_file") or cls.game_file is None: raise TypeError(f"{cls.__name__} must define 'game_file' class attribute") + # Pull description from docstring + cls._extract_description_from_docstring() + # Load game and extract metadata immediately when class is defined cls._cached_game = cls._load_game() cls._extract_metadata_from_game(cls._cached_game) @@ -143,14 +158,18 @@ def get_all_subclasses(cls): class PrisonersDilemma(CatalogGameFromContrib): + """ + Prisoner's Dilemma game. + """ game_file = "pd.nfg" - description = "Prisoner's Dilemma game." citation = "Example citation for Prisoner's Dilemma." class TwoStageMatchingPennies(CatalogGameFromContrib): + """ + Two-Stage Matching Pennies game. + """ game_file = "2smp.efg" - description = "Two-Stage Matching Pennies game." citation = "Example citation for Two-Stage Matching Pennies." @@ -160,14 +179,14 @@ class TwoStageMatchingPennies(CatalogGameFromContrib): class OneShotTrust(CatalogGame): - game_type = "efg" - description = """ + """ The unique_NE_variant makes Trust a dominant strategy, replacing the non-singleton equilibrium component from the standard version of the game where the Buyer plays "Not Trust" and the seller can play any mixture with < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and the Seller plays Abuse. """ + game_type = "efg" citation = "Kreps (1990)" @staticmethod From 450d06c1ad374331ead9dc4e2d2f6745a79588b0 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 13 Jan 2026 13:13:56 +0000 Subject: [PATCH 33/76] don't instantiate game to get metadata --- src/pygambit/catalog/catalog.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index cdf379e73..202e3a66d 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -32,12 +32,6 @@ def _game() -> Game: """Override this method in subclasses to define the game.""" raise NotImplementedError("Subclasses must implement _game() method") - @classmethod - def _extract_metadata_from_game(cls, game: Game) -> None: - """Extract metadata from the game and set as class attributes.""" - cls.title = game.title - cls.num_players = len(game.players) - @classmethod def _extract_description_from_docstring(cls) -> None: """Populate description from the class docstring.""" @@ -57,14 +51,6 @@ def __init_subclass__(cls, **kwargs): # Pull description from docstring cls._extract_description_from_docstring() - # For non-file-based games, create a temporary instance to extract metadata - try: - temp_game = cls._game() - cls._extract_metadata_from_game(temp_game) - except NotImplementedError: - # Base class, skip - pass - class CatalogGameFromContrib(CatalogGame): """ @@ -109,10 +95,6 @@ def __init_subclass__(cls, **kwargs): # Pull description from docstring cls._extract_description_from_docstring() - # Load game and extract metadata immediately when class is defined - cls._cached_game = cls._load_game() - cls._extract_metadata_from_game(cls._cached_game) - def games( game_type: Literal["all", "nfg", "efg"] = "all", @@ -162,6 +144,9 @@ class PrisonersDilemma(CatalogGameFromContrib): Prisoner's Dilemma game. """ game_file = "pd.nfg" + game_type = "nfg" + title = "Prisoner's Dilemma" + num_players = 2 citation = "Example citation for Prisoner's Dilemma." @@ -170,6 +155,9 @@ class TwoStageMatchingPennies(CatalogGameFromContrib): Two-Stage Matching Pennies game. """ game_file = "2smp.efg" + game_type = "efg" + title = "Two-Stage Matching Pennies game." + num_players = 2 citation = "Example citation for Two-Stage Matching Pennies." @@ -187,6 +175,8 @@ class OneShotTrust(CatalogGame): the Seller plays Abuse. """ game_type = "efg" + title = "One shot trust game." + num_players = 2 citation = "Kreps (1990)" @staticmethod From 9d1cd75d56574acfe7b7eff7f31353152dcd549a Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 13 Jan 2026 13:21:55 +0000 Subject: [PATCH 34/76] simplify docstring retrieval --- src/pygambit/catalog/catalog.py | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 202e3a66d..57c5f78ed 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -32,25 +32,13 @@ def _game() -> Game: """Override this method in subclasses to define the game.""" raise NotImplementedError("Subclasses must implement _game() method") - @classmethod - def _extract_description_from_docstring(cls) -> None: - """Populate description from the class docstring.""" + def __init_subclass__(cls): + """Extract description from docstring when subclass defined.""" if cls.__doc__: cls.description = inspect.cleandoc(cls.__doc__) else: cls.description = "" - def __init_subclass__(cls, **kwargs): - """Extract metadata when subclass is defined (if not a file-based game).""" - super().__init_subclass__(**kwargs) - - # Skip if this is CatalogGameFromContrib or its subclasses - if cls.__name__ == "CatalogGameFromContrib" or issubclass(cls, CatalogGameFromContrib): - return - - # Pull description from docstring - cls._extract_description_from_docstring() - class CatalogGameFromContrib(CatalogGame): """ @@ -85,16 +73,6 @@ def _load_game(cls) -> Game: else: raise ValueError(f"Game file extension must be 'nfg' or 'efg', got '{cls.game_type}'") - def __init_subclass__(cls, **kwargs): - """Validate and extract metadata when subclass is defined.""" - super().__init_subclass__(**kwargs) - - if not hasattr(cls, "game_file") or cls.game_file is None: - raise TypeError(f"{cls.__name__} must define 'game_file' class attribute") - - # Pull description from docstring - cls._extract_description_from_docstring() - def games( game_type: Literal["all", "nfg", "efg"] = "all", From 8a4c26bbf4cce2b32ffceb8719d2e68f3eb7a186 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 13 Jan 2026 13:34:16 +0000 Subject: [PATCH 35/76] simplify CatalogGameFromContrib --- src/pygambit/catalog/catalog.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 57c5f78ed..e6942587a 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -47,23 +47,13 @@ class CatalogGameFromContrib(CatalogGame): Calling any subclass will return an instance of the corresponding game. """ - game_file: str - _cached_game: Game | None = None + game_file: str | None = None def __new__(cls) -> Game: - # Return cached game if available, otherwise load it - if cls._cached_game is None: - cls._cached_game = cls._load_game() - # Return a fresh instance (not the cached one) - return cls._load_game() - - @classmethod - def _load_game(cls) -> Game: """Load the game from file.""" if cls.game_file is None: raise NotImplementedError(f"{cls.__name__} must define 'game_file'") - cls.game_type = cls.game_file.split(".")[-1] file_path = _GAMEFILES_DIR / cls.game_file if cls.game_type == "nfg": From a8148c8be947fff5101ee9121945114fd29adc00 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 13 Jan 2026 13:37:53 +0000 Subject: [PATCH 36/76] revoke need to import catalog separately --- doc/tutorials/01_quickstart.ipynb | 33 ++++++++++++++++++++------ doc/tutorials/02_extensive_form.ipynb | 34 ++++++++++++--------------- src/pygambit/__init__.py | 1 + 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index c2b4d900d..f3b6774e8 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -431,7 +431,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "id": "701aa52a", "metadata": {}, "outputs": [ @@ -441,15 +441,34 @@ "['PrisonersDilemma']" ] }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gbt.catalog.games(game_type=\"nfg\", num_players=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d89eff93-d9f3-4de0-ab61-6a2b8d2397b3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Prisoner's Dilemma game.\"" + ] + }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "from pygambit import catalog\n", - "\n", - "catalog.games(game_type=\"nfg\", num_players=2)" + "gbt.catalog.PrisonersDilemma.description" ] }, { @@ -482,7 +501,7 @@ } ], "source": [ - "g = catalog.PrisonersDilemma()\n", + "g = gbt.catalog.PrisonersDilemma()\n", "g" ] }, @@ -502,7 +521,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "f58eaa77", "metadata": {}, "outputs": [], @@ -520,7 +539,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "4119a2ac", "metadata": {}, "outputs": [], diff --git a/doc/tutorials/02_extensive_form.ipynb b/doc/tutorials/02_extensive_form.ipynb index a2c1f0eca..e5963b7ee 100644 --- a/doc/tutorials/02_extensive_form.ipynb +++ b/doc/tutorials/02_extensive_form.ipynb @@ -1123,9 +1123,7 @@ } ], "source": [ - "from pygambit import catalog\n", - "\n", - "catalog.games(game_type=\"efg\", num_players=2)" + "gbt.catalog.games(game_type=\"efg\", num_players=2)" ] }, { @@ -1139,7 +1137,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 15, "id": "2933061a-a2eb-4ca7-8ce3-f350bc0678ce", "metadata": {}, "outputs": [ @@ -1147,23 +1145,21 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n", - " The unique_NE_variant makes Trust a dominant strategy, replacing the\n", - " non-singleton equilibrium component from the standard version of the game\n", - " where the Buyer plays \"Not Trust\" and the seller can play any mixture with\n", - " < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and\n", - " the Seller plays Abuse.\n", - " \n" + "The unique_NE_variant makes Trust a dominant strategy, replacing the\n", + "non-singleton equilibrium component from the standard version of the game\n", + "where the Buyer plays \"Not Trust\" and the seller can play any mixture with\n", + "< 0.5 probability on Honor with a unique NE where the Buyer plays Trust and\n", + "the Seller plays Abuse.\n" ] } ], "source": [ - "print(catalog.OneShotTrust.description)" + "print(gbt.catalog.OneShotTrust.description)" ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "id": "4556c0bf", "metadata": {}, "outputs": [ @@ -1342,13 +1338,13 @@ "" ] }, - "execution_count": 15, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "draw_tree(catalog.OneShotTrust(unique_NE_variant=True))" + "draw_tree(gbt.catalog.OneShotTrust(unique_NE_variant=True))" ] }, { @@ -1368,7 +1364,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "id": "37c51152", "metadata": {}, "outputs": [], @@ -1386,7 +1382,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "id": "0d86a750", "metadata": {}, "outputs": [], @@ -1405,7 +1401,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "id": "1bab777f-8a0b-4f1e-9c0c-270690288243", "metadata": {}, "outputs": [], @@ -1417,7 +1413,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "id": "2b715221-e427-4092-ad2f-9f4f2b548fa4", "metadata": {}, "outputs": [], diff --git a/src/pygambit/__init__.py b/src/pygambit/__init__.py index 1d6f730bf..5f9fb08cc 100644 --- a/src/pygambit/__init__.py +++ b/src/pygambit/__init__.py @@ -26,6 +26,7 @@ nash, # noqa: F401 qre, # noqa: F401 supports, # noqa: F401 + catalog, # noqa: F401 ) import importlib.metadata From 32ff30756de9f5ae77de0c6adf7c153c38655c60 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Tue, 13 Jan 2026 13:54:28 +0000 Subject: [PATCH 37/76] test whether importing one at a time in __init__.py fixes circular import --- src/pygambit/catalog/__init__.py | 38 ++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/pygambit/catalog/__init__.py b/src/pygambit/catalog/__init__.py index 619e73f41..1d8710858 100644 --- a/src/pygambit/catalog/__init__.py +++ b/src/pygambit/catalog/__init__.py @@ -1,16 +1,30 @@ -from .catalog import games +# from .catalog import games -# Dynamically import all catalog games -_all_games = games() -_game_classes = {} +# # Dynamically import all catalog games +# _all_games = games() +# _game_classes = {} -for game_name in _all_games: - # Import each game class from catalog module - from . import catalog - _game_classes[game_name] = getattr(catalog, game_name) +# for game_name in _all_games: +# # Import each game class from catalog module +# from . import catalog +# _game_classes[game_name] = getattr(catalog, game_name) -# Add to module namespace -globals().update(_game_classes) +# # Add to module namespace +# globals().update(_game_classes) -# Build __all__ dynamically -__all__ = ["games"] + list(_all_games) # type: ignore[assignment] +# # Build __all__ dynamically +# __all__ = ["games"] + list(_all_games) # type: ignore[assignment] + +from .catalog import ( + OneShotTrust, + PrisonersDilemma, + TwoStageMatchingPennies, + games, +) + +__all__ = [ + "games", + "PrisonersDilemma", + "TwoStageMatchingPennies", + "OneShotTrust", +] From eedce0e89bdf36122f1eb8c5d8d71f48fa7b1126 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 14:27:07 +0000 Subject: [PATCH 38/76] add a couple more example games and import all in __init__.py --- src/pygambit/catalog/__init__.py | 48 ++++++++++++++++---------------- src/pygambit/catalog/catalog.py | 23 +++++++++++++++ 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/pygambit/catalog/__init__.py b/src/pygambit/catalog/__init__.py index 1d8710858..52431164c 100644 --- a/src/pygambit/catalog/__init__.py +++ b/src/pygambit/catalog/__init__.py @@ -1,30 +1,30 @@ -# from .catalog import games +from .catalog import games -# # Dynamically import all catalog games -# _all_games = games() -# _game_classes = {} +# Dynamically import all catalog games +_all_games = games() +_game_classes = {} -# for game_name in _all_games: -# # Import each game class from catalog module -# from . import catalog -# _game_classes[game_name] = getattr(catalog, game_name) +for game_name in _all_games: + # Import each game class from catalog module + from . import catalog + _game_classes[game_name] = getattr(catalog, game_name) -# # Add to module namespace -# globals().update(_game_classes) +# Add to module namespace +globals().update(_game_classes) -# # Build __all__ dynamically -# __all__ = ["games"] + list(_all_games) # type: ignore[assignment] +# Build __all__ dynamically +__all__ = ["games"] + list(_all_games) # type: ignore[assignment] -from .catalog import ( - OneShotTrust, - PrisonersDilemma, - TwoStageMatchingPennies, - games, -) +# from .catalog import ( +# OneShotTrust, +# PrisonersDilemma, +# TwoStageMatchingPennies, +# games, +# ) -__all__ = [ - "games", - "PrisonersDilemma", - "TwoStageMatchingPennies", - "OneShotTrust", -] +# __all__ = [ +# "games", +# "PrisonersDilemma", +# "TwoStageMatchingPennies", +# "OneShotTrust", +# ] diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index e6942587a..40828ff15 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -129,6 +129,29 @@ class TwoStageMatchingPennies(CatalogGameFromContrib): citation = "Example citation for Two-Stage Matching Pennies." +class Game2s2x2x2(CatalogGameFromContrib): + """ + Two stage McKelvey McLennan game with 9 equilibria each stage. + """ + + game_file = "2s2x2x2.efg" + game_type = "efg" + title = "Two stage McKelvey McLennan game with 9 equilibria each stage" + num_players = 3 + citation = "Test." + + +class Artist1(CatalogGameFromContrib): + """ + Artist problem, one stage. + """ + + game_file = "artist1.efg" + game_type = "efg" + title = "Artist problem, one stage" + num_players = 2 + citation = "Test." + ########################################## # Catalog games defined programmatically # ########################################## From e7559118f92cb0a5893b5ef6bfcb77fc00a90f2f Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 16:22:15 +0000 Subject: [PATCH 39/76] add importability to init --- src/pygambit/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pygambit/__init__.py b/src/pygambit/__init__.py index 1d6f730bf..5f9fb08cc 100644 --- a/src/pygambit/__init__.py +++ b/src/pygambit/__init__.py @@ -26,6 +26,7 @@ nash, # noqa: F401 qre, # noqa: F401 supports, # noqa: F401 + catalog, # noqa: F401 ) import importlib.metadata From 2acb948621002cf78cfacb17a5482c927e8a06dc Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 16:28:58 +0000 Subject: [PATCH 40/76] restore getting attributes from games --- doc/tutorials/01_quickstart.ipynb | 33 +++++++++++++++---- doc/tutorials/02_extensive_form.ipynb | 46 ++++++++++++++++++--------- src/pygambit/catalog/catalog.py | 35 +++++++++++++++----- 3 files changed, 84 insertions(+), 30 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index c2b4d900d..f3b6774e8 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -431,7 +431,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "id": "701aa52a", "metadata": {}, "outputs": [ @@ -441,15 +441,34 @@ "['PrisonersDilemma']" ] }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gbt.catalog.games(game_type=\"nfg\", num_players=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d89eff93-d9f3-4de0-ab61-6a2b8d2397b3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Prisoner's Dilemma game.\"" + ] + }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "from pygambit import catalog\n", - "\n", - "catalog.games(game_type=\"nfg\", num_players=2)" + "gbt.catalog.PrisonersDilemma.description" ] }, { @@ -482,7 +501,7 @@ } ], "source": [ - "g = catalog.PrisonersDilemma()\n", + "g = gbt.catalog.PrisonersDilemma()\n", "g" ] }, @@ -502,7 +521,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "f58eaa77", "metadata": {}, "outputs": [], @@ -520,7 +539,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "4119a2ac", "metadata": {}, "outputs": [], diff --git a/doc/tutorials/02_extensive_form.ipynb b/doc/tutorials/02_extensive_form.ipynb index ff7ba5659..4d038b1e9 100644 --- a/doc/tutorials/02_extensive_form.ipynb +++ b/doc/tutorials/02_extensive_form.ipynb @@ -1114,7 +1114,7 @@ { "data": { "text/plain": [ - "['TwoStageMatchingPennies', 'OneShotTrust']" + "['TwoStageMatchingPennies', 'Artist1', 'OneShotTrust']" ] }, "execution_count": 14, @@ -1123,9 +1123,7 @@ } ], "source": [ - "from pygambit import catalog\n", - "\n", - "catalog.games(game_type=\"efg\", num_players=2)" + "gbt.catalog.games(game_type=\"efg\", num_players=2)" ] }, { @@ -1134,16 +1132,34 @@ "metadata": {}, "source": [ "Some games in the catalog have configurable parameters.\n", - "For example, the `OneShotTrust` game has the `unique_NE_variant` parameter, which makes Trust a dominant strategy, replacing the\n", - " non-singleton equilibrium component from the standard version of the game\n", - " where the Buyer plays \"Not Trust\" and the seller can play any mixture with\n", - " < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and\n", - " the Seller plays Abuse." + "For example, the `OneShotTrust` game has the `unique_NE_variant` parameter, which is explained in the game's description:" ] }, { "cell_type": "code", "execution_count": 15, + "id": "2933061a-a2eb-4ca7-8ce3-f350bc0678ce", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The unique_NE_variant makes Trust a dominant strategy, replacing the\n", + "non-singleton equilibrium component from the standard version of the game\n", + "where the Buyer plays \"Not Trust\" and the seller can play any mixture with\n", + "< 0.5 probability on Honor with a unique NE where the Buyer plays Trust and\n", + "the Seller plays Abuse.\n" + ] + } + ], + "source": [ + "print(gbt.catalog.OneShotTrust.description)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, "id": "4556c0bf", "metadata": {}, "outputs": [ @@ -1322,13 +1338,13 @@ "" ] }, - "execution_count": 15, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "draw_tree(catalog.OneShotTrust(unique_NE_variant=True))" + "draw_tree(gbt.catalog.OneShotTrust(unique_NE_variant=True))" ] }, { @@ -1348,7 +1364,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "id": "37c51152", "metadata": {}, "outputs": [], @@ -1366,7 +1382,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "id": "0d86a750", "metadata": {}, "outputs": [], @@ -1385,7 +1401,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "id": "1bab777f-8a0b-4f1e-9c0c-270690288243", "metadata": {}, "outputs": [], @@ -1397,7 +1413,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "id": "2b715221-e427-4092-ad2f-9f4f2b548fa4", "metadata": {}, "outputs": [], diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index d61776eb1..8d17dda8a 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -1,3 +1,4 @@ +import inspect from pathlib import Path from typing import Literal @@ -18,7 +19,6 @@ class CatalogGame: num_players: int game_type: Literal["nfg", "efg"] description: str - citation: str def __new__(cls, *args, **kwargs) -> Game: """Create a game instance by calling the _game() method.""" @@ -36,6 +36,10 @@ def _extract_metadata_from_game(cls, game: Game) -> None: """Extract metadata from the game and set as class attributes.""" cls.title = game.title cls.num_players = len(game.players) + if cls.__doc__: + cls.description = inspect.cleandoc(cls.__doc__) + else: + cls.description = game.comment def __init_subclass__(cls, **kwargs): """Extract metadata when subclass is defined (if not a file-based game).""" @@ -143,15 +147,31 @@ def get_all_subclasses(cls): class PrisonersDilemma(CatalogGameFromContrib): + """ + Prisoner's Dilemma game. + """ game_file = "pd.nfg" - description = "Prisoner's Dilemma game." - citation = "Example citation for Prisoner's Dilemma." class TwoStageMatchingPennies(CatalogGameFromContrib): + """ + Two-Stage Matching Pennies game. + """ game_file = "2smp.efg" - description = "Two-Stage Matching Pennies game." - citation = "Example citation for Two-Stage Matching Pennies." + + +class Game2s2x2x2(CatalogGameFromContrib): + """ + Two stage McKelvey McLennan game with 9 equilibria each stage. + """ + game_file = "2s2x2x2.efg" + + +class Artist1(CatalogGameFromContrib): + """ + Artist problem, one stage. + """ + game_file = "artist1.efg" ########################################## @@ -160,15 +180,14 @@ class TwoStageMatchingPennies(CatalogGameFromContrib): class OneShotTrust(CatalogGame): - game_type = "efg" - description = """ + """ The unique_NE_variant makes Trust a dominant strategy, replacing the non-singleton equilibrium component from the standard version of the game where the Buyer plays "Not Trust" and the seller can play any mixture with < 0.5 probability on Honor with a unique NE where the Buyer plays Trust and the Seller plays Abuse. """ - citation = "Kreps (1990)" + game_type = "efg" @staticmethod def _game(unique_NE_variant: bool = False): From d88d469e70ee8933b939ec958740cf5602a1a9c5 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 16:57:10 +0000 Subject: [PATCH 41/76] finish merge --- src/pygambit/catalog/catalog.py | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 3e4603edc..50ec112ca 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -65,13 +65,23 @@ class CatalogGameFromContrib(CatalogGame): Calling any subclass will return an instance of the corresponding game. """ - game_file: str | None = None + game_file: str + _cached_game: Game | None = None def __new__(cls) -> Game: + # Return cached game if available, otherwise load it + if cls._cached_game is None: + cls._cached_game = cls._load_game() + # Return a fresh instance (not the cached one) + return cls._load_game() + + @classmethod + def _load_game(cls) -> Game: """Load the game from file.""" if cls.game_file is None: raise NotImplementedError(f"{cls.__name__} must define 'game_file'") + cls.game_type = cls.game_file.split(".")[-1] file_path = _GAMEFILES_DIR / cls.game_file if cls.game_type == "nfg": @@ -81,6 +91,17 @@ def __new__(cls) -> Game: else: raise ValueError(f"Game file extension must be 'nfg' or 'efg', got '{cls.game_type}'") + def __init_subclass__(cls, **kwargs): + """Validate and extract metadata when subclass is defined.""" + super().__init_subclass__(**kwargs) + + if not hasattr(cls, "game_file") or cls.game_file is None: + raise TypeError(f"{cls.__name__} must define 'game_file' class attribute") + + # Load game and extract metadata immediately when class is defined + cls._cached_game = cls._load_game() + cls._extract_metadata_from_game(cls._cached_game) + def games( game_type: Literal["all", "nfg", "efg"] = "all", @@ -104,14 +125,15 @@ def games( list[str] List of game class names matching the specified type. """ + def get_all_subclasses(cls): """Recursively get all subclasses.""" all_subclasses = [] for subclass in cls.__subclasses__(): - if subclass.__name__ not in ["CatalogGameFromContrib"] and ( - game_type == "all" or subclass.game_type == game_type - ) and ( - num_players is None or subclass.num_players == num_players + if ( + subclass.__name__ not in ["CatalogGameFromContrib"] + and (game_type == "all" or subclass.game_type == game_type) + and (num_players is None or subclass.num_players == num_players) ): all_subclasses.append(subclass.__name__) all_subclasses.extend(get_all_subclasses(subclass)) From 7ca3583cf22d0bd61cc40d3eedda7c524856df9c Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 16:59:04 +0000 Subject: [PATCH 42/76] initial script for generating catalog from contrib --- src/pygambit/catalog/get_contrib_games.py | 53 +++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/pygambit/catalog/get_contrib_games.py diff --git a/src/pygambit/catalog/get_contrib_games.py b/src/pygambit/catalog/get_contrib_games.py new file mode 100644 index 000000000..49b706966 --- /dev/null +++ b/src/pygambit/catalog/get_contrib_games.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +from pathlib import Path + +OUTPUT_FILE = "generated_catalog_games.py" +_GAMEFILES_DIR = Path(__file__).parent.parent.parent.parent / "contrib/games" + + +def make_class_name(filename: str) -> str: + """ + Convert a filename (without extension) into a class name. + - Replace hyphens with underscores + - Capitalise + - Prepend 'Game' if it starts with a digit + """ + name = filename.replace("-", "_") + + # Capitalise in a simple, predictable way + name = name[0].upper() + name[1:] if name else name + + if name and name[0].isdigit(): + name = f"Game{name}" + + return name + + +if __name__ == "__main__": + efg_files = list(_GAMEFILES_DIR.rglob("*.efg")) + nfg_files = list(_GAMEFILES_DIR.rglob("*.nfg")) + + print(f"Found {len(efg_files)} .efg files") + print(f"Found {len(nfg_files)} .nfg files") + + all_files = sorted(efg_files + nfg_files) + + lines = [] + lines.append("# Auto-generated file. Do not edit manually.\n") + lines.append("from .catalog_game import CatalogGameFromContrib\n\n") + + for path in all_files: + stem = path.stem + class_name = make_class_name(stem) + + lines.append(f"class {class_name}(CatalogGameFromContrib):") + lines.append(f' game_file = "{path.name}"') + lines.append("\n") + + output_path = Path(__file__) / OUTPUT_FILE + output_path.write_text("\n".join(lines), encoding="utf-8") + + print(f"Generated {len(all_files)} classes") + print(f"Output written to: {output_path}") + print("Done.") From f0bbd32568d961caa7366a470f208fd811192264 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 17:00:14 +0000 Subject: [PATCH 43/76] fix output path --- src/pygambit/catalog/get_contrib_games.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pygambit/catalog/get_contrib_games.py b/src/pygambit/catalog/get_contrib_games.py index 49b706966..3abe45ebc 100644 --- a/src/pygambit/catalog/get_contrib_games.py +++ b/src/pygambit/catalog/get_contrib_games.py @@ -45,7 +45,7 @@ def make_class_name(filename: str) -> str: lines.append(f' game_file = "{path.name}"') lines.append("\n") - output_path = Path(__file__) / OUTPUT_FILE + output_path = Path(__file__).parent / OUTPUT_FILE output_path.write_text("\n".join(lines), encoding="utf-8") print(f"Generated {len(all_files)} classes") From 4dc7227a38085103c5747719dc28e481cd45d72c Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 17:01:12 +0000 Subject: [PATCH 44/76] remove disclaimer line --- src/pygambit/catalog/get_contrib_games.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pygambit/catalog/get_contrib_games.py b/src/pygambit/catalog/get_contrib_games.py index 3abe45ebc..8b9be4504 100644 --- a/src/pygambit/catalog/get_contrib_games.py +++ b/src/pygambit/catalog/get_contrib_games.py @@ -34,7 +34,6 @@ def make_class_name(filename: str) -> str: all_files = sorted(efg_files + nfg_files) lines = [] - lines.append("# Auto-generated file. Do not edit manually.\n") lines.append("from .catalog_game import CatalogGameFromContrib\n\n") for path in all_files: From 3a8d2657262aa54e9af3a09898b9d12551be20e9 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 17:10:26 +0000 Subject: [PATCH 45/76] avoid duplicates where EFG and NFG games have the same name --- src/pygambit/catalog/get_contrib_games.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pygambit/catalog/get_contrib_games.py b/src/pygambit/catalog/get_contrib_games.py index 8b9be4504..03408ed6d 100644 --- a/src/pygambit/catalog/get_contrib_games.py +++ b/src/pygambit/catalog/get_contrib_games.py @@ -36,10 +36,16 @@ def make_class_name(filename: str) -> str: lines = [] lines.append("from .catalog_game import CatalogGameFromContrib\n\n") + class_names = [] for path in all_files: stem = path.stem class_name = make_class_name(stem) + # Avoid duplicates (some EFG and NFG have same name) + if class_name in class_names: + class_name += path.suffix.split(".")[-1].upper() + + class_names.append(class_name) lines.append(f"class {class_name}(CatalogGameFromContrib):") lines.append(f' game_file = "{path.name}"') lines.append("\n") From e481de1ecb83fa0723541226760ca4145f0afc67 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 17:16:05 +0000 Subject: [PATCH 46/76] Add all EFG and NFG games from contrib --- doc/tutorials/01_quickstart.ipynb | 58 +- doc/tutorials/02_extensive_form.ipynb | 80 ++- src/pygambit/catalog/catalog.py | 587 +++++++++++++++++- .../catalog/generated_catalog_games.py | 581 +++++++++++++++++ 4 files changed, 1266 insertions(+), 40 deletions(-) create mode 100644 src/pygambit/catalog/generated_catalog_games.py diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index f3b6774e8..371721aa9 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -438,7 +438,42 @@ { "data": { "text/plain": [ - "['PrisonersDilemma']" + "['Game2x2',\n", + " 'Game2x2a',\n", + " 'Game2x2const',\n", + " 'Game8x8',\n", + " 'Cent2NFG',\n", + " 'Coord2NFG',\n", + " 'Coord3NFG',\n", + " 'Coord4NFG',\n", + " 'Csg1',\n", + " 'Csg2',\n", + " 'Csg3',\n", + " 'Csg4',\n", + " 'Deg1',\n", + " 'Deg2',\n", + " 'E02NFG',\n", + " 'E04NFG',\n", + " 'E07NFG',\n", + " 'Loopback',\n", + " 'Mixdom',\n", + " 'Mixdom2',\n", + " 'Oneill',\n", + " 'PrisonersDilemma',\n", + " 'Perfect1',\n", + " 'Perfect2',\n", + " 'PokerNFG',\n", + " 'Sh3NFG',\n", + " 'Stengel',\n", + " 'Sww1NFG',\n", + " 'Todd1',\n", + " 'Todd2',\n", + " 'Todd3',\n", + " 'VdNFG',\n", + " 'Wink3',\n", + " 'Winkels',\n", + " 'Yamamoto',\n", + " 'Zero']" ] }, "execution_count": 13, @@ -450,27 +485,6 @@ "gbt.catalog.games(game_type=\"nfg\", num_players=2)" ] }, - { - "cell_type": "code", - "execution_count": 14, - "id": "d89eff93-d9f3-4de0-ab61-6a2b8d2397b3", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"Prisoner's Dilemma game.\"" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gbt.catalog.PrisonersDilemma.description" - ] - }, { "cell_type": "markdown", "id": "a919ddf7", diff --git a/doc/tutorials/02_extensive_form.ipynb b/doc/tutorials/02_extensive_form.ipynb index 4d038b1e9..10e59fc5f 100644 --- a/doc/tutorials/02_extensive_form.ipynb +++ b/doc/tutorials/02_extensive_form.ipynb @@ -1114,7 +1114,85 @@ { "data": { "text/plain": [ - "['TwoStageMatchingPennies', 'Artist1', 'OneShotTrust']" + "['Game2smp',\n", + " 'Game4cards',\n", + " 'Artist1',\n", + " 'Artist2',\n", + " 'Badgame1',\n", + " 'Badgame2',\n", + " 'Bagwell',\n", + " 'Bayes1a',\n", + " 'Bayes2a',\n", + " 'Bcp2',\n", + " 'Bcp3',\n", + " 'Bcp4',\n", + " 'Bhg1',\n", + " 'Bhg2',\n", + " 'Bhg3',\n", + " 'Bhg4',\n", + " 'Bhg5',\n", + " 'Caro2',\n", + " 'Cent2',\n", + " 'Cent3',\n", + " 'Cent4',\n", + " 'Cent6',\n", + " 'Centcs10',\n", + " 'Centcs6',\n", + " 'Coord2',\n", + " 'Coord2ts',\n", + " 'Coord3',\n", + " 'Coord4',\n", + " 'Cross',\n", + " 'E02',\n", + " 'E04',\n", + " 'E07',\n", + " 'E10',\n", + " 'E10a',\n", + " 'E13',\n", + " 'E16',\n", + " 'E17',\n", + " 'E18',\n", + " 'Holdout',\n", + " 'Hs1',\n", + " 'Km1',\n", + " 'Km2',\n", + " 'Km3',\n", + " 'Km6',\n", + " 'Montyhal',\n", + " 'My_2_1',\n", + " 'My_2_4',\n", + " 'My_3_3a',\n", + " 'My_3_3b',\n", + " 'My_3_3c',\n", + " 'My_3_3d',\n", + " 'My_3_3e',\n", + " 'Myerson',\n", + " 'Myerson_fig_4_2',\n", + " 'Nim',\n", + " 'Nim7',\n", + " 'Palf',\n", + " 'Palf2',\n", + " 'Palf3',\n", + " 'Pbride',\n", + " 'Poker',\n", + " 'Poker2',\n", + " 'Pvw',\n", + " 'Pvw2',\n", + " 'Sh3',\n", + " 'Spence',\n", + " 'Sww1',\n", + " 'Sww2',\n", + " 'Sww3',\n", + " 'Tim',\n", + " 'Ttt',\n", + " 'Vd',\n", + " 'W_ex1',\n", + " 'W_ex2',\n", + " 'Wilson1',\n", + " 'Work1',\n", + " 'Work2',\n", + " 'Work3',\n", + " 'OneShotTrust']" ] }, "execution_count": 14, diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 50ec112ca..18ee23895 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -147,34 +147,587 @@ def get_all_subclasses(cls): ############################ -class PrisonersDilemma(CatalogGameFromContrib): - """ - Prisoner's Dilemma game. - """ - game_file = "pd.nfg" +class Game2s2x2x2(CatalogGameFromContrib): + game_file = "2s2x2x2.efg" -class TwoStageMatchingPennies(CatalogGameFromContrib): - """ - Two-Stage Matching Pennies game. - """ +class Game2smp(CatalogGameFromContrib): game_file = "2smp.efg" -class Game2s2x2x2(CatalogGameFromContrib): - """ - Two stage McKelvey McLennan game with 9 equilibria each stage. - """ - game_file = "2s2x2x2.efg" +class Game2x2(CatalogGameFromContrib): + game_file = "2x2.nfg" + + +class Game2x2a(CatalogGameFromContrib): + game_file = "2x2a.nfg" + + +class Game2x2const(CatalogGameFromContrib): + game_file = "2x2const.nfg" + + +class Game2x2x2_nau(CatalogGameFromContrib): + game_file = "2x2x2-nau.nfg" + + +class Game2x2x2(CatalogGameFromContrib): + game_file = "2x2x2.efg" + + +class Game2x2x2NFG(CatalogGameFromContrib): + game_file = "2x2x2.nfg" + + +class Game2x2x2x2(CatalogGameFromContrib): + game_file = "2x2x2x2.nfg" + + +class Game2x2x2x2x2(CatalogGameFromContrib): + game_file = "2x2x2x2x2.nfg" + + +class Game3x3x3(CatalogGameFromContrib): + game_file = "3x3x3.nfg" + + +class Game4cards(CatalogGameFromContrib): + game_file = "4cards.efg" + + +class Game5x4x3(CatalogGameFromContrib): + game_file = "5x4x3.nfg" + + +class Game8x2x2(CatalogGameFromContrib): + game_file = "8x2x2.nfg" + + +class Game8x8(CatalogGameFromContrib): + game_file = "8x8.nfg" class Artist1(CatalogGameFromContrib): - """ - Artist problem, one stage. - """ game_file = "artist1.efg" +class Artist2(CatalogGameFromContrib): + game_file = "artist2.efg" + + +class Badgame1(CatalogGameFromContrib): + game_file = "badgame1.efg" + + +class Badgame2(CatalogGameFromContrib): + game_file = "badgame2.efg" + + +class Bagwell(CatalogGameFromContrib): + game_file = "bagwell.efg" + + +class Bayes1a(CatalogGameFromContrib): + game_file = "bayes1a.efg" + + +class Bayes2a(CatalogGameFromContrib): + game_file = "bayes2a.efg" + + +class Bcp2(CatalogGameFromContrib): + game_file = "bcp2.efg" + + +class Bcp3(CatalogGameFromContrib): + game_file = "bcp3.efg" + + +class Bcp4(CatalogGameFromContrib): + game_file = "bcp4.efg" + + +class Bhg1(CatalogGameFromContrib): + game_file = "bhg1.efg" + + +class Bhg2(CatalogGameFromContrib): + game_file = "bhg2.efg" + + +class Bhg3(CatalogGameFromContrib): + game_file = "bhg3.efg" + + +class Bhg4(CatalogGameFromContrib): + game_file = "bhg4.efg" + + +class Bhg5(CatalogGameFromContrib): + game_file = "bhg5.efg" + + +class Caro2(CatalogGameFromContrib): + game_file = "caro2.efg" + + +class Cent2(CatalogGameFromContrib): + game_file = "cent2.efg" + + +class Cent2NFG(CatalogGameFromContrib): + game_file = "cent2.nfg" + + +class Cent3(CatalogGameFromContrib): + game_file = "cent3.efg" + + +class Cent4(CatalogGameFromContrib): + game_file = "cent4.efg" + + +class Cent6(CatalogGameFromContrib): + game_file = "cent6.efg" + + +class Centcs10(CatalogGameFromContrib): + game_file = "centcs10.efg" + + +class Centcs6(CatalogGameFromContrib): + game_file = "centcs6.efg" + + +class Condjury(CatalogGameFromContrib): + game_file = "condjury.efg" + + +class Coord2(CatalogGameFromContrib): + game_file = "coord2.efg" + + +class Coord2NFG(CatalogGameFromContrib): + game_file = "coord2.nfg" + + +class Coord2ts(CatalogGameFromContrib): + game_file = "coord2ts.efg" + + +class Coord3(CatalogGameFromContrib): + game_file = "coord3.efg" + + +class Coord3NFG(CatalogGameFromContrib): + game_file = "coord3.nfg" + + +class Coord333(CatalogGameFromContrib): + game_file = "coord333.nfg" + + +class Coord4(CatalogGameFromContrib): + game_file = "coord4.efg" + + +class Coord4NFG(CatalogGameFromContrib): + game_file = "coord4.nfg" + + +class Cross(CatalogGameFromContrib): + game_file = "cross.efg" + + +class Cs(CatalogGameFromContrib): + game_file = "cs.efg" + + +class Csg1(CatalogGameFromContrib): + game_file = "csg1.nfg" + + +class Csg2(CatalogGameFromContrib): + game_file = "csg2.nfg" + + +class Csg3(CatalogGameFromContrib): + game_file = "csg3.nfg" + + +class Csg4(CatalogGameFromContrib): + game_file = "csg4.nfg" + + +class Deg1(CatalogGameFromContrib): + game_file = "deg1.nfg" + + +class Deg2(CatalogGameFromContrib): + game_file = "deg2.nfg" + + +class E01(CatalogGameFromContrib): + game_file = "e01.efg" + + +class E01NFG(CatalogGameFromContrib): + game_file = "e01.nfg" + + +class E02(CatalogGameFromContrib): + game_file = "e02.efg" + + +class E02NFG(CatalogGameFromContrib): + game_file = "e02.nfg" + + +class E03(CatalogGameFromContrib): + game_file = "e03.efg" + + +class E04(CatalogGameFromContrib): + game_file = "e04.efg" + + +class E04NFG(CatalogGameFromContrib): + game_file = "e04.nfg" + + +class E05(CatalogGameFromContrib): + game_file = "e05.efg" + + +class E06(CatalogGameFromContrib): + game_file = "e06.efg" + + +class E07(CatalogGameFromContrib): + game_file = "e07.efg" + + +class E07NFG(CatalogGameFromContrib): + game_file = "e07.nfg" + + +class E08(CatalogGameFromContrib): + game_file = "e08.efg" + + +class E09(CatalogGameFromContrib): + game_file = "e09.efg" + + +class E10(CatalogGameFromContrib): + game_file = "e10.efg" + + +class E10a(CatalogGameFromContrib): + game_file = "e10a.efg" + + +class E13(CatalogGameFromContrib): + game_file = "e13.efg" + + +class E16(CatalogGameFromContrib): + game_file = "e16.efg" + + +class E17(CatalogGameFromContrib): + game_file = "e17.efg" + + +class E18(CatalogGameFromContrib): + game_file = "e18.efg" + + +class G1(CatalogGameFromContrib): + game_file = "g1.efg" + + +class G1NFG(CatalogGameFromContrib): + game_file = "g1.nfg" + + +class G2(CatalogGameFromContrib): + game_file = "g2.efg" + + +class G2NFG(CatalogGameFromContrib): + game_file = "g2.nfg" + + +class G3(CatalogGameFromContrib): + game_file = "g3.efg" + + +class G3NFG(CatalogGameFromContrib): + game_file = "g3.nfg" + + +class Holdout(CatalogGameFromContrib): + game_file = "holdout.efg" + + +# Note: this EFG did not load correctly +# class Holdout7(CatalogGameFromContrib): +# game_file = "holdout7.efg" + + +class Hs1(CatalogGameFromContrib): + game_file = "hs1.efg" + + +class Jury_mr(CatalogGameFromContrib): + game_file = "jury_mr.efg" + + +class Jury_un(CatalogGameFromContrib): + game_file = "jury_un.efg" + + +class Km1(CatalogGameFromContrib): + game_file = "km1.efg" + + +class Km2(CatalogGameFromContrib): + game_file = "km2.efg" + + +class Km3(CatalogGameFromContrib): + game_file = "km3.efg" + + +class Km6(CatalogGameFromContrib): + game_file = "km6.efg" + + +class Loopback(CatalogGameFromContrib): + game_file = "loopback.nfg" + + +class Mixdom(CatalogGameFromContrib): + game_file = "mixdom.nfg" + + +class Mixdom2(CatalogGameFromContrib): + game_file = "mixdom2.nfg" + + +class Montyhal(CatalogGameFromContrib): + game_file = "montyhal.efg" + + +class My_2_1(CatalogGameFromContrib): + game_file = "my_2-1.efg" + + +class My_2_4(CatalogGameFromContrib): + game_file = "my_2-4.efg" + + +class My_2_8(CatalogGameFromContrib): + game_file = "my_2-8.efg" + + +class My_3_3a(CatalogGameFromContrib): + game_file = "my_3-3a.efg" + + +class My_3_3b(CatalogGameFromContrib): + game_file = "my_3-3b.efg" + + +class My_3_3c(CatalogGameFromContrib): + game_file = "my_3-3c.efg" + + +class My_3_3d(CatalogGameFromContrib): + game_file = "my_3-3d.efg" + + +class My_3_3e(CatalogGameFromContrib): + game_file = "my_3-3e.efg" + + +class My_3_4(CatalogGameFromContrib): + game_file = "my_3-4.efg" + + +class Myerson(CatalogGameFromContrib): + game_file = "myerson.efg" + + +class Myerson_fig_4_2(CatalogGameFromContrib): + game_file = "myerson_fig_4_2.efg" + + +class Nim(CatalogGameFromContrib): + game_file = "nim.efg" + + +class Nim7(CatalogGameFromContrib): + game_file = "nim7.efg" + + +class Oneill(CatalogGameFromContrib): + game_file = "oneill.nfg" + + +class Palf(CatalogGameFromContrib): + game_file = "palf.efg" + + +class Palf2(CatalogGameFromContrib): + game_file = "palf2.efg" + + +class Palf3(CatalogGameFromContrib): + game_file = "palf3.efg" + + +class Pbride(CatalogGameFromContrib): + game_file = "pbride.efg" + + +class PrisonersDilemma(CatalogGameFromContrib): + game_file = "pd.nfg" + + +class Perfect1(CatalogGameFromContrib): + game_file = "perfect1.nfg" + + +class Perfect2(CatalogGameFromContrib): + game_file = "perfect2.nfg" + + +class Perfect3(CatalogGameFromContrib): + game_file = "perfect3.nfg" + + +class Poker(CatalogGameFromContrib): + game_file = "poker.efg" + + +class PokerNFG(CatalogGameFromContrib): + game_file = "poker.nfg" + + +class Poker2(CatalogGameFromContrib): + game_file = "poker2.efg" + + +class Pvw(CatalogGameFromContrib): + game_file = "pvw.efg" + + +class Pvw2(CatalogGameFromContrib): + game_file = "pvw2.efg" + + +class Sh3(CatalogGameFromContrib): + game_file = "sh3.efg" + + +class Sh3NFG(CatalogGameFromContrib): + game_file = "sh3.nfg" + + +class Spence(CatalogGameFromContrib): + game_file = "spence.efg" + + +class Stengel(CatalogGameFromContrib): + game_file = "stengel.nfg" + + +class Sww1(CatalogGameFromContrib): + game_file = "sww1.efg" + + +class Sww1NFG(CatalogGameFromContrib): + game_file = "sww1.nfg" + + +class Sww2(CatalogGameFromContrib): + game_file = "sww2.efg" + + +class Sww3(CatalogGameFromContrib): + game_file = "sww3.efg" + + +class Tim(CatalogGameFromContrib): + game_file = "tim.efg" + + +class Todd1(CatalogGameFromContrib): + game_file = "todd1.nfg" + + +class Todd2(CatalogGameFromContrib): + game_file = "todd2.nfg" + + +class Todd3(CatalogGameFromContrib): + game_file = "todd3.nfg" + + +class Ttt(CatalogGameFromContrib): + game_file = "ttt.efg" + + +class Vd(CatalogGameFromContrib): + game_file = "vd.efg" + + +class VdNFG(CatalogGameFromContrib): + game_file = "vd.nfg" + + +class W_ex1(CatalogGameFromContrib): + game_file = "w_ex1.efg" + + +class W_ex2(CatalogGameFromContrib): + game_file = "w_ex2.efg" + + +class Wilson1(CatalogGameFromContrib): + game_file = "wilson1.efg" + + +class Wink3(CatalogGameFromContrib): + game_file = "wink3.nfg" + + +class Winkels(CatalogGameFromContrib): + game_file = "winkels.nfg" + + +class Work1(CatalogGameFromContrib): + game_file = "work1.efg" + + +class Work2(CatalogGameFromContrib): + game_file = "work2.efg" + + +class Work3(CatalogGameFromContrib): + game_file = "work3.efg" + + +class Yamamoto(CatalogGameFromContrib): + game_file = "yamamoto.nfg" + + +class Zero(CatalogGameFromContrib): + game_file = "zero.nfg" + + ########################################## # Catalog games defined programmatically # ########################################## diff --git a/src/pygambit/catalog/generated_catalog_games.py b/src/pygambit/catalog/generated_catalog_games.py new file mode 100644 index 000000000..19a1d5572 --- /dev/null +++ b/src/pygambit/catalog/generated_catalog_games.py @@ -0,0 +1,581 @@ +from .catalog_game import CatalogGameFromContrib + + +class Game2s2x2x2(CatalogGameFromContrib): + game_file = "2s2x2x2.efg" + + +class Game2smp(CatalogGameFromContrib): + game_file = "2smp.efg" + + +class Game2x2(CatalogGameFromContrib): + game_file = "2x2.nfg" + + +class Game2x2a(CatalogGameFromContrib): + game_file = "2x2a.nfg" + + +class Game2x2const(CatalogGameFromContrib): + game_file = "2x2const.nfg" + + +class Game2x2x2_nau(CatalogGameFromContrib): + game_file = "2x2x2-nau.nfg" + + +class Game2x2x2(CatalogGameFromContrib): + game_file = "2x2x2.efg" + + +class Game2x2x2NFG(CatalogGameFromContrib): + game_file = "2x2x2.nfg" + + +class Game2x2x2x2(CatalogGameFromContrib): + game_file = "2x2x2x2.nfg" + + +class Game2x2x2x2x2(CatalogGameFromContrib): + game_file = "2x2x2x2x2.nfg" + + +class Game3x3x3(CatalogGameFromContrib): + game_file = "3x3x3.nfg" + + +class Game4cards(CatalogGameFromContrib): + game_file = "4cards.efg" + + +class Game5x4x3(CatalogGameFromContrib): + game_file = "5x4x3.nfg" + + +class Game8x2x2(CatalogGameFromContrib): + game_file = "8x2x2.nfg" + + +class Game8x8(CatalogGameFromContrib): + game_file = "8x8.nfg" + + +class Artist1(CatalogGameFromContrib): + game_file = "artist1.efg" + + +class Artist2(CatalogGameFromContrib): + game_file = "artist2.efg" + + +class Badgame1(CatalogGameFromContrib): + game_file = "badgame1.efg" + + +class Badgame2(CatalogGameFromContrib): + game_file = "badgame2.efg" + + +class Bagwell(CatalogGameFromContrib): + game_file = "bagwell.efg" + + +class Bayes1a(CatalogGameFromContrib): + game_file = "bayes1a.efg" + + +class Bayes2a(CatalogGameFromContrib): + game_file = "bayes2a.efg" + + +class Bcp2(CatalogGameFromContrib): + game_file = "bcp2.efg" + + +class Bcp3(CatalogGameFromContrib): + game_file = "bcp3.efg" + + +class Bcp4(CatalogGameFromContrib): + game_file = "bcp4.efg" + + +class Bhg1(CatalogGameFromContrib): + game_file = "bhg1.efg" + + +class Bhg2(CatalogGameFromContrib): + game_file = "bhg2.efg" + + +class Bhg3(CatalogGameFromContrib): + game_file = "bhg3.efg" + + +class Bhg4(CatalogGameFromContrib): + game_file = "bhg4.efg" + + +class Bhg5(CatalogGameFromContrib): + game_file = "bhg5.efg" + + +class Caro2(CatalogGameFromContrib): + game_file = "caro2.efg" + + +class Cent2(CatalogGameFromContrib): + game_file = "cent2.efg" + + +class Cent2NFG(CatalogGameFromContrib): + game_file = "cent2.nfg" + + +class Cent3(CatalogGameFromContrib): + game_file = "cent3.efg" + + +class Cent4(CatalogGameFromContrib): + game_file = "cent4.efg" + + +class Cent6(CatalogGameFromContrib): + game_file = "cent6.efg" + + +class Centcs10(CatalogGameFromContrib): + game_file = "centcs10.efg" + + +class Centcs6(CatalogGameFromContrib): + game_file = "centcs6.efg" + + +class Condjury(CatalogGameFromContrib): + game_file = "condjury.efg" + + +class Coord2(CatalogGameFromContrib): + game_file = "coord2.efg" + + +class Coord2NFG(CatalogGameFromContrib): + game_file = "coord2.nfg" + + +class Coord2ts(CatalogGameFromContrib): + game_file = "coord2ts.efg" + + +class Coord3(CatalogGameFromContrib): + game_file = "coord3.efg" + + +class Coord3NFG(CatalogGameFromContrib): + game_file = "coord3.nfg" + + +class Coord333(CatalogGameFromContrib): + game_file = "coord333.nfg" + + +class Coord4(CatalogGameFromContrib): + game_file = "coord4.efg" + + +class Coord4NFG(CatalogGameFromContrib): + game_file = "coord4.nfg" + + +class Cross(CatalogGameFromContrib): + game_file = "cross.efg" + + +class Cs(CatalogGameFromContrib): + game_file = "cs.efg" + + +class Csg1(CatalogGameFromContrib): + game_file = "csg1.nfg" + + +class Csg2(CatalogGameFromContrib): + game_file = "csg2.nfg" + + +class Csg3(CatalogGameFromContrib): + game_file = "csg3.nfg" + + +class Csg4(CatalogGameFromContrib): + game_file = "csg4.nfg" + + +class Deg1(CatalogGameFromContrib): + game_file = "deg1.nfg" + + +class Deg2(CatalogGameFromContrib): + game_file = "deg2.nfg" + + +class E01(CatalogGameFromContrib): + game_file = "e01.efg" + + +class E01NFG(CatalogGameFromContrib): + game_file = "e01.nfg" + + +class E02(CatalogGameFromContrib): + game_file = "e02.efg" + + +class E02NFG(CatalogGameFromContrib): + game_file = "e02.nfg" + + +class E03(CatalogGameFromContrib): + game_file = "e03.efg" + + +class E04(CatalogGameFromContrib): + game_file = "e04.efg" + + +class E04NFG(CatalogGameFromContrib): + game_file = "e04.nfg" + + +class E05(CatalogGameFromContrib): + game_file = "e05.efg" + + +class E06(CatalogGameFromContrib): + game_file = "e06.efg" + + +class E07(CatalogGameFromContrib): + game_file = "e07.efg" + + +class E07NFG(CatalogGameFromContrib): + game_file = "e07.nfg" + + +class E08(CatalogGameFromContrib): + game_file = "e08.efg" + + +class E09(CatalogGameFromContrib): + game_file = "e09.efg" + + +class E10(CatalogGameFromContrib): + game_file = "e10.efg" + + +class E10a(CatalogGameFromContrib): + game_file = "e10a.efg" + + +class E13(CatalogGameFromContrib): + game_file = "e13.efg" + + +class E16(CatalogGameFromContrib): + game_file = "e16.efg" + + +class E17(CatalogGameFromContrib): + game_file = "e17.efg" + + +class E18(CatalogGameFromContrib): + game_file = "e18.efg" + + +class G1(CatalogGameFromContrib): + game_file = "g1.efg" + + +class G1NFG(CatalogGameFromContrib): + game_file = "g1.nfg" + + +class G2(CatalogGameFromContrib): + game_file = "g2.efg" + + +class G2NFG(CatalogGameFromContrib): + game_file = "g2.nfg" + + +class G3(CatalogGameFromContrib): + game_file = "g3.efg" + + +class G3NFG(CatalogGameFromContrib): + game_file = "g3.nfg" + + +class Holdout(CatalogGameFromContrib): + game_file = "holdout.efg" + + +class Holdout7(CatalogGameFromContrib): + game_file = "holdout7.efg" + + +class Hs1(CatalogGameFromContrib): + game_file = "hs1.efg" + + +class Jury_mr(CatalogGameFromContrib): + game_file = "jury_mr.efg" + + +class Jury_un(CatalogGameFromContrib): + game_file = "jury_un.efg" + + +class Km1(CatalogGameFromContrib): + game_file = "km1.efg" + + +class Km2(CatalogGameFromContrib): + game_file = "km2.efg" + + +class Km3(CatalogGameFromContrib): + game_file = "km3.efg" + + +class Km6(CatalogGameFromContrib): + game_file = "km6.efg" + + +class Loopback(CatalogGameFromContrib): + game_file = "loopback.nfg" + + +class Mixdom(CatalogGameFromContrib): + game_file = "mixdom.nfg" + + +class Mixdom2(CatalogGameFromContrib): + game_file = "mixdom2.nfg" + + +class Montyhal(CatalogGameFromContrib): + game_file = "montyhal.efg" + + +class My_2_1(CatalogGameFromContrib): + game_file = "my_2-1.efg" + + +class My_2_4(CatalogGameFromContrib): + game_file = "my_2-4.efg" + + +class My_2_8(CatalogGameFromContrib): + game_file = "my_2-8.efg" + + +class My_3_3a(CatalogGameFromContrib): + game_file = "my_3-3a.efg" + + +class My_3_3b(CatalogGameFromContrib): + game_file = "my_3-3b.efg" + + +class My_3_3c(CatalogGameFromContrib): + game_file = "my_3-3c.efg" + + +class My_3_3d(CatalogGameFromContrib): + game_file = "my_3-3d.efg" + + +class My_3_3e(CatalogGameFromContrib): + game_file = "my_3-3e.efg" + + +class My_3_4(CatalogGameFromContrib): + game_file = "my_3-4.efg" + + +class Myerson(CatalogGameFromContrib): + game_file = "myerson.efg" + + +class Myerson_fig_4_2(CatalogGameFromContrib): + game_file = "myerson_fig_4_2.efg" + + +class Nim(CatalogGameFromContrib): + game_file = "nim.efg" + + +class Nim7(CatalogGameFromContrib): + game_file = "nim7.efg" + + +class Oneill(CatalogGameFromContrib): + game_file = "oneill.nfg" + + +class Palf(CatalogGameFromContrib): + game_file = "palf.efg" + + +class Palf2(CatalogGameFromContrib): + game_file = "palf2.efg" + + +class Palf3(CatalogGameFromContrib): + game_file = "palf3.efg" + + +class Pbride(CatalogGameFromContrib): + game_file = "pbride.efg" + + +class Pd(CatalogGameFromContrib): + game_file = "pd.nfg" + + +class Perfect1(CatalogGameFromContrib): + game_file = "perfect1.nfg" + + +class Perfect2(CatalogGameFromContrib): + game_file = "perfect2.nfg" + + +class Perfect3(CatalogGameFromContrib): + game_file = "perfect3.nfg" + + +class Poker(CatalogGameFromContrib): + game_file = "poker.efg" + + +class PokerNFG(CatalogGameFromContrib): + game_file = "poker.nfg" + + +class Poker2(CatalogGameFromContrib): + game_file = "poker2.efg" + + +class Pvw(CatalogGameFromContrib): + game_file = "pvw.efg" + + +class Pvw2(CatalogGameFromContrib): + game_file = "pvw2.efg" + + +class Sh3(CatalogGameFromContrib): + game_file = "sh3.efg" + + +class Sh3NFG(CatalogGameFromContrib): + game_file = "sh3.nfg" + + +class Spence(CatalogGameFromContrib): + game_file = "spence.efg" + + +class Stengel(CatalogGameFromContrib): + game_file = "stengel.nfg" + + +class Sww1(CatalogGameFromContrib): + game_file = "sww1.efg" + + +class Sww1NFG(CatalogGameFromContrib): + game_file = "sww1.nfg" + + +class Sww2(CatalogGameFromContrib): + game_file = "sww2.efg" + + +class Sww3(CatalogGameFromContrib): + game_file = "sww3.efg" + + +class Tim(CatalogGameFromContrib): + game_file = "tim.efg" + + +class Todd1(CatalogGameFromContrib): + game_file = "todd1.nfg" + + +class Todd2(CatalogGameFromContrib): + game_file = "todd2.nfg" + + +class Todd3(CatalogGameFromContrib): + game_file = "todd3.nfg" + + +class Ttt(CatalogGameFromContrib): + game_file = "ttt.efg" + + +class Vd(CatalogGameFromContrib): + game_file = "vd.efg" + + +class VdNFG(CatalogGameFromContrib): + game_file = "vd.nfg" + + +class W_ex1(CatalogGameFromContrib): + game_file = "w_ex1.efg" + + +class W_ex2(CatalogGameFromContrib): + game_file = "w_ex2.efg" + + +class Wilson1(CatalogGameFromContrib): + game_file = "wilson1.efg" + + +class Wink3(CatalogGameFromContrib): + game_file = "wink3.nfg" + + +class Winkels(CatalogGameFromContrib): + game_file = "winkels.nfg" + + +class Work1(CatalogGameFromContrib): + game_file = "work1.efg" + + +class Work2(CatalogGameFromContrib): + game_file = "work2.efg" + + +class Work3(CatalogGameFromContrib): + game_file = "work3.efg" + + +class Yamamoto(CatalogGameFromContrib): + game_file = "yamamoto.nfg" + + +class Zero(CatalogGameFromContrib): + game_file = "zero.nfg" From 391525a0b8bd1960e1e592797a8da42bfd524492 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 17:22:27 +0000 Subject: [PATCH 47/76] remove generated file from git --- .../catalog/generated_catalog_games.py | 581 ------------------ 1 file changed, 581 deletions(-) delete mode 100644 src/pygambit/catalog/generated_catalog_games.py diff --git a/src/pygambit/catalog/generated_catalog_games.py b/src/pygambit/catalog/generated_catalog_games.py deleted file mode 100644 index 19a1d5572..000000000 --- a/src/pygambit/catalog/generated_catalog_games.py +++ /dev/null @@ -1,581 +0,0 @@ -from .catalog_game import CatalogGameFromContrib - - -class Game2s2x2x2(CatalogGameFromContrib): - game_file = "2s2x2x2.efg" - - -class Game2smp(CatalogGameFromContrib): - game_file = "2smp.efg" - - -class Game2x2(CatalogGameFromContrib): - game_file = "2x2.nfg" - - -class Game2x2a(CatalogGameFromContrib): - game_file = "2x2a.nfg" - - -class Game2x2const(CatalogGameFromContrib): - game_file = "2x2const.nfg" - - -class Game2x2x2_nau(CatalogGameFromContrib): - game_file = "2x2x2-nau.nfg" - - -class Game2x2x2(CatalogGameFromContrib): - game_file = "2x2x2.efg" - - -class Game2x2x2NFG(CatalogGameFromContrib): - game_file = "2x2x2.nfg" - - -class Game2x2x2x2(CatalogGameFromContrib): - game_file = "2x2x2x2.nfg" - - -class Game2x2x2x2x2(CatalogGameFromContrib): - game_file = "2x2x2x2x2.nfg" - - -class Game3x3x3(CatalogGameFromContrib): - game_file = "3x3x3.nfg" - - -class Game4cards(CatalogGameFromContrib): - game_file = "4cards.efg" - - -class Game5x4x3(CatalogGameFromContrib): - game_file = "5x4x3.nfg" - - -class Game8x2x2(CatalogGameFromContrib): - game_file = "8x2x2.nfg" - - -class Game8x8(CatalogGameFromContrib): - game_file = "8x8.nfg" - - -class Artist1(CatalogGameFromContrib): - game_file = "artist1.efg" - - -class Artist2(CatalogGameFromContrib): - game_file = "artist2.efg" - - -class Badgame1(CatalogGameFromContrib): - game_file = "badgame1.efg" - - -class Badgame2(CatalogGameFromContrib): - game_file = "badgame2.efg" - - -class Bagwell(CatalogGameFromContrib): - game_file = "bagwell.efg" - - -class Bayes1a(CatalogGameFromContrib): - game_file = "bayes1a.efg" - - -class Bayes2a(CatalogGameFromContrib): - game_file = "bayes2a.efg" - - -class Bcp2(CatalogGameFromContrib): - game_file = "bcp2.efg" - - -class Bcp3(CatalogGameFromContrib): - game_file = "bcp3.efg" - - -class Bcp4(CatalogGameFromContrib): - game_file = "bcp4.efg" - - -class Bhg1(CatalogGameFromContrib): - game_file = "bhg1.efg" - - -class Bhg2(CatalogGameFromContrib): - game_file = "bhg2.efg" - - -class Bhg3(CatalogGameFromContrib): - game_file = "bhg3.efg" - - -class Bhg4(CatalogGameFromContrib): - game_file = "bhg4.efg" - - -class Bhg5(CatalogGameFromContrib): - game_file = "bhg5.efg" - - -class Caro2(CatalogGameFromContrib): - game_file = "caro2.efg" - - -class Cent2(CatalogGameFromContrib): - game_file = "cent2.efg" - - -class Cent2NFG(CatalogGameFromContrib): - game_file = "cent2.nfg" - - -class Cent3(CatalogGameFromContrib): - game_file = "cent3.efg" - - -class Cent4(CatalogGameFromContrib): - game_file = "cent4.efg" - - -class Cent6(CatalogGameFromContrib): - game_file = "cent6.efg" - - -class Centcs10(CatalogGameFromContrib): - game_file = "centcs10.efg" - - -class Centcs6(CatalogGameFromContrib): - game_file = "centcs6.efg" - - -class Condjury(CatalogGameFromContrib): - game_file = "condjury.efg" - - -class Coord2(CatalogGameFromContrib): - game_file = "coord2.efg" - - -class Coord2NFG(CatalogGameFromContrib): - game_file = "coord2.nfg" - - -class Coord2ts(CatalogGameFromContrib): - game_file = "coord2ts.efg" - - -class Coord3(CatalogGameFromContrib): - game_file = "coord3.efg" - - -class Coord3NFG(CatalogGameFromContrib): - game_file = "coord3.nfg" - - -class Coord333(CatalogGameFromContrib): - game_file = "coord333.nfg" - - -class Coord4(CatalogGameFromContrib): - game_file = "coord4.efg" - - -class Coord4NFG(CatalogGameFromContrib): - game_file = "coord4.nfg" - - -class Cross(CatalogGameFromContrib): - game_file = "cross.efg" - - -class Cs(CatalogGameFromContrib): - game_file = "cs.efg" - - -class Csg1(CatalogGameFromContrib): - game_file = "csg1.nfg" - - -class Csg2(CatalogGameFromContrib): - game_file = "csg2.nfg" - - -class Csg3(CatalogGameFromContrib): - game_file = "csg3.nfg" - - -class Csg4(CatalogGameFromContrib): - game_file = "csg4.nfg" - - -class Deg1(CatalogGameFromContrib): - game_file = "deg1.nfg" - - -class Deg2(CatalogGameFromContrib): - game_file = "deg2.nfg" - - -class E01(CatalogGameFromContrib): - game_file = "e01.efg" - - -class E01NFG(CatalogGameFromContrib): - game_file = "e01.nfg" - - -class E02(CatalogGameFromContrib): - game_file = "e02.efg" - - -class E02NFG(CatalogGameFromContrib): - game_file = "e02.nfg" - - -class E03(CatalogGameFromContrib): - game_file = "e03.efg" - - -class E04(CatalogGameFromContrib): - game_file = "e04.efg" - - -class E04NFG(CatalogGameFromContrib): - game_file = "e04.nfg" - - -class E05(CatalogGameFromContrib): - game_file = "e05.efg" - - -class E06(CatalogGameFromContrib): - game_file = "e06.efg" - - -class E07(CatalogGameFromContrib): - game_file = "e07.efg" - - -class E07NFG(CatalogGameFromContrib): - game_file = "e07.nfg" - - -class E08(CatalogGameFromContrib): - game_file = "e08.efg" - - -class E09(CatalogGameFromContrib): - game_file = "e09.efg" - - -class E10(CatalogGameFromContrib): - game_file = "e10.efg" - - -class E10a(CatalogGameFromContrib): - game_file = "e10a.efg" - - -class E13(CatalogGameFromContrib): - game_file = "e13.efg" - - -class E16(CatalogGameFromContrib): - game_file = "e16.efg" - - -class E17(CatalogGameFromContrib): - game_file = "e17.efg" - - -class E18(CatalogGameFromContrib): - game_file = "e18.efg" - - -class G1(CatalogGameFromContrib): - game_file = "g1.efg" - - -class G1NFG(CatalogGameFromContrib): - game_file = "g1.nfg" - - -class G2(CatalogGameFromContrib): - game_file = "g2.efg" - - -class G2NFG(CatalogGameFromContrib): - game_file = "g2.nfg" - - -class G3(CatalogGameFromContrib): - game_file = "g3.efg" - - -class G3NFG(CatalogGameFromContrib): - game_file = "g3.nfg" - - -class Holdout(CatalogGameFromContrib): - game_file = "holdout.efg" - - -class Holdout7(CatalogGameFromContrib): - game_file = "holdout7.efg" - - -class Hs1(CatalogGameFromContrib): - game_file = "hs1.efg" - - -class Jury_mr(CatalogGameFromContrib): - game_file = "jury_mr.efg" - - -class Jury_un(CatalogGameFromContrib): - game_file = "jury_un.efg" - - -class Km1(CatalogGameFromContrib): - game_file = "km1.efg" - - -class Km2(CatalogGameFromContrib): - game_file = "km2.efg" - - -class Km3(CatalogGameFromContrib): - game_file = "km3.efg" - - -class Km6(CatalogGameFromContrib): - game_file = "km6.efg" - - -class Loopback(CatalogGameFromContrib): - game_file = "loopback.nfg" - - -class Mixdom(CatalogGameFromContrib): - game_file = "mixdom.nfg" - - -class Mixdom2(CatalogGameFromContrib): - game_file = "mixdom2.nfg" - - -class Montyhal(CatalogGameFromContrib): - game_file = "montyhal.efg" - - -class My_2_1(CatalogGameFromContrib): - game_file = "my_2-1.efg" - - -class My_2_4(CatalogGameFromContrib): - game_file = "my_2-4.efg" - - -class My_2_8(CatalogGameFromContrib): - game_file = "my_2-8.efg" - - -class My_3_3a(CatalogGameFromContrib): - game_file = "my_3-3a.efg" - - -class My_3_3b(CatalogGameFromContrib): - game_file = "my_3-3b.efg" - - -class My_3_3c(CatalogGameFromContrib): - game_file = "my_3-3c.efg" - - -class My_3_3d(CatalogGameFromContrib): - game_file = "my_3-3d.efg" - - -class My_3_3e(CatalogGameFromContrib): - game_file = "my_3-3e.efg" - - -class My_3_4(CatalogGameFromContrib): - game_file = "my_3-4.efg" - - -class Myerson(CatalogGameFromContrib): - game_file = "myerson.efg" - - -class Myerson_fig_4_2(CatalogGameFromContrib): - game_file = "myerson_fig_4_2.efg" - - -class Nim(CatalogGameFromContrib): - game_file = "nim.efg" - - -class Nim7(CatalogGameFromContrib): - game_file = "nim7.efg" - - -class Oneill(CatalogGameFromContrib): - game_file = "oneill.nfg" - - -class Palf(CatalogGameFromContrib): - game_file = "palf.efg" - - -class Palf2(CatalogGameFromContrib): - game_file = "palf2.efg" - - -class Palf3(CatalogGameFromContrib): - game_file = "palf3.efg" - - -class Pbride(CatalogGameFromContrib): - game_file = "pbride.efg" - - -class Pd(CatalogGameFromContrib): - game_file = "pd.nfg" - - -class Perfect1(CatalogGameFromContrib): - game_file = "perfect1.nfg" - - -class Perfect2(CatalogGameFromContrib): - game_file = "perfect2.nfg" - - -class Perfect3(CatalogGameFromContrib): - game_file = "perfect3.nfg" - - -class Poker(CatalogGameFromContrib): - game_file = "poker.efg" - - -class PokerNFG(CatalogGameFromContrib): - game_file = "poker.nfg" - - -class Poker2(CatalogGameFromContrib): - game_file = "poker2.efg" - - -class Pvw(CatalogGameFromContrib): - game_file = "pvw.efg" - - -class Pvw2(CatalogGameFromContrib): - game_file = "pvw2.efg" - - -class Sh3(CatalogGameFromContrib): - game_file = "sh3.efg" - - -class Sh3NFG(CatalogGameFromContrib): - game_file = "sh3.nfg" - - -class Spence(CatalogGameFromContrib): - game_file = "spence.efg" - - -class Stengel(CatalogGameFromContrib): - game_file = "stengel.nfg" - - -class Sww1(CatalogGameFromContrib): - game_file = "sww1.efg" - - -class Sww1NFG(CatalogGameFromContrib): - game_file = "sww1.nfg" - - -class Sww2(CatalogGameFromContrib): - game_file = "sww2.efg" - - -class Sww3(CatalogGameFromContrib): - game_file = "sww3.efg" - - -class Tim(CatalogGameFromContrib): - game_file = "tim.efg" - - -class Todd1(CatalogGameFromContrib): - game_file = "todd1.nfg" - - -class Todd2(CatalogGameFromContrib): - game_file = "todd2.nfg" - - -class Todd3(CatalogGameFromContrib): - game_file = "todd3.nfg" - - -class Ttt(CatalogGameFromContrib): - game_file = "ttt.efg" - - -class Vd(CatalogGameFromContrib): - game_file = "vd.efg" - - -class VdNFG(CatalogGameFromContrib): - game_file = "vd.nfg" - - -class W_ex1(CatalogGameFromContrib): - game_file = "w_ex1.efg" - - -class W_ex2(CatalogGameFromContrib): - game_file = "w_ex2.efg" - - -class Wilson1(CatalogGameFromContrib): - game_file = "wilson1.efg" - - -class Wink3(CatalogGameFromContrib): - game_file = "wink3.nfg" - - -class Winkels(CatalogGameFromContrib): - game_file = "winkels.nfg" - - -class Work1(CatalogGameFromContrib): - game_file = "work1.efg" - - -class Work2(CatalogGameFromContrib): - game_file = "work2.efg" - - -class Work3(CatalogGameFromContrib): - game_file = "work3.efg" - - -class Yamamoto(CatalogGameFromContrib): - game_file = "yamamoto.nfg" - - -class Zero(CatalogGameFromContrib): - game_file = "zero.nfg" From 5e99f3b66bec812ad5f68bb732912bdfcd668b98 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Wed, 14 Jan 2026 17:24:18 +0000 Subject: [PATCH 48/76] add generated_catalog_games.py to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9d37e0d3b..9b2c46516 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ Gambit.app/* *.ef build_support/msw/gambit.wxs build_support/osx/Info.plist +src/pygambit/catalog/generated_catalog_games.py From b832ec416a9ed364c825ea5ebad17399c74feaa9 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 15 Jan 2026 10:40:40 +0000 Subject: [PATCH 49/76] create catalog.yml instead of directly creating classes --- src/pygambit/catalog/catalog.yml | 579 ++++++++++++++++++++++ src/pygambit/catalog/get_contrib_games.py | 12 +- 2 files changed, 584 insertions(+), 7 deletions(-) create mode 100644 src/pygambit/catalog/catalog.yml diff --git a/src/pygambit/catalog/catalog.yml b/src/pygambit/catalog/catalog.yml new file mode 100644 index 000000000..0cdac1729 --- /dev/null +++ b/src/pygambit/catalog/catalog.yml @@ -0,0 +1,579 @@ +Game2s2x2x2: + file: "2s2x2x2.efg" + metadata: + +Game2smp: + file: "2smp.efg" + metadata: + +Game2x2: + file: "2x2.nfg" + metadata: + +Game2x2a: + file: "2x2a.nfg" + metadata: + +Game2x2const: + file: "2x2const.nfg" + metadata: + +Game2x2x2_nau: + file: "2x2x2-nau.nfg" + metadata: + +Game2x2x2: + file: "2x2x2.efg" + metadata: + +Game2x2x2NFG: + file: "2x2x2.nfg" + metadata: + +Game2x2x2x2: + file: "2x2x2x2.nfg" + metadata: + +Game2x2x2x2x2: + file: "2x2x2x2x2.nfg" + metadata: + +Game3x3x3: + file: "3x3x3.nfg" + metadata: + +Game4cards: + file: "4cards.efg" + metadata: + +Game5x4x3: + file: "5x4x3.nfg" + metadata: + +Game8x2x2: + file: "8x2x2.nfg" + metadata: + +Game8x8: + file: "8x8.nfg" + metadata: + +Artist1: + file: "artist1.efg" + metadata: + +Artist2: + file: "artist2.efg" + metadata: + +Badgame1: + file: "badgame1.efg" + metadata: + +Badgame2: + file: "badgame2.efg" + metadata: + +Bagwell: + file: "bagwell.efg" + metadata: + +Bayes1a: + file: "bayes1a.efg" + metadata: + +Bayes2a: + file: "bayes2a.efg" + metadata: + +Bcp2: + file: "bcp2.efg" + metadata: + +Bcp3: + file: "bcp3.efg" + metadata: + +Bcp4: + file: "bcp4.efg" + metadata: + +Bhg1: + file: "bhg1.efg" + metadata: + +Bhg2: + file: "bhg2.efg" + metadata: + +Bhg3: + file: "bhg3.efg" + metadata: + +Bhg4: + file: "bhg4.efg" + metadata: + +Bhg5: + file: "bhg5.efg" + metadata: + +Caro2: + file: "caro2.efg" + metadata: + +Cent2: + file: "cent2.efg" + metadata: + +Cent2NFG: + file: "cent2.nfg" + metadata: + +Cent3: + file: "cent3.efg" + metadata: + +Cent4: + file: "cent4.efg" + metadata: + +Cent6: + file: "cent6.efg" + metadata: + +Centcs10: + file: "centcs10.efg" + metadata: + +Centcs6: + file: "centcs6.efg" + metadata: + +Condjury: + file: "condjury.efg" + metadata: + +Coord2: + file: "coord2.efg" + metadata: + +Coord2NFG: + file: "coord2.nfg" + metadata: + +Coord2ts: + file: "coord2ts.efg" + metadata: + +Coord3: + file: "coord3.efg" + metadata: + +Coord3NFG: + file: "coord3.nfg" + metadata: + +Coord333: + file: "coord333.nfg" + metadata: + +Coord4: + file: "coord4.efg" + metadata: + +Coord4NFG: + file: "coord4.nfg" + metadata: + +Cross: + file: "cross.efg" + metadata: + +Cs: + file: "cs.efg" + metadata: + +Csg1: + file: "csg1.nfg" + metadata: + +Csg2: + file: "csg2.nfg" + metadata: + +Csg3: + file: "csg3.nfg" + metadata: + +Csg4: + file: "csg4.nfg" + metadata: + +Deg1: + file: "deg1.nfg" + metadata: + +Deg2: + file: "deg2.nfg" + metadata: + +E01: + file: "e01.efg" + metadata: + +E01NFG: + file: "e01.nfg" + metadata: + +E02: + file: "e02.efg" + metadata: + +E02NFG: + file: "e02.nfg" + metadata: + +E03: + file: "e03.efg" + metadata: + +E04: + file: "e04.efg" + metadata: + +E04NFG: + file: "e04.nfg" + metadata: + +E05: + file: "e05.efg" + metadata: + +E06: + file: "e06.efg" + metadata: + +E07: + file: "e07.efg" + metadata: + +E07NFG: + file: "e07.nfg" + metadata: + +E08: + file: "e08.efg" + metadata: + +E09: + file: "e09.efg" + metadata: + +E10: + file: "e10.efg" + metadata: + +E10a: + file: "e10a.efg" + metadata: + +E13: + file: "e13.efg" + metadata: + +E16: + file: "e16.efg" + metadata: + +E17: + file: "e17.efg" + metadata: + +E18: + file: "e18.efg" + metadata: + +G1: + file: "g1.efg" + metadata: + +G1NFG: + file: "g1.nfg" + metadata: + +G2: + file: "g2.efg" + metadata: + +G2NFG: + file: "g2.nfg" + metadata: + +G3: + file: "g3.efg" + metadata: + +G3NFG: + file: "g3.nfg" + metadata: + +Holdout: + file: "holdout.efg" + metadata: + +Holdout7: + file: "holdout7.efg" + metadata: + +Hs1: + file: "hs1.efg" + metadata: + +Jury_mr: + file: "jury_mr.efg" + metadata: + +Jury_un: + file: "jury_un.efg" + metadata: + +Km1: + file: "km1.efg" + metadata: + +Km2: + file: "km2.efg" + metadata: + +Km3: + file: "km3.efg" + metadata: + +Km6: + file: "km6.efg" + metadata: + +Loopback: + file: "loopback.nfg" + metadata: + +Mixdom: + file: "mixdom.nfg" + metadata: + +Mixdom2: + file: "mixdom2.nfg" + metadata: + +Montyhal: + file: "montyhal.efg" + metadata: + +My_2_1: + file: "my_2-1.efg" + metadata: + +My_2_4: + file: "my_2-4.efg" + metadata: + +My_2_8: + file: "my_2-8.efg" + metadata: + +My_3_3a: + file: "my_3-3a.efg" + metadata: + +My_3_3b: + file: "my_3-3b.efg" + metadata: + +My_3_3c: + file: "my_3-3c.efg" + metadata: + +My_3_3d: + file: "my_3-3d.efg" + metadata: + +My_3_3e: + file: "my_3-3e.efg" + metadata: + +My_3_4: + file: "my_3-4.efg" + metadata: + +Myerson: + file: "myerson.efg" + metadata: + +Myerson_fig_4_2: + file: "myerson_fig_4_2.efg" + metadata: + +Nim: + file: "nim.efg" + metadata: + +Nim7: + file: "nim7.efg" + metadata: + +Oneill: + file: "oneill.nfg" + metadata: + +Palf: + file: "palf.efg" + metadata: + +Palf2: + file: "palf2.efg" + metadata: + +Palf3: + file: "palf3.efg" + metadata: + +Pbride: + file: "pbride.efg" + metadata: + +Pd: + file: "pd.nfg" + metadata: + +Perfect1: + file: "perfect1.nfg" + metadata: + +Perfect2: + file: "perfect2.nfg" + metadata: + +Perfect3: + file: "perfect3.nfg" + metadata: + +Poker: + file: "poker.efg" + metadata: + +PokerNFG: + file: "poker.nfg" + metadata: + +Poker2: + file: "poker2.efg" + metadata: + +Pvw: + file: "pvw.efg" + metadata: + +Pvw2: + file: "pvw2.efg" + metadata: + +Sh3: + file: "sh3.efg" + metadata: + +Sh3NFG: + file: "sh3.nfg" + metadata: + +Spence: + file: "spence.efg" + metadata: + +Stengel: + file: "stengel.nfg" + metadata: + +Sww1: + file: "sww1.efg" + metadata: + +Sww1NFG: + file: "sww1.nfg" + metadata: + +Sww2: + file: "sww2.efg" + metadata: + +Sww3: + file: "sww3.efg" + metadata: + +Tim: + file: "tim.efg" + metadata: + +Todd1: + file: "todd1.nfg" + metadata: + +Todd2: + file: "todd2.nfg" + metadata: + +Todd3: + file: "todd3.nfg" + metadata: + +Ttt: + file: "ttt.efg" + metadata: + +Vd: + file: "vd.efg" + metadata: + +VdNFG: + file: "vd.nfg" + metadata: + +W_ex1: + file: "w_ex1.efg" + metadata: + +W_ex2: + file: "w_ex2.efg" + metadata: + +Wilson1: + file: "wilson1.efg" + metadata: + +Wink3: + file: "wink3.nfg" + metadata: + +Winkels: + file: "winkels.nfg" + metadata: + +Work1: + file: "work1.efg" + metadata: + +Work2: + file: "work2.efg" + metadata: + +Work3: + file: "work3.efg" + metadata: + +Yamamoto: + file: "yamamoto.nfg" + metadata: + +Zero: + file: "zero.nfg" + metadata: diff --git a/src/pygambit/catalog/get_contrib_games.py b/src/pygambit/catalog/get_contrib_games.py index 03408ed6d..c0321bdcb 100644 --- a/src/pygambit/catalog/get_contrib_games.py +++ b/src/pygambit/catalog/get_contrib_games.py @@ -2,7 +2,7 @@ from pathlib import Path -OUTPUT_FILE = "generated_catalog_games.py" +OUTPUT_FILE = "catalog.yml" _GAMEFILES_DIR = Path(__file__).parent.parent.parent.parent / "contrib/games" @@ -34,8 +34,6 @@ def make_class_name(filename: str) -> str: all_files = sorted(efg_files + nfg_files) lines = [] - lines.append("from .catalog_game import CatalogGameFromContrib\n\n") - class_names = [] for path in all_files: stem = path.stem @@ -46,13 +44,13 @@ def make_class_name(filename: str) -> str: class_name += path.suffix.split(".")[-1].upper() class_names.append(class_name) - lines.append(f"class {class_name}(CatalogGameFromContrib):") - lines.append(f' game_file = "{path.name}"') - lines.append("\n") + lines.append(f"{class_name}:") + lines.append(f' file: "{path.name}"') + lines.append(" metadata:\n") output_path = Path(__file__).parent / OUTPUT_FILE output_path.write_text("\n".join(lines), encoding="utf-8") - print(f"Generated {len(all_files)} classes") + print(f"Generated {len(all_files)} new entries to the catalog") print(f"Output written to: {output_path}") print("Done.") From a058c3f4e6f4c17c8ff2f9c9e73dd04c329c9d81 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 15 Jan 2026 11:08:05 +0000 Subject: [PATCH 50/76] Dynamically generate CatalogGameFromContrib subclasses from YAML --- doc/tutorials/01_quickstart.ipynb | 8 +- src/pygambit/catalog/__init__.py | 28 +- src/pygambit/catalog/catalog.py | 602 ++---------------------------- src/pygambit/catalog/catalog.yml | 8 +- 4 files changed, 48 insertions(+), 598 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index 371721aa9..c6780471e 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -495,7 +495,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 14, "id": "6db7a29a", "metadata": {}, "outputs": [ @@ -509,7 +509,7 @@ "Game(title='Two person Prisoner's Dilemma game')" ] }, - "execution_count": 15, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -535,7 +535,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 15, "id": "f58eaa77", "metadata": {}, "outputs": [], @@ -553,7 +553,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 16, "id": "4119a2ac", "metadata": {}, "outputs": [], diff --git a/src/pygambit/catalog/__init__.py b/src/pygambit/catalog/__init__.py index 52431164c..4c3385ef8 100644 --- a/src/pygambit/catalog/__init__.py +++ b/src/pygambit/catalog/__init__.py @@ -1,30 +1,20 @@ +from . import catalog from .catalog import games -# Dynamically import all catalog games +# Ensure catalog module is fully imported including all YAML-generated classes exist _all_games = games() _game_classes = {} for game_name in _all_games: - # Import each game class from catalog module - from . import catalog - _game_classes[game_name] = getattr(catalog, game_name) + try: + _game_classes[game_name] = getattr(catalog, game_name) + except AttributeError as e: + raise ImportError( + f"Catalog game '{game_name}' listed but not found in catalog module" + ) from e # Add to module namespace globals().update(_game_classes) # Build __all__ dynamically -__all__ = ["games"] + list(_all_games) # type: ignore[assignment] - -# from .catalog import ( -# OneShotTrust, -# PrisonersDilemma, -# TwoStageMatchingPennies, -# games, -# ) - -# __all__ = [ -# "games", -# "PrisonersDilemma", -# "TwoStageMatchingPennies", -# "OneShotTrust", -# ] +__all__ = ["games", *list(_all_games)] diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 18ee23895..93b4b1ad4 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -1,7 +1,10 @@ import inspect +import sys from pathlib import Path from typing import Literal +import yaml + # import numpy as np from pygambit.gambit import Game, read_efg, read_nfg @@ -146,587 +149,44 @@ def get_all_subclasses(cls): # Catalog games from files # ############################ - -class Game2s2x2x2(CatalogGameFromContrib): - game_file = "2s2x2x2.efg" - - -class Game2smp(CatalogGameFromContrib): - game_file = "2smp.efg" - - -class Game2x2(CatalogGameFromContrib): - game_file = "2x2.nfg" - - -class Game2x2a(CatalogGameFromContrib): - game_file = "2x2a.nfg" - - -class Game2x2const(CatalogGameFromContrib): - game_file = "2x2const.nfg" - - -class Game2x2x2_nau(CatalogGameFromContrib): - game_file = "2x2x2-nau.nfg" - - -class Game2x2x2(CatalogGameFromContrib): - game_file = "2x2x2.efg" - - -class Game2x2x2NFG(CatalogGameFromContrib): - game_file = "2x2x2.nfg" - - -class Game2x2x2x2(CatalogGameFromContrib): - game_file = "2x2x2x2.nfg" - - -class Game2x2x2x2x2(CatalogGameFromContrib): - game_file = "2x2x2x2x2.nfg" - - -class Game3x3x3(CatalogGameFromContrib): - game_file = "3x3x3.nfg" - - -class Game4cards(CatalogGameFromContrib): - game_file = "4cards.efg" - - -class Game5x4x3(CatalogGameFromContrib): - game_file = "5x4x3.nfg" - - -class Game8x2x2(CatalogGameFromContrib): - game_file = "8x2x2.nfg" - - -class Game8x8(CatalogGameFromContrib): - game_file = "8x8.nfg" - - -class Artist1(CatalogGameFromContrib): - game_file = "artist1.efg" - - -class Artist2(CatalogGameFromContrib): - game_file = "artist2.efg" - - -class Badgame1(CatalogGameFromContrib): - game_file = "badgame1.efg" - - -class Badgame2(CatalogGameFromContrib): - game_file = "badgame2.efg" - - -class Bagwell(CatalogGameFromContrib): - game_file = "bagwell.efg" - - -class Bayes1a(CatalogGameFromContrib): - game_file = "bayes1a.efg" - - -class Bayes2a(CatalogGameFromContrib): - game_file = "bayes2a.efg" - - -class Bcp2(CatalogGameFromContrib): - game_file = "bcp2.efg" - - -class Bcp3(CatalogGameFromContrib): - game_file = "bcp3.efg" - - -class Bcp4(CatalogGameFromContrib): - game_file = "bcp4.efg" - - -class Bhg1(CatalogGameFromContrib): - game_file = "bhg1.efg" - - -class Bhg2(CatalogGameFromContrib): - game_file = "bhg2.efg" - - -class Bhg3(CatalogGameFromContrib): - game_file = "bhg3.efg" - - -class Bhg4(CatalogGameFromContrib): - game_file = "bhg4.efg" - - -class Bhg5(CatalogGameFromContrib): - game_file = "bhg5.efg" - - -class Caro2(CatalogGameFromContrib): - game_file = "caro2.efg" - - -class Cent2(CatalogGameFromContrib): - game_file = "cent2.efg" - - -class Cent2NFG(CatalogGameFromContrib): - game_file = "cent2.nfg" - - -class Cent3(CatalogGameFromContrib): - game_file = "cent3.efg" - - -class Cent4(CatalogGameFromContrib): - game_file = "cent4.efg" - - -class Cent6(CatalogGameFromContrib): - game_file = "cent6.efg" - - -class Centcs10(CatalogGameFromContrib): - game_file = "centcs10.efg" - - -class Centcs6(CatalogGameFromContrib): - game_file = "centcs6.efg" - - -class Condjury(CatalogGameFromContrib): - game_file = "condjury.efg" - - -class Coord2(CatalogGameFromContrib): - game_file = "coord2.efg" - - -class Coord2NFG(CatalogGameFromContrib): - game_file = "coord2.nfg" - - -class Coord2ts(CatalogGameFromContrib): - game_file = "coord2ts.efg" - - -class Coord3(CatalogGameFromContrib): - game_file = "coord3.efg" - - -class Coord3NFG(CatalogGameFromContrib): - game_file = "coord3.nfg" - - -class Coord333(CatalogGameFromContrib): - game_file = "coord333.nfg" - - -class Coord4(CatalogGameFromContrib): - game_file = "coord4.efg" - - -class Coord4NFG(CatalogGameFromContrib): - game_file = "coord4.nfg" - - -class Cross(CatalogGameFromContrib): - game_file = "cross.efg" - - -class Cs(CatalogGameFromContrib): - game_file = "cs.efg" - - -class Csg1(CatalogGameFromContrib): - game_file = "csg1.nfg" - - -class Csg2(CatalogGameFromContrib): - game_file = "csg2.nfg" - - -class Csg3(CatalogGameFromContrib): - game_file = "csg3.nfg" - - -class Csg4(CatalogGameFromContrib): - game_file = "csg4.nfg" - - -class Deg1(CatalogGameFromContrib): - game_file = "deg1.nfg" - - -class Deg2(CatalogGameFromContrib): - game_file = "deg2.nfg" - - -class E01(CatalogGameFromContrib): - game_file = "e01.efg" - - -class E01NFG(CatalogGameFromContrib): - game_file = "e01.nfg" - - -class E02(CatalogGameFromContrib): - game_file = "e02.efg" - - -class E02NFG(CatalogGameFromContrib): - game_file = "e02.nfg" - - -class E03(CatalogGameFromContrib): - game_file = "e03.efg" - - -class E04(CatalogGameFromContrib): - game_file = "e04.efg" - - -class E04NFG(CatalogGameFromContrib): - game_file = "e04.nfg" - - -class E05(CatalogGameFromContrib): - game_file = "e05.efg" - - -class E06(CatalogGameFromContrib): - game_file = "e06.efg" - - -class E07(CatalogGameFromContrib): - game_file = "e07.efg" - - -class E07NFG(CatalogGameFromContrib): - game_file = "e07.nfg" - - -class E08(CatalogGameFromContrib): - game_file = "e08.efg" - - -class E09(CatalogGameFromContrib): - game_file = "e09.efg" - - -class E10(CatalogGameFromContrib): - game_file = "e10.efg" - - -class E10a(CatalogGameFromContrib): - game_file = "e10a.efg" - - -class E13(CatalogGameFromContrib): - game_file = "e13.efg" +_CATALOG_YAML = Path(__file__).parent / "catalog.yml" -class E16(CatalogGameFromContrib): - game_file = "e16.efg" +def _load_catalog_from_yaml(path: Path) -> dict[str, dict]: + if not path.exists(): + raise FileNotFoundError(f"Catalog YAML not found: {path}") + with path.open("r", encoding="utf-8") as f: + return yaml.safe_load(f) or {} -class E17(CatalogGameFromContrib): - game_file = "e17.efg" - - -class E18(CatalogGameFromContrib): - game_file = "e18.efg" - - -class G1(CatalogGameFromContrib): - game_file = "g1.efg" - - -class G1NFG(CatalogGameFromContrib): - game_file = "g1.nfg" - - -class G2(CatalogGameFromContrib): - game_file = "g2.efg" - - -class G2NFG(CatalogGameFromContrib): - game_file = "g2.nfg" - - -class G3(CatalogGameFromContrib): - game_file = "g3.efg" - - -class G3NFG(CatalogGameFromContrib): - game_file = "g3.nfg" - - -class Holdout(CatalogGameFromContrib): - game_file = "holdout.efg" - - -# Note: this EFG did not load correctly -# class Holdout7(CatalogGameFromContrib): -# game_file = "holdout7.efg" - - -class Hs1(CatalogGameFromContrib): - game_file = "hs1.efg" - - -class Jury_mr(CatalogGameFromContrib): - game_file = "jury_mr.efg" - - -class Jury_un(CatalogGameFromContrib): - game_file = "jury_un.efg" - - -class Km1(CatalogGameFromContrib): - game_file = "km1.efg" - - -class Km2(CatalogGameFromContrib): - game_file = "km2.efg" - - -class Km3(CatalogGameFromContrib): - game_file = "km3.efg" - - -class Km6(CatalogGameFromContrib): - game_file = "km6.efg" - - -class Loopback(CatalogGameFromContrib): - game_file = "loopback.nfg" - - -class Mixdom(CatalogGameFromContrib): - game_file = "mixdom.nfg" - - -class Mixdom2(CatalogGameFromContrib): - game_file = "mixdom2.nfg" - - -class Montyhal(CatalogGameFromContrib): - game_file = "montyhal.efg" - - -class My_2_1(CatalogGameFromContrib): - game_file = "my_2-1.efg" - - -class My_2_4(CatalogGameFromContrib): - game_file = "my_2-4.efg" - - -class My_2_8(CatalogGameFromContrib): - game_file = "my_2-8.efg" - - -class My_3_3a(CatalogGameFromContrib): - game_file = "my_3-3a.efg" - - -class My_3_3b(CatalogGameFromContrib): - game_file = "my_3-3b.efg" - - -class My_3_3c(CatalogGameFromContrib): - game_file = "my_3-3c.efg" - - -class My_3_3d(CatalogGameFromContrib): - game_file = "my_3-3d.efg" - - -class My_3_3e(CatalogGameFromContrib): - game_file = "my_3-3e.efg" - - -class My_3_4(CatalogGameFromContrib): - game_file = "my_3-4.efg" - - -class Myerson(CatalogGameFromContrib): - game_file = "myerson.efg" - - -class Myerson_fig_4_2(CatalogGameFromContrib): - game_file = "myerson_fig_4_2.efg" - - -class Nim(CatalogGameFromContrib): - game_file = "nim.efg" - - -class Nim7(CatalogGameFromContrib): - game_file = "nim7.efg" - - -class Oneill(CatalogGameFromContrib): - game_file = "oneill.nfg" - - -class Palf(CatalogGameFromContrib): - game_file = "palf.efg" - - -class Palf2(CatalogGameFromContrib): - game_file = "palf2.efg" - - -class Palf3(CatalogGameFromContrib): - game_file = "palf3.efg" - - -class Pbride(CatalogGameFromContrib): - game_file = "pbride.efg" - - -class PrisonersDilemma(CatalogGameFromContrib): - game_file = "pd.nfg" - - -class Perfect1(CatalogGameFromContrib): - game_file = "perfect1.nfg" - - -class Perfect2(CatalogGameFromContrib): - game_file = "perfect2.nfg" - - -class Perfect3(CatalogGameFromContrib): - game_file = "perfect3.nfg" - - -class Poker(CatalogGameFromContrib): - game_file = "poker.efg" - - -class PokerNFG(CatalogGameFromContrib): - game_file = "poker.nfg" - - -class Poker2(CatalogGameFromContrib): - game_file = "poker2.efg" - - -class Pvw(CatalogGameFromContrib): - game_file = "pvw.efg" - - -class Pvw2(CatalogGameFromContrib): - game_file = "pvw2.efg" - - -class Sh3(CatalogGameFromContrib): - game_file = "sh3.efg" - - -class Sh3NFG(CatalogGameFromContrib): - game_file = "sh3.nfg" - - -class Spence(CatalogGameFromContrib): - game_file = "spence.efg" - - -class Stengel(CatalogGameFromContrib): - game_file = "stengel.nfg" - - -class Sww1(CatalogGameFromContrib): - game_file = "sww1.efg" - - -class Sww1NFG(CatalogGameFromContrib): - game_file = "sww1.nfg" - - -class Sww2(CatalogGameFromContrib): - game_file = "sww2.efg" - - -class Sww3(CatalogGameFromContrib): - game_file = "sww3.efg" - - -class Tim(CatalogGameFromContrib): - game_file = "tim.efg" - - -class Todd1(CatalogGameFromContrib): - game_file = "todd1.nfg" - - -class Todd2(CatalogGameFromContrib): - game_file = "todd2.nfg" - - -class Todd3(CatalogGameFromContrib): - game_file = "todd3.nfg" - - -class Ttt(CatalogGameFromContrib): - game_file = "ttt.efg" - - -class Vd(CatalogGameFromContrib): - game_file = "vd.efg" - - -class VdNFG(CatalogGameFromContrib): - game_file = "vd.nfg" - - -class W_ex1(CatalogGameFromContrib): - game_file = "w_ex1.efg" - - -class W_ex2(CatalogGameFromContrib): - game_file = "w_ex2.efg" - - -class Wilson1(CatalogGameFromContrib): - game_file = "wilson1.efg" - - -class Wink3(CatalogGameFromContrib): - game_file = "wink3.nfg" - - -class Winkels(CatalogGameFromContrib): - game_file = "winkels.nfg" - - -class Work1(CatalogGameFromContrib): - game_file = "work1.efg" - - -class Work2(CatalogGameFromContrib): - game_file = "work2.efg" - +def _generate_contrib_game_classes(catalog: dict[str, dict]) -> None: + """ + Dynamically generate CatalogGameFromContrib subclasses from YAML + and attach them to this module's namespace. + """ + module = sys.modules[__name__] -class Work3(CatalogGameFromContrib): - game_file = "work3.efg" + for class_name, entry in catalog.items(): + if "file" not in entry: + raise ValueError(f"Missing 'file' for catalog entry '{class_name}'") + game_file = entry["file"] -class Yamamoto(CatalogGameFromContrib): - game_file = "yamamoto.nfg" + cls = type( + class_name, + (CatalogGameFromContrib,), + { + "game_file": game_file, + "__module__": __name__, + }, + ) + setattr(module, class_name, cls) -class Zero(CatalogGameFromContrib): - game_file = "zero.nfg" +# Generate classes at import time +_catalog_data = _load_catalog_from_yaml(_CATALOG_YAML) +_generate_contrib_game_classes(_catalog_data) ########################################## # Catalog games defined programmatically # diff --git a/src/pygambit/catalog/catalog.yml b/src/pygambit/catalog/catalog.yml index 0cdac1729..7f806c4be 100644 --- a/src/pygambit/catalog/catalog.yml +++ b/src/pygambit/catalog/catalog.yml @@ -322,9 +322,9 @@ Holdout: file: "holdout.efg" metadata: -Holdout7: - file: "holdout7.efg" - metadata: +# Holdout7: # This game does not work (ValueError: Parse error in game file: Probabilities must sum to exactly one) +# file: "holdout7.efg" +# metadata: Hs1: file: "hs1.efg" @@ -442,7 +442,7 @@ Pbride: file: "pbride.efg" metadata: -Pd: +PrisonersDilemma: file: "pd.nfg" metadata: From ebf69818f7fbd74e2a5b883bf2bcda2dffc5f2e7 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 15 Jan 2026 11:09:42 +0000 Subject: [PATCH 51/76] remove src/pygambit/catalog/generated_catalog_games.py from gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9b2c46516..9d37e0d3b 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,3 @@ Gambit.app/* *.ef build_support/msw/gambit.wxs build_support/osx/Info.plist -src/pygambit/catalog/generated_catalog_games.py From 3a3f7625097d5704709b88792f28e90e742c4906 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 15 Jan 2026 11:29:00 +0000 Subject: [PATCH 52/76] rename catalog gen script to update.py --- src/pygambit/catalog/{get_contrib_games.py => update.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/pygambit/catalog/{get_contrib_games.py => update.py} (100%) diff --git a/src/pygambit/catalog/get_contrib_games.py b/src/pygambit/catalog/update.py similarity index 100% rename from src/pygambit/catalog/get_contrib_games.py rename to src/pygambit/catalog/update.py From 2dac733305d706a64a514a7c4c7d9afe9caf7dd1 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 15 Jan 2026 12:00:51 +0000 Subject: [PATCH 53/76] update the catalog instead of overwiting it --- src/pygambit/catalog/update.py | 44 +++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/pygambit/catalog/update.py b/src/pygambit/catalog/update.py index c0321bdcb..d2514ee58 100644 --- a/src/pygambit/catalog/update.py +++ b/src/pygambit/catalog/update.py @@ -2,7 +2,9 @@ from pathlib import Path -OUTPUT_FILE = "catalog.yml" +import yaml + +_CATALOG_YAML = Path(__file__).parent / "catalog.yml" _GAMEFILES_DIR = Path(__file__).parent.parent.parent.parent / "contrib/games" @@ -28,29 +30,43 @@ def make_class_name(filename: str) -> str: efg_files = list(_GAMEFILES_DIR.rglob("*.efg")) nfg_files = list(_GAMEFILES_DIR.rglob("*.nfg")) - print(f"Found {len(efg_files)} .efg files") - print(f"Found {len(nfg_files)} .nfg files") + print(f"Found {len(efg_files)} .efg files in contrib/games") + print(f"Found {len(nfg_files)} .nfg files in contrib/games") all_files = sorted(efg_files + nfg_files) + # Get the current class names from the catalog + with open(_CATALOG_YAML, encoding="utf-8") as f: + catalog = yaml.safe_load(f) or {} + file_names = [entry["file"] for entry in catalog.values() if "file" in entry] + + # Iterate through contrib/games and update the catalog + # with new/missing entries lines = [] class_names = [] + new_entries_counter = 0 for path in all_files: stem = path.stem - class_name = make_class_name(stem) - # Avoid duplicates (some EFG and NFG have same name) + class_name = make_class_name(stem) + # Avoid duplicates by appending EFG or NFG if class_name in class_names: class_name += path.suffix.split(".")[-1].upper() - class_names.append(class_name) - lines.append(f"{class_name}:") - lines.append(f' file: "{path.name}"') - lines.append(" metadata:\n") - - output_path = Path(__file__).parent / OUTPUT_FILE - output_path.write_text("\n".join(lines), encoding="utf-8") - print(f"Generated {len(all_files)} new entries to the catalog") - print(f"Output written to: {output_path}") + # Add any new entries to the catalog + if path.name not in file_names: + lines.append(f"{class_name}:") + lines.append(f' file: "{path.name}"') + lines.append(" metadata:\n") + new_entries_counter += 1 + + # Update the yml + new_entries = yaml.safe_load("\n".join(lines)) or {} + catalog.update(new_entries) + with _CATALOG_YAML.open("w", encoding="utf-8") as f: + yaml.safe_dump(catalog, f, sort_keys=False) + + print(f"Added {new_entries_counter} new entries to the catalog") + print(f"Output written to: {_CATALOG_YAML}") print("Done.") From 2a2cafdc67bbe5d1e42e5fc167fd46814d6f04da Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 15 Jan 2026 13:30:39 +0000 Subject: [PATCH 54/76] write to catalog via dictionary --- src/pygambit/catalog/catalog.yml | 725 +++++++++++++------------------ src/pygambit/catalog/update.py | 17 +- 2 files changed, 300 insertions(+), 442 deletions(-) diff --git a/src/pygambit/catalog/catalog.yml b/src/pygambit/catalog/catalog.yml index 7f806c4be..59d04054d 100644 --- a/src/pygambit/catalog/catalog.yml +++ b/src/pygambit/catalog/catalog.yml @@ -1,579 +1,436 @@ Game2s2x2x2: - file: "2s2x2x2.efg" - metadata: - + file: 2s2x2x2.efg + metadata: null Game2smp: - file: "2smp.efg" - metadata: - + file: 2smp.efg + metadata: null Game2x2: - file: "2x2.nfg" - metadata: - + file: 2x2.nfg + metadata: null Game2x2a: - file: "2x2a.nfg" - metadata: - + file: 2x2a.nfg + metadata: null Game2x2const: - file: "2x2const.nfg" - metadata: - + file: 2x2const.nfg + metadata: null Game2x2x2_nau: - file: "2x2x2-nau.nfg" - metadata: - + file: 2x2x2-nau.nfg + metadata: null Game2x2x2: - file: "2x2x2.efg" - metadata: - + file: 2x2x2.efg + metadata: null Game2x2x2NFG: - file: "2x2x2.nfg" - metadata: - + file: 2x2x2.nfg + metadata: null Game2x2x2x2: - file: "2x2x2x2.nfg" - metadata: - + file: 2x2x2x2.nfg + metadata: null Game2x2x2x2x2: - file: "2x2x2x2x2.nfg" - metadata: - + file: 2x2x2x2x2.nfg + metadata: null Game3x3x3: - file: "3x3x3.nfg" - metadata: - + file: 3x3x3.nfg + metadata: null Game4cards: - file: "4cards.efg" - metadata: - + file: 4cards.efg + metadata: null Game5x4x3: - file: "5x4x3.nfg" - metadata: - + file: 5x4x3.nfg + metadata: null Game8x2x2: - file: "8x2x2.nfg" - metadata: - + file: 8x2x2.nfg + metadata: null Game8x8: - file: "8x8.nfg" - metadata: - + file: 8x8.nfg + metadata: null Artist1: - file: "artist1.efg" - metadata: - + file: artist1.efg + metadata: null Artist2: - file: "artist2.efg" - metadata: - + file: artist2.efg + metadata: null Badgame1: - file: "badgame1.efg" - metadata: - + file: badgame1.efg + metadata: null Badgame2: - file: "badgame2.efg" - metadata: - + file: badgame2.efg + metadata: null Bagwell: - file: "bagwell.efg" - metadata: - + file: bagwell.efg + metadata: null Bayes1a: - file: "bayes1a.efg" - metadata: - + file: bayes1a.efg + metadata: null Bayes2a: - file: "bayes2a.efg" - metadata: - + file: bayes2a.efg + metadata: null Bcp2: - file: "bcp2.efg" - metadata: - + file: bcp2.efg + metadata: null Bcp3: - file: "bcp3.efg" - metadata: - + file: bcp3.efg + metadata: null Bcp4: - file: "bcp4.efg" - metadata: - + file: bcp4.efg + metadata: null Bhg1: - file: "bhg1.efg" - metadata: - + file: bhg1.efg + metadata: null Bhg2: - file: "bhg2.efg" - metadata: - + file: bhg2.efg + metadata: null Bhg3: - file: "bhg3.efg" - metadata: - + file: bhg3.efg + metadata: null Bhg4: - file: "bhg4.efg" - metadata: - + file: bhg4.efg + metadata: null Bhg5: - file: "bhg5.efg" - metadata: - + file: bhg5.efg + metadata: null Caro2: - file: "caro2.efg" - metadata: - + file: caro2.efg + metadata: null Cent2: - file: "cent2.efg" - metadata: - + file: cent2.efg + metadata: null Cent2NFG: - file: "cent2.nfg" - metadata: - + file: cent2.nfg + metadata: null Cent3: - file: "cent3.efg" - metadata: - + file: cent3.efg + metadata: null Cent4: - file: "cent4.efg" - metadata: - + file: cent4.efg + metadata: null Cent6: - file: "cent6.efg" - metadata: - + file: cent6.efg + metadata: null Centcs10: - file: "centcs10.efg" - metadata: - + file: centcs10.efg + metadata: null Centcs6: - file: "centcs6.efg" - metadata: - + file: centcs6.efg + metadata: null Condjury: - file: "condjury.efg" - metadata: - + file: condjury.efg + metadata: null Coord2: - file: "coord2.efg" - metadata: - + file: coord2.efg + metadata: null Coord2NFG: - file: "coord2.nfg" - metadata: - + file: coord2.nfg + metadata: null Coord2ts: - file: "coord2ts.efg" - metadata: - + file: coord2ts.efg + metadata: null Coord3: - file: "coord3.efg" - metadata: - + file: coord3.efg + metadata: null Coord3NFG: - file: "coord3.nfg" - metadata: - + file: coord3.nfg + metadata: null Coord333: - file: "coord333.nfg" - metadata: - + file: coord333.nfg + metadata: null Coord4: - file: "coord4.efg" - metadata: - + file: coord4.efg + metadata: null Coord4NFG: - file: "coord4.nfg" - metadata: - + file: coord4.nfg + metadata: null Cross: - file: "cross.efg" - metadata: - + file: cross.efg + metadata: null Cs: - file: "cs.efg" - metadata: - + file: cs.efg + metadata: null Csg1: - file: "csg1.nfg" - metadata: - + file: csg1.nfg + metadata: null Csg2: - file: "csg2.nfg" - metadata: - + file: csg2.nfg + metadata: null Csg3: - file: "csg3.nfg" - metadata: - + file: csg3.nfg + metadata: null Csg4: - file: "csg4.nfg" - metadata: - + file: csg4.nfg + metadata: null Deg1: - file: "deg1.nfg" - metadata: - + file: deg1.nfg + metadata: null Deg2: - file: "deg2.nfg" - metadata: - + file: deg2.nfg + metadata: null E01: - file: "e01.efg" - metadata: - + file: e01.efg + metadata: null E01NFG: - file: "e01.nfg" - metadata: - + file: e01.nfg + metadata: null E02: - file: "e02.efg" - metadata: - + file: e02.efg + metadata: null E02NFG: - file: "e02.nfg" - metadata: - + file: e02.nfg + metadata: null E03: - file: "e03.efg" - metadata: - + file: e03.efg + metadata: null E04: - file: "e04.efg" - metadata: - + file: e04.efg + metadata: null E04NFG: - file: "e04.nfg" - metadata: - + file: e04.nfg + metadata: null E05: - file: "e05.efg" - metadata: - + file: e05.efg + metadata: null E06: - file: "e06.efg" - metadata: - + file: e06.efg + metadata: null E07: - file: "e07.efg" - metadata: - + file: e07.efg + metadata: null E07NFG: - file: "e07.nfg" - metadata: - + file: e07.nfg + metadata: null E08: - file: "e08.efg" - metadata: - + file: e08.efg + metadata: null E09: - file: "e09.efg" - metadata: - + file: e09.efg + metadata: null E10: - file: "e10.efg" - metadata: - + file: e10.efg + metadata: null E10a: - file: "e10a.efg" - metadata: - + file: e10a.efg + metadata: null E13: - file: "e13.efg" - metadata: - + file: e13.efg + metadata: null E16: - file: "e16.efg" - metadata: - + file: e16.efg + metadata: null E17: - file: "e17.efg" - metadata: - + file: e17.efg + metadata: null E18: - file: "e18.efg" - metadata: - + file: e18.efg + metadata: null G1: - file: "g1.efg" - metadata: - + file: g1.efg + metadata: null G1NFG: - file: "g1.nfg" - metadata: - + file: g1.nfg + metadata: null G2: - file: "g2.efg" - metadata: - + file: g2.efg + metadata: null G2NFG: - file: "g2.nfg" - metadata: - + file: g2.nfg + metadata: null G3: - file: "g3.efg" - metadata: - + file: g3.efg + metadata: null G3NFG: - file: "g3.nfg" - metadata: - + file: g3.nfg + metadata: null Holdout: - file: "holdout.efg" + file: holdout.efg + metadata: null +Holdout7: + file: holdout7.efg metadata: - -# Holdout7: # This game does not work (ValueError: Parse error in game file: Probabilities must sum to exactly one) -# file: "holdout7.efg" -# metadata: - + valid_game: false # This game does not work (ValueError: Parse error in game file: Probabilities must sum to exactly one) Hs1: - file: "hs1.efg" - metadata: - + file: hs1.efg + metadata: null Jury_mr: - file: "jury_mr.efg" - metadata: - + file: jury_mr.efg + metadata: null Jury_un: - file: "jury_un.efg" - metadata: - + file: jury_un.efg + metadata: null Km1: - file: "km1.efg" - metadata: - + file: km1.efg + metadata: null Km2: - file: "km2.efg" - metadata: - + file: km2.efg + metadata: null Km3: - file: "km3.efg" - metadata: - + file: km3.efg + metadata: null Km6: - file: "km6.efg" - metadata: - + file: km6.efg + metadata: null Loopback: - file: "loopback.nfg" - metadata: - + file: loopback.nfg + metadata: null Mixdom: - file: "mixdom.nfg" - metadata: - + file: mixdom.nfg + metadata: null Mixdom2: - file: "mixdom2.nfg" - metadata: - + file: mixdom2.nfg + metadata: null Montyhal: - file: "montyhal.efg" - metadata: - + file: montyhal.efg + metadata: null My_2_1: - file: "my_2-1.efg" - metadata: - + file: my_2-1.efg + metadata: null My_2_4: - file: "my_2-4.efg" - metadata: - + file: my_2-4.efg + metadata: null My_2_8: - file: "my_2-8.efg" - metadata: - + file: my_2-8.efg + metadata: null My_3_3a: - file: "my_3-3a.efg" - metadata: - + file: my_3-3a.efg + metadata: null My_3_3b: - file: "my_3-3b.efg" - metadata: - + file: my_3-3b.efg + metadata: null My_3_3c: - file: "my_3-3c.efg" - metadata: - + file: my_3-3c.efg + metadata: null My_3_3d: - file: "my_3-3d.efg" - metadata: - + file: my_3-3d.efg + metadata: null My_3_3e: - file: "my_3-3e.efg" - metadata: - + file: my_3-3e.efg + metadata: null My_3_4: - file: "my_3-4.efg" - metadata: - + file: my_3-4.efg + metadata: null Myerson: - file: "myerson.efg" - metadata: - + file: myerson.efg + metadata: null Myerson_fig_4_2: - file: "myerson_fig_4_2.efg" - metadata: - + file: myerson_fig_4_2.efg + metadata: null Nim: - file: "nim.efg" - metadata: - + file: nim.efg + metadata: null Nim7: - file: "nim7.efg" - metadata: - + file: nim7.efg + metadata: null Oneill: - file: "oneill.nfg" - metadata: - + file: oneill.nfg + metadata: null Palf: - file: "palf.efg" - metadata: - + file: palf.efg + metadata: null Palf2: - file: "palf2.efg" - metadata: - + file: palf2.efg + metadata: null Palf3: - file: "palf3.efg" - metadata: - + file: palf3.efg + metadata: null Pbride: - file: "pbride.efg" - metadata: - + file: pbride.efg + metadata: null PrisonersDilemma: - file: "pd.nfg" - metadata: - + file: pd.nfg + metadata: null Perfect1: - file: "perfect1.nfg" - metadata: - + file: perfect1.nfg + metadata: null Perfect2: - file: "perfect2.nfg" - metadata: - + file: perfect2.nfg + metadata: null Perfect3: - file: "perfect3.nfg" - metadata: - + file: perfect3.nfg + metadata: null Poker: - file: "poker.efg" - metadata: - + file: poker.efg + metadata: null PokerNFG: - file: "poker.nfg" - metadata: - + file: poker.nfg + metadata: null Poker2: - file: "poker2.efg" - metadata: - + file: poker2.efg + metadata: null Pvw: - file: "pvw.efg" - metadata: - + file: pvw.efg + metadata: null Pvw2: - file: "pvw2.efg" - metadata: - + file: pvw2.efg + metadata: null Sh3: - file: "sh3.efg" - metadata: - + file: sh3.efg + metadata: null Sh3NFG: - file: "sh3.nfg" - metadata: - + file: sh3.nfg + metadata: null Spence: - file: "spence.efg" - metadata: - + file: spence.efg + metadata: null Stengel: - file: "stengel.nfg" - metadata: - + file: stengel.nfg + metadata: null Sww1: - file: "sww1.efg" - metadata: - + file: sww1.efg + metadata: null Sww1NFG: - file: "sww1.nfg" - metadata: - + file: sww1.nfg + metadata: null Sww2: - file: "sww2.efg" - metadata: - + file: sww2.efg + metadata: null Sww3: - file: "sww3.efg" - metadata: - + file: sww3.efg + metadata: null Tim: - file: "tim.efg" - metadata: - + file: tim.efg + metadata: null Todd1: - file: "todd1.nfg" - metadata: - + file: todd1.nfg + metadata: null Todd2: - file: "todd2.nfg" - metadata: - + file: todd2.nfg + metadata: null Todd3: - file: "todd3.nfg" - metadata: - + file: todd3.nfg + metadata: null Ttt: - file: "ttt.efg" - metadata: - + file: ttt.efg + metadata: null Vd: - file: "vd.efg" - metadata: - + file: vd.efg + metadata: null VdNFG: - file: "vd.nfg" - metadata: - + file: vd.nfg + metadata: null W_ex1: - file: "w_ex1.efg" - metadata: - + file: w_ex1.efg + metadata: null W_ex2: - file: "w_ex2.efg" - metadata: - + file: w_ex2.efg + metadata: null Wilson1: - file: "wilson1.efg" - metadata: - + file: wilson1.efg + metadata: null Wink3: - file: "wink3.nfg" - metadata: - + file: wink3.nfg + metadata: null Winkels: - file: "winkels.nfg" - metadata: - + file: winkels.nfg + metadata: null Work1: - file: "work1.efg" - metadata: - + file: work1.efg + metadata: null Work2: - file: "work2.efg" - metadata: - + file: work2.efg + metadata: null Work3: - file: "work3.efg" - metadata: - + file: work3.efg + metadata: null Yamamoto: - file: "yamamoto.nfg" - metadata: - + file: yamamoto.nfg + metadata: null Zero: - file: "zero.nfg" - metadata: + file: zero.nfg + metadata: null diff --git a/src/pygambit/catalog/update.py b/src/pygambit/catalog/update.py index d2514ee58..7ecbc70e3 100644 --- a/src/pygambit/catalog/update.py +++ b/src/pygambit/catalog/update.py @@ -45,27 +45,28 @@ def make_class_name(filename: str) -> str: lines = [] class_names = [] new_entries_counter = 0 + new_entries = {} for path in all_files: stem = path.stem - class_name = make_class_name(stem) + # Avoid duplicates by appending EFG or NFG - if class_name in class_names: + if class_name in new_entries: class_name += path.suffix.split(".")[-1].upper() - class_names.append(class_name) - # Add any new entries to the catalog if path.name not in file_names: - lines.append(f"{class_name}:") - lines.append(f' file: "{path.name}"') - lines.append(" metadata:\n") + new_entries[class_name] = { + "file": path.name, + "metadata": {}, + } new_entries_counter += 1 # Update the yml new_entries = yaml.safe_load("\n".join(lines)) or {} catalog.update(new_entries) with _CATALOG_YAML.open("w", encoding="utf-8") as f: - yaml.safe_dump(catalog, f, sort_keys=False) + dumped = yaml.safe_dump(catalog, sort_keys=False, default_flow_style=False) + f.write(dumped) print(f"Added {new_entries_counter} new entries to the catalog") print(f"Output written to: {_CATALOG_YAML}") From f31203bc456fc30911a04e665ab0f3fdd187fec9 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 15 Jan 2026 16:07:23 +0000 Subject: [PATCH 55/76] refactor: update YAML handling with ruamel.yaml --- src/pygambit/catalog/catalog.yml | 288 +++++++++++++++---------------- src/pygambit/catalog/update.py | 17 +- 2 files changed, 153 insertions(+), 152 deletions(-) diff --git a/src/pygambit/catalog/catalog.yml b/src/pygambit/catalog/catalog.yml index 59d04054d..b84ae0573 100644 --- a/src/pygambit/catalog/catalog.yml +++ b/src/pygambit/catalog/catalog.yml @@ -1,436 +1,436 @@ Game2s2x2x2: file: 2s2x2x2.efg - metadata: null + metadata: Game2smp: file: 2smp.efg - metadata: null + metadata: Game2x2: file: 2x2.nfg - metadata: null + metadata: Game2x2a: file: 2x2a.nfg - metadata: null + metadata: Game2x2const: file: 2x2const.nfg - metadata: null + metadata: Game2x2x2_nau: file: 2x2x2-nau.nfg - metadata: null + metadata: Game2x2x2: file: 2x2x2.efg - metadata: null + metadata: Game2x2x2NFG: file: 2x2x2.nfg - metadata: null + metadata: Game2x2x2x2: file: 2x2x2x2.nfg - metadata: null + metadata: Game2x2x2x2x2: file: 2x2x2x2x2.nfg - metadata: null + metadata: Game3x3x3: file: 3x3x3.nfg - metadata: null + metadata: Game4cards: file: 4cards.efg - metadata: null + metadata: Game5x4x3: file: 5x4x3.nfg - metadata: null + metadata: Game8x2x2: file: 8x2x2.nfg - metadata: null + metadata: Game8x8: file: 8x8.nfg - metadata: null + metadata: Artist1: file: artist1.efg - metadata: null + metadata: Artist2: file: artist2.efg - metadata: null + metadata: Badgame1: file: badgame1.efg - metadata: null + metadata: Badgame2: file: badgame2.efg - metadata: null + metadata: Bagwell: file: bagwell.efg - metadata: null + metadata: Bayes1a: file: bayes1a.efg - metadata: null + metadata: Bayes2a: file: bayes2a.efg - metadata: null + metadata: Bcp2: file: bcp2.efg - metadata: null + metadata: Bcp3: file: bcp3.efg - metadata: null + metadata: Bcp4: file: bcp4.efg - metadata: null + metadata: Bhg1: file: bhg1.efg - metadata: null + metadata: Bhg2: file: bhg2.efg - metadata: null + metadata: Bhg3: file: bhg3.efg - metadata: null + metadata: Bhg4: file: bhg4.efg - metadata: null + metadata: Bhg5: file: bhg5.efg - metadata: null + metadata: Caro2: file: caro2.efg - metadata: null + metadata: Cent2: file: cent2.efg - metadata: null + metadata: Cent2NFG: file: cent2.nfg - metadata: null + metadata: Cent3: file: cent3.efg - metadata: null + metadata: Cent4: file: cent4.efg - metadata: null + metadata: Cent6: file: cent6.efg - metadata: null + metadata: Centcs10: file: centcs10.efg - metadata: null + metadata: Centcs6: file: centcs6.efg - metadata: null + metadata: Condjury: file: condjury.efg - metadata: null + metadata: Coord2: file: coord2.efg - metadata: null + metadata: Coord2NFG: file: coord2.nfg - metadata: null + metadata: Coord2ts: file: coord2ts.efg - metadata: null + metadata: Coord3: file: coord3.efg - metadata: null + metadata: Coord3NFG: file: coord3.nfg - metadata: null + metadata: Coord333: file: coord333.nfg - metadata: null + metadata: Coord4: file: coord4.efg - metadata: null + metadata: Coord4NFG: file: coord4.nfg - metadata: null + metadata: Cross: file: cross.efg - metadata: null + metadata: Cs: file: cs.efg - metadata: null + metadata: Csg1: file: csg1.nfg - metadata: null + metadata: Csg2: file: csg2.nfg - metadata: null + metadata: Csg3: file: csg3.nfg - metadata: null + metadata: Csg4: file: csg4.nfg - metadata: null + metadata: Deg1: file: deg1.nfg - metadata: null + metadata: Deg2: file: deg2.nfg - metadata: null + metadata: E01: file: e01.efg - metadata: null + metadata: E01NFG: file: e01.nfg - metadata: null + metadata: E02: file: e02.efg - metadata: null + metadata: E02NFG: file: e02.nfg - metadata: null + metadata: E03: file: e03.efg - metadata: null + metadata: E04: file: e04.efg - metadata: null + metadata: E04NFG: file: e04.nfg - metadata: null + metadata: E05: file: e05.efg - metadata: null + metadata: E06: file: e06.efg - metadata: null + metadata: E07: file: e07.efg - metadata: null + metadata: E07NFG: file: e07.nfg - metadata: null + metadata: E08: file: e08.efg - metadata: null + metadata: E09: file: e09.efg - metadata: null + metadata: E10: file: e10.efg - metadata: null + metadata: E10a: file: e10a.efg - metadata: null + metadata: E13: file: e13.efg - metadata: null + metadata: E16: file: e16.efg - metadata: null + metadata: E17: file: e17.efg - metadata: null + metadata: E18: file: e18.efg - metadata: null + metadata: G1: file: g1.efg - metadata: null + metadata: G1NFG: file: g1.nfg - metadata: null + metadata: G2: file: g2.efg - metadata: null + metadata: G2NFG: file: g2.nfg - metadata: null + metadata: G3: file: g3.efg - metadata: null + metadata: G3NFG: file: g3.nfg - metadata: null + metadata: Holdout: file: holdout.efg - metadata: null + metadata: Holdout7: file: holdout7.efg metadata: valid_game: false # This game does not work (ValueError: Parse error in game file: Probabilities must sum to exactly one) Hs1: file: hs1.efg - metadata: null + metadata: Jury_mr: file: jury_mr.efg - metadata: null + metadata: Jury_un: file: jury_un.efg - metadata: null + metadata: Km1: file: km1.efg - metadata: null + metadata: Km2: file: km2.efg - metadata: null + metadata: Km3: file: km3.efg - metadata: null + metadata: Km6: file: km6.efg - metadata: null + metadata: Loopback: file: loopback.nfg - metadata: null + metadata: Mixdom: file: mixdom.nfg - metadata: null + metadata: Mixdom2: file: mixdom2.nfg - metadata: null + metadata: Montyhal: file: montyhal.efg - metadata: null + metadata: My_2_1: file: my_2-1.efg - metadata: null + metadata: My_2_4: file: my_2-4.efg - metadata: null + metadata: My_2_8: file: my_2-8.efg - metadata: null + metadata: My_3_3a: file: my_3-3a.efg - metadata: null + metadata: My_3_3b: file: my_3-3b.efg - metadata: null + metadata: My_3_3c: file: my_3-3c.efg - metadata: null + metadata: My_3_3d: file: my_3-3d.efg - metadata: null + metadata: My_3_3e: file: my_3-3e.efg - metadata: null + metadata: My_3_4: file: my_3-4.efg - metadata: null + metadata: Myerson: file: myerson.efg - metadata: null + metadata: Myerson_fig_4_2: file: myerson_fig_4_2.efg - metadata: null + metadata: Nim: file: nim.efg - metadata: null + metadata: Nim7: file: nim7.efg - metadata: null + metadata: Oneill: file: oneill.nfg - metadata: null + metadata: Palf: file: palf.efg - metadata: null + metadata: Palf2: file: palf2.efg - metadata: null + metadata: Palf3: file: palf3.efg - metadata: null + metadata: Pbride: file: pbride.efg - metadata: null + metadata: PrisonersDilemma: file: pd.nfg - metadata: null + metadata: Perfect1: file: perfect1.nfg - metadata: null + metadata: Perfect2: file: perfect2.nfg - metadata: null + metadata: Perfect3: file: perfect3.nfg - metadata: null + metadata: Poker: file: poker.efg - metadata: null + metadata: PokerNFG: file: poker.nfg - metadata: null + metadata: Poker2: file: poker2.efg - metadata: null + metadata: Pvw: file: pvw.efg - metadata: null + metadata: Pvw2: file: pvw2.efg - metadata: null + metadata: Sh3: file: sh3.efg - metadata: null + metadata: Sh3NFG: file: sh3.nfg - metadata: null + metadata: Spence: file: spence.efg - metadata: null + metadata: Stengel: file: stengel.nfg - metadata: null + metadata: Sww1: file: sww1.efg - metadata: null + metadata: Sww1NFG: file: sww1.nfg - metadata: null + metadata: Sww2: file: sww2.efg - metadata: null + metadata: Sww3: file: sww3.efg - metadata: null + metadata: Tim: file: tim.efg - metadata: null + metadata: Todd1: file: todd1.nfg - metadata: null + metadata: Todd2: file: todd2.nfg - metadata: null + metadata: Todd3: file: todd3.nfg - metadata: null + metadata: Ttt: file: ttt.efg - metadata: null + metadata: Vd: file: vd.efg - metadata: null + metadata: VdNFG: file: vd.nfg - metadata: null + metadata: W_ex1: file: w_ex1.efg - metadata: null + metadata: W_ex2: file: w_ex2.efg - metadata: null + metadata: Wilson1: file: wilson1.efg - metadata: null + metadata: Wink3: file: wink3.nfg - metadata: null + metadata: Winkels: file: winkels.nfg - metadata: null + metadata: Work1: file: work1.efg - metadata: null + metadata: Work2: file: work2.efg - metadata: null + metadata: Work3: file: work3.efg - metadata: null + metadata: Yamamoto: file: yamamoto.nfg - metadata: null + metadata: Zero: file: zero.nfg - metadata: null + metadata: diff --git a/src/pygambit/catalog/update.py b/src/pygambit/catalog/update.py index 7ecbc70e3..20aeffc2a 100644 --- a/src/pygambit/catalog/update.py +++ b/src/pygambit/catalog/update.py @@ -2,7 +2,7 @@ from pathlib import Path -import yaml +from ruamel.yaml import YAML _CATALOG_YAML = Path(__file__).parent / "catalog.yml" _GAMEFILES_DIR = Path(__file__).parent.parent.parent.parent / "contrib/games" @@ -27,6 +27,11 @@ def make_class_name(filename: str) -> str: if __name__ == "__main__": + # Use ruamel.yaml to preserve comments + yaml = YAML() + yaml.preserve_quotes = True + yaml.default_flow_style = False + efg_files = list(_GAMEFILES_DIR.rglob("*.efg")) nfg_files = list(_GAMEFILES_DIR.rglob("*.nfg")) @@ -37,13 +42,11 @@ def make_class_name(filename: str) -> str: # Get the current class names from the catalog with open(_CATALOG_YAML, encoding="utf-8") as f: - catalog = yaml.safe_load(f) or {} + catalog = yaml.load(f) or {} file_names = [entry["file"] for entry in catalog.values() if "file" in entry] # Iterate through contrib/games and update the catalog # with new/missing entries - lines = [] - class_names = [] new_entries_counter = 0 new_entries = {} for path in all_files: @@ -61,12 +64,10 @@ def make_class_name(filename: str) -> str: } new_entries_counter += 1 - # Update the yml - new_entries = yaml.safe_load("\n".join(lines)) or {} + # Update the catalog catalog.update(new_entries) with _CATALOG_YAML.open("w", encoding="utf-8") as f: - dumped = yaml.safe_dump(catalog, sort_keys=False, default_flow_style=False) - f.write(dumped) + yaml.dump(catalog, f) print(f"Added {new_entries_counter} new entries to the catalog") print(f"Output written to: {_CATALOG_YAML}") From 2d3744fde3c35c48c47a1da7dbd5faeab1f6a2aa Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 15 Jan 2026 16:26:03 +0000 Subject: [PATCH 56/76] ignore games that are marked as invalid in the catalog --- doc/tutorials/01_quickstart.ipynb | 29 +++++++++++++++++++---- doc/tutorials/02_extensive_form.ipynb | 34 +++++++++++++++++++++++---- src/pygambit/catalog/catalog.py | 30 ++++++++++++++++------- 3 files changed, 75 insertions(+), 18 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index c6780471e..ee5652966 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -485,6 +485,27 @@ "gbt.catalog.games(game_type=\"nfg\", num_players=2)" ] }, + { + "cell_type": "code", + "execution_count": 14, + "id": "ff307b74-16bc-4889-b57c-fef3124ce5ca", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "''" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gbt.catalog.PrisonersDilemma.description" + ] + }, { "cell_type": "markdown", "id": "a919ddf7", @@ -495,7 +516,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "id": "6db7a29a", "metadata": {}, "outputs": [ @@ -509,7 +530,7 @@ "Game(title='Two person Prisoner's Dilemma game')" ] }, - "execution_count": 14, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -535,7 +556,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "id": "f58eaa77", "metadata": {}, "outputs": [], @@ -553,7 +574,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "id": "4119a2ac", "metadata": {}, "outputs": [], diff --git a/doc/tutorials/02_extensive_form.ipynb b/doc/tutorials/02_extensive_form.ipynb index 10e59fc5f..4fed4cfbb 100644 --- a/doc/tutorials/02_extensive_form.ipynb +++ b/doc/tutorials/02_extensive_form.ipynb @@ -1238,6 +1238,30 @@ { "cell_type": "code", "execution_count": 16, + "id": "bd8b291b-6cc6-4f9a-b5c3-0d1f4c05252b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "Game(title='One-shot trust game, after Kreps (1990)')" + ], + "text/plain": [ + "Game(title='One-shot trust game, after Kreps (1990)')" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gbt.catalog.OneShotTrust(unique_NE_variant=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, "id": "4556c0bf", "metadata": {}, "outputs": [ @@ -1416,7 +1440,7 @@ "" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -1442,7 +1466,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "id": "37c51152", "metadata": {}, "outputs": [], @@ -1460,7 +1484,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "id": "0d86a750", "metadata": {}, "outputs": [], @@ -1479,7 +1503,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "id": "1bab777f-8a0b-4f1e-9c0c-270690288243", "metadata": {}, "outputs": [], @@ -1491,7 +1515,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "id": "2b715221-e427-4092-ad2f-9f4f2b548fa4", "metadata": {}, "outputs": [], diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 93b4b1ad4..d9ec6ea6a 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -171,17 +171,29 @@ def _generate_contrib_game_classes(catalog: dict[str, dict]) -> None: raise ValueError(f"Missing 'file' for catalog entry '{class_name}'") game_file = entry["file"] + metadata = entry.get("metadata", {}) - cls = type( - class_name, - (CatalogGameFromContrib,), - { - "game_file": game_file, - "__module__": __name__, - }, - ) + # Build class attributes dict + class_attrs = { + "game_file": game_file, + "__module__": __name__, + } + + # Add metadata fields as class attributes + if metadata and "valid_game" in metadata and metadata["valid_game"] is False: + pass # Marked as invalid game, do not create class + else: + if metadata: + for key, value in metadata.items(): + class_attrs[key] = value + + cls = type( + class_name, + (CatalogGameFromContrib,), + class_attrs, + ) - setattr(module, class_name, cls) + setattr(module, class_name, cls) # Generate classes at import time From 50062a98dca623ba772f6efc1be65732ea84bce5 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Thu, 15 Jan 2026 16:44:35 +0000 Subject: [PATCH 57/76] Add ability to filter catalog games by custom metadata fields --- doc/tutorials/01_quickstart.ipynb | 41 ++++++++++++++++++++++---- src/pygambit/catalog/catalog.py | 48 ++++++++++++++++++++++++++----- src/pygambit/catalog/catalog.yml | 1 + 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/doc/tutorials/01_quickstart.ipynb b/doc/tutorials/01_quickstart.ipynb index ee5652966..354218160 100644 --- a/doc/tutorials/01_quickstart.ipynb +++ b/doc/tutorials/01_quickstart.ipynb @@ -485,9 +485,38 @@ "gbt.catalog.games(game_type=\"nfg\", num_players=2)" ] }, + { + "cell_type": "markdown", + "id": "fd3f5829-c268-451a-8bc7-28e766e7d655", + "metadata": {}, + "source": [ + "The catalog can also be searched with custom metadata filters:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "2e680610-e2c9-45bc-84bd-67006aca5174", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['PrisonersDilemma']" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gbt.catalog.games(tutorial=1)" + ] + }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "id": "ff307b74-16bc-4889-b57c-fef3124ce5ca", "metadata": {}, "outputs": [ @@ -497,7 +526,7 @@ "''" ] }, - "execution_count": 14, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -516,7 +545,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "id": "6db7a29a", "metadata": {}, "outputs": [ @@ -530,7 +559,7 @@ "Game(title='Two person Prisoner's Dilemma game')" ] }, - "execution_count": 15, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -556,7 +585,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "id": "f58eaa77", "metadata": {}, "outputs": [], @@ -574,7 +603,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "id": "4119a2ac", "metadata": {}, "outputs": [], diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index d9ec6ea6a..156410aff 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -109,9 +109,10 @@ def __init_subclass__(cls, **kwargs): def games( game_type: Literal["all", "nfg", "efg"] = "all", num_players: int | None = None, + **metadata_filters, ) -> list[str]: """ - Return a list of catalog game names. + Return a list of catalog game class names. Parameters ---------- @@ -122,23 +123,56 @@ def games( - "efg": return only extensive-form games num_players : int | None, default None If specified, only return games with the given number of players. + **metadata_filters + Additional keyword arguments to filter by metadata fields. + For example, `tutorial=1` filters for games with `tutorial: 1` in metadata. Returns ------- list[str] - List of game class names matching the specified type. + List of game class names matching the specified filters. + + Examples + -------- + >>> games(tutorial=1) # Games with a custom metadata field 'tutorial' equal to 1 + >>> games(game_type="efg", num_players=2) # 2-player extensive-form games """ def get_all_subclasses(cls): """Recursively get all subclasses.""" all_subclasses = [] for subclass in cls.__subclasses__(): - if ( - subclass.__name__ not in ["CatalogGameFromContrib"] - and (game_type == "all" or subclass.game_type == game_type) - and (num_players is None or subclass.num_players == num_players) - ): + # Check standard filters + if subclass.__name__ in ["CatalogGameFromContrib"]: + all_subclasses.extend(get_all_subclasses(subclass)) + continue + + if game_type != "all" and not hasattr(subclass, "game_type"): + all_subclasses.extend(get_all_subclasses(subclass)) + continue + + if game_type != "all" and subclass.game_type != game_type: + all_subclasses.extend(get_all_subclasses(subclass)) + continue + + if num_players is not None and not hasattr(subclass, "num_players"): + all_subclasses.extend(get_all_subclasses(subclass)) + continue + + if num_players is not None and subclass.num_players != num_players: + all_subclasses.extend(get_all_subclasses(subclass)) + continue + + # Check metadata filters + metadata_match = True + for key, value in metadata_filters.items(): + if not hasattr(subclass, key) or getattr(subclass, key) != value: + metadata_match = False + break + + if metadata_match: all_subclasses.append(subclass.__name__) + all_subclasses.extend(get_all_subclasses(subclass)) return all_subclasses diff --git a/src/pygambit/catalog/catalog.yml b/src/pygambit/catalog/catalog.yml index b84ae0573..cf9d79d65 100644 --- a/src/pygambit/catalog/catalog.yml +++ b/src/pygambit/catalog/catalog.yml @@ -335,6 +335,7 @@ Pbride: PrisonersDilemma: file: pd.nfg metadata: + tutorial: 1 Perfect1: file: perfect1.nfg metadata: From 861da2c7552c0833671fb23594066d5b283ad0e8 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 14:14:11 +0000 Subject: [PATCH 58/76] initial catalog test ideas --- tests/test_catalog.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/test_catalog.py diff --git a/tests/test_catalog.py b/tests/test_catalog.py new file mode 100644 index 000000000..63ead571b --- /dev/null +++ b/tests/test_catalog.py @@ -0,0 +1,19 @@ +# import pytest + +from pygambit.catalog import OneShotTrust + + +class TestCatalogGameBase: + """Tests for CatalogGame base class and subclassing.""" + + # This test will only work if CatalogGame added to __init__.py + # def test_catalog_game_not_instantiable(self): + # """CatalogGame should not be directly instantiable.""" + # with pytest.raises(NotImplementedError): + # gbt.catalog.CatalogGame() + + def test_custom_game_subclass_extracts_metadata(self): + """Custom CatalogGame subclasses should extract metadata from _game().""" + assert OneShotTrust.num_players == 2 + assert OneShotTrust.game_type == "efg" + assert OneShotTrust.title == "One-shot trust game, after Kreps (1990)" From 8eefc523c87acee5eda9fafe1ad64e766ee3a734 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 14:18:55 +0000 Subject: [PATCH 59/76] add test_custom_game_instantiation --- tests/test_catalog.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 63ead571b..273c1f062 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -1,6 +1,7 @@ # import pytest -from pygambit.catalog import OneShotTrust +from pygambit import Game +from pygambit.catalog import OneShotTrust, PrisonersDilemma class TestCatalogGameBase: @@ -17,3 +18,8 @@ def test_custom_game_subclass_extracts_metadata(self): assert OneShotTrust.num_players == 2 assert OneShotTrust.game_type == "efg" assert OneShotTrust.title == "One-shot trust game, after Kreps (1990)" + + def test_custom_game_instantiation(self): + """Custom CatalogGame subclasses should return Game instances.""" + assert isinstance(OneShotTrust(), Game) # from catalog.py + assert isinstance(PrisonersDilemma(), Game) # from catalog.yml From 31e6e932950c2b12ef8cd1c4dc5c40f02a395186 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 14:23:43 +0000 Subject: [PATCH 60/76] add test_catalog_py_game_with_parameters --- tests/test_catalog.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 273c1f062..2fcd6ba6a 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -19,7 +19,14 @@ def test_custom_game_subclass_extracts_metadata(self): assert OneShotTrust.game_type == "efg" assert OneShotTrust.title == "One-shot trust game, after Kreps (1990)" - def test_custom_game_instantiation(self): - """Custom CatalogGame subclasses should return Game instances.""" - assert isinstance(OneShotTrust(), Game) # from catalog.py - assert isinstance(PrisonersDilemma(), Game) # from catalog.yml + def test_catalog_yml_game_instantiation(self): + """Custom CatalogGame subclasses reading from catalog.yml should return Game instances.""" + assert isinstance(PrisonersDilemma(), Game) + + def test_catalog_py_game_with_parameters(self): + """ + Custom CatalogGame subclass from catalog.py should return Game + and support parameters. + """ + assert isinstance(OneShotTrust(unique_NE_variant=False), Game) + assert isinstance(OneShotTrust(unique_NE_variant=True), Game) From 37e37af6c68357d9f13be95afb5959676b8789b2 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 14:29:27 +0000 Subject: [PATCH 61/76] fix game caching in catalog --- src/pygambit/catalog/catalog.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 156410aff..701172955 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -22,11 +22,14 @@ class CatalogGame: num_players: int game_type: Literal["nfg", "efg"] description: str + _cached_game: Game | None = None def __new__(cls, *args, **kwargs) -> Game: """Create a game instance by calling the _game() method.""" if hasattr(cls, "_game") and cls._game is not CatalogGame._game: - return cls._game(*args, **kwargs) + if cls._cached_game is None: + cls._cached_game = cls._game(*args, **kwargs) + return cls._cached_game raise NotImplementedError("Subclasses must implement _game() method") @staticmethod @@ -69,14 +72,11 @@ class CatalogGameFromContrib(CatalogGame): """ game_file: str - _cached_game: Game | None = None def __new__(cls) -> Game: - # Return cached game if available, otherwise load it if cls._cached_game is None: cls._cached_game = cls._load_game() - # Return a fresh instance (not the cached one) - return cls._load_game() + return cls._cached_game @classmethod def _load_game(cls) -> Game: @@ -248,6 +248,7 @@ class OneShotTrust(CatalogGame): the Seller plays Abuse. """ game_type = "efg" + test_suite = True @staticmethod def _game(unique_NE_variant: bool = False): From f80b33fec12326b7dd6ae56e735896dc94f4bcea Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 14:51:28 +0000 Subject: [PATCH 62/76] simplify game caching --- src/pygambit/catalog/catalog.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 701172955..c985d8060 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -55,13 +55,9 @@ def __init_subclass__(cls, **kwargs): if cls.__name__ == "CatalogGameFromContrib" or issubclass(cls, CatalogGameFromContrib): return - # For non-file-based games, create a temporary instance to extract metadata - try: - temp_game = cls._game() - cls._extract_metadata_from_game(temp_game) - except NotImplementedError: - # Base class, skip - pass + # Load game and extract metadata immediately when class is defined + cls._cached_game = cls._game() + cls._extract_metadata_from_game(cls._cached_game) class CatalogGameFromContrib(CatalogGame): @@ -248,7 +244,7 @@ class OneShotTrust(CatalogGame): the Seller plays Abuse. """ game_type = "efg" - test_suite = True + # test_suite = True @staticmethod def _game(unique_NE_variant: bool = False): From 608bd7f5545c661f7692df81ebc24dd291ea7e8f Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 14:55:17 +0000 Subject: [PATCH 63/76] simplify error handling --- src/pygambit/catalog/catalog.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index c985d8060..5f0a6668d 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -26,11 +26,9 @@ class CatalogGame: def __new__(cls, *args, **kwargs) -> Game: """Create a game instance by calling the _game() method.""" - if hasattr(cls, "_game") and cls._game is not CatalogGame._game: - if cls._cached_game is None: - cls._cached_game = cls._game(*args, **kwargs) - return cls._cached_game - raise NotImplementedError("Subclasses must implement _game() method") + if cls._cached_game is None: + cls._cached_game = cls._game(*args, **kwargs) + return cls._cached_game @staticmethod def _game() -> Game: From e1d762759a02424c12984c81f192b750a31e051a Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 15:06:47 +0000 Subject: [PATCH 64/76] tidy _load_game func --- src/pygambit/catalog/catalog.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 5f0a6668d..4379807bc 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -75,26 +75,25 @@ def __new__(cls) -> Game: @classmethod def _load_game(cls) -> Game: """Load the game from file.""" - if cls.game_file is None: - raise NotImplementedError(f"{cls.__name__} must define 'game_file'") + if not hasattr(cls, "game_file") or cls.game_file is None: + raise TypeError(f"{cls.__name__} must define 'game_file' class attribute") - cls.game_type = cls.game_file.split(".")[-1] + game_type = cls.game_file.split(".")[-1] file_path = _GAMEFILES_DIR / cls.game_file - if cls.game_type == "nfg": + if game_type == "nfg": + cls.game_type = "nfg" return read_nfg(str(file_path)) - elif cls.game_type == "efg": + elif game_type == "efg": + cls.game_type = "efg" return read_efg(str(file_path)) else: - raise ValueError(f"Game file extension must be 'nfg' or 'efg', got '{cls.game_type}'") + raise ValueError(f"Game file extension must be 'nfg' or 'efg', got '{game_type}'") def __init_subclass__(cls, **kwargs): """Validate and extract metadata when subclass is defined.""" super().__init_subclass__(**kwargs) - if not hasattr(cls, "game_file") or cls.game_file is None: - raise TypeError(f"{cls.__name__} must define 'game_file' class attribute") - # Load game and extract metadata immediately when class is defined cls._cached_game = cls._load_game() cls._extract_metadata_from_game(cls._cached_game) From 8d57dd2dabe11a0a5c3d7e56df1564ca00bc41f8 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 15:12:00 +0000 Subject: [PATCH 65/76] assert len(OneShotTrust.description) > 0 --- tests/test_catalog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 2fcd6ba6a..36a442fc3 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -18,6 +18,7 @@ def test_custom_game_subclass_extracts_metadata(self): assert OneShotTrust.num_players == 2 assert OneShotTrust.game_type == "efg" assert OneShotTrust.title == "One-shot trust game, after Kreps (1990)" + assert len(OneShotTrust.description) > 0 def test_catalog_yml_game_instantiation(self): """Custom CatalogGame subclasses reading from catalog.yml should return Game instances.""" From 05226ef9ab32f6538ef6249c79f727e21d25ea12 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 15:28:42 +0000 Subject: [PATCH 66/76] replace OneShotTrust with TestGame in test_catalog.py --- src/pygambit/catalog/__init__.py | 10 ++++--- tests/test_catalog.py | 46 +++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/pygambit/catalog/__init__.py b/src/pygambit/catalog/__init__.py index 4c3385ef8..a26c0f3af 100644 --- a/src/pygambit/catalog/__init__.py +++ b/src/pygambit/catalog/__init__.py @@ -1,11 +1,13 @@ from . import catalog from .catalog import games -# Ensure catalog module is fully imported including all YAML-generated classes exist -_all_games = games() +# Ensure catalog module is fully imported including all YAML-generated classes +_all_catalog_classes = games() +_all_catalog_classes.append("CatalogGame") # Ensure base class is included + _game_classes = {} -for game_name in _all_games: +for game_name in _all_catalog_classes: try: _game_classes[game_name] = getattr(catalog, game_name) except AttributeError as e: @@ -17,4 +19,4 @@ globals().update(_game_classes) # Build __all__ dynamically -__all__ = ["games", *list(_all_games)] +__all__ = ["games", *list(_all_catalog_classes)] diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 36a442fc3..0b287ea74 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -1,7 +1,23 @@ -# import pytest - from pygambit import Game -from pygambit.catalog import OneShotTrust, PrisonersDilemma +from pygambit.catalog import CatalogGame, PrisonersDilemma + + +class TestGame(CatalogGame): + """ + Test game description. + """ + + game_type = "efg" + + @staticmethod + def _game(some_param: bool = False): + if some_param: + return Game.new_tree( + players=["A", "B"], title="Test game T" + ) + return Game.new_tree( + players=["A", "B"], title="Test game F" + ) class TestCatalogGameBase: @@ -15,19 +31,19 @@ class TestCatalogGameBase: def test_custom_game_subclass_extracts_metadata(self): """Custom CatalogGame subclasses should extract metadata from _game().""" - assert OneShotTrust.num_players == 2 - assert OneShotTrust.game_type == "efg" - assert OneShotTrust.title == "One-shot trust game, after Kreps (1990)" - assert len(OneShotTrust.description) > 0 - - def test_catalog_yml_game_instantiation(self): - """Custom CatalogGame subclasses reading from catalog.yml should return Game instances.""" - assert isinstance(PrisonersDilemma(), Game) + assert TestGame.num_players == 2 + assert TestGame.game_type == "efg" + assert TestGame.title == "Test game F" + assert len(TestGame.description) > 0 def test_catalog_py_game_with_parameters(self): """ - Custom CatalogGame subclass from catalog.py should return Game - and support parameters. + Custom CatalogGame subclass should return Game + and support parameters. """ - assert isinstance(OneShotTrust(unique_NE_variant=False), Game) - assert isinstance(OneShotTrust(unique_NE_variant=True), Game) + assert isinstance(TestGame(some_param=False), Game) + assert isinstance(TestGame(some_param=True), Game) + + def test_catalog_yml_game_instantiation(self): + """Custom CatalogGame subclasses reading from catalog.yml should return Game instances.""" + assert isinstance(PrisonersDilemma(), Game) From fb66b81b9c0adf7242a7b4b9982e85a3903d4aae Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 15:39:30 +0000 Subject: [PATCH 67/76] add test_catalog_game_not_instantiable --- tests/test_catalog.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 0b287ea74..7c76c4937 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -1,3 +1,5 @@ +import pytest + from pygambit import Game from pygambit.catalog import CatalogGame, PrisonersDilemma @@ -20,14 +22,13 @@ def _game(some_param: bool = False): ) -class TestCatalogGameBase: +class TestCatalogGame: """Tests for CatalogGame base class and subclassing.""" - # This test will only work if CatalogGame added to __init__.py - # def test_catalog_game_not_instantiable(self): - # """CatalogGame should not be directly instantiable.""" - # with pytest.raises(NotImplementedError): - # gbt.catalog.CatalogGame() + def test_catalog_game_not_instantiable(self): + """CatalogGame should not be directly instantiable.""" + with pytest.raises(NotImplementedError): + CatalogGame() def test_custom_game_subclass_extracts_metadata(self): """Custom CatalogGame subclasses should extract metadata from _game().""" From e6ae99be5553654c170de0437080f0541d632d0f Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 15:50:19 +0000 Subject: [PATCH 68/76] test games function filtering on efg/nfg --- tests/test_catalog.py | 65 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 7c76c4937..7936ffa7c 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -1,6 +1,6 @@ import pytest -from pygambit import Game +from pygambit import Game, catalog from pygambit.catalog import CatalogGame, PrisonersDilemma @@ -48,3 +48,66 @@ def test_catalog_py_game_with_parameters(self): def test_catalog_yml_game_instantiation(self): """Custom CatalogGame subclasses reading from catalog.yml should return Game instances.""" assert isinstance(PrisonersDilemma(), Game) + + +class TestGamesFunction: + """Tests for the games() query function.""" + + def test_games_returns_list_of_strings(self): + """games() should return a list of class name strings.""" + result = catalog.games() + assert isinstance(result, list) + assert all(isinstance(name, str) for name in result) + + def test_games_filter_by_game_type(self): + """Filtering should split games into NFG/EFG.""" + nfg_games = catalog.games(game_type="nfg") + efg_games = catalog.games(game_type="efg") + all_games = catalog.games(game_type="all") + assert len(all_games) == len(set(nfg_games + efg_games)) + +# def test_games_filter_by_num_players(self): +# """games(num_players=n) should return only n-player games.""" +# two_player_games = gbt.catalog.games(num_players=2) +# for game_name in two_player_games: +# game_class = getattr(gbt.catalog, game_name) +# assert game_class.num_players == 2 + +# def test_games_combined_filters(self): +# """games() should support combining multiple filters.""" +# result = gbt.catalog.games(game_type="nfg", num_players=2) +# assert isinstance(result, list) +# for game_name in result: +# game_class = getattr(gbt.catalog, game_name) +# assert game_class.game_type == "nfg" +# assert game_class.num_players == 2 + +# def test_games_filter_by_custom_metadata(self): +# """games() should filter by custom metadata fields.""" +# # Assuming PrisonersDilemma has tutorial: 1 in metadata +# tutorial_games = gbt.catalog.games(tutorial=1) +# assert isinstance(tutorial_games, list) +# if tutorial_games: +# for game_name in tutorial_games: +# game_class = getattr(gbt.catalog, game_name) +# assert hasattr(game_class, "tutorial") +# assert game_class.tutorial == 1 + +# def test_games_all_filter(self): +# """games(game_type='all') should return all games.""" +# all_games = gbt.catalog.games(game_type="all") +# nfg_games = gbt.catalog.games(game_type="nfg") +# efg_games = gbt.catalog.games(game_type="efg") +# # All games should be union of NFG and EFG +# assert len(all_games) == len(set(nfg_games + efg_games)) + +# def test_games_nonexistent_filter(self): +# """games() with non-matching filters should return empty list.""" +# result = gbt.catalog.games(num_players=999) +# assert result == [] + +# def test_games_excludes_base_classes(self): +# """games() should not include base classes like CatalogGameFromContrib.""" +# result = gbt.catalog.games() +# assert "CatalogGame" not in result +# assert "CatalogGameFromContrib" not in result From b8219fda73818856c8960af10119f8cb7be34b3b Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 15:51:48 +0000 Subject: [PATCH 69/76] add test_games_filter_by_num_players --- tests/test_catalog.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 7936ffa7c..c196717d7 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -66,12 +66,12 @@ def test_games_filter_by_game_type(self): all_games = catalog.games(game_type="all") assert len(all_games) == len(set(nfg_games + efg_games)) -# def test_games_filter_by_num_players(self): -# """games(num_players=n) should return only n-player games.""" -# two_player_games = gbt.catalog.games(num_players=2) -# for game_name in two_player_games: -# game_class = getattr(gbt.catalog, game_name) -# assert game_class.num_players == 2 + def test_games_filter_by_num_players(self): + """games(num_players=n) should return only n-player games.""" + two_player_games = catalog.games(num_players=3) + for game_name in two_player_games: + game_class = getattr(catalog, game_name) + assert game_class.num_players == 3 # def test_games_combined_filters(self): # """games() should support combining multiple filters.""" From 173098ffa9317ddc8bf3fbf1cb39e90c11753a3f Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 15:57:46 +0000 Subject: [PATCH 70/76] add a test for custom metadata field in catalog games func --- src/pygambit/catalog/catalog.yml | 2 +- tests/test_catalog.py | 27 ++++++++------------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/pygambit/catalog/catalog.yml b/src/pygambit/catalog/catalog.yml index cf9d79d65..277cfae7e 100644 --- a/src/pygambit/catalog/catalog.yml +++ b/src/pygambit/catalog/catalog.yml @@ -335,7 +335,7 @@ Pbride: PrisonersDilemma: file: pd.nfg metadata: - tutorial: 1 + test_suite: true Perfect1: file: perfect1.nfg metadata: diff --git a/tests/test_catalog.py b/tests/test_catalog.py index c196717d7..d0af2a431 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -73,25 +73,14 @@ def test_games_filter_by_num_players(self): game_class = getattr(catalog, game_name) assert game_class.num_players == 3 -# def test_games_combined_filters(self): -# """games() should support combining multiple filters.""" -# result = gbt.catalog.games(game_type="nfg", num_players=2) -# assert isinstance(result, list) -# for game_name in result: -# game_class = getattr(gbt.catalog, game_name) -# assert game_class.game_type == "nfg" -# assert game_class.num_players == 2 - -# def test_games_filter_by_custom_metadata(self): -# """games() should filter by custom metadata fields.""" -# # Assuming PrisonersDilemma has tutorial: 1 in metadata -# tutorial_games = gbt.catalog.games(tutorial=1) -# assert isinstance(tutorial_games, list) -# if tutorial_games: -# for game_name in tutorial_games: -# game_class = getattr(gbt.catalog, game_name) -# assert hasattr(game_class, "tutorial") -# assert game_class.tutorial == 1 + def test_games_filter_by_custom_metadata(self): + """games() should filter by custom metadata fields.""" + custom_filter_games = catalog.games(test_suite=True) + assert isinstance(custom_filter_games, list) + for game_name in custom_filter_games: + game_class = getattr(catalog, game_name) + assert hasattr(game_class, "test_suite") + assert game_class.test_suite is True # def test_games_all_filter(self): # """games(game_type='all') should return all games.""" From 3f2303ac592ffe26245a346fceeeadc8a75c884e Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 15:59:50 +0000 Subject: [PATCH 71/76] add test_games_excludes_base_classes --- tests/test_catalog.py | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/tests/test_catalog.py b/tests/test_catalog.py index d0af2a431..bce77d33c 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -82,21 +82,8 @@ def test_games_filter_by_custom_metadata(self): assert hasattr(game_class, "test_suite") assert game_class.test_suite is True -# def test_games_all_filter(self): -# """games(game_type='all') should return all games.""" -# all_games = gbt.catalog.games(game_type="all") -# nfg_games = gbt.catalog.games(game_type="nfg") -# efg_games = gbt.catalog.games(game_type="efg") -# # All games should be union of NFG and EFG -# assert len(all_games) == len(set(nfg_games + efg_games)) - -# def test_games_nonexistent_filter(self): -# """games() with non-matching filters should return empty list.""" -# result = gbt.catalog.games(num_players=999) -# assert result == [] - -# def test_games_excludes_base_classes(self): -# """games() should not include base classes like CatalogGameFromContrib.""" -# result = gbt.catalog.games() -# assert "CatalogGame" not in result -# assert "CatalogGameFromContrib" not in result + def test_games_excludes_base_classes(self): + """games() should not include base classes like CatalogGameFromContrib.""" + result = catalog.games() + assert "CatalogGame" not in result + assert "CatalogGameFromContrib" not in result From 8fb41013c7934e6796e62f2adc9a0181f0fe64c4 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 16:01:25 +0000 Subject: [PATCH 72/76] test exact description content --- tests/test_catalog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_catalog.py b/tests/test_catalog.py index bce77d33c..7cffc16b8 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -35,7 +35,7 @@ def test_custom_game_subclass_extracts_metadata(self): assert TestGame.num_players == 2 assert TestGame.game_type == "efg" assert TestGame.title == "Test game F" - assert len(TestGame.description) > 0 + assert TestGame.description == "Test game description." def test_catalog_py_game_with_parameters(self): """ From cc97d910af19f7fe31153b61d79b9d406f9f6052 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 16:05:05 +0000 Subject: [PATCH 73/76] rename TestGame to ExampleGame --- tests/test_catalog.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 7cffc16b8..40cd53fa7 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -4,7 +4,7 @@ from pygambit.catalog import CatalogGame, PrisonersDilemma -class TestGame(CatalogGame): +class ExampleGame(CatalogGame): """ Test game description. """ @@ -32,18 +32,18 @@ def test_catalog_game_not_instantiable(self): def test_custom_game_subclass_extracts_metadata(self): """Custom CatalogGame subclasses should extract metadata from _game().""" - assert TestGame.num_players == 2 - assert TestGame.game_type == "efg" - assert TestGame.title == "Test game F" - assert TestGame.description == "Test game description." + assert ExampleGame.num_players == 2 + assert ExampleGame.game_type == "efg" + assert ExampleGame.title == "Test game F" + assert ExampleGame.description == "Test game description." def test_catalog_py_game_with_parameters(self): """ Custom CatalogGame subclass should return Game and support parameters. """ - assert isinstance(TestGame(some_param=False), Game) - assert isinstance(TestGame(some_param=True), Game) + assert isinstance(ExampleGame(some_param=False), Game) + assert isinstance(ExampleGame(some_param=True), Game) def test_catalog_yml_game_instantiation(self): """Custom CatalogGame subclasses reading from catalog.yml should return Game instances.""" From bf9f0138f2ec954f5253e90780fad76824fe0c15 Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 16:26:23 +0000 Subject: [PATCH 74/76] add initial API reference for catalog module --- doc/pygambit.api.rst | 11 +++++++++++ src/pygambit/catalog/catalog.py | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/doc/pygambit.api.rst b/doc/pygambit.api.rst index ae029bae7..b3383d18d 100644 --- a/doc/pygambit.api.rst +++ b/doc/pygambit.api.rst @@ -325,3 +325,14 @@ Computation of quantal response equilibria logit_estimate LogitQREMixedStrategyFitResult LogitQREMixedBehaviorFitResult + + +Game catalog +~~~~~~~~~~~~ + +.. currentmodule:: pygambit.catalog + +.. autosummary:: + :toctree: api/ + + games diff --git a/src/pygambit/catalog/catalog.py b/src/pygambit/catalog/catalog.py index 4379807bc..fff2c1ae3 100644 --- a/src/pygambit/catalog/catalog.py +++ b/src/pygambit/catalog/catalog.py @@ -117,8 +117,8 @@ def games( num_players : int | None, default None If specified, only return games with the given number of players. **metadata_filters - Additional keyword arguments to filter by metadata fields. - For example, `tutorial=1` filters for games with `tutorial: 1` in metadata. + Additional keyword arguments to filter by catalog.yml metadata fields. + For example, `x=1` filters for games with `x: 1` in metadata. Returns ------- @@ -127,7 +127,7 @@ def games( Examples -------- - >>> games(tutorial=1) # Games with a custom metadata field 'tutorial' equal to 1 + >>> games(x=1) # Games with a custom metadata field 'x' equal to 1 >>> games(game_type="efg", num_players=2) # 2-player extensive-form games """ From 052014db6bf16a4fe22f0847655b2a1d18cc4a3e Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 16:38:55 +0000 Subject: [PATCH 75/76] use update script to update api reference doc --- doc/pygambit.api.rst | 145 +++++++++++++++++++++++++++++++++ src/pygambit/catalog/update.py | 46 +++++++++++ 2 files changed, 191 insertions(+) diff --git a/doc/pygambit.api.rst b/doc/pygambit.api.rst index b3383d18d..173e492cc 100644 --- a/doc/pygambit.api.rst +++ b/doc/pygambit.api.rst @@ -336,3 +336,148 @@ Game catalog :toctree: api/ games + Artist1 + Artist2 + Badgame1 + Badgame2 + Bagwell + Bayes1a + Bayes2a + Bcp2 + Bcp3 + Bcp4 + Bhg1 + Bhg2 + Bhg3 + Bhg4 + Bhg5 + Caro2 + Cent2 + Cent2NFG + Cent3 + Cent4 + Cent6 + Centcs10 + Centcs6 + Condjury + Coord2 + Coord2NFG + Coord2ts + Coord3 + Coord333 + Coord3NFG + Coord4 + Coord4NFG + Cross + Cs + Csg1 + Csg2 + Csg3 + Csg4 + Deg1 + Deg2 + E01 + E01NFG + E02 + E02NFG + E03 + E04 + E04NFG + E05 + E06 + E07 + E07NFG + E08 + E09 + E10 + E10a + E13 + E16 + E17 + E18 + G1 + G1NFG + G2 + G2NFG + G3 + G3NFG + Game2s2x2x2 + Game2smp + Game2x2 + Game2x2a + Game2x2const + Game2x2x2 + Game2x2x2NFG + Game2x2x2_nau + Game2x2x2x2 + Game2x2x2x2x2 + Game3x3x3 + Game4cards + Game5x4x3 + Game8x2x2 + Game8x8 + Holdout + Holdout7 + Hs1 + Jury_mr + Jury_un + Km1 + Km2 + Km3 + Km6 + Loopback + Mixdom + Mixdom2 + Montyhal + My_2_1 + My_2_4 + My_2_8 + My_3_3a + My_3_3b + My_3_3c + My_3_3d + My_3_3e + My_3_4 + Myerson + Myerson_fig_4_2 + Nim + Nim7 + Oneill + Palf + Palf2 + Palf3 + Pbride + Perfect1 + Perfect2 + Perfect3 + Poker + Poker2 + PokerNFG + PrisonersDilemma + Pvw + Pvw2 + Sh3 + Sh3NFG + Spence + Stengel + Sww1 + Sww1NFG + Sww2 + Sww3 + Tim + Todd1 + Todd2 + Todd3 + Ttt + Vd + VdNFG + W_ex1 + W_ex2 + Wilson1 + Wink3 + Winkels + Work1 + Work2 + Work3 + Yamamoto + Zero diff --git a/src/pygambit/catalog/update.py b/src/pygambit/catalog/update.py index 20aeffc2a..10f5f9c68 100644 --- a/src/pygambit/catalog/update.py +++ b/src/pygambit/catalog/update.py @@ -6,6 +6,7 @@ _CATALOG_YAML = Path(__file__).parent / "catalog.yml" _GAMEFILES_DIR = Path(__file__).parent.parent.parent.parent / "contrib/games" +_API_RST = Path(__file__).parent.parent.parent.parent / "doc/pygambit.api.rst" def make_class_name(filename: str) -> str: @@ -26,6 +27,48 @@ def make_class_name(filename: str) -> str: return name +def update_api_rst(catalog: dict[str, dict]) -> None: + """Update the Game catalog section in pygambit.api.rst with all class names.""" + with open(_API_RST, encoding="utf-8") as f: + content = f.read() + + # Find the Game catalog section + game_catalog_start = content.find("Game catalog\n~~~~~~~~~~~~") + if game_catalog_start == -1: + print("Warning: 'Game catalog' section not found in pygambit.api.rst") + return + + # Find the autosummary block + autosummary_start = content.find(".. autosummary::", game_catalog_start) + toctree_start = content.find(":toctree: api/", autosummary_start) + + # Find the next section (starts with ~~) + next_section = content.find("\n~~", toctree_start) + if next_section == -1: + next_section = len(content) + + # Build the new toctree content + class_names = sorted(catalog.keys()) + new_toctree = ".. autosummary::\n :toctree: api/\n\n games\n" + for class_name in class_names: + new_toctree += f" {class_name}\n" + + # Replace the old toctree with the new one + old_toctree_start = content.rfind(".. autosummary::", game_catalog_start, toctree_start + 100) + old_toctree_end = next_section + + new_content = ( + content[:old_toctree_start] + + new_toctree + + content[old_toctree_end:] + ) + + with open(_API_RST, "w", encoding="utf-8") as f: + f.write(new_content) + + print(f"Updated {_API_RST} with {len(class_names)} catalog games") + + if __name__ == "__main__": # Use ruamel.yaml to preserve comments yaml = YAML() @@ -69,6 +112,9 @@ def make_class_name(filename: str) -> str: with _CATALOG_YAML.open("w", encoding="utf-8") as f: yaml.dump(catalog, f) + # Update the RST documentation + update_api_rst(catalog) + print(f"Added {new_entries_counter} new entries to the catalog") print(f"Output written to: {_CATALOG_YAML}") print("Done.") From 510b135def8c23f11248180e9bc3044de3c3bafd Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Fri, 16 Jan 2026 16:58:48 +0000 Subject: [PATCH 76/76] ignore invalid games when updating API docs --- doc/pygambit.api.rst | 37 +++++++++++++++++----------------- src/pygambit/catalog/update.py | 11 ++++++---- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/doc/pygambit.api.rst b/doc/pygambit.api.rst index 173e492cc..8a83e8b8d 100644 --- a/doc/pygambit.api.rst +++ b/doc/pygambit.api.rst @@ -336,6 +336,21 @@ Game catalog :toctree: api/ games + Game2s2x2x2 + Game2smp + Game2x2 + Game2x2a + Game2x2const + Game2x2x2_nau + Game2x2x2 + Game2x2x2NFG + Game2x2x2x2 + Game2x2x2x2x2 + Game3x3x3 + Game4cards + Game5x4x3 + Game8x2x2 + Game8x8 Artist1 Artist2 Badgame1 @@ -364,8 +379,8 @@ Game catalog Coord2NFG Coord2ts Coord3 - Coord333 Coord3NFG + Coord333 Coord4 Coord4NFG Cross @@ -401,23 +416,7 @@ Game catalog G2NFG G3 G3NFG - Game2s2x2x2 - Game2smp - Game2x2 - Game2x2a - Game2x2const - Game2x2x2 - Game2x2x2NFG - Game2x2x2_nau - Game2x2x2x2 - Game2x2x2x2x2 - Game3x3x3 - Game4cards - Game5x4x3 - Game8x2x2 - Game8x8 Holdout - Holdout7 Hs1 Jury_mr Jury_un @@ -447,13 +446,13 @@ Game catalog Palf2 Palf3 Pbride + PrisonersDilemma Perfect1 Perfect2 Perfect3 Poker - Poker2 PokerNFG - PrisonersDilemma + Poker2 Pvw Pvw2 Sh3 diff --git a/src/pygambit/catalog/update.py b/src/pygambit/catalog/update.py index 10f5f9c68..cf05c4eec 100644 --- a/src/pygambit/catalog/update.py +++ b/src/pygambit/catalog/update.py @@ -48,10 +48,13 @@ def update_api_rst(catalog: dict[str, dict]) -> None: next_section = len(content) # Build the new toctree content - class_names = sorted(catalog.keys()) new_toctree = ".. autosummary::\n :toctree: api/\n\n games\n" - for class_name in class_names: - new_toctree += f" {class_name}\n" + for class_name, entry in catalog.items(): + metadata = entry.get("metadata", {}) + if metadata and "valid_game" in metadata and metadata["valid_game"] is False: + pass # Marked as invalid game, do not add class + else: + new_toctree += f" {class_name}\n" # Replace the old toctree with the new one old_toctree_start = content.rfind(".. autosummary::", game_catalog_start, toctree_start + 100) @@ -66,7 +69,7 @@ def update_api_rst(catalog: dict[str, dict]) -> None: with open(_API_RST, "w", encoding="utf-8") as f: f.write(new_content) - print(f"Updated {_API_RST} with {len(class_names)} catalog games") + print(f"Updated {_API_RST} with new catalog game names") if __name__ == "__main__":