Skip to content

Commit c253ce3

Browse files
committed
Compute non-reduced strategies for a given player. Add
* GamePlayerRep::m_nonReducedStrategies; * MakeNonReducedStrats and GetNonReducedStrategies methods * Cython interface
1 parent 7df3d34 commit c253ce3

5 files changed

Lines changed: 111 additions & 3 deletions

File tree

src/games/game.cc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ GamePlayerRep::~GamePlayerRep()
6666
for (auto strategy : m_strategies) {
6767
strategy->Invalidate();
6868
}
69+
for (auto strategy : m_nonReducedStrategies) {
70+
strategy->Invalidate();
71+
}
6972
}
7073

7174
void GamePlayerRep::MakeStrategy()
@@ -166,6 +169,42 @@ void GamePlayerRep::MakeReducedStrats(GameNodeRep *n, GameNodeRep *nn)
166169
}
167170
}
168171

172+
void GamePlayerRep::MakeNonReducedStratsRecursiveImpl(GameStrategyRep *p_strat,
173+
Infosets::iterator it_infoset)
174+
{
175+
if (it_infoset == GetInfosets().end()) {
176+
GameStrategyRep *p_strat_copy(new GameStrategyRep(*p_strat));
177+
p_strat_copy->m_number = m_nonReducedStrategies.size() + 1;
178+
m_nonReducedStrategies.push_back(p_strat_copy);
179+
return;
180+
}
181+
182+
GameInfosetRep *current_infoset = (*it_infoset);
183+
184+
for (const auto &action : current_infoset->GetActions()) {
185+
GameStrategyRep *p_strat_copy(new GameStrategyRep(*p_strat));
186+
187+
p_strat_copy->m_behav[current_infoset->GetNumber()] = action->GetNumber();
188+
p_strat_copy->SetLabel(p_strat_copy->GetLabel() + action->GetLabel());
189+
MakeNonReducedStratsRecursiveImpl(p_strat_copy, std::next(it_infoset));
190+
delete p_strat_copy;
191+
}
192+
}
193+
194+
void GamePlayerRep::MakeNonReducedStrats()
195+
{
196+
for (GameStrategyRep *strat : m_nonReducedStrategies) {
197+
delete strat;
198+
}
199+
m_nonReducedStrategies.clear();
200+
201+
GameStrategyRep *starting_strategy = new GameStrategyRep(this, 0, "");
202+
starting_strategy->m_behav = Array<int>(GetInfosets().size());
203+
204+
MakeNonReducedStratsRecursiveImpl(starting_strategy, GetInfosets().begin());
205+
delete starting_strategy;
206+
}
207+
169208
size_t GamePlayerRep::NumSequences() const
170209
{
171210
if (!m_game->IsTree()) {

src/games/game.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -375,26 +375,31 @@ class GamePlayerRep : public GameObject {
375375
template <class T> friend class MixedBehaviorProfile;
376376
template <class T> friend class MixedStrategyProfile;
377377

378+
public:
379+
using Infosets = ElementCollection<GamePlayer, GameInfosetRep>;
380+
using Strategies = ElementCollection<GamePlayer, GameStrategyRep>;
381+
382+
private:
378383
/// @name Building reduced form strategies
379384
//@{
380385
void MakeStrategy();
381386
void MakeReducedStrats(class GameNodeRep *, class GameNodeRep *);
387+
void MakeNonReducedStrats();
388+
void MakeNonReducedStratsRecursiveImpl(GameStrategyRep *, Infosets::iterator);
382389
//@}
383390

384391
GameRep *m_game;
385392
int m_number;
386393
std::string m_label;
387394
std::vector<GameInfosetRep *> m_infosets;
388395
std::vector<GameStrategyRep *> m_strategies;
396+
std::vector<GameStrategyRep *> m_nonReducedStrategies;
389397

390398
GamePlayerRep(GameRep *p_game, int p_id) : m_game(p_game), m_number(p_id) {}
391399
GamePlayerRep(GameRep *p_game, int p_id, int m_strats);
392400
~GamePlayerRep() override;
393401

394402
public:
395-
using Infosets = ElementCollection<GamePlayer, GameInfosetRep>;
396-
using Strategies = ElementCollection<GamePlayer, GameStrategyRep>;
397-
398403
int GetNumber() const { return m_number; }
399404
Game GetGame() const;
400405

@@ -416,6 +421,10 @@ class GamePlayerRep : public GameObject {
416421
GameStrategy GetStrategy(int st) const;
417422
/// Returns the array of strategies available to the player
418423
Strategies GetStrategies() const;
424+
/// Returns the st'th non-reduced strategy for the player
425+
GameStrategy GetNonReducedStrategy(int st) const;
426+
/// Returns the array of non-reduced strategies available to the player
427+
Strategies GetNonReducedStrategies() const;
419428
//@}
420429

421430
/// @name Sequences
@@ -792,6 +801,16 @@ inline GamePlayerRep::Strategies GamePlayerRep::GetStrategies() const
792801
m_game->BuildComputedValues();
793802
return Strategies(this, &m_strategies);
794803
}
804+
inline GameStrategy GamePlayerRep::GetNonReducedStrategy(int st) const
805+
{
806+
m_game->BuildComputedValues();
807+
return m_nonReducedStrategies.at(st - 1);
808+
}
809+
inline GamePlayerRep::Strategies GamePlayerRep::GetNonReducedStrategies() const
810+
{
811+
m_game->BuildComputedValues();
812+
return Strategies(this, &m_nonReducedStrategies);
813+
}
795814

796815
inline Game GameNodeRep::GetGame() const { return m_game; }
797816

src/games/gametree.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,7 @@ void GameTreeRep::ClearComputedValues() const
853853
strategy->Invalidate();
854854
}
855855
player->m_strategies.clear();
856+
player->m_nonReducedStrategies.clear();
856857
}
857858
const_cast<GameTreeRep *>(this)->m_nodePlays.clear();
858859
m_computedValues = false;
@@ -866,6 +867,7 @@ void GameTreeRep::BuildComputedValues() const
866867
const_cast<GameTreeRep *>(this)->Canonicalize();
867868
for (const auto &player : m_players) {
868869
player->MakeReducedStrats(m_root, nullptr);
870+
player->MakeNonReducedStrats();
869871
}
870872
m_computedValues = true;
871873
}

src/pygambit/gambit.pxd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ cdef extern from "games/game.h":
174174

175175
c_GameStrategy GetStrategy(int) except +IndexError
176176
Strategies GetStrategies() except +
177+
c_GameStrategy GetNonReducedStrategy(int) except +IndexError
178+
Strategies GetNonReducedStrategies() except +
177179

178180
c_GameInfoset GetInfoset(int) except +IndexError
179181
Infosets GetInfosets() except +

src/pygambit/player.pxi

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,47 @@ class PlayerStrategies:
148148
raise TypeError(f"Strategy index must be int or str, not {index.__class__.__name__}")
149149

150150

151+
@cython.cclass
152+
class PlayerNonReducedStrategies:
153+
"""The set of non-reduced strategies available to a player."""
154+
player = cython.declare(c_GamePlayer)
155+
156+
def __init__(self, *args, **kwargs) -> None:
157+
raise ValueError("Cannot create PlayerNonReducedStrategies outside a Game.")
158+
159+
@staticmethod
160+
@cython.cfunc
161+
def wrap(player: c_GamePlayer) -> PlayerNonReducedStrategies:
162+
obj = PlayerNonReducedStrategies.__new__(PlayerNonReducedStrategies)
163+
obj.player = player
164+
return obj
165+
166+
def __repr__(self) -> str:
167+
return f"PlayerNonReducedStrategies(player={Player.wrap(self.player)})"
168+
169+
def __len__(self):
170+
"""The number of non-reduced strategies for the player in the game."""
171+
return self.player.deref().GetNonReducedStrategies().size()
172+
173+
def __iter__(self) -> typing.Iterator[Strategy]:
174+
for strategy in self.player.deref().GetNonReducedStrategies():
175+
yield Strategy.wrap(strategy)
176+
177+
def __getitem__(self, index: typing.Union[int, str]) -> Strategy:
178+
if isinstance(index, str):
179+
if not index.strip():
180+
raise ValueError("Strategy label cannot be empty or all whitespace")
181+
matches = [x for x in self if x.label == index.strip()]
182+
if not matches:
183+
raise KeyError(f"Player has no strategy with label '{index}'")
184+
if len(matches) > 1:
185+
raise ValueError(f"Player has multiple strategies with label '{index}'")
186+
return matches[0]
187+
if isinstance(index, int):
188+
return Strategy.wrap(self.player.deref().GetNonReducedStrategy(index + 1))
189+
raise TypeError(f"Strategy index must be int or str, not {index.__class__.__name__}")
190+
191+
151192
@cython.cclass
152193
class Player:
153194
"""A player in a ``Game``."""
@@ -214,6 +255,11 @@ class Player:
214255
"""Returns the set of strategies belonging to the player."""
215256
return PlayerStrategies.wrap(self.player)
216257

258+
@property
259+
def non_reduced_strategies(self) -> PlayerNonReducedStrategies:
260+
"""Returns the set of nonreduced strategies belonging to the player."""
261+
return PlayerNonReducedStrategies.wrap(self.player)
262+
217263
@property
218264
def infosets(self) -> PlayerInfosets:
219265
"""Returns the set of information sets at which the player has the decision."""

0 commit comments

Comments
 (0)