Skip to content

Issue 522 #524

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
5 changes: 3 additions & 2 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
been removed as planned. (#357)

### Added
- Implement `GetPlays()` (C++) and `get_plays` (Python) to compute the set of terminal nodes consistent
with a node, information set, or action (#517)
- Implement `GameRep::GetPlays` (C++) and `Node.plays`, `Infoset.plays`, and `Action.plays` (Python)
to compute the set of terminal nodes consistent with a node, information set, or action (#517)
- Implement `GameRep::GetVeto` (C++) and `Action.veto` (Python) to compute the veto of an action (#522).


## [16.3.1] - unreleased
Expand Down
1 change: 1 addition & 0 deletions doc/pygambit.api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ Information about the game
Action.precedes
Action.prob
Action.plays
Action.power
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be Action.veto


.. autosummary::

Expand Down
3 changes: 3 additions & 0 deletions src/games/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,9 @@ class GameRep : public BaseGameRep {
/// Returns the set of terminal nodes which are descendants of members of an action
virtual std::vector<GameNode> GetPlays(GameAction action) const { throw UndefinedException(); }

/// Returns the power of an action
virtual std::vector<GameNode> GetVeto(GameAction action) const { throw UndefinedException(); }

/// Returns true if the game is perfect recall. If not,
/// a pair of violating information sets is returned in the parameters.
virtual bool IsPerfectRecall(GameInfoset &, GameInfoset &) const = 0;
Expand Down
13 changes: 13 additions & 0 deletions src/games/gametree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,19 @@ std::vector<GameNode> GameTreeRep::GetPlays(GameAction action) const
return plays;
}

std::vector<GameNode> GameTreeRep::GetVeto(GameAction action) const
{
std::vector<GameNode> veto;
const std::vector<GameNode> aplays = GetPlays(action);

for (const auto &node : GetPlays(action->GetInfoset())) {
if (!contains(aplays, node)) {
veto.push_back(node);
}
}
return veto;
}

void GameTreeRep::DeleteOutcome(const GameOutcome &p_outcome)
{
IncrementVersion();
Expand Down
2 changes: 2 additions & 0 deletions src/games/gametree.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ class GameTreeRep : public GameExplicitRep {
std::vector<GameNode> GetPlays(GameInfoset infoset) const override;
std::vector<GameNode> GetPlays(GameAction action) const override;

std::vector<GameNode> GetVeto(GameAction action) const override;

Game CopySubgame(GameNode) const override;
//@}

Expand Down
11 changes: 10 additions & 1 deletion src/pygambit/action.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,18 @@ class Action:

@property
def plays(self) -> typing.List[Node]:
"""Returns a list of all terminal `Node` objects consistent with it.
"""Returns a list of all terminal `Node` objects consistent with the action.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simpler is just to say 'terminal nodes' rather than 'terminal Node objects'

"""
return [
Node.wrap(n) for n in
self.action.deref().GetInfoset().deref().GetGame().deref().GetPlays(self.action)
]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring has not been updated.

It would be useful to write a brief explanation of what the 'veto' of an action is here - a few lines so the documentation is self-contained

@property
def veto(self) -> typing.List[Node]:
"""Returns power of the action.
"""
return [
Node.wrap(n) for n in
self.action.deref().GetInfoset().deref().GetGame().deref().GetVeto(self.action)
]
1 change: 1 addition & 0 deletions src/pygambit/gambit.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ cdef extern from "games/game.h":
stdvector[c_GameNode] GetPlays(c_GameNode) except +
stdvector[c_GameNode] GetPlays(c_GameInfoset) except +
stdvector[c_GameNode] GetPlays(c_GameAction) except +
stdvector[c_GameNode] GetVeto(c_GameAction) except +
bool IsPerfectRecall() except +

c_GameInfoset AppendMove(c_GameNode, c_GamePlayer, int) except +ValueError
Expand Down
15 changes: 15 additions & 0 deletions tests/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,18 @@ def test_action_plays():
} # paths=[0, 1, 0], [0, 1]

assert set(test_action.plays) == expected_set_of_plays


def test_action_veto():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This explanation would be better put in the docstring of veto rather than in the tests.

"""Verify `action.veto` returns the action's veto
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be useful to have a few test fixtures rather than a single test (see other examples throughout the test suite for setting up fixtures). In particular think about potential edge cases - e.g. actions at the root node, actions at singleton information sets versus nontrivial information sets, so on.

(terminal `Node` objects contained in the information set associated with this action
that cannot be reached from this action).
"""
game = games.read_from_file("e01.efg")
list_infosets = list(game.infosets)

test_action = list_infosets[2].actions[0] # members' paths=[0, 1, 0], [0, 1]

expected_set_of_plays = set(list_infosets[2].plays) - set(test_action.plays)

assert set(test_action.veto) == expected_set_of_plays