Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/games/game.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ GamePlayerRep::~GamePlayerRep()
for (auto strategy : m_strategies) {
strategy->Invalidate();
}
for (auto strategy : m_nonReducedStrategies) {
strategy->Invalidate();
}
}

void GamePlayerRep::MakeStrategy()
Expand Down Expand Up @@ -166,6 +169,42 @@ void GamePlayerRep::MakeReducedStrats(GameNodeRep *n, GameNodeRep *nn)
}
}

void GamePlayerRep::MakeNonReducedStratsRecursiveImpl(GameStrategyRep *p_strat,
Infosets::iterator it_infoset)
{
if (it_infoset == GetInfosets().end()) {
auto p_strat_copy(new GameStrategyRep(*p_strat));
p_strat_copy->m_number = m_nonReducedStrategies.size() + 1;
m_nonReducedStrategies.push_back(p_strat_copy);
return;
}

GameInfosetRep *current_infoset = (*it_infoset);

for (const auto &action : current_infoset->GetActions()) {
auto p_strat_copy(new GameStrategyRep(*p_strat));

p_strat_copy->m_behav[current_infoset->GetNumber()] = action->GetNumber();
p_strat_copy->SetLabel(p_strat_copy->GetLabel() + action->GetLabel());
MakeNonReducedStratsRecursiveImpl(p_strat_copy, std::next(it_infoset));
delete p_strat_copy;
}
}

void GamePlayerRep::MakeNonReducedStrats()
{
for (GameStrategyRep *strat : m_nonReducedStrategies) {
delete strat;
}
m_nonReducedStrategies.clear();

auto starting_strategy = new GameStrategyRep(this, 0, "");
starting_strategy->m_behav = Array<int>(GetInfosets().size());

MakeNonReducedStratsRecursiveImpl(starting_strategy, GetInfosets().begin());
delete starting_strategy;
}

size_t GamePlayerRep::NumSequences() const
{
if (!m_game->IsTree()) {
Expand Down
25 changes: 22 additions & 3 deletions src/games/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,26 +375,31 @@ class GamePlayerRep : public GameObject {
template <class T> friend class MixedBehaviorProfile;
template <class T> friend class MixedStrategyProfile;

public:
using Infosets = ElementCollection<GamePlayer, GameInfosetRep>;
using Strategies = ElementCollection<GamePlayer, GameStrategyRep>;

private:
/// @name Building reduced form strategies
//@{
void MakeStrategy();
void MakeReducedStrats(class GameNodeRep *, class GameNodeRep *);
void MakeNonReducedStrats();
void MakeNonReducedStratsRecursiveImpl(GameStrategyRep *, Infosets::iterator);
//@}

GameRep *m_game;
int m_number;
std::string m_label;
std::vector<GameInfosetRep *> m_infosets;
std::vector<GameStrategyRep *> m_strategies;
std::vector<GameStrategyRep *> m_nonReducedStrategies;

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

public:
using Infosets = ElementCollection<GamePlayer, GameInfosetRep>;
using Strategies = ElementCollection<GamePlayer, GameStrategyRep>;

int GetNumber() const { return m_number; }
Game GetGame() const;

Expand All @@ -416,6 +421,10 @@ class GamePlayerRep : public GameObject {
GameStrategy GetStrategy(int st) const;
/// Returns the array of strategies available to the player
Strategies GetStrategies() const;
/// Returns the st'th non-reduced strategy for the player
GameStrategy GetNonReducedStrategy(int st) const;
/// Returns the array of non-reduced strategies available to the player
Strategies GetNonReducedStrategies() const;
//@}

/// @name Sequences
Expand Down Expand Up @@ -792,6 +801,16 @@ inline GamePlayerRep::Strategies GamePlayerRep::GetStrategies() const
m_game->BuildComputedValues();
return Strategies(this, &m_strategies);
}
inline GameStrategy GamePlayerRep::GetNonReducedStrategy(int st) const
{
m_game->BuildComputedValues();
return m_nonReducedStrategies.at(st - 1);
}
inline GamePlayerRep::Strategies GamePlayerRep::GetNonReducedStrategies() const
{
m_game->BuildComputedValues();
return Strategies(this, &m_nonReducedStrategies);
}

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

Expand Down
2 changes: 2 additions & 0 deletions src/games/gametree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,7 @@ void GameTreeRep::ClearComputedValues() const
strategy->Invalidate();
}
player->m_strategies.clear();
player->m_nonReducedStrategies.clear();
}
const_cast<GameTreeRep *>(this)->m_nodePlays.clear();
m_computedValues = false;
Expand All @@ -866,6 +867,7 @@ void GameTreeRep::BuildComputedValues() const
const_cast<GameTreeRep *>(this)->Canonicalize();
for (const auto &player : m_players) {
player->MakeReducedStrats(m_root, nullptr);
player->MakeNonReducedStrats();
}
m_computedValues = true;
}
Expand Down
2 changes: 2 additions & 0 deletions src/pygambit/gambit.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ cdef extern from "games/game.h":

c_GameStrategy GetStrategy(int) except +IndexError
Strategies GetStrategies() except +
c_GameStrategy GetNonReducedStrategy(int) except +IndexError
Strategies GetNonReducedStrategies() except +

c_GameInfoset GetInfoset(int) except +IndexError
Infosets GetInfosets() except +
Expand Down
48 changes: 48 additions & 0 deletions src/pygambit/player.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,49 @@ class PlayerStrategies:
raise TypeError(f"Strategy index must be int or str, not {index.__class__.__name__}")


@cython.cclass
class PlayerNonReducedStrategies:
"""The set of non-reduced strategies available to a player."""
player = cython.declare(c_GamePlayer)

def __init__(self, *args, **kwargs) -> None:
raise ValueError("Cannot create PlayerNonReducedStrategies outside a Game.")

@staticmethod
@cython.cfunc
def wrap(player: c_GamePlayer) -> PlayerNonReducedStrategies:
obj: PlayerNonReducedStrategies = PlayerNonReducedStrategies.__new__(
PlayerNonReducedStrategies
)
obj.player = player
return obj

def __repr__(self) -> str:
return f"PlayerNonReducedStrategies(player={Player.wrap(self.player)})"

def __len__(self):
"""The number of non-reduced strategies for the player in the game."""
return self.player.deref().GetNonReducedStrategies().size()

def __iter__(self) -> typing.Iterator[Strategy]:
for strategy in self.player.deref().GetNonReducedStrategies():
yield Strategy.wrap(strategy)

def __getitem__(self, index: typing.Union[int, str]) -> Strategy:
if isinstance(index, str):
if not index.strip():
raise ValueError("Strategy label cannot be empty or all whitespace")
matches = [x for x in self if x.label == index.strip()]
if not matches:
raise KeyError(f"Player has no strategy with label '{index}'")
if len(matches) > 1:
raise ValueError(f"Player has multiple strategies with label '{index}'")
return matches[0]
if isinstance(index, int):
return Strategy.wrap(self.player.deref().GetNonReducedStrategy(index + 1))
raise TypeError(f"Strategy index must be int or str, not {index.__class__.__name__}")


@cython.cclass
class Player:
"""A player in a ``Game``."""
Expand Down Expand Up @@ -214,6 +257,11 @@ class Player:
"""Returns the set of strategies belonging to the player."""
return PlayerStrategies.wrap(self.player)

@property
def non_reduced_strategies(self) -> PlayerNonReducedStrategies:
"""Returns the set of nonreduced strategies belonging to the player."""
return PlayerNonReducedStrategies.wrap(self.player)

@property
def infosets(self) -> PlayerInfosets:
"""Returns the set of information sets at which the player has the decision."""
Expand Down