diff --git a/ChangeLog b/ChangeLog index 61b96d1dc..00bca61ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,10 @@ and of payoffs, beliefs and regrets of mixed behavior profiles on large game trees. Path following (logit and hp) also spends about half as long factorizing Jacobians. (#1162) +### Fixed +- `repr()` of a `Game` with an empty title was missing its closing parenthesis + (`Game(id=...` instead of `Game(id=...)`). + ## [17.0.0-beta.1] - 2026-09-08 ### Added diff --git a/src/pygambit/game.pxi b/src/pygambit/game.pxi index 925256eae..4133a7048 100644 --- a/src/pygambit/game.pxi +++ b/src/pygambit/game.pxi @@ -102,7 +102,7 @@ class Game: if self.title: return f"Game(title='{self.title}')" else: - return f"Game(id={hash(self)}" + return f"Game(id={hash(self)})" def _repr_html_(self): if isinstance(self, ExtensiveGame): diff --git a/tests/test_game.py b/tests/test_game.py index 8fc25669c..2cfc168c2 100644 --- a/tests/test_game.py +++ b/tests/test_game.py @@ -107,3 +107,21 @@ def test_collection_rejects_integer_indexing(getter): collection = getter(games.create_stripped_down_poker_efg()) with pytest.raises(TypeError): _ = collection[0] + + +@pytest.mark.parametrize( + "game", + [ + pytest.param(gbt.StrategicGame([2, 2], title=""), id="strategic"), + pytest.param(gbt.ExtensiveGame(["1", "2"], title=""), id="extensive"), + ], +) +def test_repr_untitled_game(game): + # A game without a title is represented by its identity; the representation + # must be well-formed (closing parenthesis included), just as for titled games. + assert repr(game) == f"Game(id={hash(game)})" + + +def test_repr_titled_game(): + game = gbt.StrategicGame([2, 2], title="Coordination") + assert repr(game) == "Game(title='Coordination')"