diff --git a/src/games/game.cc b/src/games/game.cc index 6a6aea9d7..70fb2218a 100644 --- a/src/games/game.cc +++ b/src/games/game.cc @@ -200,6 +200,8 @@ size_t GamePlayerRep::NumSequences() const // class GameRep //======================================================================== +GamePlayer GameRep::GetPlayer(int pl) const { return m_players.at(pl - 1); } + Array GameRep::GetPlayers() const { Array ret(NumPlayers()); diff --git a/src/games/game.h b/src/games/game.h index 1eb9ae83f..d464ff5fc 100644 --- a/src/games/game.h +++ b/src/games/game.h @@ -395,6 +395,7 @@ class GameRep : public BaseGameRep { template friend class TableMixedStrategyProfileRep; protected: + std::vector m_players; std::string m_title, m_comment; unsigned int m_version{0}; @@ -535,7 +536,7 @@ class GameRep : public BaseGameRep { /// Returns the number of players in the game virtual size_t NumPlayers() const = 0; /// Returns the pl'th player in the game - virtual GamePlayer GetPlayer(int pl) const = 0; + GamePlayer GetPlayer(int pl) const; /// Returns the set of players in the game virtual Array GetPlayers() const; /// Returns the chance (nature) player diff --git a/src/games/gameagg.cc b/src/games/gameagg.cc index 179c8296e..53854e360 100644 --- a/src/games/gameagg.cc +++ b/src/games/gameagg.cc @@ -201,20 +201,20 @@ Game GameAGGRep::Copy() const Array GameAGGRep::NumStrategies() const { Array ns; - for (int pl = 1; pl <= aggPtr->getNumPlayers(); pl++) { - ns.push_back(m_players[pl]->NumStrategies()); + for (const auto &player : m_players) { + ns.push_back(player->NumStrategies()); } return ns; } GameStrategy GameAGGRep::GetStrategy(int p_index) const { - for (int pl = 1; pl <= aggPtr->getNumPlayers(); pl++) { - if (static_cast(m_players[pl]->NumStrategies()) >= p_index) { - return m_players[pl]->GetStrategy(p_index); + for (const auto &player : m_players) { + if (static_cast(player->NumStrategies()) >= p_index) { + return player->GetStrategy(p_index); } else { - p_index -= m_players[pl]->NumStrategies(); + p_index -= player->NumStrategies(); } } throw IndexException(); diff --git a/src/games/gameagg.h b/src/games/gameagg.h index ed8e87af6..c3a1e9074 100644 --- a/src/games/gameagg.h +++ b/src/games/gameagg.h @@ -33,7 +33,6 @@ class GameAGGRep : public GameRep { private: std::shared_ptr aggPtr; - Array m_players; public: /// @name Lifecycle @@ -77,8 +76,6 @@ class GameAGGRep : public GameRep { //@{ /// Returns the number of players in the game size_t NumPlayers() const override { return aggPtr->getNumPlayers(); } - /// Returns the pl'th player in the game - GamePlayer GetPlayer(int pl) const override { return m_players[pl]; } /// Returns the chance (nature) player GamePlayer GetChance() const override { throw UndefinedException(); } /// Creates a new player in the game, with no moves diff --git a/src/games/gamebagg.cc b/src/games/gamebagg.cc index a51e2dcc6..afe6a6c89 100644 --- a/src/games/gamebagg.cc +++ b/src/games/gamebagg.cc @@ -213,7 +213,7 @@ GameBAGGRep::GameBAGGRep(std::shared_ptr _baggPtr) for (int pl = 1; pl <= baggPtr->getNumPlayers(); pl++) { for (int j = 0; j < baggPtr->getNumTypes(pl - 1); j++, k++) { m_players.push_back(new GamePlayerRep(this, k, baggPtr->getNumActions(pl - 1, j))); - m_players[k]->m_label = lexical_cast(k); + m_players.back()->m_label = std::to_string(k); agent2baggPlayer[k] = pl; std::for_each(m_players.back()->m_strategies.begin(), m_players.back()->m_strategies.end(), [st = 1](GameStrategyRep *s) mutable { s->SetLabel(std::to_string(st++)); }); @@ -236,8 +236,8 @@ Game GameBAGGRep::Copy() const Array GameBAGGRep::NumStrategies() const { Array ns; - for (size_t pl = 1; pl <= NumPlayers(); pl++) { - ns.push_back(m_players[pl]->m_strategies.size()); + for (const auto &player : m_players) { + ns.push_back(player->m_strategies.size()); } return ns; } @@ -245,8 +245,8 @@ Array GameBAGGRep::NumStrategies() const int GameBAGGRep::MixedProfileLength() const { int res = 0; - for (size_t pl = 1; pl <= NumPlayers(); pl++) { - res += m_players[pl]->m_strategies.size(); + for (const auto &player : m_players) { + res += player->m_strategies.size(); } return res; } diff --git a/src/games/gamebagg.h b/src/games/gamebagg.h index 6569b97a2..f0ebb53f6 100644 --- a/src/games/gamebagg.h +++ b/src/games/gamebagg.h @@ -34,7 +34,6 @@ class GameBAGGRep : public GameRep { private: std::shared_ptr baggPtr; Array agent2baggPlayer; - Array m_players; public: /// @name Lifecycle @@ -78,8 +77,6 @@ class GameBAGGRep : public GameRep { //@{ /// Returns the number of players in the game size_t NumPlayers() const override { return m_players.size(); } - /// Returns the pl'th player in the game - GamePlayer GetPlayer(int pl) const override { return m_players[pl]; } /// Returns the chance (nature) player GamePlayer GetChance() const override { throw UndefinedException(); } /// Creates a new player in the game, with no moves diff --git a/src/games/gameexpl.cc b/src/games/gameexpl.cc index 16ae6d19d..21ce9e306 100644 --- a/src/games/gameexpl.cc +++ b/src/games/gameexpl.cc @@ -95,9 +95,9 @@ Rational GameExplicitRep::GetMaxPayoff(const GamePlayer &p_player) const Array GameExplicitRep::NumStrategies() const { const_cast(this)->BuildComputedValues(); - Array dim(m_players.size()); - for (size_t pl = 1; pl <= m_players.size(); pl++) { - dim[pl] = m_players[pl]->m_strategies.size(); + Array dim; + for (const auto &player : m_players) { + dim.push_back(player->m_strategies.size()); } return dim; } diff --git a/src/games/gameexpl.h b/src/games/gameexpl.h index 80b9a7815..3ace43077 100644 --- a/src/games/gameexpl.h +++ b/src/games/gameexpl.h @@ -31,7 +31,6 @@ class GameExplicitRep : public GameRep { template friend class MixedStrategyProfile; protected: - Array m_players; Array m_outcomes; public: @@ -69,8 +68,6 @@ class GameExplicitRep : public GameRep { //@{ /// Returns the number of players in the game size_t NumPlayers() const override { return m_players.size(); } - /// Returns the pl'th player in the game - GamePlayer GetPlayer(int pl) const override { return m_players[pl]; } //@} /// @name Outcomes diff --git a/src/games/gametree.cc b/src/games/gametree.cc index ca8ecb3e4..d10dcc739 100644 --- a/src/games/gametree.cc +++ b/src/games/gametree.cc @@ -808,7 +808,7 @@ void GameTreeRep::Canonicalize() NumberNodes(m_root, nodeindex); for (size_t pl = 0; pl <= m_players.size(); pl++) { - GamePlayerRep *player = (pl) ? m_players[pl] : m_chance; + GamePlayerRep *player = (pl) ? m_players[pl - 1] : m_chance; // Sort nodes within information sets according to ID. // Coded using a bubble sort for simplicity; large games might diff --git a/tests/test_players.py b/tests/test_players.py index 4597a5226..7743a2acf 100644 --- a/tests/test_players.py +++ b/tests/test_players.py @@ -39,8 +39,14 @@ def test_player_index_by_string(): def test_player_index_out_of_range(): game = gbt.Game.new_table([2, 2]) + print(f"Number of players: {len(game.players)}") + assert len(game.players) == 2 + with pytest.raises(IndexError): + _ = game.players[2] with pytest.raises(IndexError): _ = game.players[3] + with pytest.raises(IndexError): + _ = game.players[-1] def test_player_index_invalid():