def __init__(self, players): self.players = list(players) random.shuffle(self.players) self.round = 1 self.active_player_index = 0 self.scores = dict((id, 0) for id in self.players) self.turn_state = TurnState() self.winner = None self.last_update = time.time() self.prev_id = None self.id = None self.num_updates = 0
def testDieChoiceDependsOnWinScore3(self): # State where choice deviates for low and high, but not medium scores state = TurnState(side_dice=SideDiceState({DieFace.Tank: 1}), throw=DiceThrow({ DieFace.Ray: 5, DieFace.Cow: 1, DieFace.Human: 2, DieFace.Chicken: 4 })) self.assertEqual(DieFace.Chicken, self.action_selector.select_die(state)) self.assertEqual(DieFace.Chicken, self.action_selector.select_die(state, 4)) self.assertEqual(DieFace.Chicken, self.action_selector.select_die(state, 5)) self.assertEqual(DieFace.Chicken, self.action_selector.select_die(state, 6)) self.assertEqual(DieFace.Ray, self.action_selector.select_die(state, 1)) self.assertEqual(DieFace.Ray, self.action_selector.select_die(state, 2)) self.assertEqual(DieFace.Ray, self.action_selector.select_die(state, 3)) self.assertEqual(DieFace.Ray, self.action_selector.select_die(state, 7)) self.assertEqual(DieFace.Ray, self.action_selector.select_die(state, 8))
def testDieChoiceDependsOnWinScore2(self): # State where choice deviates only for a couple of higher win scores. # It is more typical that there is a deviation for lower win scores up until a limit. state = TurnState(side_dice=SideDiceState({DieFace.Tank: 1}), throw=DiceThrow({ DieFace.Ray: 4, DieFace.Cow: 1, DieFace.Human: 3, DieFace.Chicken: 4 })) self.assertEqual(DieFace.Ray, self.action_selector.select_die(state)) self.assertEqual(DieFace.Ray, self.action_selector.select_die(state, 3)) self.assertEqual(DieFace.Chicken, self.action_selector.select_die(state, 4)) self.assertEqual(DieFace.Chicken, self.action_selector.select_die(state, 5)) score_ray0 = self.action_selector.expected_score( SearchState(1, 4, 0, 0, 0)) score_ray4 = self.action_selector.expected_score( SearchState(1, 4, 0, 0, 4)) score_kip0 = self.action_selector.expected_score( SearchState(1, 0, 4, 1, 0)) score_kip4 = self.action_selector.expected_score( SearchState(1, 0, 4, 1, 4)) self.assertGreater(score_ray0, score_kip0) self.assertGreater(score_kip4, score_ray4) self.assertGreaterEqual(score_ray0, score_ray4) self.assertGreaterEqual(score_kip0, score_kip4)
def _next_turn(self): start_index = self.active_player_index while True: self.active_player_index += 1 if self.active_player_index == len(self.players): self.active_player_index = 0 self.round += 1 # Skip players that have been removed from the game if self.active_player != "": break # Guard against endless loop assert (self.active_player_index != start_index) self.turn_state = TurnState()
def testShouldStopWhenWinningEvenWhenOddsAreGood(self): state = TurnState(side_dice=SideDiceState({ DieFace.Ray: 5, DieFace.Cow: 2 })) self.assertTrue(self.action_selector.should_stop(state, 1)) self.assertTrue(self.action_selector.should_stop(state, 2)) self.assertFalse(self.action_selector.should_stop(state, 3)) self.assertFalse(self.action_selector.should_stop(state))
def __setstate__(self, state): chksum = game_state_id(state) if state["id"] != chksum: raise ChecksumMismatchException(f'{state["id"]} != {chksum}') # Set defaults for optional variables self.winner = None self.turn_state = None turn_state_dict = state.get("turn_state", None) if turn_state_dict: state["turn_state"] = TurnState.from_dict(turn_state_dict) active_player = state.get("active_player", None) if active_player: state["active_player_index"] = state["players"].index( active_player) del state["active_player"] self.__dict__.update(state)
class GameState: def __init__(self, players): self.players = list(players) random.shuffle(self.players) self.round = 1 self.active_player_index = 0 self.scores = dict((id, 0) for id in self.players) self.turn_state = TurnState() self.winner = None self.last_update = time.time() self.prev_id = None self.id = None self.num_updates = 0 @property def done(self): return self.turn_state is None @property def active_player(self): assert (not self.done) return self.players[self.active_player_index] @property def awaits_input(self): assert (not self.done) return (not self.turn_state.done) and self.turn_state.awaits_input @property def age_in_seconds(self): return time.time() - self.last_update @property def num_players(self): return len(self.scores) def has_player(self, id): return id in self.scores def next(self, input=None): assert (not self.done) if self.id: self.prev_id = self.id self.id = None if not self.turn_state.done: self.turn_state = self.turn_state.next(input) else: self._end_turn() self.num_updates += 1 self.last_update = time.time() def remove_player(self): del self.scores[self.active_player] self.players[self.active_player_index] = "" def _next_turn(self): start_index = self.active_player_index while True: self.active_player_index += 1 if self.active_player_index == len(self.players): self.active_player_index = 0 self.round += 1 # Skip players that have been removed from the game if self.active_player != "": break # Guard against endless loop assert (self.active_player_index != start_index) self.turn_state = TurnState() def _active_player_wins(self): self.winner = self.active_player self.turn_state = None self.active_player_index = None def _end_turn(self): assert (not self.done) assert (self.turn_state.done) if self.active_player != "": # Only update score when player was not just removed self.scores[self.active_player] += self.turn_state.score if self.scores[self.active_player] >= TARGET_SCORE: return self._active_player_wins() self._next_turn() if len(self.scores) == 1: # When only one player remains, they have won return self._active_player_wins() def __getstate__(self): state = { "round": self.round, "num_updates": self.num_updates, "last_update": int(self.last_update), "players": self.players, "scores": self.scores, } if not self.done: state["turn_state"] = self.turn_state.to_dict() state["active_player"] = self.active_player if self.prev_id: state["prev_id"] = self.prev_id if self.winner: state["winner"] = self.winner state["id"] = game_state_id(state) return state def __setstate__(self, state): chksum = game_state_id(state) if state["id"] != chksum: raise ChecksumMismatchException(f'{state["id"]} != {chksum}') # Set defaults for optional variables self.winner = None self.turn_state = None turn_state_dict = state.get("turn_state", None) if turn_state_dict: state["turn_state"] = TurnState.from_dict(turn_state_dict) active_player = state.get("active_player", None) if active_player: state["active_player_index"] = state["players"].index( active_player) del state["active_player"] self.__dict__.update(state) def __str__(self): return str(self.__getstate__()) def as_json(self): return json.dumps(self.__getstate__()) @classmethod def from_json(cls, json_string): return cls.from_dict(json.loads(json_string)) @classmethod def from_dict(cls, dict): self = cls.__new__(cls) self.__setstate__(dict) return self