Skip to content

Commit a78277b

Browse files
committed
Experiment with removing indexing outcomes by number
1 parent 1ce412b commit a78277b

File tree

7 files changed

+35
-63
lines changed

7 files changed

+35
-63
lines changed

src/games/file.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,14 @@ void ReadOutcomeList(GameFileLexer &p_parser, Game &p_nfg)
411411
void ParseOutcomeBody(GameFileLexer &p_parser, Game &p_nfg)
412412
{
413413
ReadOutcomeList(p_parser, p_nfg);
414+
const auto &outcomes = p_nfg->GetOutcomes();
415+
std::vector<GameOutcome> outcome_index;
416+
outcome_index.reserve(outcomes.size());
417+
std::copy(outcomes.begin(), outcomes.end(), std::back_inserter(outcome_index));
414418
for (const auto &profile : StrategyContingencies(p_nfg)) {
415419
p_parser.ExpectCurrentToken(TOKEN_NUMBER, "outcome index");
416420
if (const int outcomeId = std::stoi(p_parser.GetLastText())) {
417-
profile->SetOutcome(p_nfg->GetOutcome(outcomeId));
421+
profile->SetOutcome(outcome_index[outcomeId - 1]);
418422
}
419423
p_parser.GetNextToken();
420424
}

src/games/game.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,8 +1102,6 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
11021102

11031103
/// @name Outcomes
11041104
//@{
1105-
/// Returns the index'th outcome defined in the game
1106-
GameOutcome GetOutcome(int index) const { return m_outcomes.at(index - 1); }
11071105
/// Returns the set of outcomes in the game
11081106
Outcomes GetOutcomes() const
11091107
{

src/gui/gameframe.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -969,9 +969,9 @@ void GameFrame::OnEditNode(wxCommandEvent &)
969969
if (dialog.ShowModal() == wxID_OK) {
970970
try {
971971
m_doc->DoSetNodeLabel(m_doc->GetSelectNode(), dialog.GetNodeName());
972-
if (dialog.GetOutcome() > 0) {
973-
m_doc->DoSetOutcome(m_doc->GetSelectNode(),
974-
m_doc->GetGame()->GetOutcome(dialog.GetOutcome()));
972+
if (const auto outcomeId = dialog.GetOutcome(); outcomeId > 0) {
973+
const auto outcome = *std::next(m_doc->GetGame()->GetOutcomes().begin(), outcomeId - 1);
974+
m_doc->DoSetOutcome(m_doc->GetSelectNode(), outcome);
975975
}
976976
else {
977977
m_doc->DoSetOutcome(m_doc->GetSelectNode(), nullptr);

src/pygambit/gambit.pxd

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,6 @@ cdef extern from "games/game.h":
264264
c_GamePlayer NewPlayer() except +
265265

266266
int NumOutcomes() except +
267-
c_GameOutcome GetOutcome(int) except +IndexError
268267
Outcomes GetOutcomes() except +
269268
c_GameOutcome NewOutcome() except +
270269
void DeleteOutcome(c_GameOutcome) except +

src/pygambit/game.pxi

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ class GameOutcomes:
237237
for outcome in self.game.deref().GetOutcomes():
238238
yield Outcome.wrap(outcome)
239239

240-
def __getitem__(self, index: int | str) -> Outcome:
240+
def __getitem__(self, index: str) -> Outcome:
241241
if isinstance(index, str):
242242
if not index.strip():
243243
raise ValueError("Outcome label cannot be empty or all whitespace")
@@ -247,9 +247,7 @@ class GameOutcomes:
247247
if len(matches) > 1:
248248
raise ValueError(f"Game has multiple outcomes with label '{index}'")
249249
return matches[0]
250-
if isinstance(index, int):
251-
return Outcome.wrap(self.game.deref().GetOutcome(index + 1))
252-
raise TypeError(f"Outcome index must be int or str, not {index.__class__.__name__}")
250+
raise TypeError(f"Outcome index must be str, not {index.__class__.__name__}")
253251

254252

255253
@cython.cclass

tests/test_game.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,6 @@ def test_from_dict():
9595
assert game.players[1].label == "b"
9696

9797

98-
def test_game_get_outcome_by_index():
99-
game = gbt.Game.new_table([2, 2])
100-
assert game[0, 0] == game.outcomes[0]
101-
102-
103-
def test_game_get_outcome_by_label():
104-
game = gbt.Game.new_table([2, 2])
105-
game.players[0].strategies[0].label = "defect"
106-
game.players[1].strategies[0].label = "cooperate"
107-
assert game["defect", "cooperate"] == game.outcomes[0]
108-
109-
11098
def test_game_get_outcome_invalid_tuple_size():
11199
game = gbt.Game.new_table([2, 2])
112100
with pytest.raises(KeyError):
@@ -141,7 +129,7 @@ def test_game_get_outcome_unmatched_label():
141129

142130
def test_game_get_outcome_with_strategies():
143131
game = gbt.Game.new_table([2, 2])
144-
assert game[game.players[0].strategies[0], game.players[1].strategies[0]] == game.outcomes[0]
132+
_ = game[game.players[0].strategies[0], game.players[1].strategies[0]]
145133

146134

147135
def test_game_get_outcome_with_bad_strategies():
@@ -162,7 +150,8 @@ def test_game_dereference_invalid():
162150
def test_mixed_strategy_profile_game_structure_changed_no_tree():
163151
g = gbt.Game.from_arrays([[2, 2], [0, 0]], [[0, 0], [1, 1]])
164152
profiles = [g.mixed_strategy_profile(rational=b) for b in [False, True]]
165-
g.outcomes[0][g.players[0]] = 3
153+
outcome = next(iter(g.outcomes))
154+
outcome[g.players[0]] = 3
166155
for profile in profiles:
167156
with pytest.raises(gbt.GameStructureChangedError):
168157
profile.copy()

tests/test_outcomes.py

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,37 +17,20 @@ def test_outcome_add(game: gbt.Game):
1717
)
1818
def test_outcome_delete(game: gbt.Game):
1919
outcome_count = len(game.outcomes)
20-
game.delete_outcome(game.outcomes[0])
20+
game.delete_outcome(next(iter(game.outcomes)))
2121
assert len(game.outcomes) == outcome_count - 1
2222

2323

24-
@pytest.mark.parametrize(
25-
"game,label",
26-
[(gbt.Game.new_table([2, 2]), "outcome label")]
27-
)
28-
def test_outcome_label(game: gbt.Game, label: str):
29-
game.outcomes[0].label = label
30-
assert game.outcomes[0].label == label
31-
32-
3324
@pytest.mark.parametrize(
3425
"game,label",
3526
[(gbt.Game.new_table([2, 2]), "outcome label")]
3627
)
3728
def test_outcome_index_label(game: gbt.Game, label: str):
38-
game.outcomes[0].label = label
39-
assert game.outcomes[0] == game.outcomes[label]
29+
outcome = next(iter(game.outcomes))
30+
outcome.label = label
4031
assert game.outcomes[label].label == label
4132

4233

43-
@pytest.mark.parametrize(
44-
"game", [gbt.Game.new_table([2, 2])]
45-
)
46-
def test_outcome_index_int_range(game: gbt.Game):
47-
with pytest.raises(IndexError):
48-
_ = game.outcomes[2 * len(game.outcomes)]
49-
50-
5134
@pytest.mark.parametrize(
5235
"game", [gbt.Game.new_table([2, 2])]
5336
)
@@ -68,27 +51,28 @@ def test_outcome_payoff_by_player_label():
6851
game = gbt.Game.new_table([2, 2])
6952
game.players[0].label = "joe"
7053
game.players[1].label = "dan"
71-
game.outcomes[0]["joe"] = 1
72-
game.outcomes[0]["dan"] = 2
73-
game.outcomes[1]["joe"] = 3
74-
game.outcomes[1]["dan"] = 4
75-
assert game.outcomes[0]["joe"] == 1
76-
assert game.outcomes[0]["dan"] == 2
77-
assert game.outcomes[1]["joe"] == 3
78-
assert game.outcomes[1]["dan"] == 4
54+
outcomes = list(game.outcomes)
55+
outcomes[0]["joe"] = 1
56+
outcomes[0]["dan"] = 2
57+
outcomes[1]["joe"] = 3
58+
outcomes[1]["dan"] = 4
59+
assert outcomes[0]["joe"] == 1
60+
assert outcomes[0]["dan"] == 2
61+
assert outcomes[1]["joe"] == 3
62+
assert outcomes[1]["dan"] == 4
7963

8064

8165
def test_outcome_payoff_by_player():
8266
game = gbt.Game.new_table([2, 2])
8367
game.players[0].label = "joe"
8468
game.players[1].label = "dan"
85-
game.outcomes[0]["joe"] = 1
86-
game.outcomes[0]["dan"] = 2
87-
game.outcomes[1]["joe"] = 3
88-
game.outcomes[1]["dan"] = 4
89-
player1 = game.players[0]
90-
player2 = game.players[1]
91-
assert game.outcomes[0][player1] == 1
92-
assert game.outcomes[0][player2] == 2
93-
assert game.outcomes[1][player1] == 3
94-
assert game.outcomes[1][player2] == 4
69+
outcomes = list(game.outcomes)
70+
outcomes[0]["joe"] = 1
71+
outcomes[0]["dan"] = 2
72+
outcomes[1]["joe"] = 3
73+
outcomes[1]["dan"] = 4
74+
player1, player2 = game.players
75+
assert outcomes[0][player1] == 1
76+
assert outcomes[0][player2] == 2
77+
assert outcomes[1][player1] == 3
78+
assert outcomes[1][player2] == 4

0 commit comments

Comments
 (0)