Skip to content

Commit 6ef5d57

Browse files
committed
Rationalise implementation of outcome objects
This performs some outstanding rationalisation of `GameObjectRep` objects: * Removes indexing of payoffs by player index * Templates `GetPayoff` to obtain payoffs directly in the desired representation * Store payoffs using a `std::map` instead of `Array`. Closes #490.
1 parent 70ee889 commit 6ef5d57

20 files changed

+68
-64
lines changed

src/games/behavmixed.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ void MixedBehaviorProfile<T>::GetPayoff(const GameNode &node, const T &prob,
381381
const GamePlayer &player, T &value) const
382382
{
383383
if (node->GetOutcome()) {
384-
value += prob * static_cast<T>(node->GetOutcome()->GetPayoff(player));
384+
value += prob * node->GetOutcome()->GetPayoff<T>(player);
385385
}
386386

387387
if (!node->IsTerminal()) {
@@ -520,7 +520,7 @@ void MixedBehaviorProfile<T>::ComputePass2_beliefs_nodeValues_actionValues(
520520
if (node->GetOutcome()) {
521521
GameOutcome outcome = node->GetOutcome();
522522
for (auto player : m_support.GetGame()->GetPlayers()) {
523-
map_nodeValues[node][player] += static_cast<T>(outcome->GetPayoff(player));
523+
map_nodeValues[node][player] += outcome->GetPayoff<T>(player);
524524
}
525525
}
526526

src/games/behavpure.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ T PureBehaviorProfile::GetPayoff(const GameNode &p_node, const GamePlayer &p_pla
4949
T payoff(0);
5050

5151
if (p_node->GetOutcome()) {
52-
payoff += static_cast<T>(p_node->GetOutcome()->GetPayoff(p_player));
52+
payoff += p_node->GetOutcome()->GetPayoff<T>(p_player);
5353
}
5454

5555
if (!p_node->IsTerminal()) {

src/games/game.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ namespace Gambit {
4141
// class GameOutcomeRep
4242
//========================================================================
4343

44-
GameOutcomeRep::GameOutcomeRep(GameRep *p_game, int p_number)
45-
: m_game(p_game), m_number(p_number), m_payoffs(m_game->NumPlayers())
44+
GameOutcomeRep::GameOutcomeRep(GameRep *p_game, int p_number) : m_game(p_game), m_number(p_number)
4645
{
46+
for (const auto &player : p_game->GetPlayers()) {
47+
m_payoffs[player] = Number();
48+
}
4749
}
4850

4951
//========================================================================

src/games/game.h

+16-13
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class GameOutcomeRep : public GameObject {
121121
GameRep *m_game;
122122
int m_number;
123123
std::string m_label;
124-
Array<Number> m_payoffs;
124+
std::map<GamePlayerRep *, Number> m_payoffs;
125125

126126
/// @name Lifecycle
127127
//@{
@@ -143,12 +143,8 @@ class GameOutcomeRep : public GameObject {
143143
/// Sets the text label associated with the outcome
144144
void SetLabel(const std::string &p_label) { m_label = p_label; }
145145

146-
/// Gets the payoff associated with the outcome to player 'pl'
147-
const Number &GetPayoff(int pl) const { return m_payoffs[pl]; }
148146
/// Gets the payoff associated with the outcome to the player
149-
const Number &GetPayoff(const GamePlayer &p_player) const;
150-
/// Sets the payoff to player 'pl'
151-
void SetPayoff(int pl, const Number &p_value);
147+
template <class T> const T &GetPayoff(const GamePlayer &p_player) const;
152148
/// Sets the payoff to the player
153149
void SetPayoff(const GamePlayer &p_player, const Number &p_value);
154150
//@}
@@ -586,27 +582,34 @@ class GameRep : public BaseGameRep {
586582
// all classes to be defined.
587583

588584
inline Game GameOutcomeRep::GetGame() const { return m_game; }
589-
inline const Number &GameOutcomeRep::GetPayoff(const GamePlayer &p_player) const
585+
586+
template <class T> const T &GameOutcomeRep::GetPayoff(const GamePlayer &p_player) const
590587
{
591-
if (p_player->GetGame() != GetGame()) {
588+
try {
589+
return static_cast<const T &>(m_payoffs.at(p_player));
590+
}
591+
catch (const std::out_of_range &) {
592592
throw MismatchException();
593593
}
594-
return m_payoffs[p_player->GetNumber()];
595594
}
596595

597-
inline void GameOutcomeRep::SetPayoff(int pl, const Number &p_value)
596+
template <> inline const Number &GameOutcomeRep::GetPayoff(const GamePlayer &p_player) const
598597
{
599-
m_game->IncrementVersion();
600-
m_payoffs[pl] = p_value;
598+
try {
599+
return m_payoffs.at(p_player);
600+
}
601+
catch (const std::out_of_range &) {
602+
throw MismatchException();
603+
}
601604
}
602605

603606
inline void GameOutcomeRep::SetPayoff(const GamePlayer &p_player, const Number &p_value)
604607
{
605608
if (p_player->GetGame() != GetGame()) {
606609
throw MismatchException();
607610
}
611+
m_payoffs[p_player] = p_value;
608612
m_game->IncrementVersion();
609-
m_payoffs[p_player->GetNumber()] = p_value;
610613
}
611614

612615
inline GamePlayer GameStrategyRep::GetPlayer() const { return m_player; }

src/games/gameexpl.cc

+4-4
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ Rational GameExplicitRep::GetMinPayoff(int player) const
6666
p2 = NumPlayers();
6767
}
6868

69-
Rational minpay = static_cast<Rational>(m_outcomes.front()->GetPayoff(p1));
69+
Rational minpay = m_outcomes.front()->GetPayoff<Rational>(GetPlayer(p1));
7070
for (auto outcome : m_outcomes) {
7171
for (int p = p1; p <= p2; p++) {
72-
minpay = std::min(minpay, static_cast<Rational>(outcome->GetPayoff(p)));
72+
minpay = std::min(minpay, outcome->GetPayoff<Rational>(GetPlayer(p)));
7373
}
7474
}
7575
return minpay;
@@ -91,10 +91,10 @@ Rational GameExplicitRep::GetMaxPayoff(int player) const
9191
p2 = NumPlayers();
9292
}
9393

94-
Rational maxpay = static_cast<Rational>(m_outcomes.front()->GetPayoff(p1));
94+
Rational maxpay = m_outcomes.front()->GetPayoff<Rational>(GetPlayer(p1));
9595
for (auto outcome : m_outcomes) {
9696
for (int p = p1; p <= p2; p++) {
97-
maxpay = std::max(maxpay, static_cast<Rational>(outcome->GetPayoff(p)));
97+
maxpay = std::max(maxpay, outcome->GetPayoff<Rational>(GetPlayer(p)));
9898
}
9999
}
100100
return maxpay;

src/games/gametable.cc

+9-10
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Rational TablePureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) cons
9393
{
9494
GameOutcomeRep *outcome = dynamic_cast<GameTableRep &>(*m_nfg).m_results[m_index];
9595
if (outcome) {
96-
return static_cast<Rational>(outcome->GetPayoff(p_player));
96+
return outcome->GetPayoff<Rational>(p_player);
9797
}
9898
else {
9999
return Rational(0);
@@ -102,13 +102,12 @@ Rational TablePureStrategyProfileRep::GetPayoff(const GamePlayer &p_player) cons
102102

103103
Rational TablePureStrategyProfileRep::GetStrategyValue(const GameStrategy &p_strategy) const
104104
{
105-
int player = p_strategy->GetPlayer()->GetNumber();
105+
const auto &player = p_strategy->GetPlayer();
106106
GameOutcomeRep *outcome =
107107
dynamic_cast<GameTableRep &>(*m_nfg)
108-
.m_results[m_index - m_profile.at(p_strategy->GetPlayer())->m_offset +
109-
p_strategy->m_offset];
108+
.m_results[m_index - m_profile.at(player)->m_offset + p_strategy->m_offset];
110109
if (outcome) {
111-
return static_cast<Rational>(outcome->GetPayoff(player));
110+
return outcome->GetPayoff<Rational>(player);
112111
}
113112
else {
114113
return Rational(0);
@@ -164,7 +163,7 @@ T TableMixedStrategyProfileRep<T>::GetPayoff(int pl, int index, int current) con
164163
auto &g = dynamic_cast<GameTableRep &>(*game);
165164
GameOutcomeRep *outcome = g.m_results[index];
166165
if (outcome) {
167-
return static_cast<T>(outcome->GetPayoff(pl));
166+
return outcome->GetPayoff<T>(this->m_support.GetGame()->GetPlayer(pl));
168167
}
169168
else {
170169
return T(0);
@@ -197,7 +196,7 @@ void TableMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, int const_pl, int c
197196
auto &g = dynamic_cast<GameTableRep &>(*game);
198197
GameOutcomeRep *outcome = g.m_results[index];
199198
if (outcome) {
200-
value += prob * static_cast<T>(outcome->GetPayoff(pl));
199+
value += prob * outcome->GetPayoff<T>(this->m_support.GetGame()->GetPlayer(pl));
201200
}
202201
}
203202
else {
@@ -230,7 +229,7 @@ void TableMixedStrategyProfileRep<T>::GetPayoffDeriv(int pl, int const_pl1, int
230229
auto &g = dynamic_cast<GameTableRep &>(*game);
231230
GameOutcomeRep *outcome = g.m_results[index];
232231
if (outcome) {
233-
value += prob * static_cast<T>(outcome->GetPayoff(pl));
232+
value += prob * outcome->GetPayoff<T>(this->m_support.GetGame()->GetPlayer(pl));
234233
}
235234
}
236235
else {
@@ -371,7 +370,7 @@ void GameTableRep::WriteNfgFile(std::ostream &p_file) const
371370
p_file << "{ " + QuoteString(outcome->GetLabel()) << ' '
372371
<< FormatList(
373372
players,
374-
[outcome](const GamePlayer &p) { return std::string(outcome->GetPayoff(p)); },
373+
[outcome](const GamePlayer &p) { return outcome->GetPayoff<std::string>(p); },
375374
true, false)
376375
<< " }" << std::endl;
377376
}
@@ -393,7 +392,7 @@ GamePlayer GameTableRep::NewPlayer()
393392
auto player = new GamePlayerRep(this, m_players.size() + 1, 1);
394393
m_players.push_back(player);
395394
for (auto outcome : m_outcomes) {
396-
outcome->m_payoffs.push_back(Number());
395+
outcome->m_payoffs[player] = Number();
397396
}
398397
return player;
399398
}

src/games/gametree.cc

+5-6
Original file line numberDiff line numberDiff line change
@@ -720,8 +720,8 @@ Rational SubtreeSum(const GameNode &p_node)
720720
}
721721

722722
if (p_node->GetOutcome()) {
723-
for (int pl = 1; pl <= p_node->GetGame()->NumPlayers(); pl++) {
724-
sum += static_cast<Rational>(p_node->GetOutcome()->GetPayoff(pl));
723+
for (const auto &player : p_node->GetGame()->GetPlayers()) {
724+
sum += p_node->GetOutcome()->GetPayoff<Rational>(player);
725725
}
726726
}
727727
return sum;
@@ -916,7 +916,7 @@ void WriteEfgFile(std::ostream &f, const GameNode &n)
916916
f << n->GetOutcome()->GetNumber() << " " << QuoteString(n->GetOutcome()->GetLabel()) << ' '
917917
<< FormatList(
918918
n->GetGame()->GetPlayers(),
919-
[n](const GamePlayer &p) { return std::string(n->GetOutcome()->GetPayoff(p)); }, true)
919+
[n](const GamePlayer &p) { return n->GetOutcome()->GetPayoff<std::string>(p); }, true)
920920
<< std::endl;
921921
}
922922
else {
@@ -968,11 +968,10 @@ int GameTreeRep::BehavProfileLength() const
968968
GamePlayer GameTreeRep::NewPlayer()
969969
{
970970
IncrementVersion();
971-
GamePlayerRep *player = nullptr;
972-
player = new GamePlayerRep(this, m_players.size() + 1);
971+
auto player = new GamePlayerRep(this, m_players.size() + 1);
973972
m_players.push_back(player);
974973
for (auto &outcome : m_outcomes) {
975-
outcome->m_payoffs.push_back(Number());
974+
outcome->m_payoffs[player] = Number();
976975
}
977976
ClearComputedValues();
978977
return player;

src/games/nash.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ template <class T> class SubgameSolution {
103103
for (const auto &player : p_subroot->GetGame()->GetPlayers()) {
104104
T value = p_profile.GetPayoff(*subplayer);
105105
if (outcome) {
106-
value += static_cast<T>(outcome->GetPayoff(*subplayer));
106+
value += outcome->GetPayoff<T>(*subplayer);
107107
}
108108
solution.node_values[p_subroot]->SetPayoff(player, Number(static_cast<Rational>(value)));
109109
++subplayer;

src/games/writer.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ std::string WriteHTMLFile(const Game &p_game, const GamePlayer &p_rowPlayer,
7070
for (const auto &player : p_game->GetPlayers()) {
7171
try {
7272
if (profile->GetOutcome()) {
73-
theHtml += static_cast<std::string>(profile->GetOutcome()->GetPayoff(player));
73+
theHtml += profile->GetOutcome()->GetPayoff<std::string>(player);
7474
}
7575
else {
7676
theHtml += "0";
@@ -148,7 +148,7 @@ std::string WriteLaTeXFile(const Game &p_game, const GamePlayer &p_rowPlayer,
148148
for (const auto &player : p_game->GetPlayers()) {
149149
try {
150150
if (profile->GetOutcome()) {
151-
theHtml += static_cast<std::string>(profile->GetOutcome()->GetPayoff(player));
151+
theHtml += profile->GetOutcome()->GetPayoff<std::string>(player);
152152
}
153153
else {
154154
theHtml += "0";

src/gui/dleditnode.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,10 @@ dialogEditNode::dialogEditNode(wxWindow *p_parent, const Gambit::GameNode &p_nod
114114
item = "Outcome" + Gambit::lexical_cast<std::string>(outc);
115115
}
116116

117-
item += (" (" + static_cast<std::string>(outcome->GetPayoff(1)) + ", " +
118-
static_cast<std::string>(outcome->GetPayoff(2)));
117+
item += (" (" + outcome->GetPayoff<std::string>(efg->GetPlayer(1)) + ", " +
118+
outcome->GetPayoff<std::string>(efg->GetPlayer(2)));
119119
if (efg->NumPlayers() > 2) {
120-
item += ", " + static_cast<std::string>(outcome->GetPayoff(3));
120+
item += ", " + outcome->GetPayoff<std::string>(efg->GetPlayer(3));
121121
if (efg->NumPlayers() > 3) {
122122
item += ",...)";
123123
}

src/gui/efgdisplay.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ void gbtPayoffEditor::BeginEdit(gbtNodeEntry *p_entry, int p_player)
5454
m_entry = p_entry;
5555
m_outcome = p_entry->GetNode()->GetOutcome();
5656
m_player = p_player;
57-
SetValue(
58-
wxString(static_cast<std::string>(m_outcome->GetPayoff(p_player)).c_str(), *wxConvCurrent));
57+
SetValue(wxString(
58+
m_outcome->GetPayoff<std::string>(p_entry->GetNode()->GetGame()->GetPlayer(p_player))
59+
.c_str(),
60+
*wxConvCurrent));
5961
SetSize(wxSize(GetSize().GetWidth(), GetBestSize().GetHeight()));
6062
SetSelection(-1, -1);
6163
Show(true);

src/gui/efglayout.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -271,12 +271,12 @@ void gbtNodeEntry::DrawOutcome(wxDC &p_dc, bool p_noHints) const
271271
Gambit::GamePlayer player = m_node->GetGame()->GetPlayer(pl);
272272
p_dc.SetTextForeground(m_style->GetPlayerColor(pl));
273273

274-
std::string payoff = static_cast<std::string>(outcome->GetPayoff(pl));
274+
const auto &payoff = outcome->GetPayoff<std::string>(player);
275275

276276
if (payoff.find('/') != std::string::npos) {
277277
p_dc.SetPen(wxPen(m_style->GetPlayerColor(pl), 1, wxPENSTYLE_SOLID));
278278
int oldX = point.x;
279-
point = DrawFraction(p_dc, point, static_cast<Gambit::Rational>(outcome->GetPayoff(pl)));
279+
point = DrawFraction(p_dc, point, outcome->GetPayoff<Rational>(player));
280280
m_payoffRect.push_back(wxRect(oldX - 5, point.y - height / 2, point.x - oldX + 10, height));
281281
}
282282
else {

src/gui/gamedoc.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -811,16 +811,16 @@ void gbtGameDocument::DoCopyOutcome(GameNode p_node, GameOutcome p_outcome)
811811
{
812812
GameOutcome outcome = m_game->NewOutcome();
813813
outcome->SetLabel("Outcome" + lexical_cast<std::string>(outcome->GetNumber()));
814-
for (int pl = 1; pl <= m_game->NumPlayers(); pl++) {
815-
outcome->SetPayoff(pl, p_outcome->GetPayoff(pl));
814+
for (const auto &player : m_game->GetPlayers()) {
815+
outcome->SetPayoff(player, p_outcome->GetPayoff<Number>(player));
816816
}
817817
p_node->SetOutcome(outcome);
818818
UpdateViews(GBT_DOC_MODIFIED_PAYOFFS);
819819
}
820820

821821
void gbtGameDocument::DoSetPayoff(GameOutcome p_outcome, int p_player, const wxString &p_value)
822822
{
823-
p_outcome->SetPayoff(p_player, Number(p_value.ToStdString()));
823+
p_outcome->SetPayoff(m_game->GetPlayer(p_player), Number(p_value.ToStdString()));
824824
UpdateViews(GBT_DOC_MODIFIED_PAYOFFS);
825825
}
826826

src/pygambit/gambit.pxd

+1-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ cdef extern from "games/game.h":
150150
string GetLabel() except +
151151
void SetLabel(string) except +
152152

153-
c_Number GetPayoff(c_GamePlayer) except +IndexError
153+
T GetPayoff[T](c_GamePlayer) except +IndexError
154154
void SetPayoff(c_GamePlayer, c_Number) except +IndexError
155155

156156
cdef cppclass c_GameNodeRep "GameNodeRep":

src/pygambit/outcome.pxi

+1-3
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,7 @@ class Outcome:
8585
resolved_player = cython.cast(Player,
8686
self.game._resolve_player(player, "Outcome.__getitem__"))
8787
payoff = (
88-
cython.cast(bytes,
89-
self.outcome.deref().GetPayoff(resolved_player.player).as_string())
90-
.decode("ascii")
88+
self.outcome.deref().GetPayoff[string](resolved_player.player).decode("ascii")
9189
)
9290
if "." in payoff:
9391
return decimal.Decimal(payoff)

src/solvers/enumpoly/behavextend.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ PolynomialSystem<double> NashExpectedPayoffDiffPolys(
256256
if (NashNodeProbabilityPoly(p_solution, node_prob, BehavStratSpace, support, var_index,
257257
node, infoset, action)) {
258258
if (node->GetOutcome()) {
259-
node_prob *= static_cast<double>(node->GetOutcome()->GetPayoff(player));
259+
node_prob *= node->GetOutcome()->GetPayoff<double>(player);
260260
}
261261
next_poly += node_prob;
262262
}
@@ -397,7 +397,7 @@ PolynomialSystem<double> ANFExpectedPayoffDiffPolys(const MixedBehaviorProfile<d
397397
terminal, player->GetNumber(), infoset->GetNumber(),
398398
action->GetNumber())) {
399399
if (terminal->GetOutcome()) {
400-
node_prob *= static_cast<double>(terminal->GetOutcome()->GetPayoff(player));
400+
node_prob *= terminal->GetOutcome()->GetPayoff<double>(player);
401401
}
402402
next_poly += node_prob;
403403
}

src/solvers/enumpoly/gameseq.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ void GameSequenceForm::FillTableau(const GameNode &n, const Rational &prob,
7171
if (n->GetOutcome()) {
7272
for (auto player : m_support.GetGame()->GetPlayers()) {
7373
GetPayoffEntry(p_currentSequences, player) +=
74-
prob * static_cast<Rational>(n->GetOutcome()->GetPayoff(player));
74+
prob * n->GetOutcome()->GetPayoff<Rational>(player);
7575
}
7676
}
7777
if (!n->GetInfoset()) {

src/solvers/lcp/efglcp.cc

+4-4
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,10 @@ void NashLcpBehaviorSolver<T>::FillTableau(Matrix<T> &A, const GameNode &n, T pr
247247

248248
GameOutcome outcome = n->GetOutcome();
249249
if (outcome) {
250-
A(s1, ns1 + s2) +=
251-
Rational(prob) * (static_cast<Rational>(outcome->GetPayoff(1)) - p_solution.maxpay);
252-
A(ns1 + s2, s1) +=
253-
Rational(prob) * (static_cast<Rational>(outcome->GetPayoff(2)) - p_solution.maxpay);
250+
A(s1, ns1 + s2) += Rational(prob) * (outcome->GetPayoff<Rational>(n->GetGame()->GetPlayer(1)) -
251+
p_solution.maxpay);
252+
A(ns1 + s2, s1) += Rational(prob) * (outcome->GetPayoff<Rational>(n->GetGame()->GetPlayer(2)) -
253+
p_solution.maxpay);
254254
}
255255
if (n->IsTerminal()) {
256256
return;

src/solvers/logit/logbehav.imp

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ template <class T> void LogBehavProfile<T>::ComputeSolutionDataPass2(const GameN
183183
if (node->GetOutcome()) {
184184
GameOutcome outcome = node->GetOutcome();
185185
for (auto player : m_game->GetPlayers()) {
186-
m_nodeValues[node][player] += static_cast<T>(outcome->GetPayoff(player));
186+
m_nodeValues[node][player] += outcome->GetPayoff<T>(player);
187187
}
188188
}
189189

0 commit comments

Comments
 (0)