From a9e3cbf3b18ef13e9808b4da707604a5691474ee Mon Sep 17 00:00:00 2001 From: Ed Chalstrey Date: Mon, 22 Jun 2026 14:07:27 +0100 Subject: [PATCH] refactor: rename drawing package from draw_tree to gtdraw and update tutorials accordingly --- ...ree_settings.yaml => gtdraw_settings.yaml} | 0 build_support/catalog/test_update.py | 124 +++++------ build_support/catalog/update.py | 38 ++-- doc/developer.catalog.rst | 12 +- doc/tutorials/02_extensive_form.ipynb | 60 +----- doc/tutorials/03_stripped_down_poker.ipynb | 55 +---- doc/tutorials/04_creating_images.ipynb | 200 ++---------------- .../agent_versus_non_agent_regret.ipynb | 9 +- .../openspiel.ipynb | 16 +- pyproject.toml | 1 + 10 files changed, 122 insertions(+), 393 deletions(-) rename build_support/catalog/{draw_tree_settings.yaml => gtdraw_settings.yaml} (100%) diff --git a/build_support/catalog/draw_tree_settings.yaml b/build_support/catalog/gtdraw_settings.yaml similarity index 100% rename from build_support/catalog/draw_tree_settings.yaml rename to build_support/catalog/gtdraw_settings.yaml diff --git a/build_support/catalog/test_update.py b/build_support/catalog/test_update.py index b3abfa56b..645dc25f9 100644 --- a/build_support/catalog/test_update.py +++ b/build_support/catalog/test_update.py @@ -9,10 +9,10 @@ ----------------------- ``update.py`` depends on four external resources that are replaced in tests: -1. ``DRAW_TREE_SETTINGS_CONFIG`` (a ``Path``) — swapped for a tmp YAML file so - ``catalog_draw_tree_settings`` reads controlled config without touching the - real ``draw_tree_settings.yaml``. ``monkeypatch.setattr(update, - "DRAW_TREE_SETTINGS_CONFIG", yaml_file)`` replaces the module-level path for +1. ``GTDRAW_SETTINGS_CONFIG`` (a ``Path``) — swapped for a tmp YAML file so + ``catalog_gtdraw_settings`` reads controlled config without touching the + real ``gtdraw_settings.yaml``. ``monkeypatch.setattr(update, + "GTDRAW_SETTINGS_CONFIG", yaml_file)`` replaces the module-level path for the duration of a single test and restores it automatically on teardown. 2. ``CATALOG_HIERARCHY_CONFIG`` (a ``Path``) — swapped for a tmp YAML file so @@ -20,8 +20,8 @@ ``catalog_hierarchy.yaml``. Swap via ``monkeypatch.setattr(update, "CATALOG_HIERARCHY_CONFIG", yaml_file)``. -3. ``generate_tex`` / ``generate_png`` / ``generate_pdf`` / ``generate_svg`` - (functions imported from ``draw_tree``) — replaced with no-ops or +3. ``tex`` / ``png`` / ``pdf`` / ``svg`` + (functions imported from ``gtdraw``) — replaced with no-ops or call-tracking lambdas. This lets us test RST-generation logic without actually invoking LaTeX, and lets us assert whether image generation was triggered at all. @@ -37,7 +37,7 @@ import pytest -pytest.importorskip("draw_tree") # update.py imports draw_tree at module level +pytest.importorskip("gtdraw") # update.py imports gtdraw at module level pytest.importorskip("yaml") import pandas as pd # noqa: E402 @@ -57,7 +57,7 @@ "sublevel_scaling": 0, } -# A self-contained draw_tree_settings YAML config used by settings tests. +# A self-contained gtdraw_settings YAML config used by settings tests. # Slugs are entirely fictional: # "testgroup2000" – group-level prefix covering testgroup2000/* # "othergroup1999" – group-level prefix covering othergroup1999/* @@ -88,8 +88,8 @@ def _write_yaml(path, content=_YAML_CONFIG): """Write *content* to *path* and return *path*. - Used to create a temporary draw_tree_settings YAML file that can be - pointed at via ``monkeypatch.setattr(update, "DRAW_TREE_SETTINGS_CONFIG", + Used to create a temporary gtdraw_settings YAML file that can be + pointed at via ``monkeypatch.setattr(update, "GTDRAW_SETTINGS_CONFIG", path)`` without touching the real config file. """ path.write_text(content, encoding="utf-8") @@ -130,7 +130,7 @@ def _make_image_files(catalog_dir, slug, fmt="efg"): ``generate_rst_table`` checks that all expected image files exist before deciding whether to regenerate them. Touching empty files satisfies that - check without requiring real draw_tree output, so tests that are not + check without requiring real gtdraw output, so tests that are not specifically about image generation can use this helper to set up the pre-existing-images state. @@ -148,40 +148,40 @@ def _make_image_files(catalog_dir, slug, fmt="efg"): # --------------------------------------------------------------------------- -# Tests for catalog_draw_tree_settings +# Tests for catalog_gtdraw_settings # --------------------------------------------------------------------------- @pytest.mark.catalog_update class TestCatalogDrawTreeSettings: - """Unit tests for ``catalog_draw_tree_settings(slug) -> dict``. + """Unit tests for ``catalog_gtdraw_settings(slug) -> dict``. Each test writes a temporary YAML config and redirects the module-level - ``DRAW_TREE_SETTINGS_CONFIG`` path to it via ``monkeypatch.setattr``. - This means the real ``draw_tree_settings.yaml`` is never read or modified. + ``GTDRAW_SETTINGS_CONFIG`` path to it via ``monkeypatch.setattr``. + This means the real ``gtdraw_settings.yaml`` is never read or modified. """ def test_no_override_returns_defaults(self, tmp_path, monkeypatch): """A slug with no matching entry in ``overrides`` returns the defaults verbatim.""" yaml_file = _write_yaml(tmp_path / "settings.yaml") - monkeypatch.setattr(update, "DRAW_TREE_SETTINGS_CONFIG", yaml_file) - result = update.catalog_draw_tree_settings("unknowngame/v1") + monkeypatch.setattr(update, "GTDRAW_SETTINGS_CONFIG", yaml_file) + result = update.catalog_gtdraw_settings("unknowngame/v1") assert result == _YAML_DEFAULTS def test_exact_slug_override_applied(self, tmp_path, monkeypatch): """A key in ``overrides`` that exactly matches the slug is merged into defaults.""" yaml_file = _write_yaml(tmp_path / "settings.yaml") - monkeypatch.setattr(update, "DRAW_TREE_SETTINGS_CONFIG", yaml_file) - result = update.catalog_draw_tree_settings("testgroup2000/fig2") + monkeypatch.setattr(update, "GTDRAW_SETTINGS_CONFIG", yaml_file) + result = update.catalog_gtdraw_settings("testgroup2000/fig2") assert result["action_label_position"] == pytest.approx(0.4) assert result["color_scheme"] == "gambit" # defaults still present def test_prefix_slug_override_applied(self, tmp_path, monkeypatch): """A group-level key (e.g. ``"testgroup2000"``) matches any slug that starts with it.""" yaml_file = _write_yaml(tmp_path / "settings.yaml") - monkeypatch.setattr(update, "DRAW_TREE_SETTINGS_CONFIG", yaml_file) + monkeypatch.setattr(update, "GTDRAW_SETTINGS_CONFIG", yaml_file) # "testgroup2000/fig1" is not listed explicitly; it matches the group prefix - result = update.catalog_draw_tree_settings("testgroup2000/fig1") + result = update.catalog_gtdraw_settings("testgroup2000/fig1") assert result["sublevel_scaling"] == 1 def test_specific_key_wins_over_group(self, tmp_path, monkeypatch): @@ -203,19 +203,19 @@ def test_specific_key_wins_over_group(self, tmp_path, monkeypatch): sublevel_scaling: 2 """) yaml_file = _write_yaml(tmp_path / "settings.yaml", config) - monkeypatch.setattr(update, "DRAW_TREE_SETTINGS_CONFIG", yaml_file) - result = update.catalog_draw_tree_settings("testgroup2000/fig2") + monkeypatch.setattr(update, "GTDRAW_SETTINGS_CONFIG", yaml_file) + result = update.catalog_gtdraw_settings("testgroup2000/fig2") assert result["sublevel_scaling"] == 2 def test_group_override_does_not_bleed_to_other_game(self, tmp_path, monkeypatch): """A group-level override applies only to games whose slug starts with that prefix.""" yaml_file = _write_yaml(tmp_path / "settings.yaml") - monkeypatch.setattr(update, "DRAW_TREE_SETTINGS_CONFIG", yaml_file) + monkeypatch.setattr(update, "GTDRAW_SETTINGS_CONFIG", yaml_file) # "othergroup1999" override sets shared_terminal_depth = False - result_other = update.catalog_draw_tree_settings("othergroup1999/fig1") + result_other = update.catalog_gtdraw_settings("othergroup1999/fig1") assert result_other["shared_terminal_depth"] is False # "testgroup2000" has a different override; shared_terminal_depth should be True (default) - result_test = update.catalog_draw_tree_settings("testgroup2000/fig1") + result_test = update.catalog_gtdraw_settings("testgroup2000/fig1") assert result_test["shared_terminal_depth"] is True def test_no_overrides_section_returns_defaults(self, tmp_path, monkeypatch): @@ -226,8 +226,8 @@ def test_no_overrides_section_returns_defaults(self, tmp_path, monkeypatch): sublevel_scaling: 0 """) yaml_file = _write_yaml(tmp_path / "settings.yaml", config) - monkeypatch.setattr(update, "DRAW_TREE_SETTINGS_CONFIG", yaml_file) - result = update.catalog_draw_tree_settings("anygame/v1") + monkeypatch.setattr(update, "GTDRAW_SETTINGS_CONFIG", yaml_file) + result = update.catalog_gtdraw_settings("anygame/v1") assert result == {"color_scheme": "gambit", "sublevel_scaling": 0} @@ -343,12 +343,12 @@ def test_file_without_double_underscore_excluded(self, tmp_path): class TestGenerateRstTable: """Tests for ``generate_rst_table(df, rst_path, ...)``. - Image generation (generate_tex / generate_png / etc.) is mocked out so + Image generation (tex / png / etc.) is mocked out so that tests can run without LaTeX installed and without reading from the real catalog directory. ``_mock_generates`` uses ``monkeypatch.setattr`` to replace each of the - four draw_tree generate functions in the ``update`` module's namespace with + four gtdraw functions in the ``update`` module's namespace with a no-op. Because the replacement is scoped to the test, the originals are automatically restored afterward. @@ -360,11 +360,11 @@ class TestGenerateRstTable: """ def _no_op_generate(self, *args, **kwargs): - """Stand-in for draw_tree generate_* functions; does nothing.""" + """Stand-in for gtdraw functions; does nothing.""" def _mock_generates(self, monkeypatch): - """Replace all four draw_tree image-generation functions with no-ops.""" - for name in ["generate_tex", "generate_png", "generate_pdf", "generate_svg"]: + """Replace all four gtdraw image-generation functions with no-ops.""" + for name in ["tex", "png", "pdf", "svg"]: monkeypatch.setattr(update, name, self._no_op_generate) def test_efg_row_produces_rst_with_slug_and_title(self, tmp_path, monkeypatch): @@ -380,10 +380,10 @@ def test_efg_row_produces_rst_with_slug_and_title(self, tmp_path, monkeypatch): assert "Fake Author (2000) Figure 1" in rst assert f'pygambit.catalog.load("{slug}")' in rst assert f":download:`{slug}.efg" in rst # source game file download link - assert f":download:`{slug}.ef" in rst # draw_tree intermediate file download link + assert f":download:`{slug}.ef" in rst # gtdraw intermediate file download link def test_nfg_row_produces_rst_with_save_to(self, tmp_path, monkeypatch): - """An NFG game row uses the ``save_to`` form of the draw_tree call (no .ef involved).""" + """An NFG game row uses the ``save_to`` form of the draw call (no .ef involved).""" self._mock_generates(monkeypatch) catalog_dir = tmp_path / "catalog" slug = "fakeauthor2001/matrix1" @@ -423,8 +423,8 @@ def test_row_without_description_is_skipped(self, tmp_path, monkeypatch): rst = rst_path.read_text() assert "fakeauthor2000/fig1" not in rst - def test_curated_ef_used_in_draw_tree_call(self, tmp_path, monkeypatch): - """When a curated .ef file exists alongside the .efg, the RST draw_tree call + def test_curated_ef_used_in_draw_call(self, tmp_path, monkeypatch): + """When a curated .ef file exists alongside the .efg, the RST draw call references the .ef path directly rather than ``pygambit.catalog.load``.""" self._mock_generates(monkeypatch) catalog_dir = tmp_path / "catalog" @@ -438,20 +438,20 @@ def test_curated_ef_used_in_draw_tree_call(self, tmp_path, monkeypatch): rst_path = tmp_path / "out.rst" update.generate_rst_table(df, rst_path, catalog_dir=catalog_dir) rst = rst_path.read_text() - # Find the draw_tree( call line in the jupyter-execute block - draw_tree_call = next(line for line in rst.splitlines() if "draw_tree(" in line) - assert f'"../catalog/{slug}.ef"' in draw_tree_call - assert "catalog.load" not in draw_tree_call + # Find the draw( call line in the jupyter-execute block + draw_call = next(line for line in rst.splitlines() if "draw(" in line) + assert f'"../catalog/{slug}.ef"' in draw_call + assert "catalog.load" not in draw_call def test_images_not_regenerated_when_all_exist(self, tmp_path, monkeypatch): """If all expected image files are already present and ``regenerate_images`` is - False, none of the draw_tree generate functions are called.""" + False, none of the gtdraw image-generation functions are called.""" calls = [] # Replace generate_* with lambdas that record invocations - monkeypatch.setattr(update, "generate_tex", lambda *a, **k: calls.append("tex")) - monkeypatch.setattr(update, "generate_png", lambda *a, **k: calls.append("png")) - monkeypatch.setattr(update, "generate_pdf", lambda *a, **k: calls.append("pdf")) - monkeypatch.setattr(update, "generate_svg", lambda *a, **k: calls.append("svg")) + monkeypatch.setattr(update, "tex", lambda *a, **k: calls.append("tex")) + monkeypatch.setattr(update, "png", lambda *a, **k: calls.append("png")) + monkeypatch.setattr(update, "pdf", lambda *a, **k: calls.append("pdf")) + monkeypatch.setattr(update, "svg", lambda *a, **k: calls.append("svg")) catalog_dir = tmp_path / "catalog" slug = "fakeauthor2000/fig1" _make_image_files(catalog_dir, slug, "efg") # all images already exist @@ -465,14 +465,14 @@ def test_images_regenerated_when_flag_set(self, tmp_path, monkeypatch): if the image files already exist. A curated .ef file is placed in the catalog dir so ``update.py`` uses it - as the draw_tree source rather than calling ``gbt.catalog.load``, which + as the gtdraw source rather than calling ``gbt.catalog.load``, which would require the real catalog to be present. """ calls = [] - monkeypatch.setattr(update, "generate_tex", lambda *a, **k: calls.append("tex")) - monkeypatch.setattr(update, "generate_png", lambda *a, **k: calls.append("png")) - monkeypatch.setattr(update, "generate_pdf", lambda *a, **k: calls.append("pdf")) - monkeypatch.setattr(update, "generate_svg", lambda *a, **k: calls.append("svg")) + monkeypatch.setattr(update, "tex", lambda *a, **k: calls.append("tex")) + monkeypatch.setattr(update, "png", lambda *a, **k: calls.append("png")) + monkeypatch.setattr(update, "pdf", lambda *a, **k: calls.append("pdf")) + monkeypatch.setattr(update, "svg", lambda *a, **k: calls.append("svg")) catalog_dir = tmp_path / "catalog" slug = "fakeauthor2000/fig1" _make_image_files(catalog_dir, slug, "efg") @@ -524,8 +524,8 @@ def test_multi_variant_efg_without_primary_ef_produces_tab_set(self, tmp_path, m assert ".. tab-set::" in rst assert ".. tab-item:: Default" in rst assert ".. tab-item:: Wide" in rst - assert 'draw_tree(pygambit.catalog.load("fakevariant2001/fig1")' in rst - assert 'draw_tree("../catalog/fakevariant2001/fig1__wide.ef"' in rst + assert 'draw(pygambit.catalog.load("fakevariant2001/fig1")' in rst + assert 'draw("../catalog/fakevariant2001/fig1__wide.ef"' in rst def test_single_variant_efg_produces_no_tab_set(self, tmp_path, monkeypatch): """A single curated .ef file (or no .ef file) does not produce a ``tab-set``.""" @@ -548,10 +548,10 @@ def test_per_variant_images_generated(self, tmp_path, monkeypatch): Two variants × four generate functions = eight total calls. """ calls = [] - monkeypatch.setattr(update, "generate_tex", lambda *a, **k: calls.append("tex")) - monkeypatch.setattr(update, "generate_png", lambda *a, **k: calls.append("png")) - monkeypatch.setattr(update, "generate_pdf", lambda *a, **k: calls.append("pdf")) - monkeypatch.setattr(update, "generate_svg", lambda *a, **k: calls.append("svg")) + monkeypatch.setattr(update, "tex", lambda *a, **k: calls.append("tex")) + monkeypatch.setattr(update, "png", lambda *a, **k: calls.append("png")) + monkeypatch.setattr(update, "pdf", lambda *a, **k: calls.append("pdf")) + monkeypatch.setattr(update, "svg", lambda *a, **k: calls.append("svg")) catalog_dir = tmp_path / "catalog" slug = "fakevariant2001/fig1" game_dir = catalog_dir / "fakevariant2001" @@ -567,10 +567,10 @@ def test_per_variant_images_not_regenerated_when_all_exist(self, tmp_path, monke """If all variant image files already exist and ``regenerate_images`` is False, generate functions are not called.""" calls = [] - monkeypatch.setattr(update, "generate_tex", lambda *a, **k: calls.append("tex")) - monkeypatch.setattr(update, "generate_png", lambda *a, **k: calls.append("png")) - monkeypatch.setattr(update, "generate_pdf", lambda *a, **k: calls.append("pdf")) - monkeypatch.setattr(update, "generate_svg", lambda *a, **k: calls.append("svg")) + monkeypatch.setattr(update, "tex", lambda *a, **k: calls.append("tex")) + monkeypatch.setattr(update, "png", lambda *a, **k: calls.append("png")) + monkeypatch.setattr(update, "pdf", lambda *a, **k: calls.append("pdf")) + monkeypatch.setattr(update, "svg", lambda *a, **k: calls.append("svg")) catalog_dir = tmp_path / "catalog" slug = "fakevariant2001/fig1" game_dir = catalog_dir / "fakevariant2001" @@ -658,7 +658,7 @@ class TestHierarchicalRstOutput: """Tests that ``generate_rst_table`` produces correctly nested dropdown RST.""" def _mock_generates(self, monkeypatch): - for name in ["generate_tex", "generate_png", "generate_pdf", "generate_svg"]: + for name in ["tex", "png", "pdf", "svg"]: monkeypatch.setattr(update, name, lambda *a, **k: None) def _write_hierarchy_yaml(self, tmp_path, content=_HIERARCHY_YAML): diff --git a/build_support/catalog/update.py b/build_support/catalog/update.py index ccfb77634..0cf51385e 100644 --- a/build_support/catalog/update.py +++ b/build_support/catalog/update.py @@ -5,21 +5,21 @@ import pandas as pd import yaml -from draw_tree import generate_pdf, generate_png, generate_svg, generate_tex +from gtdraw import pdf, png, svg, tex import pygambit as gbt CATALOG_RST_TABLE = Path(__file__).parent.parent.parent / "doc" / "catalog_table.rst" CATALOG_DIR = Path(__file__).parent.parent.parent / "catalog" MAKEFILE_AM = Path(__file__).parent.parent.parent / "Makefile.am" -DRAW_TREE_SETTINGS_CONFIG = Path(__file__).parent / "draw_tree_settings.yaml" +GTDRAW_SETTINGS_CONFIG = Path(__file__).parent / "gtdraw_settings.yaml" CATALOG_HIERARCHY_CONFIG = Path(__file__).parent / "catalog_hierarchy.yaml" SUPPORTED_GAME_FORMATS = {"efg", "nfg"} -def catalog_draw_tree_settings(slug: str) -> dict: - """Return the draw_tree settings for a given catalog slug.""" - with open(DRAW_TREE_SETTINGS_CONFIG, encoding="utf-8") as f: +def catalog_gtdraw_settings(slug: str) -> dict: + """Return the gtdraw settings for a given catalog slug.""" + with open(GTDRAW_SETTINGS_CONFIG, encoding="utf-8") as f: config = yaml.safe_load(f) settings = dict(config["defaults"]) overrides = config.get("overrides", {}) @@ -166,8 +166,8 @@ def _write_game_entry( if variant["ef_path"].exists() else gbt.catalog.load(slug) ) - for func in [generate_tex, generate_png, generate_pdf, generate_svg]: - func(source, save_to=str(viz_path), **catalog_draw_tree_settings(vkey)) + for func in [tex, png, pdf, svg]: + func(source, save_to=str(viz_path), **catalog_gtdraw_settings(vkey)) img_ef = catalog_dir / "img" / f"{vkey}.ef" if not img_ef.exists() and variant["ef_path"].exists(): shutil.copy2(variant["ef_path"], img_ef) @@ -185,8 +185,8 @@ def _write_game_entry( viz_path.parent.mkdir(parents=True, exist_ok=True) curated_ef = catalog_dir / f"{slug}.ef" source = str(curated_ef) if curated_ef.exists() else gbt.catalog.load(slug) - for func in [generate_tex, generate_png, generate_pdf, generate_svg]: - func(source, save_to=str(viz_path), **catalog_draw_tree_settings(slug)) + for func in [tex, png, pdf, svg]: + func(source, save_to=str(viz_path), **catalog_gtdraw_settings(slug)) img_ef = catalog_dir / "img" / f"{slug}.ef" if not img_ef.exists() and curated_ef.exists(): shutil.copy2(curated_ef, img_ef) @@ -228,7 +228,7 @@ def _write_game_entry( label = variant["label"] vkey = variant["variant_key"] settings_str = ", ".join( - f"{k}={v!r}" for k, v in catalog_draw_tree_settings(vkey).items() + f"{k}={v!r}" for k, v in catalog_gtdraw_settings(vkey).items() ) f.write(f"{i2}.. tab-item:: {label}\n") f.write(f"{i2}\n") @@ -236,11 +236,11 @@ def _write_game_entry( f.write(f"{i3} :hide-code:\n") f.write(f"{i3} \n") f.write(f"{i4}import pygambit\n") - f.write(f"{i4}from draw_tree import draw_tree\n") + f.write(f"{i4}from gtdraw import draw\n") if variant["ef_path"].exists(): - f.write(f'{i4}draw_tree("../catalog/{vkey}.ef", {settings_str})\n') + f.write(f'{i4}draw("../catalog/{vkey}.ef", {settings_str})\n') else: - f.write(f'{i4}draw_tree(pygambit.catalog.load("{slug}"), {settings_str})\n') + f.write(f'{i4}draw(pygambit.catalog.load("{slug}"), {settings_str})\n') f.write(f"{i2}\n") f.write(f"{i1}\n") else: @@ -248,19 +248,19 @@ def _write_game_entry( f.write(f"{i1} :hide-code:\n") f.write(f"{i1} \n") f.write(f"{i2}import pygambit\n") - f.write(f"{i2}from draw_tree import draw_tree\n") + f.write(f"{i2}from gtdraw import draw\n") if row["Format"] == "efg": settings_str = ", ".join( - f"{k}={v!r}" for k, v in catalog_draw_tree_settings(slug).items() + f"{k}={v!r}" for k, v in catalog_gtdraw_settings(slug).items() ) curated_ef = catalog_dir / f"{slug}.ef" if curated_ef.exists(): - f.write(f'{i2}draw_tree("../catalog/{slug}.ef", {settings_str})\n') + f.write(f'{i2}draw("../catalog/{slug}.ef", {settings_str})\n') else: - f.write(f'{i2}draw_tree(pygambit.catalog.load("{slug}"), {settings_str})\n') + f.write(f'{i2}draw(pygambit.catalog.load("{slug}"), {settings_str})\n') elif row["Format"] == "nfg": f.write( - f'{i2}draw_tree(pygambit.catalog.load("{slug}"), ' + f'{i2}draw(pygambit.catalog.load("{slug}"), ' f'save_to="../catalog/img/{slug}.png")\n' ) f.write(f"{i1}\n") @@ -403,7 +403,7 @@ def update_makefile( help=( "Force regeneration of all game visualisation images (PNG, PDF, SVG, TeX), " "even if they already exist. Use this to pick up changes to game files or " - "draw_tree_settings.yaml." + "gtdraw_settings.yaml." ), ) args = parser.parse_args() diff --git a/doc/developer.catalog.rst b/doc/developer.catalog.rst index c37c8b567..4bde0a3c8 100644 --- a/doc/developer.catalog.rst +++ b/doc/developer.catalog.rst @@ -75,7 +75,7 @@ Currently supported representations are: (e.g. ``catalog/source/game.ef``). When present, ``update.py`` will use this file directly as input to DrawTree instead of auto-generating the layout from the ``.efg``, preserving any hand-tuned layout. - Consult the `DrawTree docs `_ for the ``.ef`` format. + Consult the `DrawTree docs `_ for the ``.ef`` format. In general, ``.ef`` files should only be added when the layout they provide differs significantly from what DrawTree produces automatically; if the auto-generated layout is reasonable, there is @@ -101,10 +101,10 @@ Currently supported representations are: Ensure you have installed the package in editable mode to automatically pick up the new game file(s) in the ``pygambit.catalog`` module without reinstalling each time. Then use the ``update.py`` script to update Gambit's documentation & build files, as well as generating images for the new game(s). - If you want to customise the visualisation parameters for your game(s), edit ``build_support/catalog/draw_tree_settings.yaml``. + If you want to customise the visualisation parameters for your game(s), edit ``build_support/catalog/gtdraw_settings.yaml``. Add an entry under ``overrides`` keyed by your game's exact slug, or by a shared prefix (e.g. the author-year folder name) to apply settings to all games from that source. More specific entries (longer keys) take precedence over shorter ones. - Consult the `DrawTree docs `_ for available settings. + Consult the `DrawTree docs `_ for available settings. .. code-block:: bash @@ -129,8 +129,8 @@ Currently supported representations are: .. tip:: - If the game visuals for extensive form games need some work and you aren't sure which settings to change in ``build_support/catalog/draw_tree_settings.yaml``, try loading the EFG in the DrawTree GUI and adjusting the layout there. - There is an option to `download settings `_ which can be used in the Gambit catalog. + If the game visuals for extensive form games need some work and you aren't sure which settings to change in ``build_support/catalog/gtdraw_settings.yaml``, try loading the EFG in the DrawTree GUI and adjusting the layout there. + There is an option to `download settings `_ which can be used in the Gambit catalog. 5. **[Optional] Test your updates to the documentation locally:** @@ -142,7 +142,7 @@ Currently supported representations are: 6. **Submit a pull request to GitHub with all changes.** Submit a PR according to the :ref:`usual workflow `. - Ensure that any additions and changes to game files, ``build_support/catalog/draw_tree_settings.yaml``, ``build_support/catalog/update.py`` and ``build_support/catalog/catalog.am`` are included. + Ensure that any additions and changes to game files, ``build_support/catalog/gtdraw_settings.yaml``, ``build_support/catalog/update.py`` and ``build_support/catalog/catalog.am`` are included. .. important:: diff --git a/doc/tutorials/02_extensive_form.ipynb b/doc/tutorials/02_extensive_form.ipynb index 5f81eeff0..1f4f6fd0e 100644 --- a/doc/tutorials/02_extensive_form.ipynb +++ b/doc/tutorials/02_extensive_form.ipynb @@ -4,30 +4,7 @@ "cell_type": "markdown", "id": "96019084", "metadata": {}, - "source": [ - "# 2) Extensive-form games\n", - "\n", - "In the first tutorial, we used Gambit to set up the Prisoner's Dilemma, an example of a normal (strategic) form game.\n", - "\n", - "Gambit can also be used to set up extensive-form games; the game is represented as a tree, where each node represents a decision point for a player, and the branches represent the possible actions they can take.\n", - "\n", - "**Example: One-shot trust game with binary actions**\n", - "\n", - "[Kreps (1990)](#references) introduced a game commonly referred to as the **trust game**.\n", - "We will build a one-shot version of this game using Gambit's game transformation operations.\n", - "\n", - "The game can be defined as follows:\n", - "- There are two players, a **Buyer** and a **Seller**.\n", - "- The Buyer moves first and has two actions, **Trust** or **Not trust**.\n", - "- If the Buyer chooses **Not trust**, then the game ends, and both players receive payoffs of `0`.\n", - "- If the Buyer chooses **Trust**, then the Seller has a choice with two actions, **Honor** or **Abuse**.\n", - "- If the Seller chooses **Honor**, both players receive payoffs of `1`;\n", - "- If the Seller chooses **Abuse**, the Buyer receives a payoff of `-1` and the Seller receives a payoff of `2`.\n", - "\n", - "In addition to `pygambit`, this tutorial introduces the `draw_tree` package, which can be used to draw extensive form games in Python.\n", - "If you're running this tutorial on your local machine, you'll need to install the requirements for [draw_tree](https://github.com/gambitproject/draw_tree), which include LaTeX, in order to run the `draw_tree` cells.\n", - "Another option for visualising extensive form games is to install the Gambit GUI and use it to load the EFG file generated at the end of this tutorial." - ] + "source": "# 2) Extensive-form games\n\nIn the first tutorial, we used Gambit to set up the Prisoner's Dilemma, an example of a normal (strategic) form game.\n\nGambit can also be used to set up extensive-form games; the game is represented as a tree, where each node represents a decision point for a player, and the branches represent the possible actions they can take.\n\n**Example: One-shot trust game with binary actions**\n\n[Kreps (1990)](#references) introduced a game commonly referred to as the **trust game**.\nWe will build a one-shot version of this game using Gambit's game transformation operations.\n\nThe game can be defined as follows:\n- There are two players, a **Buyer** and a **Seller**.\n- The Buyer moves first and has two actions, **Trust** or **Not trust**.\n- If the Buyer chooses **Not trust**, then the game ends, and both players receive payoffs of `0`.\n- If the Buyer chooses **Trust**, then the Seller has a choice with two actions, **Honor** or **Abuse**.\n- If the Seller chooses **Honor**, both players receive payoffs of `1`;\n- If the Seller chooses **Abuse**, the Buyer receives a payoff of `-1` and the Seller receives a payoff of `2`.\n\nIn addition to `pygambit`, this tutorial introduces the `gtdraw` package, which can be used to draw extensive form games in Python.\nIf you're running this tutorial on your local machine, you'll need to install the requirements for [gtdraw](https://www.gambit-project.org/gtdraw/), which include LaTeX, in order to run the `gtdraw` cells.\nAnother option for visualising extensive form games is to install the Gambit GUI and use it to load the EFG file generated at the end of this tutorial." }, { "cell_type": "code", @@ -35,11 +12,7 @@ "id": "5946289b", "metadata": {}, "outputs": [], - "source": [ - "from draw_tree import draw_tree\n", - "\n", - "import pygambit as gbt" - ] + "source": "from gtdraw import draw\n\nimport pygambit as gbt" }, { "cell_type": "markdown", @@ -76,9 +49,7 @@ "id": "3cd94917", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g)" - ] + "source": "draw(g)" }, { "cell_type": "markdown", @@ -110,9 +81,7 @@ "id": "45638fda-7e25-4c8e-b709-24b05780581b", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g)" - ] + "source": "draw(g)" }, { "cell_type": "markdown", @@ -142,9 +111,7 @@ "id": "ce41e9fe-cca4-46fb-8e9d-b2c27342e5ef", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g)" - ] + "source": "draw(g)" }, { "cell_type": "markdown", @@ -180,9 +147,7 @@ "id": "b3408c55-714e-4a6f-b598-e338839442e4", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g)" - ] + "source": "draw(g)" }, { "cell_type": "markdown", @@ -214,9 +179,7 @@ "id": "09bedb3a-aac7-46e6-ae93-c47932c746d4", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g)" - ] + "source": "draw(g)" }, { "cell_type": "markdown", @@ -248,9 +211,7 @@ "id": "cba0e562-2989-4dae-a0f0-b121635ba032", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g)" - ] + "source": "draw(g)" }, { "cell_type": "markdown", @@ -297,10 +258,7 @@ "id": "56899a29-cc53-48db-9eb4-ed2295517400", "metadata": {}, "outputs": [], - "source": [ - "g = gbt.catalog.load(\"journals/ijgt/selten1975/fig2\")\n", - "draw_tree(g)" - ] + "source": "g = gbt.catalog.load(\"journals/ijgt/selten1975/fig2\")\ndraw(g)" }, { "cell_type": "markdown", diff --git a/doc/tutorials/03_stripped_down_poker.ipynb b/doc/tutorials/03_stripped_down_poker.ipynb index 522ac1467..5dc52da3d 100644 --- a/doc/tutorials/03_stripped_down_poker.ipynb +++ b/doc/tutorials/03_stripped_down_poker.ipynb @@ -4,38 +4,7 @@ "cell_type": "markdown", "id": "98eb65d8", "metadata": {}, - "source": [ - "# 3) Stripped-down poker\n", - "\n", - "In this tutorial, we'll create an extensive-form representation of a one-card poker game from [Reiley et al (2008)](#references), a classroom game under the name \"stripped-down poker\".\n", - "This is perhaps the simplest interesting game with imperfect information.\n", - "\n", - "We'll use \"stripped-down poker\" to demonstrate and explain the following with Gambit:\n", - "\n", - "1. Setting up an extensive-form game with imperfect information using [information sets](#information-sets)\n", - "2. [Computing and interpreting Nash equilibria](#computing-and-interpreting-nash-equilibria) and understanding mixed behaviour and mixed strategy profiles\n", - "3. [Acceptance criteria for Nash equilibria](#acceptance-criteria-for-nash-equilibria)\n", - "\n", - "In our version of the game, there are two players, **Alice** and **Bob**, and a deck of cards, with equal numbers of **King** and **Queen** cards.\n", - "\n", - "- The game begins with each player putting \\$1 in the pot.\n", - " - A card is dealt at random to Alice.\n", - " - Alice observes her card.\n", - " - Bob does not observe the card.\n", - "- Alice then chooses either to **Bet** or to **Fold**.\n", - " - If she chooses to Fold, Bob wins the pot and the game ends.\n", - " - If she chooses to Bet, she adds another \\$1 to the pot.\n", - "- Bob then chooses either to **Call** or **Fold**.\n", - " - If he chooses to Fold, Alice wins the pot and the game ends.\n", - " - If he chooses to Call, he adds another $1 to the pot.\n", - "- There is then a showdown, in which Alice reveals her card.\n", - " - If she has a King, then she wins the pot.\n", - " - If she has a Queen, then Bob wins the pot.\n", - "\n", - "In addition to `pygambit`, this tutorial uses the `draw_tree` package, which can be used to draw extensive form games in Python.\n", - "If you're running this tutorial on your local machine, you'll need to install the requirements for [draw_tree](https://github.com/gambitproject/draw_tree), which include LaTeX, in order to run the `draw_tree` cells.\n", - "Another option for visualising extensive form games is to install the Gambit GUI and use it to load a saved EFG file." - ] + "source": "# 3) Stripped-down poker\n\nIn this tutorial, we'll create an extensive-form representation of a one-card poker game from [Reiley et al (2008)](#references), a classroom game under the name \"stripped-down poker\".\nThis is perhaps the simplest interesting game with imperfect information.\n\nWe'll use \"stripped-down poker\" to demonstrate and explain the following with Gambit:\n\n1. Setting up an extensive-form game with imperfect information using [information sets](#information-sets)\n2. [Computing and interpreting Nash equilibria](#computing-and-interpreting-nash-equilibria) and understanding mixed behaviour and mixed strategy profiles\n3. [Acceptance criteria for Nash equilibria](#acceptance-criteria-for-nash-equilibria)\n\nIn our version of the game, there are two players, **Alice** and **Bob**, and a deck of cards, with equal numbers of **King** and **Queen** cards.\n\n- The game begins with each player putting \\$1 in the pot.\n - A card is dealt at random to Alice.\n - Alice observes her card.\n - Bob does not observe the card.\n- Alice then chooses either to **Bet** or to **Fold**.\n - If she chooses to Fold, Bob wins the pot and the game ends.\n - If she chooses to Bet, she adds another \\$1 to the pot.\n- Bob then chooses either to **Call** or **Fold**.\n - If he chooses to Fold, Alice wins the pot and the game ends.\n - If he chooses to Call, he adds another $1 to the pot.\n- There is then a showdown, in which Alice reveals her card.\n - If she has a King, then she wins the pot.\n - If she has a Queen, then Bob wins the pot.\n\nIn addition to `pygambit`, this tutorial uses the `gtdraw` package, which can be used to draw extensive form games in Python.\nIf you're running this tutorial on your local machine, you'll need to install the requirements for [gtdraw](https://www.gambit-project.org/gtdraw/), which include LaTeX, in order to run the `gtdraw` cells.\nAnother option for visualising extensive form games is to install the Gambit GUI and use it to load a saved EFG file." }, { "cell_type": "code", @@ -43,11 +12,7 @@ "id": "69cbfe81", "metadata": {}, "outputs": [], - "source": [ - "from draw_tree import draw_tree\n", - "\n", - "import pygambit as gbt" - ] + "source": "from gtdraw import draw\n\nimport pygambit as gbt" }, { "cell_type": "markdown", @@ -124,9 +89,7 @@ "id": "867cb1d8-7a5d-45d1-9349-9bbc2a4e2344", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g, color_scheme=\"gambit\")" - ] + "source": "draw(g, color_scheme=\"gambit\")" }, { "cell_type": "markdown", @@ -163,9 +126,7 @@ "id": "0c522c2d-992e-48b6-a1f8-0696d33cdbe0", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g, color_scheme=\"gambit\")" - ] + "source": "draw(g, color_scheme=\"gambit\")" }, { "cell_type": "markdown", @@ -205,9 +166,7 @@ "id": "e85b3346-2fea-4a73-aa72-9efb436c68c1", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g, color_scheme=\"gambit\")" - ] + "source": "draw(g, color_scheme=\"gambit\")" }, { "cell_type": "markdown", @@ -271,9 +230,7 @@ "id": "fdee7b53-7820-44df-9d17-d15d0b9667aa", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g, color_scheme=\"gambit\")" - ] + "source": "draw(g, color_scheme=\"gambit\")" }, { "cell_type": "markdown", diff --git a/doc/tutorials/04_creating_images.ipynb b/doc/tutorials/04_creating_images.ipynb index ec15e8f76..426cefc78 100644 --- a/doc/tutorials/04_creating_images.ipynb +++ b/doc/tutorials/04_creating_images.ipynb @@ -14,22 +14,13 @@ "id": "338dd237-2280-453c-a687-d65b9f9a56b6", "metadata": {}, "outputs": [], - "source": [ - "from draw_tree import draw_tree\n", - "\n", - "import pygambit as gbt" - ] + "source": "from gtdraw import draw\n\nimport pygambit as gbt" }, { "cell_type": "markdown", "id": "53808931-cdc1-4b39-9a62-8c5166077a84", "metadata": {}, - "source": [ - "Using a combination of `pygambit` and the Gambit project's LaTeX graphics package `draw_tree`, we can generate publication quality images for games with just a few lines of code.\n", - "\n", - "This tutorial will demonstrate the key functionality of `draw_tree` when used for games built with `pygambit`, using an example game derived from the Gambit catalog.\n", - "First, let's load the game:" - ] + "source": "Using a combination of `pygambit` and the Gambit project's LaTeX graphics package `gtdraw`, we can generate publication quality images for games with just a few lines of code.\n\nThis tutorial will demonstrate the key functionality of `gtdraw` when used for games built with `pygambit`, using an example game derived from the Gambit catalog.\nFirst, let's load the game:" }, { "cell_type": "code", @@ -45,9 +36,7 @@ "cell_type": "markdown", "id": "f4141e1a-9728-4186-b547-d37050a63e66", "metadata": {}, - "source": [ - "Now let's see how `draw_tree` renders it with default settings:" - ] + "source": "Now let's see how `gtdraw` renders it with default settings:" }, { "cell_type": "code", @@ -55,9 +44,7 @@ "id": "e76fc30e-c03c-4fc6-ada1-733500609ca0", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(g)" - ] + "source": "draw(g)" }, { "cell_type": "markdown", @@ -74,12 +61,7 @@ "id": "c9d4639a-8dc6-42c5-99a0-0a831117b551", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(\n", - " g,\n", - " shared_terminal_depth=True\n", - ")" - ] + "source": "draw(\n g,\n shared_terminal_depth=True\n)" }, { "cell_type": "markdown", @@ -95,13 +77,7 @@ "id": "d6e7638e-9380-45ac-8809-9fd4da71be46", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5\n", - ")" - ] + "source": "draw(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5\n)" }, { "cell_type": "markdown", @@ -117,14 +93,7 @@ "id": "0abb278d-2887-46fd-b9c3-0dc987d2da25", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5,\n", - " level_scaling=0.75\n", - ")" - ] + "source": "draw(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5,\n level_scaling=0.75\n)" }, { "cell_type": "markdown", @@ -141,15 +110,7 @@ "id": "b96266fb-4bcb-414b-b78b-ee53e40af210", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5,\n", - " level_scaling=0.75,\n", - " sublevel_scaling=0\n", - ")" - ] + "source": "draw(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5,\n level_scaling=0.75,\n sublevel_scaling=0\n)" }, { "cell_type": "markdown", @@ -165,16 +126,7 @@ "id": "7cb624c2-b3c3-4775-b1cd-7ef0e9d24eb1", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5,\n", - " level_scaling=0.75,\n", - " sublevel_scaling=0,\n", - " width_scaling=1.25\n", - ")" - ] + "source": "draw(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5,\n level_scaling=0.75,\n sublevel_scaling=0,\n width_scaling=1.25\n)" }, { "cell_type": "markdown", @@ -190,17 +142,7 @@ "id": "6d326b08-87e2-4d49-80bf-e16981ee221f", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5,\n", - " level_scaling=0.75,\n", - " sublevel_scaling=0,\n", - " width_scaling=1.25,\n", - " color_scheme=\"gambit\"\n", - ")" - ] + "source": "draw(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5,\n level_scaling=0.75,\n sublevel_scaling=0,\n width_scaling=1.25,\n color_scheme=\"gambit\"\n)" }, { "cell_type": "markdown", @@ -218,18 +160,7 @@ "id": "bf4926bf-47eb-4758-8916-4893ae04e88d", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5,\n", - " level_scaling=0.75,\n", - " sublevel_scaling=0,\n", - " width_scaling=1.25,\n", - " color_scheme=\"gambit\",\n", - " edge_thickness=1.5\n", - ")" - ] + "source": "draw(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5,\n level_scaling=0.75,\n sublevel_scaling=0,\n width_scaling=1.25,\n color_scheme=\"gambit\",\n edge_thickness=1.5\n)" }, { "cell_type": "markdown", @@ -247,31 +178,13 @@ "id": "0f82b80c-8f7d-41ff-9e24-ffb1524a95be", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5,\n", - " level_scaling=0.75,\n", - " sublevel_scaling=0,\n", - " width_scaling=1.25,\n", - " color_scheme=\"gambit\",\n", - " edge_thickness=1.5,\n", - " action_label_position=0.6\n", - ")" - ] + "source": "draw(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5,\n level_scaling=0.75,\n sublevel_scaling=0,\n width_scaling=1.25,\n color_scheme=\"gambit\",\n edge_thickness=1.5,\n action_label_position=0.6\n)" }, { "cell_type": "markdown", "id": "f77731a4-1c3a-4e83-a69e-b3b8404059b5", "metadata": {}, - "source": [ - "## Saving images\n", - "\n", - "Once we are happy with our image, we can save it as a Tex file, or generate a PNG or PDF with the rendered image.\n", - "Each of the following functions takes the same arguments as the `draw_tree` examples above.\n", - "Setting the `save_to` argument will determine where the `.ef` file used by `draw_tree` to preserve layout information is saved, as well as the output image or Tex file:" - ] + "source": "## Saving images\n\nOnce we are happy with our image, we can save it as a Tex file, or generate a PNG or PDF with the rendered image.\nEach of the following functions takes the same arguments as the `draw` examples above.\nSetting the `save_to` argument will determine where the `.ef` file used by `gtdraw` to preserve layout information is saved, as well as the output image or Tex file:" }, { "cell_type": "code", @@ -279,100 +192,19 @@ "id": "e6045291-653e-4702-a95c-5c00159d4b43", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5,\n", - " level_scaling=0.75,\n", - " sublevel_scaling=0,\n", - " width_scaling=1.25,\n", - " color_scheme=\"gambit\",\n", - " edge_thickness=1.5,\n", - " action_label_position=0.6,\n", - " save_to=\"2smp\" # Creates 2smp.ef\n", - ")" - ] + "source": "draw(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5,\n level_scaling=0.75,\n sublevel_scaling=0,\n width_scaling=1.25,\n color_scheme=\"gambit\",\n edge_thickness=1.5,\n action_label_position=0.6,\n save_to=\"2smp\" # Creates 2smp.ef\n)" }, { "cell_type": "markdown", "id": "d21634cc-64fd-4eed-8887-b4e3a80734fd", "metadata": {}, - "source": [ - "### Save to TeX\n", - "\n", - "```python\n", - "from draw_tree generate_tex\n", - "generate_tex(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5,\n", - " level_scaling=0.75,\n", - " sublevel_scaling=0,\n", - " width_scaling=1.25,\n", - " color_scheme=\"gambit\",\n", - " edge_thickness=1.5,\n", - " action_label_position=0.6,\n", - " save_to=\"2smp\" # Creates 2smp.ef and 2smp.tex\n", - ")\n", - "```\n", - "\n", - "### Save to PDF\n", - "\n", - "```python\n", - "from draw_tree import generate_pdf\n", - "generate_pdf(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5,\n", - " level_scaling=0.75,\n", - " sublevel_scaling=0,\n", - " width_scaling=1.25,\n", - " color_scheme=\"gambit\",\n", - " edge_thickness=1.5,\n", - " action_label_position=0.6,\n", - " save_to=\"2smp\" # Creates 2smp.ef and 2smp.pdf\n", - ")\n", - "```\n", - "\n", - "### Save to PNG\n", - "\n", - "```python\n", - "from draw_tree import generate_png\n", - "generate_png(\n", - " g,\n", - " shared_terminal_depth=True,\n", - " scale_factor=0.5,\n", - " level_scaling=0.75,\n", - " sublevel_scaling=0,\n", - " width_scaling=1.25,\n", - " color_scheme=\"gambit\",\n", - " edge_thickness=1.5,\n", - " action_label_position=0.6,\n", - " save_to=\"2smp\" # Creates 2smp.ef and 2smp.png\n", - ")\n", - "```" - ] + "source": "### Save to TeX\n\n```python\nfrom gtdraw import tex\ntex(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5,\n level_scaling=0.75,\n sublevel_scaling=0,\n width_scaling=1.25,\n color_scheme=\"gambit\",\n edge_thickness=1.5,\n action_label_position=0.6,\n save_to=\"2smp\" # Creates 2smp.ef and 2smp.tex\n)\n```\n\n### Save to PDF\n\n```python\nfrom gtdraw import pdf\npdf(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5,\n level_scaling=0.75,\n sublevel_scaling=0,\n width_scaling=1.25,\n color_scheme=\"gambit\",\n edge_thickness=1.5,\n action_label_position=0.6,\n save_to=\"2smp\" # Creates 2smp.ef and 2smp.pdf\n)\n```\n\n### Save to PNG\n\n```python\nfrom gtdraw import png\npng(\n g,\n shared_terminal_depth=True,\n scale_factor=0.5,\n level_scaling=0.75,\n sublevel_scaling=0,\n width_scaling=1.25,\n color_scheme=\"gambit\",\n edge_thickness=1.5,\n action_label_position=0.6,\n save_to=\"2smp\" # Creates 2smp.ef and 2smp.png\n)\n```" }, { "cell_type": "markdown", "id": "0fa1bb4f-4dc4-4619-b20a-f84948a69185", "metadata": {}, - "source": [ - "## Further adjustments to game images\n", - "\n", - "If your image requires further adjustments, you can manually edit your game's `.ef` file.\n", - "You can find information on EF format specs in the [draw_tree docs](https://github.com/gambitproject/draw_tree).\n", - "\n", - "EF files that are manually created or (exported from [Game Theory Explorer](https://gametheoryexplorer-a68c7.web.app/) can be drawn by `draw_tree` with the same functions explored in this tutorial, but you should drop the formatting parameters:\n", - "\n", - "```python\n", - "draw_tree('path/to/game.ef')\n", - "generate_tex('path/to/game.ef')\n", - "generate_pdf('path/to/game.ef')\n", - "generate_png('path/to/game.ef')\n", - "```" - ] + "source": "## Further adjustments to game images\n\nIf your image requires further adjustments, you can manually edit your game's `.ef` file.\nYou can find information on EF format specs in the [gtdraw docs](https://www.gambit-project.org/gtdraw/).\n\nEF files that are manually created or (exported from [Game Theory Explorer](https://gametheoryexplorer-a68c7.web.app/) can be drawn by `gtdraw` with the same functions explored in this tutorial, but you should drop the formatting parameters:\n\n```python\ndraw('path/to/game.ef')\ntex('path/to/game.ef')\npdf('path/to/game.ef')\npng('path/to/game.ef')\n```" } ], "metadata": { diff --git a/doc/tutorials/advanced_tutorials/agent_versus_non_agent_regret.ipynb b/doc/tutorials/advanced_tutorials/agent_versus_non_agent_regret.ipynb index e1df0b2ab..e5af4cf76 100644 --- a/doc/tutorials/advanced_tutorials/agent_versus_non_agent_regret.ipynb +++ b/doc/tutorials/advanced_tutorials/agent_versus_non_agent_regret.ipynb @@ -32,14 +32,7 @@ "id": "5142d6ba-da13-4500-bca6-e68b608bfae9", "metadata": {}, "outputs": [], - "source": [ - "from draw_tree import draw_tree\n", - "\n", - "import pygambit as gbt\n", - "\n", - "g = gbt.catalog.load(\"books/myerson1991/fig4_2\")\n", - "draw_tree(g)" - ] + "source": "from gtdraw import draw\n\nimport pygambit as gbt\n\ng = gbt.catalog.load(\"books/myerson1991/fig4_2\")\ndraw(g)" }, { "cell_type": "markdown", diff --git a/doc/tutorials/interoperability_tutorials/openspiel.ipynb b/doc/tutorials/interoperability_tutorials/openspiel.ipynb index c3abb6aee..65129899c 100644 --- a/doc/tutorials/interoperability_tutorials/openspiel.ipynb +++ b/doc/tutorials/interoperability_tutorials/openspiel.ipynb @@ -468,17 +468,7 @@ "id": "b913fc7a", "metadata": {}, "outputs": [], - "source": [ - "from draw_tree import draw_tree\n", - "\n", - "draw_tree(\n", - " gbt_hanabi_game,\n", - " color_scheme=\"gambit\",\n", - " edge_thickness=2,\n", - " action_label_position=0.8,\n", - " shared_terminal_depth=True\n", - ")" - ] + "source": "from gtdraw import draw\n\ndraw(\n gbt_hanabi_game,\n color_scheme=\"gambit\",\n edge_thickness=2,\n action_label_position=0.8,\n shared_terminal_depth=True\n)" }, { "cell_type": "markdown", @@ -751,9 +741,7 @@ "id": "ed920d33-b7c6-4cc1-b055-7244a5bf42d8", "metadata": {}, "outputs": [], - "source": [ - "draw_tree(gbt_one_card_poker, color_scheme=\"gambit\")" - ] + "source": "draw(gbt_one_card_poker, color_scheme=\"gambit\")" }, { "cell_type": "markdown", diff --git a/pyproject.toml b/pyproject.toml index c318f952a..4fe92d3f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ doc = [ "jupyter_sphinx", "pyyaml", "sphinxcontrib-bibtex", + "gtdraw", ] [project.urls]