Skip to content

Commit

Permalink
mixed behav profile: payoff, infoset_value, and action_value all thro…
Browse files Browse the repository at this point in the history
…w value errors when called with the chance player
  • Loading branch information
rahulsavani committed Oct 11, 2023
1 parent 2150a73 commit fc9734a
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 3 deletions.
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- Empty or all-whitespace strings cannot be used to access members of games in pygambit.
- Remaining compatibility code for wxWidgets 2.x removed from graphical interface.
- Migrated to pytest for testing of pygambit.
- ValueErrors raised for mixed behavior profiles when payoff, action_value, or infoset_value are
called with the chance player.


## [16.1.0a3] - 2023-09-29
Expand Down
21 changes: 18 additions & 3 deletions src/pygambit/behav.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,13 @@ class MixedBehaviorProfile:
If `player` is a `Player` from a different game.
KeyError
If `player` is a string and no player in the game has that label.
ValueError
If `player` resolves to the chance player
"""
return self._payoff(self.game._resolve_player(player, 'payoff'))
resolved_player = self.game._resolve_player(player, 'payoff')
if resolved_player.is_chance:
raise ValueError("payoff() is not defined for the chance player")
return self._payoff(resolved_player)

def infoset_value(self, infoset: typing.Union[Infoset, str]):
"""Returns the expected payoff to the player conditional on reaching an information set,
Expand All @@ -303,8 +308,13 @@ class MixedBehaviorProfile:
If `infoset` is an `Infoset` from a different game.
KeyError
If `infoset` is a string and no information set in the game has that label.
ValueError
If `infoset` resolves to an infoset that belongs to the chance player
"""
return self._infoset_value(self.game._resolve_infoset(infoset, 'infoset_value'))
resolved_infoset = self.game._resolve_infoset(infoset, 'infoset_value')
if resolved_infoset.player.is_chance:
raise ValueError("infoset_value() is not defined for the chance player")
return self._infoset_value(resolved_infoset)

def action_value(self, action: typing.Union[Action, str]):
"""Returns the expected payoff to the player of playing an action conditional on reaching its
Expand All @@ -322,8 +332,13 @@ class MixedBehaviorProfile:
If `action` is an `Action` from a different game.
KeyError
If `action` is a string and no action in the game has that label.
ValueError
If `action` resolves to an action that belongs to the chance player
"""
return self._action_value(self.game._resolve_action(action, 'action_value'))
resolved_action = self.game._resolve_action(action, 'action_value')
if resolved_action.infoset.player.is_chance:
raise ValueError("action_value() is not defined for the chance player")
return self._action_value(resolved_action)

def infoset_prob(self, infoset: typing.Union[Infoset, str]):
"""Returns the probability with which an information set is reached.
Expand Down
32 changes: 32 additions & 0 deletions src/pygambit/tests/test_behav.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ def setUp(self):
self.profile_double = self.game.mixed_behavior_profile()
self.profile_rational = self.game.mixed_behavior_profile(True)

self.game_with_chance = gbt.Game.read_game(
"test_games/complicated_extensive_game.efg"
)
self.profile_double_with_chance = self.game_with_chance.mixed_behavior_profile()
self.profile_rational_with_chance = self.game_with_chance.mixed_behavior_profile(True)

def tearDown(self):
del self.game
del self.profile_double
Expand Down Expand Up @@ -724,3 +730,29 @@ def test_infoset_belief(self):
self.profile_rational.belief(self.game.infosets[0].members[0]) ==
gbt.Rational(1, 1)
)

def test_payoff_with_chance_player(self):
"""Test to ensure that payoff called with the chance player raises a ValueError"""
chance_player = self.game_with_chance.players.chance
self.assertRaises(ValueError, self.profile_double_with_chance.payoff, chance_player)
self.assertRaises(ValueError, self.profile_rational_with_chance.payoff, chance_player)

def test_infoset_value_with_chance_player_infoset(self):
"""Test to ensure that infoset_value called with an infoset of the chance player
raises a ValueError
"""
chance_infoset = self.game_with_chance.players.chance.infosets[0]
self.assertRaises(ValueError, self.profile_double_with_chance.infoset_value,
chance_infoset)
self.assertRaises(ValueError, self.profile_rational_with_chance.infoset_value,
chance_infoset)

def test_action_value_with_chance_player_action(self):
"""Test to ensure that action_value called with an action of the chance player
raises a ValueError
"""
chance_action = self.game_with_chance.players.chance.infosets[0].actions[0]
self.assertRaises(ValueError, self.profile_double_with_chance.action_value,
chance_action)
self.assertRaises(ValueError, self.profile_rational_with_chance.action_value,
chance_action)

0 comments on commit fc9734a

Please sign in to comment.