def __create_default_game_state(self, score_rule: ScoreRule) -> GameState: empty_scheme = None player1 = Player(1, empty_scheme, ControllerType.player.value) player2 = Player(2, empty_scheme, ControllerType.player.value) return GameState(size=Point(10, 10), initial_position=InitialPosition.empty, score_rule=score_rule, players=(player1, player2))
def logic(self): enemysquadron.update() blockgrid.update() self._collision_grid.update() GameState.logic(self) if self._time > -1 and self._game_running: #If this is a timed game... self._time -= 1 if not self._time: #If we run out of time... gamedata.lives = 0 enemysquadron.increase_difficulty() if self._game_running: gamedata.alarm = blockgrid.is_above_threshold() config.loop_sound(ALARM if gamedata.alarm else None) else: config.loop_sound(None) if self._game_running and (not gamedata.lives or Block.block_full): #If we run out of lives or the blocks go past the top of the screen... pygame.mixer.music.fadeout(FADE_TIME) self._ship.change_state(Ship.STATES.DYING) self._ufo.change_state(UFO.STATES.GAMEOVER) self._ufo.kill() enemysquadron.celebrate() self._game_running = False gamedata.alarm = False elif not self._game_running: #If we've gotten a Game Over... if not self._ship.respawn_time: #Once the ship's been destroyed... for i in (self._ship, self._ship.flames, self._ship.light_column): i.kill() if not pygame.mixer.music.get_busy(): #If the song has faded out... HUD.add(self.hud_text.game_over, self.hud_text.press_fire) config.play_music(GAME_OVER_PATH) self.__game_over()
def __start_game(self): score_rule = rules.SCORE_RULE_FROM_NAME[ self._score_rule_combobox.currentText()] initial_pos = rules.INITIAL_POSITION_FROM_RULE[ self._initial_position_combobox.currentText()] width = self.width_slider.value height = self.height_slider.value players = self.players_config.get_players() game_state = GameState(size=Point(width, height), score_rule=score_rule, initial_position=initial_pos, players=players) self.__star_game_window(game_state)
def render(self): hud = partial(make_text, surfaces=True) if blockgrid.any_active(): self.group_list[2] = BLOCKS else: blockgrid.cache_block_image() self.group_list[2] = blockgrid.block_buffer if gamedata.score != gamedata.prev_score: #If our score has changed since the last frame... self.hud_text.score.image = hud("%s: %i" % (GAME_TEXT[0], gamedata.score)) gamedata.prev_score = gamedata.score if gamedata.lives != gamedata.prev_lives and self.hud_text.lives.alive(): #If we've gained or lost lives since the last frame... self.hud_text.lives.image = hud("%s: %i" % (GAME_TEXT[1], gamedata.lives)) gamedata.prev_lives = gamedata.lives if gamedata.wave != gamedata.prev_wave: self.hud_text.wave.image = hud("%s %i" % (GAME_TEXT[4], gamedata.wave)) gamedata.prev_wave = gamedata.wave if self._time >= 0: #If we haven't run out of time, and we're actually in timed mode... time_left = ((self._time // 60 // 60), (self._time // 60) % 60) self.hud_text.time.image = hud(TIME_FORMAT.format(*time_left)) GameState.render(self) if not self._ship.image.get_alpha(): #If our ship is invisible... pygame.draw.circle(config.screen, color.WHITE, self._ship.rect.center, int(150*log1p((3 * 60) - self._ship.respawn_time)) + 32, 16) pygame.display.flip()
def test_ai_make_turn(self): class TargetAi(Controller): def __init__(self, position_to_move: Point): super().__init__() self._position_to_move = position_to_move @property def name(self) -> str: return '' @property def is_ai(self) -> bool: return True def get_position(self, game_state: GameState, player: Player) -> Point: return self._position_to_move first_player_pos = Point(0, 0) second_player_pos = Point(1, 0) first_player = Player(1, None, TargetAi(first_player_pos)) second_player = Player(2, None, TargetAi(second_player_pos)) game_state = GameState(size=Point(10, 10), score_rule=ScoreRule.russian, initial_position=InitialPosition.empty, players=(first_player, second_player)) game_state.ai_make_turn() game_state.ai_make_turn() for pos in self._iterate_over_game_field(game_state.game_field): cell = game_state.game_field[pos] if pos == first_player_pos: if cell.real_owner != first_player: self.fail() elif pos == second_player_pos: if cell.real_owner != second_player: self.fail() elif cell.real_owner != game_state.empty_player: self.fail()
def metric_security(self, current_player: Player, game_state: GameState, point: Point): helper = game_state.game_helper if game_state.is_near_border(point): return 1 near_count = 0 near_sum = 0 for pos in game_state.get_near_position(point): if helper.is_ally_point(pos, current_player): near_count += 1 near_sum += 1 if helper.is_enemy_point(pos, current_player): near_sum -= 0.1 for pos in helper.get_diagonal_points(point): if helper.is_ally_point(pos, current_player): near_count += 1 near_sum += 0.5 if helper.is_enemy_point(pos, current_player): near_sum -= 0.05 if near_count == 0: return 0 else: return near_sum / near_count
def metric_efficiency(self, current_player: Player, game_state: GameState, point: Point): helper = game_state.game_helper cell = game_state.game_field[point] if cell.real_owner == current_player: return 0 enemy_count = 0 for pos in game_state.get_near_position(point): if helper.is_enemy_point(pos, current_player): enemy_count += 1 if enemy_count == 0: return 0 elif enemy_count == 1: return 1 elif enemy_count == 2: return 0.9 else: return 1 / enemy_count
def start_game(self, plr_arg = ["Test0", "Test1", "Test2", "Test3"]): """Start a new game, deal first hands, and send msgs. plr_arg (dict): dict of player num, name pairs. """ # Might as well error check this here. All games must have 4 players. if len(plr_arg) != NUM_PLAYERS: log.exception("Tried to start a game with <{0} players." "".format(NUM_PLAYERS)) self.players = [Player(x, plr_arg[x]) for x in range(NUM_PLAYERS)] self.gs = GameState(self.generate_id()) self.deal_hand() self.gs.active_player = self.gs.next_player(self.gs.dealer) self.gs.game_mode = GAME_MODE.BID self.gs.trump = None return self.publish('sog', None, None)
class Game: """Define object for Game object with instance variables: stack_deck (bool): fixed hands for testing purposes. deck_seed (float)[0..1]: used to seed if stack_deck is True id (integer): unique id for game object mode (integer): setting for game mode players (list): array of Player objects for players in game teams (dict): player id : local player num pairings (?) gs (object): current game state deck (object): Deck object containing Card objects """ def __init__(self): self.players = [] self.gs = None self.deck = cards.Deck() def __repr__(self): """Return descriptive string when asked to print object.""" return "Cinch game with players: {0}".format( ", ".join(str(plr.name) for plr in self.players)) def check_bid_legality(self, player, bid): """Check a proposed bid for legality against the current gs. Assumes that player is indeed the active player. Returns a string indicating the bid type, or False if illegal bid. player (Player): player object of player making bid (replace w/ pNum?) bid (int): integer [0-5] value of bid; BID.PASS=0, BID.CINCH=5 """ if self.gs.game_mode != GAME_MODE.BID: return False # Can't bid during play phase. if bid == BID.PASS: return 'pass' # Always legal to pass. if bid < BID.PASS: return False # Bid outside legal range. if bid > BID.CINCH: return False # Bid outside legal range. if bid > self.gs.high_bid: return 'high' # New high bid; legal. if (bid == BID.CINCH) & (player.pNum == self.gs.dealer): return 'cntr' # Dealer has option to counter-cinch. return False # If we get here, no legal options left. def check_play_legality(self, player, card_num): """Check a proposed play for legality against the current gs. Assumes that player is indeed the active player. Returns boolean. player (Player): player object of player playing a play card_num (int): encoding of card to be played by player """ # Search player's hand for card where card_num = card.code has_card = False for card in player.hand: if card.code == card_num: has_card = True break if not has_card: return False # Must play a card in hand. if self.gs.game_mode != GAME_MODE.PLAY: return False # Can't play during bid phase. if len(self.gs.cards_in_play) == 0: return True # No restrictions on what cards can be led. if card.suit == self.gs.trump: return True # Trump is always OK if card.suit == self.gs.cards_in_play[0].suit: return True # Not trump, but followed suit. for each_card in player.hand: if each_card.suit == self.gs.cards_in_play[0].suit: return False # Could have followed suit with a different card. return True # Couldn't follow suit, throwing off. def dbupdate(self): """Write a completed gamestate to the sqlite database.""" log.debug("Trying to add a row for the new game.") # Get the automatic game ID to use in the Events table in place of the # random engine-generated game ID. The game timestamp is that of the # first game event. autogen_game_id = db.Games.insert( Timestamp=self.gs.events[0]['timestamp'], PlayerName0=self.players[0].name, PlayerName1=self.players[1].name, PlayerName2=self.players[2].name, PlayerName3=self.players[3].name ) log.debug("Grabbed a game_id from the database.") # Write everything from Events to the database. log.info("Writing game data for local game %s, db game %s.", self.gs.game_id, autogen_game_id) for action in self.gs.events: db.Events.insert( game_id=autogen_game_id, HandNumber=action['hand_num'], Timestamp=action['timestamp'], EventString=action['output'] ) db.commit() def deal_hand(self): """Deal new hand to each player and set card ownership.""" for player in self.players: player.hand = sorted([self.deck.deal_one() for x in range (STARTING_HAND_SIZE)], reverse = True) for card in player.hand: card.owner = player.pNum def generate_id(self, size=6): """Generate random character string of specified size. Uses digits, upper- and lower-case letters. """ chars = string.ascii_letters + string.digits # There is a 1.38e-07% chance of identical game_ids with 3 games. # This chance rises to 1% when there are 6,599 simultaneous games. # While not a perfect solution, we like to live dangerously. return ''.join(random.choice(chars) for x in range(size)) def handle_bid(self, player_num, bid): """Invoke bid processing logic on incoming bid and send update to clients, or indicate illegal bid to single player. player_num (int): local player number bid (int): integer [0-5] of bid being made """ # Check that player_num is active player. #---------------------------------------- if player_num != self.gs.active_player: log.warning("Non-active player attempted to bid.") return None # Ignore bid_status = self.check_bid_legality(self.players[player_num], bid) if bid_status is False: return False # Not a legal bid; return False # Game router will chastise appropriately. # Legal bid was made; update game state and log/publish. #------------------------------------------------------- elif bid_status == 'pass': pass # Couldn't resist. elif bid_status == 'high': self.gs.high_bid = bid self.gs.declarer = player_num elif bid_status == 'cntr': self.gs.declarer = player_num # Set declarer; bid already Cinch. # Is bidding over? Either way, publish and return. if self.gs.active_player == self.gs.dealer: # Dealer always bids last self.gs.active_player = self.gs.declarer self.gs.game_mode = GAME_MODE.PLAY return self.publish('eob', player_num, bid) else: self.gs.active_player = self.gs.next_player(self.gs.active_player) return self.publish('bid', player_num, bid) def handle_card_played(self, player_num, card_num): """Invoke play processing logic on incoming play and send update to clients, or indicate illegal play to single player. player_num (int): local player number card_num (int): integer encoding of card being played by player """ # Check that player_num is active player. #---------------------------------------- if player_num != self.gs.active_player: log.warning("Non-active player attempted to play a card.") return None # Ignore if not (self.check_play_legality(self.players[player_num], card_num)): return False # Not a legal play; return False # Game router will chastise appropriately. # Remove card from player's hand and put into play. #-------------------------------------------------- for card_pos, card in list(enumerate(self.players[player_num].hand)): if card.code == card_num: a = self.players[player_num].hand.pop(card_pos) self.gs.cards_in_play.append(a) if self.gs.trump is None: # First card played this hand? self.gs.trump = card.suit self.gs.active_player = self.gs.next_player( self.gs.active_player) return self.publish('trp', player_num, card) break # Check for end of trick and handle, otherwise return. #----------------------------------------------------- winning_card = self.gs.trick_winning_card() if winning_card is None: # Trick is not over self.gs.active_player = self.gs.next_player(self.gs.active_player) return self.publish('crd', player_num, card) else: trick_winner = winning_card.owner self.gs.active_player = trick_winner self.gs.team_stacks[trick_winner % TEAM_SIZE] += self.gs.cards_in_play self.gs.cards_in_play = [] # Check for end of hand and handle, otherwise return. #---------------------------------------------------- cards_left = len(self.players[0].hand) if cards_left != 0: # More tricks to play return self.publish('eot', player_num, card) # Log hand results and check victory conditions. self.gs.score_hand() victor = False for score in self.gs.scores: if score >= WINNING_SCORE: victor = True break # Also, if we've reached MAX_HANDS, end the game anyway. # message['win'] defaults to 0.5; client/database/stats should # interpret a game with a win value of 0.5 as a draw. if self.gs.hand_number == MAX_HANDS: victor = True # This block breaks if there are more than two teams. if victor: if self.gs.scores[self.gs.declarer % TEAM_SIZE] >= WINNING_SCORE: self.gs.winner = self.gs.declarer % TEAM_SIZE elif self.gs.scores[(self.gs.declarer + 1) % TEAM_SIZE] >= WINNING_SCORE: self.gs.winner = (self.gs.declarer + 1) % TEAM_SIZE else: pass # Don't need to set winner if we reached on MAX_HANDS. return self.publish('eog', player_num, card) # If no victor, set up for next hand. gs = self.gs # Operate on local variable for speed++ gs.team_stacks = [[] for _ in range(NUM_TEAMS)] gs.dealer = gs.next_player(gs.dealer) gs.declarer = gs.dealer self.deck = cards.Deck() self.deal_hand() gs.active_player = gs.next_player(gs.dealer) gs.high_bid = 0 gs.game_mode = GAME_MODE.BID gs.trump = None self.gs = gs return self.publish('eoh', player_num, card) def publish(self, status, pNum, data): """Translate game actions into messages for clients. Also write data to the appropriate game log file on the server. status (str): 3-char code specifying the event type. Legal values: bid: 3/hand, normal bid. eob: 1/hand, final bid. trp: 1/hand, first card played. crd: 26/hand, normal card play. eot: 8/hand, end of trick. eoh: 1/hand, end of hand. eog: 1/game, end of game. pNum (int): local player number data (int or Card): Card object being played by player for modes trp, crd, eot, eoh, eog; integer encoding of bid for bid and eob. """ gs = self.gs # Make local copy for speed++; it's not edited in here. # Initialize the output. Message always contains actvP, so do it here. message = {'actvP': gs.active_player} message['actor'] = pNum if status in ['sog', 'eob', 'eoh']: # Handle switching game modes first. message['mode'] = gs.game_mode if status in ['trp', 'crd', 'eot', 'eoh', 'eog']: if status == 'trp': message['trp'] = gs.trump # Player declared Suit as trump. message['playC'] = data.code # Player played Card. if status in ['eot', 'eoh', 'eog']: message['remP'] = gs._t_w_card.owner # Player won the trick with Card. if status in ['eoh', 'eog']: message['mp'] = ['',]*NUM_TEAMS # Initialize # We will end up with a list of NUM_TEAMS strings, # where the string is the match points out of 'hljg' won. message['mp'][gs._results['high_holder']] += 'h' message['mp'][gs._results['low_holder']] += 'l' try: message['mp'][gs._results['jack_holder']] += 'j' except TypeError: # No jack out, NoneType. pass try: message['mp'][gs._results['game_holder']] += 'g' except TypeError: # Game tied and not awarded, NoneType. pass message['gp'] = gs._results['game_points'] message['sco'] = gs.scores if status == 'eog': message['win'] = gs.winner else: # Status must be 'eoh': Set up for next hand. message['dlr'] = gs.dealer # Player deals. output = [message.copy() for _ in range(NUM_PLAYERS)] for player in self.players: output[player.pNum]['addC'] = [card.code for card in player.hand] output[player.pNum]['tgt'] = player.pNum elif status in ['bid', 'eob']: message['bid'] = data # Start of game is the same as end of hand except there are different # logging requirements, so there's a little bit of duplicated code. # Update: Text logging has been deprecated; this duplicated code # could be cleaned up or merged later, but it isn't hurting anything # for now. elif status == 'sog': message['dlr'] = gs.dealer # Player deals. output = [message.copy() for _ in range(NUM_PLAYERS)] for player in self.players: output[player.pNum]['addC'] = [card.code for card in player.hand] output[player.pNum]['tgt'] = player.pNum # Note: New hands are handled differently from all others because they # are the only ones dealing with private information. If status is # 'eoh'/'sog', output will be a length-4 list containing 4 dicts with # 'tgt' containing an integer, and 'addC' containing the new hands. If # not, output will be a dict with 'tgt' containing a length-4 list. # (With 4 meaning NUM_PLAYERS, of course.) if status not in ['eoh', 'sog']: message['tgt'] = [i for i in range(NUM_PLAYERS)] output = message if status in ['eoh', 'sog']: for x in output: # output is a list self.gs.events.append({'hand_num':self.gs.hand_number, 'timestamp':datetime.utcnow().isoformat(), 'output':str(x)}) else: self.gs.events.append({'hand_num':self.gs.hand_number, 'timestamp':datetime.utcnow().isoformat(), 'output':str(output)}) if status in ['eoh', 'eog']: gs.hand_number += 1 if status in ['eog']: self.dbupdate() return output def start_game(self, plr_arg = ["Test0", "Test1", "Test2", "Test3"]): """Start a new game, deal first hands, and send msgs. plr_arg (dict): dict of player num, name pairs. """ # Might as well error check this here. All games must have 4 players. if len(plr_arg) != NUM_PLAYERS: log.exception("Tried to start a game with <{0} players." "".format(NUM_PLAYERS)) self.players = [Player(x, plr_arg[x]) for x in range(NUM_PLAYERS)] self.gs = GameState(self.generate_id()) self.deal_hand() self.gs.active_player = self.gs.next_player(self.gs.dealer) self.gs.game_mode = GAME_MODE.BID self.gs.trump = None return self.publish('sog', None, None) def join_game_in_progress(self, pNum, name): """Facilitate a player joining a game in progress. pNum (int): The target player number. name (str): The player's nickname. """ self.players[pNum].name = name message = dict( actvP=self.gs.active_player, actor=None, addC=[card.code for card in self.players[pNum].hand], dlr=self.gs.dealer, mode=self.gs.game_mode, tgt=pNum, resumeData=dict( handSizes=[len(x.hand) for x in self.players], trp=self.gs.trump, sco=self.gs.scores, highBid=self.gs.high_bid, declarer=self.gs.declarer, cip=[(c.code, c.owner) for c in self.gs.cards_in_play]) ) return message