def pass_gamestate(self): Round = self.round Tokens_Inventories = np.concatenate( (expand_inventory(self.inventory, self.players)[self.active_player:, :], expand_inventory( self.inventory, self.players)[:self.active_player, :])).flatten() Air_Supply = self.air_supply Token_Positions = expand_vector(categorize_tokens(self.tokens)) Player_Positions = np.concatenate( (self.positions[self.active_player:, :], self.positions[:self.active_player, :])).flatten() Intermediate_Scores = np.concatenate( (self.player_scores[self.active_player:], self.player_scores[:self.active_player])).flatten() return np.hstack( (Round, Tokens_Inventories, Air_Supply, Token_Positions, Player_Positions, Intermediate_Scores))
def next_round(self): # reset air supply to 25 self.air_supply = 25 # remove blanks from token row self.tokens = np.delete(self.tokens, np.where(self.tokens == -1)[0]) # clean up tokens collected in the previous round for i in range(self.players): # score any tokens that players got back to the sub with if self.positions[i, 0] == -1: self.player_scores[i] += list(map(sum, self.inventory))[i] # drop any tokens that did not make it back else: # set up a counter to track how many tokens have been added to the current stack stacked = 0 # loop through players for j in range(self.players): # request drops until the player's inventory is empty while len(self.inventory[j]) > 0: # request which token to drop selected_token = death_drop( categorize_tokens(self.inventory[j])) # translate the mask back to an indx selected_token = np.where(selected_token == 1)[0][0] # add the selected tokens to the token row # if this is the first token in a stack if stacked == 0: # add it to the end of the row self.tokens = np.append( self.tokens, self.inventory[j][selected_token]) # if the stack is not full, add to it else: self.tokens[-1] += self.inventory[j][ selected_token] # remove the tokens from the player's inventory self.inventory[j] = np.delete(self.inventory[j], selected_token) # update the stack counter stacked += 1 # if the stack is full, reset the counter to 0 if stacked == 3: stacked = 0 # fully clear player inventories self.inventory = [np.array([]) for i in range(self.players)] # set the active player to the deepest player self.active_player = np.argmax(self.positions[:, 0]) # put each player on the sub (position -1) with downward heading (+1) self.positions = np.tile([-1, 1], (self.players, 1)) # update the round counter self.round += 1
def simulate_game(players, model_turn_around, model_pick_up, model_drop_token, noise): # declare lists to store individual components of the game state TurnID = [] # int, indexing identifier Active_Player = [] # int, indexing identifier Round = [] # int Tokens_Inventories = [ ] # array with dim [players, 32], row 0 is inventory of active player Air_Supply = [] # int Token_Positions = [] # vector Player_Positions = [ ] # array with dim [players, 2], row 0 is active player Intermediate_Scores = [] # vector, index 0 is active player # additional lists to document the decisions made by the player on a given turn Turn_Around = [0] # bool, happens prior to board update Pick_Up = [0] # bool, happens after board update Drop = [[0 for i in range(32)]] # vector, happens after board update # initiate a new game state game = GameState(players) # start turn counter, document initial game state turn = 0 TurnID.append(turn) Active_Player.append(game.active_player) Round.append(game.round) Tokens_Inventories.append( np.concatenate((expand_inventory(game.inventory, game.players)[game.active_player:, :], expand_inventory( game.inventory, game.players)[:game.active_player, :])).flatten()) Air_Supply.append(game.air_supply) Token_Positions.append(expand_vector(categorize_tokens(game.tokens))) Player_Positions.append( np.concatenate((game.positions[game.active_player:, :], game.positions[:game.active_player, :])).flatten()) Intermediate_Scores.append( np.concatenate((game.player_scores[game.active_player:], game.player_scores[:game.active_player])).flatten()) # declare a flag for game end continue_game = True # take turns until the game is over while continue_game: # take a turn, collect the player actions taken continue_game, turn_taken, turn_around, pick_up, drop = take_turn( game, model_turn_around, model_pick_up, model_drop_token, noise) # skip to next turn if active player is already back at the sub if turn_taken == False: continue # document the game state turn += 1 TurnID.append(turn) Active_Player.append(game.active_player) Round.append(game.round) Tokens_Inventories.append( np.concatenate( (expand_inventory(game.inventory, game.players)[game.active_player:, :], expand_inventory( game.inventory, game.players)[:game.active_player, :])).flatten()) Air_Supply.append(game.air_supply) Token_Positions.append(expand_vector(categorize_tokens(game.tokens))) Player_Positions.append( np.concatenate((game.positions[game.active_player:, :], game.positions[:game.active_player, :])).flatten()) Intermediate_Scores.append( np.concatenate( (game.player_scores[game.active_player:], game.player_scores[:game.active_player])).flatten()) # document the player decisions Turn_Around.append(int(turn_around)) Pick_Up.append(int(pick_up)) Drop.append(expand_vector(drop)) # wrap the board states into a dataframe Game_Log = pd.DataFrame({ "TurnID": TurnID, "Active_Player": Active_Player, "Round": Round, "Tokens_Inventories": Tokens_Inventories, "Air_Supply": Air_Supply, "Token_Positions": Token_Positions, "Player_Positions": Player_Positions, "Intermediate_Scores": Intermediate_Scores, "Turn_Around": Turn_Around, "Pick_Up": Pick_Up, "Drop": Drop }) # document the winner # ignoring tie breakers, all players lose when tied Placement = dict([(i, 0) for i in range(len(game.player_scores))]) if not np.all(game.player_scores == game.player_scores[0]): Placement[np.argmax(game.player_scores)] = 1 return Game_Log, Placement
def take_turn(game, model_turn_around, model_pick_up, model_drop_token, noise): # reporter variables turn_taken = False turn_around = False pick_up = False drop = np.array([]) # check that the active player is not already back on the submarine if not (game.positions[game.active_player, 0] == -1 and game.positions[game.active_player, 1] == -1): # note that the turn was not taken turn_taken = True # update the air supply game.update_air_supply() # determine whether the active player turns around if game.positions[game.active_player, 1] == 1 and game.positions[game.active_player, 0] != -1: if turn_around_decision(game.pass_gamestate(), model_turn_around, noise): # turn the player back toward the sub if True game.positions[game.active_player, 1] = -1 turn_around = True # roll the dice roll = die_roll(len(game.inventory[game.active_player])) # move the player along the token lineup until they consume their moves or reach the end while roll > 0: # check to see if the player is on the last available space when moving down if game.positions[game.active_player, 1] == 1: if game.positions[game.active_player, 0] == max( np.setdiff1d( np.arange(len(game.tokens)), game.positions[np.arange(len(game.positions)) != game.active_player][:, 0])): break # check to see if the player is at the sub when moving up else: if game.positions[game.active_player, 0] == -1: break # move the player a step in the appropriate direction game.positions[game.active_player, 0] += game.positions[game.active_player, 1] # continue moving without consuming the roll if the token is already occupied if game.positions[game.active_player, 0] in game.positions[ np.arange(len(game.positions)) != game.active_player][:, 0]: continue # check to see if they have reached the sub if game.positions[game.active_player, 0] == -1: break # consume one movement from the roll roll = roll - 1 # determine whether the active player picks up or drops a token # check to see if a token is available if game.tokens[game.positions[game.active_player, 0]] > -1: if pick_up_decision(game.pass_gamestate(), model_pick_up, noise): # pick up the token if True game.collect_token() pick_up = True elif len(game.inventory[game.active_player]) > 0: # select tokens to drop drop = drop_decision( categorize_tokens(game.inventory[game.active_player]), game.pass_gamestate(), model_drop_token, noise) # drop a token if any indicated if sum(drop > 0): game.drop_token(drop) # update the active player if game.active_player < game.players - 1: game.active_player += 1 else: game.active_player = 0 # check to see if oxygen has run out if game.air_supply <= 0 or np.all(game.positions[:, 0] == -1): # start the next round game.next_round(noise) # return a boolean indicating whether the game is over alongside descriptions of player's actions return (game.round <= 3, turn_taken, turn_around, pick_up, drop)