risktools module
This file contains the RISK simulator that consitutes the backbone of the RISK AI Assignment
""" This file contains the RISK simulator that consitutes the backbone of the RISK AI Assignment """ import gui.riskengine as riskengine import random import zipfile import xml.dom.minidom import math import json class RiskBoard(): """ Stores all of the information about the current Risk game that doesn't change over the course of the game, things like: Which territories make up the map Which territories are connected to which What the continents are and which territories they are composed of What the sequence of card turn-in values is What players are in the game What pictures are on the cards What the set of all cards is """ def __init__(self): """ Initialize a Risk Board. All of the variables are initially empty. They should be filled in through other function calls """ self.players = [] """ An array of RiskPlayer objects """ self.player_to_id = dict() """ A dictionary which is indexed by player name and stores the id number of that player """ self.id_to_player = dict() """ A dictionary which is indexed by player id and stores the player name of that player """ self.territories = [] """ An array of RiskTerritory objects """ self.territory_to_id = dict() """ A dictionary which is indexed by territory name and stores the id number of that territory """ self.cards = [] """ An array of RiskCard objects """ self.card_to_id = dict() """ A dictionary which is indexed by card name (territory name of pictured territory, or wildcard) Stores the id of that card """ self.pictures = [] """ An array of string descriptions of the different card pictures """ self.continents = dict() """ A dictionary of RiskContinent objects indexed by continent name, stores corresponding RiskContinent object """ self.turn_in_values = [] """ An array of the card turn-in-values turn_in_values[i] gives the number of troops received for the i-th card turn-in """ self.increment_value = 0 """ A number specifying the incremental gain in received troops for card turn-ins beyond the length of turn-in-values array """ def from_string(self, s): """ Load RiskBoard information from a string. We assume the string was created by the to_string function. """ ss = s.split('|') #Players ssp = ss[1].split(';') for p in ssp: if len(p) > 0: np = RiskPlayer(None,None,None,None) np.from_string(p) self.add_player(np) #Territories sst = ss[2].split(';') for t in sst: if t: nt = RiskTerritory(None,None) nt.from_string(t) self.add_territory(nt) #Continents ssc = ss[3].split(';') for c in ssc: if c: nc = RiskContinent(None,None) nc.from_string(c) self.add_continent(nc) #Cards ssd = ss[4].split(';') for d in ssd: if d: nd = RiskCard(None,None,None) nd.from_string(d) self.add_card(nd) #Turn in values self.turn_in_values = json.loads(ss[5]) #Increment value self.increment_value = json.loads(ss[6]) #Pictures self.pictures = json.loads(ss[7]) def to_string(self): """Save the current RiskBoard to a string. This is used to save games.""" output_string = 'RISKBOARD|' #Players for p in self.players: output_string = output_string + p.to_string() output_string = output_string + ';' output_string = output_string + '|' #Territories for t in self.territories: output_string = output_string + t.to_string() output_string = output_string + ';' output_string = output_string + '|' #Continents for n,c in self.continents.iteritems(): output_string = output_string + c.to_string() + ';' output_string = output_string + '|' #Cards for c in self.cards: output_string = output_string + c.to_string() output_string = output_string + ';' output_string = output_string + '|' #Turn In Values output_string = output_string + json.dumps(self.turn_in_values) output_string = output_string + '|' #Increment value output_string = output_string + json.dumps(self.increment_value) output_string = output_string + '|' #Pictures output_string = output_string + json.dumps(self.pictures) return output_string # def shuffle_players(self): # """ # Randomize the player order. Make sure everything is right after that # """ # player_order = range(len(self.players)) # random.shuffle(player_order) # shuffled_players = [] # player_counter = 0 # for i in player_order: # p = self.players[i] # p.id = player_counter # shuffled_players.append(p) # self.player_to_id[p.name] = p.id # self.id_to_player[p.id] = p.name # player_counter += 1 # self.players = shuffled_players def add_player(self, player): """ Add a player object to the list of players. Modify player_to_id and id_to_player to include new player """ self.players.append(player) self.player_to_id[player.name] = player.id self.id_to_player[player.id] = player.name def add_territory(self, territory): """ Add a territory object to the list of territories. Modify territory_to_id to include new territory """ self.territories.append(territory) self.territory_to_id[territory.name] = territory.id def add_card(self, card): """ Add a card object to the list of cards Modify card_to_id to include the new card """ self.cards.append(card) self.card_to_id[self.territories[card.territory].name] = card.id def shuffle_cards(self): """Randomly shuffle the array of cards and reassign the card ids to be consistent""" random.shuffle(self.cards) for i in range(len(self.cards)): self.cards[i].id = i self.card_to_id[self.territories[self.cards[i].territory].name] = i def add_continent(self, continent): """Add a continent object to the list of continents""" if continent.name not in self.continents: self.continents[continent.name] = continent def set_turn_in_values(self, tiv): """Set the array of turn-in values (for card turn-ins)""" self.turn_in_values = tiv def set_increment_value(self, iv): """Set the increment value for card turn-ins beyond the end of turn_in_values""" self.increment_value = iv def print_board(self): """Display the Risk Board to the output.""" print 'RISK BOARD' #PRINT TERRITORIES BY CONTINENT print 'CONTINENTS' for c in self.continents.values(): c.print_continent(self) #PRINT CARDS print 'CARDS' for c in self.cards: c.print_card(self) #PRINT PLAYERS print 'PLAYERS' for p in self.players: p.print_player(self) class RiskTerritory(): """Stores all of the information for a territory""" def __init__(self, name, id): self.name = name """ The name of the territory (a string) """ self.id = id """ The id number of the territory (an integer) """ self.neighbors = [] """ An list of the id numbers of all territories neighboring this one """ def add_neighbor(self, neighbor): """Add a neighbor id to the neighbor list""" self.neighbors.append(neighbor) def print_territory(self, board, indent=0): """Display information about this territory to the output""" for i in range(indent): print ' ', print '[', self.name, '] (', self.id, ')' for i in range(indent+2): print ' ', print 'Neighbors:' for n in self.neighbors: for i in range(indent+3): print ' ', print board.territories[n].name def to_string(self): """Save the current Territory information to a string""" s = json.dumps(self.name) + '&' + json.dumps(self.id) + '&' + json.dumps(self.neighbors) return s def from_string(self, s): """Load information about this territory from a string""" ss = s.split('&') self.name = json.loads(ss[0]) self.id = json.loads(ss[1]) self.neighbors = json.loads(ss[2]) class RiskPlayer(): """Stores all information about a player in the game""" def __init__(self, name, id, free_armies, conquered_territory): """Initializes the various components of this RiskPlayer object""" self.name = name """ The Player's name (string) """ self.id = id """ The Player's id (integer) """ self.cards = [] """ A list of card id numbers that this player holds (integers) """ self.free_armies = free_armies """ The number of armies this player has in hand, waiting to be placed on the board """ self.conquered_territory = conquered_territory """ A boolean stating whether or not this player conquered a territory on their current turn """ def add_card(self, card): """Adds a card (card id number) to the player's list of card ids""" self.cards.append(card) def add_armies(self, n): """Adds a number (n) of armies to the player's free_armies count""" self.free_armies += n def print_player(self, board, indent=0): """Displays information about this player to the output""" for i in range(indent): print ' ', print '<', self.name, '[', self.id, ']> (', self.free_armies, ' free armies )' for i in range(indent+2): print ' ', print 'Cards:' for c in self.cards: board.cards[c].print_card(board, indent+3) def copy_player(self): """Creates a copy of this player and returns it""" np = RiskPlayer(self.name, self.id, self.free_armies, self.conquered_territory) np.cards = self.cards[:] return np def to_string(self): """Saves this player to a string""" s = json.dumps(self.name) + '.' + json.dumps(self.id) + '.' + json.dumps(self.cards) + '.' + json.dumps(self.free_armies) + '.' + json.dumps(self.conquered_territory) return s def from_string(self, s): """Loads player information from a string""" ss = s.split('.') self.name = json.loads(ss[0]) self.id = json.loads(ss[1]) self.cards = json.loads(ss[2]) self.free_armies = json.loads(ss[3]) self.conquered_territory = json.loads(ss[4]) class RiskCard(): """Stores all the information for a risk card""" def __init__(self, territory, picture, id): """Initializes card information""" self.territory = territory """ The territory id number of the territory pictured on the card """ self.picture = picture """ The picture on the card """ self.id = id """ The id number of this card """ def print_card(self, board, indent=0): """Displays information about this card to the output""" for i in range(indent): print ' ', print board.territories[self.territory].name, ' : ', self.picture def to_string(self): """Saves card information to a string""" s = str(self.territory) + '.' + str(self.picture) + '.' + str(self.id) return s def from_string(self, s): """Loads card information from a string""" ss = s.split('.') self.territory = json.loads(ss[0]) self.picture = json.loads(ss[1]) self.id = json.loads(ss[2]) class RiskContinent(): """Stores all information related to a continent""" def __init__(self, name, reward): """Initializes the continent information""" self.name = name """The name of this continent (string) """ self.reward = reward """ The reward for this continent This is the number of troops bonus that a player gets if the player owns all of the territories in this continent """ self.territories = [] """ A list of the territory id numbers of the territories in this continent""" def add_territory(self, territory): """Adds a territory to this continent""" if territory not in self.territories: self.territories.append(territory) def print_continent(self,board,indent=0): """Displays information about continent to output""" for i in range(indent): print ' ', print '{', self.name, '} : ', self.reward for t in self.territories: board.territories[t].print_territory(board, indent+2) def to_string(self): """Save continent information to string""" s = json.dumps(self.name) + '&' + json.dumps(self.reward) + '&' + json.dumps(self.territories) return s def from_string(self, s): """Load continent information from string""" ss = s.split('&') self.name = json.loads(ss[0]) self.reward = json.loads(ss[1]) self.territories = json.loads(ss[2]) def nextPlayer(state): """ Moves the state's current_player to the next player in the order Also ensures that when it exits, the new state.current_player is still in the game Input: RiskState object """ incrementPlayer(state) while state.current_player not in state.owners and None not in state.owners: incrementPlayer(state) def incrementPlayer(state): """ Increments the current_player of the input state Input: RiskState object """ state.current_player = state.current_player + 1 if state.current_player >= len(state.players): state.current_player = 0 class RiskState(): """Stores all the information about a state of a Risk game""" def __init__(self, players, armies, owners, current_player, turn_type, turn_in_number, last_attacker, last_defender, cards, board): """Initializes a RiskState object""" self.players = players """An array of RiskPlayer objects (the players in the game)""" self.armies = armies """ An array of integers, indexed by territory id number, that stores the number of armies on that territory """ self.owners = owners """ An array of integers, indexed by territory id number, that stores the player id number of the player who owns that territory """ self.current_player = current_player """ The player id number of the current player (player whose turn it is) """ self.turn_type = turn_type """ What kind of turn is it currently Choices = (PreAssign, PrePlace, Place, TurnInCards, Attack, Occupy, Fortify, GameOver) """ self.cards = cards """ A list of all the card id numbers of the cards still in the deck """ self.turn_in_number = turn_in_number """ How many card sets have been turned in? """ self.last_attacker = last_attacker """ What is the territory id of the last territory that was attacking Important to determine troop movements after territory is conquered """ self.last_defender = last_defender """ What is the territory id of the last territory that was defending Important to determine troop movements after territory is conquered """ self.board = board """ A RiskBoard object that stores all the non-changing information about this risk game """ def to_string(self): """Saves this state to a string""" s = 'RISKSTATE|' #players for p in self.players: s = s + p.to_string() s = s + ';' s = s + '|' + json.dumps(self.armies) + '|' + json.dumps(self.owners) + '|' s = s + json.dumps(self.current_player) + '|' + json.dumps(self.turn_type) + '|' s = s + json.dumps(self.cards) + '|' + json.dumps(self.turn_in_number) + '|' s = s + json.dumps(self.last_attacker) + '|' + json.dumps(self.last_defender) return s def from_string(self, s, board): """Loads this state from a string""" ss = s.split('|') if ss[0] != 'RISKSTATE': print 'THIS IS AN INVALID RISKSTATE' ps = ss[1].split(';') self.players = [] for p in ps: if p:#len(p) > 0: np = RiskPlayer(None, None, None, None) np.from_string(p) self.players.append(np) self.armies = json.loads(ss[2]) self.owners = json.loads(ss[3]) self.current_player = json.loads(ss[4]) self.turn_type = json.loads(ss[5]) self.cards = json.loads(ss[6]) self.turn_in_number = json.loads(ss[7]) self.last_attacker = json.loads(ss[8]) self.last_defender = json.loads(ss[9]) self.board = board def print_state(self): """Displays information about this state to the output""" print 'PLAYERS' for p in self.players: p.print_player(self.board) print 'OWNERS/ARMIES' for i in range(len(self.armies)): if self.owners[i] in self.board.id_to_player: print self.board.territories[i].name, '[', self.board.id_to_player[self.owners[i]], '] : ', self.armies[i] else: print self.board.territories[i].name, '[', self.owners[i], '] : ', self.armies[i] if self.current_player < len(self.players): print 'CURRENT PLAYER: ', self.players[self.current_player].name, '[', self.current_player, ']' else: print 'CURRENT PLAYER: ??? [', self.current_player, ']' print len(self.cards), ' CARDS LEFT' def copy_state(self): """ Creates a (deep) copy of this state and return it. Modifications to new state won't change original, except for the risk board, of which there is only one (it shouldn't be modified!) """ copy_players = [] for p in self.players: copy_players.append(p.copy_player()) return RiskState(copy_players,self.armies[:],self.owners[:],self.current_player,self.turn_type,self.turn_in_number, self.last_attacker, self.last_defender, self.cards[:],self.board) class RiskAction(): """Stores the information about an action in a risk game""" def __init__(self, type, to_territory, from_territory, troops): """Initializes a RiskAction""" self.type = type """ What kind of action this is? Should be a string possible types = (PreAssign, PrePlace, Place, TurnInCards, Attack, Occupy, Fortify) """ self.to_territory = to_territory """ This stores the territory id of the place the action is going For Each type it contains: PreAssign: The territory id being chosen by player PrePlace: The territory id of the territory where troop is being placed Place: The territory id of the territory where troop is being placed TurnInCards: The card id of first of the cards being turned in, or None, if there are no cards to turn in Attack: The territory id of the territory being attacked Occupy: The territory id of the territory into which troops are moving Fortify: The territory id of the territory into which troops are moving """ self.from_territory = from_territory """ This stores the territory id of the place the action is coming from For Each type it contains: PreAssign: None PrePlace: None Place: None TurnInCards: The card id of second of the cards being turned in, or None, if there are no cards to turn in Attack: The territory id of the territory doing the attacking Occupy: The territory id of the territory from which troops are moving Fortify: The territory id of the territory from which troops are moving """ self.troops = troops """ This stores the number of troops involved in the action For Each type it contains: PreAssign: None PrePlace: None Place: None TurnInCards: The card id of third of the cards being turned in, or None, if there are no cards to turn in Attack: None Occupy: Number of troops moving into conquered territory Fortify: Number of troops moving to other territory """ def print_action(self): """Displays information about this action to the output""" print self.description() def description(self, newline=False): """returns string description of this action, useful for display""" d = "" d = d + str(self.type) if newline: d = d + "\n" if self.type == 'TurnInCards': d = d + ' CARD 1: ' + str(self.from_territory) elif self.type == 'Attack' or self.type == 'Occupy' or self.type == 'Fortify': d = d + ' FROM: ' + str(self.from_territory) if newline: d = d + "\n" if self.type == 'TurnInCards': d = d + ' CARD 2: ' + str(self.to_territory) elif self.type == 'Place' or self.type == 'PrePlace': d = d + ' IN: ' + str(self.to_territory) else: d = d + ' TO: ' + str(self.to_territory) if newline: d = d + "\n" if self.type == 'TurnInCards': d = d + ' CARD 3: ' + str(self.troops) elif self.type == 'Occupy' or self.type == 'Fortify': d = d + ' NUM: ' + str(self.troops) return d def to_string(self): """Saves this action to a string""" s = 'RISKACTION|' + json.dumps(self.type) + '|' + json.dumps(self.from_territory) + '|' + json.dumps(self.to_territory) + '|' + json.dumps(self.troops) return s def from_string(self, s): """Loads this action from a string""" ss = s.split('|') if ss[0] != 'RISKACTION': print 'THIS IS NOT A RISK ACTION STRING!' self.type = json.loads(ss[1]) self.from_territory = json.loads(ss[2]) self.to_territory = json.loads(ss[3]) self.troops = json.loads(ss[4]) def translateAction(state, action): """ Takes a RiskAction and converts it into what needs to be returned by the AI to compete in the interactive risk game. Don't modify """ if action.type == 'PreAssign' or action.type == 'PrePlace' or action.type == 'Place': return filter(lambda x:x.name == action.to_territory, riskengine.territories.values())[0] elif action.type == 'TurnInCards': if action.to_territory is None: return None,None,None else: return filter(lambda x:x.territory == state.board.territories[state.board.cards[action.to_territory].territory].name, riskengine.currentplayer.cards)[0],filter(lambda x:x.territory == state.board.territories[state.board.cards[action.from_territory].territory].name, riskengine.currentplayer.cards)[0],filter(lambda x:x.territory == state.board.territories[state.board.cards[action.troops].territory].name, riskengine.currentplayer.cards)[0] elif action.type == 'Attack': if action.to_territory is None: return None,None return filter(lambda x:x.name == action.from_territory, riskengine.territories.values())[0], filter(lambda x:x.name == action.to_territory, riskengine.territories.values())[0] elif action.type == 'Occupy': return action.troops elif action.type == 'Fortify': if action.to_territory is None: return None,None,0 return filter(lambda x:x.name == action.from_territory, riskengine.territories.values())[0], filter(lambda x:x.name == action.to_territory, riskengine.territories.values())[0], action.troops else: print 'ILLEGAL ACTION TYPE!' def simulateAction(input_state, action): """ Returns a list of all possible future states that this action could lead to, along with the probability of each. For all but attack actions, there is only a single possibility. This function makes a copy of the input_state, so it is not modified Returns: [state0, state1, ....], [probability0, probability1] """ advance_player = False #Handle attacks differently, because there are multiple possible outcomes rstates = [] rsprobs = [] if action.type == 'Attack': rstates,rsprobs = simulateAttack(input_state, action) else: s = input_state.copy_state() if action.type == 'PreAssign': simulatePreAssignAction(s, action) advance_player = True elif action.type == 'PrePlace': simulatePrePlaceAction(s, action) advance_player = True elif action.type == 'TurnInCards': simulateTurnInCardsAction(s, action) elif action.type == 'Place': simulatePlaceAction(s, action) elif action.type == 'Occupy': simulateOccupyAction(s, action) elif action.type == 'Fortify': simulateFortifyAction(s, action) advance_player = True else: print 'ILLEGAL ACTION TYPE!' rstates = [s] rsprobs = [1] for state in rstates: nextType(state, action) if advance_player: nextPlayer(state) #If it is the beginning of the turn, do the beginning of the turn stuff for the next player if advance_player and (state.turn_type == 'Place' or state.turn_type == 'TurnInCards'): beginTurn(state) return rstates, rsprobs def getReinforcementNum(state, player_id): """ Determine how many troop reinforcements the indicated player should get in the given state. This is calculated from the number of territories and continents they occupy """ #Count territories owned by the current player territory_num = state.owners.count(player_id) #Get that divided by three (with a min of three) territory_troops = int(max(3, math.floor(territory_num / 3.0))) #See if they own all of any continents continent_troops = 0 for c in state.board.continents.itervalues(): owned = True for t in c.territories: if state.owners[t] != player_id: owned = False break if owned: continent_troops = continent_troops + c.reward return territory_troops + continent_troops def beginTurn(state): """ Takes care of beginning-of-turn stuff (cards handled separately) Calculates new free armies from territories and continents """ #Update the player's free armies count state.players[state.current_player].free_armies = getReinforcementNum(state, state.current_player) def nextType(state, action): """Sets a state's turn type after an action is performed""" if action.type == 'PreAssign': #Move to PrePlace if all countries are owned if not None in state.owners: state.turn_type = 'PrePlace' elif action.type == 'PrePlace': #Move to Place if no one has any free armies left done = True for p in state.players: if p.free_armies > 0: done = False if done: state.turn_type = 'Place' elif action.type == 'Place': if state.players[state.current_player].free_armies == 0: state.turn_type = 'Attack' elif action.type == 'TurnInCards': if len(state.players[state.current_player].cards) <= 3 or action.to_territory is None: state.turn_type = 'Place' if state.players[state.current_player].conquered_territory and len(state.players[state.current_player].cards) < 5: state.turn_type = 'Place' elif action.type == 'Attack': #If attack is over, change to Fortify if action.to_territory is None: state.turn_type = 'Fortify' elif state.armies[state.board.territory_to_id[action.to_territory]] == 0: #The attack was successful state.turn_type = 'Occupy' state.players[state.current_player].conquered_territory = True if max(state.owners) == min(state.owners): #GAME OVER IN THIS STATE state.turn_type = 'GameOver' elif action.type == 'Occupy': #Moved troops in, still attacking state.turn_type = 'Attack' if len(state.players[state.current_player].cards) > 5: #If this player has too many cards from defeating someone, must turn them in now (THIS Needs to happen after occupy) state.turn_type = 'TurnInCards' elif action.type == 'Fortify': #Done with turn, now next players turn state.turn_type = 'TurnInCards' else: print 'ILLEGAL ACTION TYPE!' def getAllowedActions(state): """ Returns a list of allowed actions in this current state. An AI Agent simply has to select one of these """ if state.turn_type == 'PreAssign': return getPreAssignActions(state) elif state.turn_type == 'PrePlace': return getPrePlaceActions(state) elif state.turn_type == 'Place': return getPlaceActions(state) elif state.turn_type == 'TurnInCards': return getTurnInCardsActions(state) elif state.turn_type == 'Attack': return getAttackActions(state) elif state.turn_type == 'Occupy': return getOccupyActions(state) elif state.turn_type == 'Fortify': return getFortifyActions(state) else: print 'THE STATE\'S TURN PHASE OF > ', state.turn_type, '< IS NOT VALID' def getPreAssignActions(state): """Returns a list of all the PreAssign actions possible in this state""" #An Action is to select an unoccupied territory actions = [] for i in range(len(state.owners)): if state.owners[i] is None: to_territory = state.board.territories[i].name a = RiskAction('PreAssign', to_territory, None, None) actions.append(a) return actions def simulatePreAssignAction(state, action): """Execute the given action in the given state. This will modify the state to reflect the outcome of the state.""" idx = state.board.territory_to_id[action.to_territory] if state.owners[idx] != None or state.armies[idx] != 0 or state.players[state.current_player].free_armies < 1: print 'INVALID PREASSIGN ACTION!' state.owners[idx] = state.current_player state.armies[idx] = 1 state.players[state.current_player].free_armies -= 1 def getPrePlaceActions(state): """Returns a list of all the PrePlace actions possible in this state""" #An action is to place a troop in a territory occupied by the current player actions = [] for i in range(len(state.owners)): if state.owners[i] == state.current_player: to_territory = state.board.territories[i].name a = RiskAction('PrePlace', to_territory, None, None) actions.append(a) return actions def simulatePrePlaceAction(state, action): """Execute the given action in the given state. This will modify the state to reflect the outcome of the state.""" idx = state.board.territory_to_id[action.to_territory] if state.owners[idx] == state.current_player and state.players[state.current_player].free_armies > 0: state.armies[idx] = state.armies[idx] + 1 state.players[state.current_player].free_armies -= 1 else: print 'INVALID PREPLACE ACTION:' print 'NO PREPLACE ACTION WILL OCCUR' def getTurnInCardsActions(state): """Returns a list of all the TurnInCards actions possible in this state""" actions = [] if len(state.players[state.current_player].cards) >= 3: for i1 in range(len(state.players[state.current_player].cards)-2): c1 = state.players[state.current_player].cards[i1] for i2 in range(i1+1, len(state.players[state.current_player].cards)-1): c2 = state.players[state.current_player].cards[i2] for i3 in range(i2+1, len(state.players[state.current_player].cards)): c3 = state.players[state.current_player].cards[i3] if isCardSet(state, c1, c2, c3): a = RiskAction('TurnInCards', c1, c2, c3) actions.append(a) if len(state.players[state.current_player].cards) < 5: #Don't have to turn in yet, so make a Non-Action a = RiskAction('TurnInCards', None, None, None) actions.append(a) return actions def isCardSet(state, c1, c2, c3): """See if this trio of cards can be turned in (is it a set?)""" #Get the pictures on the cards p1 = state.board.cards[c1].picture p2 = state.board.cards[c2].picture p3 = state.board.cards[c3].picture #If there is one wildcard in the set then it must be a set if 'Wildcard' in [p1,p2,p3]: return True #No wildcards, so are they all the same kind? if p1 == p2 and p2 == p3: return True else: #Not all the same, so they have to all be different #i.e. if any are the same, then they aren't a set if p1==p2 or p1==p3 or p2==p3: return False #The only case that gets here is when they are all different, so a set return True def pause(): """Utility if you ever want to pause to see output at some point. For debugging""" i = raw_input('Paused. Press a key to continue . . . ') def getTurnInValue(state): """See what the current card turn-in-value is""" value = 0 if state.turn_in_number >= len(state.board.turn_in_values): #Calculate turn in, more than scripted values value = state.board.turn_in_values[-1] + state.board.increment_value * (state.turn_in_number - len(state.board.turn_in_values) + 1) else: #Just grab scripted value value = state.board.turn_in_values[state.turn_in_number] state.turn_in_number = state.turn_in_number + 1 return value def simulateTurnInCardsAction(state, action): """ Execute the given action in the given state. This will modify the state to reflect the outcome of the action. """ #See if they want to turn in cards if action.to_territory is None: return if (not isCardSet(state, action.to_territory, action.from_territory, action.troops)) or action.to_territory not in state.players[state.current_player].cards or action.from_territory not in state.players[state.current_player].cards or action.troops not in state.players[state.current_player].cards: print 'INVALID TURN-IN-CARDS ACTION!' action.print_action() print 'NO TURN-IN-CARDS ACTION WILL BE TAKEN' #Modify cards appropriately (cards indices are stored in to_territory, from_territory, and troops #Add turn in value to players free_armies turn_in_troops = getTurnInValue(state) state.players[state.current_player].free_armies += turn_in_troops #Add two troops to any territory that appears on one of the cards owned by this player for i in [action.from_territory, action.to_territory, action.troops]: t = state.board.cards[i].territory if state.owners[t] == state.current_player: state.armies[t] = state.armies[t] + 2 #Remove these cards from the player's hand state.players[state.current_player].cards.remove(i) def getPlaceActions(state): """ Returns a list of all the Place actions possible in this state An action is to place a troop in a territory occupied by the current player """ actions = [] for i in range(len(state.owners)): if state.owners[i] == state.current_player: to_territory = state.board.territories[i].name a = RiskAction('Place', to_territory, None, None) actions.append(a) return actions def simulatePlaceAction(state, action): """ Execute the given action in the given state. This will modify the state to reflect the outcome of the state. """ idx = state.board.territory_to_id[action.to_territory] if state.owners[idx] == state.current_player and state.players[state.current_player].free_armies >= 1: state.armies[idx] = state.armies[idx] + 1 state.players[state.current_player].free_armies -= 1 else: print 'INVALID PLACE ACTION: ' action.print_action() print 'NO PLACE ACTION WILL OCCUR' def getAttackActions(state): """ Returns a list of all the Attack actions possible in this state An action is to attack another territory from a territory owned by the current_player where 2 or more troops are """ actions = [] for i in range(len(state.owners)): if state.owners[i] == state.current_player and state.armies[i] >= 2: from_territory = state.board.territories[i].name for n in state.board.territories[i].neighbors: if state.owners[n] != state.current_player: to_territory = state.board.territories[n].name a = RiskAction('Attack', to_territory, from_territory, None) actions.append(a) no_action = RiskAction('Attack', None, None, None) actions.append(no_action) return actions def getNumAttackSuccessors(state, action): """ Determines how many possible states could result from this attack action This will determine how many successor states we create for the attack action """ #Get indices of involved territories a_idx = state.board.territory_to_id[action.from_territory] d_idx = state.board.territory_to_id[action.to_territory] #Get number of dice that will be involved (This assumes attacker always attacks with all dice) a_num_dice = min(3, state.armies[a_idx]-1) d_num_dice = min(2, state.armies[d_idx]) #The number of possible future states will be the minimum of the dice amounts + 1 return min(a_num_dice, d_num_dice) + 1 def getAttackOutcome(a_num_dice, d_num_dice, outcome_index): """ This will compute the probability of the outcome_index'th possible result of a battle between the given number of dice. It will return the attacker_loss, defender_loss, outcome_probability """ #Index by attacker dice, defender dice, outcome number outcome_probabilities = [[[0.4167, 0.5833], [0.2546, 0.7454]],[[0.5787, 0.4213],[0.2276, 0.3241, 0.4483]],[[0.6597, 0.3404],[0.3717, 0.3358, 0.2926]]] #print 'Getting attack outcome for : ', a_num_dice, ' : ', d_num_dice, ' : ', outcome_index attacker_loss = 0 defender_loss = 0 outcome_probability = outcome_probabilities[a_num_dice-1][d_num_dice-1][outcome_index] total_loss = min(a_num_dice, d_num_dice) if total_loss == 1: if outcome_index == 0: defender_loss = 1 elif outcome_index == 1: attacker_loss = 1 else: print 'Attack Outcome Index invalid' elif total_loss == 2: if outcome_index == 0: defender_loss = 2 elif outcome_index == 1: defender_loss = 1 attacker_loss = 1 elif outcome_index == 2: attacker_loss = 2 else: print 'Attack Outcome index invalid' else: print 'Total Loss is invalid. It is: ', total_loss, ' A Num Dice: ', a_num_dice, 'D Num Dice: ', d_num_dice, ' Outcome Index: ', outcome_index return attacker_loss, defender_loss, outcome_probability def simulateAttack(input_state, action): """ Simulates attack action. Returns a list of possible successor states and a list of their probabilities. This copies the input_state, so it is not modified. """ if action.from_territory is None: #This is the end of attack action. Give the player a card if they earned it cur_state = input_state.copy_state() if cur_state.players[cur_state.current_player].conquered_territory: not_duplicate = False card = random.choice(cur_state.cards) while not_duplicate: #Randomly choose a card and give it to this player card = random.choice(cur_state.cards) if card not in cur_state.players[cur_state.current_player].cards: not_duplicate = True cur_state.players[cur_state.current_player].cards.append(card) #Reset the players conquered_territory flag cur_state.players[cur_state.current_player].conquered_territory = False return [cur_state], [1] #How many possible attack outcomes are there num_successors = getNumAttackSuccessors(input_state, action) successor_states = [] successor_probs = [] for i in range(num_successors): cur_state = input_state.copy_state() cur_prob = 0 if action.type == 'Attack': cur_prob = simulateAttackAction(cur_state, action, i) else: print 'ILLEGAL ACTION TYPE' nextType(cur_state, action) successor_states.append(cur_state) successor_probs.append(cur_prob) return successor_states, successor_probs def simulateAttackAction(state, action, outcome_index): """ Execute the given action in the given state, assuming that the outcome of the battle is given by outcome_index. This will modify the state to reflect the outcome of the state. """ #a is attacker and d is defender #Get indices of involved territories a_idx = state.board.territory_to_id[action.from_territory] d_idx = state.board.territory_to_id[action.to_territory] #MAKE SURE THIS IS VALID if state.owners[a_idx] != state.current_player or state.owners[d_idx] == state.current_player or state.armies[a_idx] <= 1: print 'INVALID ATTACK ACTION: ' action.print_action() print 'NO ATTACK ACTION WILL BE TAKEN' return #Set last attacker and defender variables in the state state.last_attacker = a_idx state.last_defender = d_idx #Player id of defender defender = state.owners[d_idx] #Get number of dice that will be involved (This assumes attacker always attacks with all dice) a_num_dice = min(3, state.armies[a_idx]-1) d_num_dice = min(2, state.armies[d_idx]) if state.armies[d_idx] == 0: print 'THERE ARE NO ARMIES IN TERRITORY: ', state.board.territories[d_idx].name print 'ACTION: ', action.print_action() print 'State: ', state.print_state() #Get the outcome and probability for the input outcome index a_loss, d_loss, outcome_probability = getAttackOutcome(a_num_dice, d_num_dice, outcome_index) state.armies[d_idx] = max(state.armies[d_idx] - d_loss, 0) state.armies[a_idx] = state.armies[a_idx] - a_loss #Check if the defender is at zero, then change owner (armies will be moved in with the next action (Occupy) if state.armies[d_idx] == 0: state.owners[d_idx] = state.current_player #Check to see if the defender is now done completely, in which case we need to give her cards to the attacker. if defender not in state.owners: #The defender is done state.players[state.current_player].cards.extend(state.players[defender].cards) state.players[defender].cards = [] return outcome_probability def getOccupyActions(state): """ Returns a list of all the Occupy actions possible in this state An action is to move an amount of troops into the newly conquered country (must leave at least 1 behind, and must move at least min(3,number_there -1)) """ actions = [] if state.last_defender is None or state.last_attacker is None: return actions to_territory = state.board.territories[state.last_defender].name from_territory = state.board.territories[state.last_attacker].name for k in range(min(state.armies[state.last_attacker]-1,3), state.armies[state.last_attacker]): a = RiskAction('Occupy', to_territory, from_territory, k) actions.append(a) return actions def simulateOccupyAction(state, action): """ Execute the given action in the given state. This will modify the state to reflect the outcome of the state. """ to_idx = state.board.territory_to_id[action.to_territory] from_idx = state.board.territory_to_id[action.from_territory] #Make sure that the occupy action is correctly constructed if to_idx != state.last_defender or from_idx != state.last_attacker or state.armies[from_idx] - action.troops < 1 or state.owners[from_idx] != state.current_player or state.owners[to_idx] != state.current_player: #This move is invalid print 'INVALID OCCUPY ACTION: ' action.print_action() print 'To index = ', to_idx print 'Last defender = ', state.last_defender print 'From index = ', from_idx print 'Last attacker = ', state.last_attacker print 'NO OCCUPY ACTION WILL BE TAKEN' pause() return state.armies[to_idx] = state.armies[to_idx] + action.troops state.armies[from_idx] = state.armies[from_idx] - action.troops def getFortifyActions(state): """ Returns a list of all the Fortify actions possible in this state An action is to move troops from one territory to a neighboring territory (both owned by current_player), leaving at least 1 troop in the from_territory """ actions = [] for i in range(len(state.owners)): if state.owners[i] == state.current_player and state.armies[i] >= 2: from_territory = state.board.territories[i].name for n in state.board.territories[i].neighbors: if state.owners[n] == state.current_player: to_territory = state.board.territories[n].name for k in range(1, state.armies[i]): a = RiskAction('Fortify', to_territory, from_territory, k) actions.append(a) no_action = RiskAction('Fortify', None, None, 0) actions.append(no_action) return actions def simulateFortifyAction(state, action): """ Execute the given action in the given state. This will modify the state to reflect the outcome of the state. """ if action.to_territory is None: return to_idx = state.board.territory_to_id[action.to_territory] from_idx = state.board.territory_to_id[action.from_territory] #Make sure that the fortify action is correctly constructed if state.armies[from_idx] - action.troops < 1 or state.owners[from_idx] != state.current_player or state.owners[to_idx] != state.current_player or to_idx not in state.board.territories[from_idx].neighbors: #This move is invalid print 'INVALID FORTIFY ACTION: ' action.print_action() print 'NO FORTIFY ACTION WILL BE TAKEN' return state.armies[to_idx] = state.armies[to_idx] + action.troops state.armies[from_idx] = state.armies[from_idx] - action.troops def createRiskBoard(): """ Creates a RiskBoard from the current riskengine state. Used to interface with the GUI that allows humans to play the AIs """ board = RiskBoard() #Create Continents for c in riskengine.continents: rc = RiskContinent(c[0],c[1]) board.add_continent(rc) #Create Territories id_counter = 0 for t in riskengine.territories.values(): rt = RiskTerritory(t.name, id_counter) board.add_territory(rt) tc = board.continents[t.continent] tc.add_territory(rt.id) id_counter += 1 #Add in neighbors of each territory for t in riskengine.territories.values(): rt = board.territories[board.territory_to_id[t.name]] for n in t.neighbors: rt.add_neighbor(board.territory_to_id[n.name]) #Create cards for c in riskengine.allcards: rc = RiskCard(board.territory_to_id[c.territory], c.picture, len(board.cards)) board.add_card(rc) #Create and add players id_counter = 0 for p in riskengine.playerorder: rp = RiskPlayer(p.name, id_counter, p.freeArmies, p.conqueredTerritory) id_counter += 1 for c in p.cards: rc = RiskCard(board.territory_to_id[c.territory], c.picture, len(board.cards)) board.add_card(rc) rp.add_card(board.card_to_id[board.territories[board.territory_to_id[c.territory]].name]) board.add_player(rp) board.set_turn_in_values(riskengine.cardvals) board.set_increment_value(riskengine.incrementval) return board def createRiskState(board, function_name, occupying=None): """ Creates a RiskState from the current riskengine state. Used to interface with the GUI that allows humans to play the AIs """ #players, armies, owners, current_player, turn_type #Create players players = [] id_counter = 0 for p in riskengine.playerorder: rp = RiskPlayer(p.name, id_counter, p.freeArmies, p.conqueredTerritory) for c in p.cards: rp.add_card(board.card_to_id[c.territory]) players.append(rp) id_counter += 1 #Create armies and owners armies = [0]*len(board.territories) owners = [None]*len(board.territories) for t in riskengine.territories.values(): idx = board.territory_to_id[t.name] if t.player is not None: armies[idx] = t.armies owners[idx] = board.player_to_id[t.player.name] #Determine the current player current_player_obj = filter(lambda x:x.name == riskengine.currentplayer.name, players)[0] current_player = current_player_obj.id turn_type = None if riskengine.phase == 'Preposition' and function_name == 'Assignment': turn_type = 'PreAssign' elif riskengine.phase == 'Preposition' and function_name == 'Placement': turn_type = 'PrePlace' elif riskengine.phase == 'Place' and function_name == 'Placement': turn_type = 'Place' elif riskengine.phase == 'Place' and function_name == 'TurnInCards': turn_type = 'TurnInCards' elif riskengine.phase == 'Attack' and function_name == 'Attack': turn_type = 'Attack' elif riskengine.phase == 'Attack' and function_name == 'Occupation': turn_type = 'Occupy' elif riskengine.phase == 'Attack' and function_name == 'Fortification': turn_type = 'Fortify' #current card turn in index turn_in_number = riskengine.currentcard #Determine last attacker and defender last_attacker = None last_defender = None if occupying is not None: last_attacker = board.territory_to_id[occupying[0]] last_defender = board.territory_to_id[occupying[1]] #Create Cards cards = [] for c in riskengine.allcards: cards.append(board.card_to_id[c.territory]) state = RiskState(players,armies,owners,current_player,turn_type,turn_in_number, last_attacker, last_defender, cards, board) return state def getInitialState(board): """Get the initial state for this board.""" #Initialize the state with the information in the board free_armies = 45 - 5*(len(board.players)-1) state_players = [] for p in board.players: new_player = p.copy_player() state_players.append(new_player) new_player.free_armies = free_armies #Initialize other state elements armies = [0]*len(board.territories) owners = [None]*len(board.territories) current_player = 0 turn_type = 'PreAssign' turn_in_number = 0 last_attacker = None last_defender = None #Cards cards = [0]*len(board.cards) for i in range(len(cards)): cards[i] = board.cards[i].id state = RiskState(state_players, armies, owners, current_player, turn_type, turn_in_number, last_attacker, last_defender, cards, board) return state def loadBoard(filename): """Loads a RiskBoard from the given filename""" #Open the zip file to get map data zfile = zipfile.ZipFile(filename) board = RiskBoard() loadTerritories(zfile, board) #Close the zip file zfile.close() return board def loadTerritories(zfile, board): """Load territory (and other) information from a file.""" terr = xml.dom.minidom.parseString(zfile.read("territory.xml")) terr_structs = terr.getElementsByTagName("territory") #Create and add territories for t in terr_structs: name = t.getAttribute("name") nt = RiskTerritory(name, len(board.territories)) board.add_territory(nt) #Create continents, add in neighbors for terrs in terr_structs: name = terrs.getAttribute("name") cname = terrs.getAttribute("continent") tidx = board.territory_to_id[name] ncon = RiskContinent(cname, 0) board.add_continent(ncon) continent = board.continents[cname] continent.add_territory(tidx) neighbors = terrs.getElementsByTagName("neighbor") for neigh in neighbors: nidx = board.territory_to_id[neigh.childNodes[0].data] board.territories[tidx].add_neighbor(nidx) cont_structs = terr.getElementsByTagName("continent") for con in cont_structs: continent = board.continents[con.getAttribute("name")] continent.reward = int(con.getAttribute("value")) loadCards(terr, board) def loadCards(xmlFile, board): """Load the data for the cards from the file.""" cardvals = [] card = xmlFile.getElementsByTagName("cards")[0] for pic in card.getElementsByTagName("picture"): board.pictures.append(pic.childNodes[0].data) for cvalue in card.getElementsByTagName("card"): cardvals.append(int(cvalue.childNodes[0].data)) incremtag = card.getElementsByTagName("increase")[0] incrementval = int(incremtag.childNodes[0].data) board.set_turn_in_values(cardvals) board.set_increment_value(incrementval) createCards(board) def createCards(board): """Create the set of cards.""" wildcard = "Wildcard" for terr in range(2): car = RiskCard(terr, wildcard, terr) board.add_card(car) for terr in range(2, len(board.territories)): car = RiskCard(terr, random.choice(board.pictures), terr) board.add_card(car) board.shuffle_cards()
Functions
def beginTurn(
state)
Takes care of beginning-of-turn stuff (cards handled separately) Calculates new free armies from territories and continents
def beginTurn(state): """ Takes care of beginning-of-turn stuff (cards handled separately) Calculates new free armies from territories and continents """ #Update the player's free armies count state.players[state.current_player].free_armies = getReinforcementNum(state, state.current_player)
def createCards(
board)
Create the set of cards.
def createCards(board): """Create the set of cards.""" wildcard = "Wildcard" for terr in range(2): car = RiskCard(terr, wildcard, terr) board.add_card(car) for terr in range(2, len(board.territories)): car = RiskCard(terr, random.choice(board.pictures), terr) board.add_card(car) board.shuffle_cards()
def createRiskBoard(
)
Creates a RiskBoard from the current riskengine state.
Used to interface with the GUI that allows humans to play the AIs
def createRiskBoard(): """ Creates a RiskBoard from the current riskengine state. Used to interface with the GUI that allows humans to play the AIs """ board = RiskBoard() #Create Continents for c in riskengine.continents: rc = RiskContinent(c[0],c[1]) board.add_continent(rc) #Create Territories id_counter = 0 for t in riskengine.territories.values(): rt = RiskTerritory(t.name, id_counter) board.add_territory(rt) tc = board.continents[t.continent] tc.add_territory(rt.id) id_counter += 1 #Add in neighbors of each territory for t in riskengine.territories.values(): rt = board.territories[board.territory_to_id[t.name]] for n in t.neighbors: rt.add_neighbor(board.territory_to_id[n.name]) #Create cards for c in riskengine.allcards: rc = RiskCard(board.territory_to_id[c.territory], c.picture, len(board.cards)) board.add_card(rc) #Create and add players id_counter = 0 for p in riskengine.playerorder: rp = RiskPlayer(p.name, id_counter, p.freeArmies, p.conqueredTerritory) id_counter += 1 for c in p.cards: rc = RiskCard(board.territory_to_id[c.territory], c.picture, len(board.cards)) board.add_card(rc) rp.add_card(board.card_to_id[board.territories[board.territory_to_id[c.territory]].name]) board.add_player(rp) board.set_turn_in_values(riskengine.cardvals) board.set_increment_value(riskengine.incrementval) return board
def createRiskState(
board, function_name, occupying=None)
Creates a RiskState from the current riskengine state.
Used to interface with the GUI that allows humans to play the AIs
def createRiskState(board, function_name, occupying=None): """ Creates a RiskState from the current riskengine state. Used to interface with the GUI that allows humans to play the AIs """ #players, armies, owners, current_player, turn_type #Create players players = [] id_counter = 0 for p in riskengine.playerorder: rp = RiskPlayer(p.name, id_counter, p.freeArmies, p.conqueredTerritory) for c in p.cards: rp.add_card(board.card_to_id[c.territory]) players.append(rp) id_counter += 1 #Create armies and owners armies = [0]*len(board.territories) owners = [None]*len(board.territories) for t in riskengine.territories.values(): idx = board.territory_to_id[t.name] if t.player is not None: armies[idx] = t.armies owners[idx] = board.player_to_id[t.player.name] #Determine the current player current_player_obj = filter(lambda x:x.name == riskengine.currentplayer.name, players)[0] current_player = current_player_obj.id turn_type = None if riskengine.phase == 'Preposition' and function_name == 'Assignment': turn_type = 'PreAssign' elif riskengine.phase == 'Preposition' and function_name == 'Placement': turn_type = 'PrePlace' elif riskengine.phase == 'Place' and function_name == 'Placement': turn_type = 'Place' elif riskengine.phase == 'Place' and function_name == 'TurnInCards': turn_type = 'TurnInCards' elif riskengine.phase == 'Attack' and function_name == 'Attack': turn_type = 'Attack' elif riskengine.phase == 'Attack' and function_name == 'Occupation': turn_type = 'Occupy' elif riskengine.phase == 'Attack' and function_name == 'Fortification': turn_type = 'Fortify' #current card turn in index turn_in_number = riskengine.currentcard #Determine last attacker and defender last_attacker = None last_defender = None if occupying is not None: last_attacker = board.territory_to_id[occupying[0]] last_defender = board.territory_to_id[occupying[1]] #Create Cards cards = [] for c in riskengine.allcards: cards.append(board.card_to_id[c.territory]) state = RiskState(players,armies,owners,current_player,turn_type,turn_in_number, last_attacker, last_defender, cards, board) return state
def getAllowedActions(
state)
Returns a list of allowed actions in this current state.
An AI Agent simply has to select one of these
def getAllowedActions(state): """ Returns a list of allowed actions in this current state. An AI Agent simply has to select one of these """ if state.turn_type == 'PreAssign': return getPreAssignActions(state) elif state.turn_type == 'PrePlace': return getPrePlaceActions(state) elif state.turn_type == 'Place': return getPlaceActions(state) elif state.turn_type == 'TurnInCards': return getTurnInCardsActions(state) elif state.turn_type == 'Attack': return getAttackActions(state) elif state.turn_type == 'Occupy': return getOccupyActions(state) elif state.turn_type == 'Fortify': return getFortifyActions(state) else: print 'THE STATE\'S TURN PHASE OF > ', state.turn_type, '< IS NOT VALID'
def getAttackActions(
state)
Returns a list of all the Attack actions possible in this state An action is to attack another territory from a territory owned by the current_player where 2 or more troops are
def getAttackActions(state): """ Returns a list of all the Attack actions possible in this state An action is to attack another territory from a territory owned by the current_player where 2 or more troops are """ actions = [] for i in range(len(state.owners)): if state.owners[i] == state.current_player and state.armies[i] >= 2: from_territory = state.board.territories[i].name for n in state.board.territories[i].neighbors: if state.owners[n] != state.current_player: to_territory = state.board.territories[n].name a = RiskAction('Attack', to_territory, from_territory, None) actions.append(a) no_action = RiskAction('Attack', None, None, None) actions.append(no_action) return actions
def getAttackOutcome(
a_num_dice, d_num_dice, outcome_index)
This will compute the probability of the outcome_index'th possible result of a battle between the given number of dice. It will return the attacker_loss, defender_loss, outcome_probability
def getAttackOutcome(a_num_dice, d_num_dice, outcome_index): """ This will compute the probability of the outcome_index'th possible result of a battle between the given number of dice. It will return the attacker_loss, defender_loss, outcome_probability """ #Index by attacker dice, defender dice, outcome number outcome_probabilities = [[[0.4167, 0.5833], [0.2546, 0.7454]],[[0.5787, 0.4213],[0.2276, 0.3241, 0.4483]],[[0.6597, 0.3404],[0.3717, 0.3358, 0.2926]]] #print 'Getting attack outcome for : ', a_num_dice, ' : ', d_num_dice, ' : ', outcome_index attacker_loss = 0 defender_loss = 0 outcome_probability = outcome_probabilities[a_num_dice-1][d_num_dice-1][outcome_index] total_loss = min(a_num_dice, d_num_dice) if total_loss == 1: if outcome_index == 0: defender_loss = 1 elif outcome_index == 1: attacker_loss = 1 else: print 'Attack Outcome Index invalid' elif total_loss == 2: if outcome_index == 0: defender_loss = 2 elif outcome_index == 1: defender_loss = 1 attacker_loss = 1 elif outcome_index == 2: attacker_loss = 2 else: print 'Attack Outcome index invalid' else: print 'Total Loss is invalid. It is: ', total_loss, ' A Num Dice: ', a_num_dice, 'D Num Dice: ', d_num_dice, ' Outcome Index: ', outcome_index return attacker_loss, defender_loss, outcome_probability
def getFortifyActions(
state)
Returns a list of all the Fortify actions possible in this state An action is to move troops from one territory to a neighboring territory (both owned by current_player), leaving at least 1 troop in the from_territory
def getFortifyActions(state): """ Returns a list of all the Fortify actions possible in this state An action is to move troops from one territory to a neighboring territory (both owned by current_player), leaving at least 1 troop in the from_territory """ actions = [] for i in range(len(state.owners)): if state.owners[i] == state.current_player and state.armies[i] >= 2: from_territory = state.board.territories[i].name for n in state.board.territories[i].neighbors: if state.owners[n] == state.current_player: to_territory = state.board.territories[n].name for k in range(1, state.armies[i]): a = RiskAction('Fortify', to_territory, from_territory, k) actions.append(a) no_action = RiskAction('Fortify', None, None, 0) actions.append(no_action) return actions
def getInitialState(
board)
Get the initial state for this board.
def getInitialState(board): """Get the initial state for this board.""" #Initialize the state with the information in the board free_armies = 45 - 5*(len(board.players)-1) state_players = [] for p in board.players: new_player = p.copy_player() state_players.append(new_player) new_player.free_armies = free_armies #Initialize other state elements armies = [0]*len(board.territories) owners = [None]*len(board.territories) current_player = 0 turn_type = 'PreAssign' turn_in_number = 0 last_attacker = None last_defender = None #Cards cards = [0]*len(board.cards) for i in range(len(cards)): cards[i] = board.cards[i].id state = RiskState(state_players, armies, owners, current_player, turn_type, turn_in_number, last_attacker, last_defender, cards, board) return state
def getNumAttackSuccessors(
state, action)
Determines how many possible states could result from this attack action This will determine how many successor states we create for the attack action
def getNumAttackSuccessors(state, action): """ Determines how many possible states could result from this attack action This will determine how many successor states we create for the attack action """ #Get indices of involved territories a_idx = state.board.territory_to_id[action.from_territory] d_idx = state.board.territory_to_id[action.to_territory] #Get number of dice that will be involved (This assumes attacker always attacks with all dice) a_num_dice = min(3, state.armies[a_idx]-1) d_num_dice = min(2, state.armies[d_idx]) #The number of possible future states will be the minimum of the dice amounts + 1 return min(a_num_dice, d_num_dice) + 1
def getOccupyActions(
state)
Returns a list of all the Occupy actions possible in this state An action is to move an amount of troops into the newly conquered country (must leave at least 1 behind, and must move at least min(3,number_there -1))
def getOccupyActions(state): """ Returns a list of all the Occupy actions possible in this state An action is to move an amount of troops into the newly conquered country (must leave at least 1 behind, and must move at least min(3,number_there -1)) """ actions = [] if state.last_defender is None or state.last_attacker is None: return actions to_territory = state.board.territories[state.last_defender].name from_territory = state.board.territories[state.last_attacker].name for k in range(min(state.armies[state.last_attacker]-1,3), state.armies[state.last_attacker]): a = RiskAction('Occupy', to_territory, from_territory, k) actions.append(a) return actions
def getPlaceActions(
state)
Returns a list of all the Place actions possible in this state An action is to place a troop in a territory occupied by the current player
def getPlaceActions(state): """ Returns a list of all the Place actions possible in this state An action is to place a troop in a territory occupied by the current player """ actions = [] for i in range(len(state.owners)): if state.owners[i] == state.current_player: to_territory = state.board.territories[i].name a = RiskAction('Place', to_territory, None, None) actions.append(a) return actions
def getPreAssignActions(
state)
Returns a list of all the PreAssign actions possible in this state
def getPreAssignActions(state): """Returns a list of all the PreAssign actions possible in this state""" #An Action is to select an unoccupied territory actions = [] for i in range(len(state.owners)): if state.owners[i] is None: to_territory = state.board.territories[i].name a = RiskAction('PreAssign', to_territory, None, None) actions.append(a) return actions
def getPrePlaceActions(
state)
Returns a list of all the PrePlace actions possible in this state
def getPrePlaceActions(state): """Returns a list of all the PrePlace actions possible in this state""" #An action is to place a troop in a territory occupied by the current player actions = [] for i in range(len(state.owners)): if state.owners[i] == state.current_player: to_territory = state.board.territories[i].name a = RiskAction('PrePlace', to_territory, None, None) actions.append(a) return actions
def getReinforcementNum(
state, player_id)
Determine how many troop reinforcements the indicated player should get in the given state. This is calculated from the number of territories and continents they occupy
def getReinforcementNum(state, player_id): """ Determine how many troop reinforcements the indicated player should get in the given state. This is calculated from the number of territories and continents they occupy """ #Count territories owned by the current player territory_num = state.owners.count(player_id) #Get that divided by three (with a min of three) territory_troops = int(max(3, math.floor(territory_num / 3.0))) #See if they own all of any continents continent_troops = 0 for c in state.board.continents.itervalues(): owned = True for t in c.territories: if state.owners[t] != player_id: owned = False break if owned: continent_troops = continent_troops + c.reward return territory_troops + continent_troops
def getTurnInCardsActions(
state)
Returns a list of all the TurnInCards actions possible in this state
def getTurnInCardsActions(state): """Returns a list of all the TurnInCards actions possible in this state""" actions = [] if len(state.players[state.current_player].cards) >= 3: for i1 in range(len(state.players[state.current_player].cards)-2): c1 = state.players[state.current_player].cards[i1] for i2 in range(i1+1, len(state.players[state.current_player].cards)-1): c2 = state.players[state.current_player].cards[i2] for i3 in range(i2+1, len(state.players[state.current_player].cards)): c3 = state.players[state.current_player].cards[i3] if isCardSet(state, c1, c2, c3): a = RiskAction('TurnInCards', c1, c2, c3) actions.append(a) if len(state.players[state.current_player].cards) < 5: #Don't have to turn in yet, so make a Non-Action a = RiskAction('TurnInCards', None, None, None) actions.append(a) return actions
def getTurnInValue(
state)
See what the current card turn-in-value is
def getTurnInValue(state): """See what the current card turn-in-value is""" value = 0 if state.turn_in_number >= len(state.board.turn_in_values): #Calculate turn in, more than scripted values value = state.board.turn_in_values[-1] + state.board.increment_value * (state.turn_in_number - len(state.board.turn_in_values) + 1) else: #Just grab scripted value value = state.board.turn_in_values[state.turn_in_number] state.turn_in_number = state.turn_in_number + 1 return value
def incrementPlayer(
state)
Increments the current_player of the input state Input: RiskState object
def incrementPlayer(state): """ Increments the current_player of the input state Input: RiskState object """ state.current_player = state.current_player + 1 if state.current_player >= len(state.players): state.current_player = 0
def isCardSet(
state, c1, c2, c3)
See if this trio of cards can be turned in (is it a set?)
def isCardSet(state, c1, c2, c3): """See if this trio of cards can be turned in (is it a set?)""" #Get the pictures on the cards p1 = state.board.cards[c1].picture p2 = state.board.cards[c2].picture p3 = state.board.cards[c3].picture #If there is one wildcard in the set then it must be a set if 'Wildcard' in [p1,p2,p3]: return True #No wildcards, so are they all the same kind? if p1 == p2 and p2 == p3: return True else: #Not all the same, so they have to all be different #i.e. if any are the same, then they aren't a set if p1==p2 or p1==p3 or p2==p3: return False #The only case that gets here is when they are all different, so a set return True
def loadBoard(
filename)
Loads a RiskBoard from the given filename
def loadBoard(filename): """Loads a RiskBoard from the given filename""" #Open the zip file to get map data zfile = zipfile.ZipFile(filename) board = RiskBoard() loadTerritories(zfile, board) #Close the zip file zfile.close() return board
def loadCards(
xmlFile, board)
Load the data for the cards from the file.
def loadCards(xmlFile, board): """Load the data for the cards from the file.""" cardvals = [] card = xmlFile.getElementsByTagName("cards")[0] for pic in card.getElementsByTagName("picture"): board.pictures.append(pic.childNodes[0].data) for cvalue in card.getElementsByTagName("card"): cardvals.append(int(cvalue.childNodes[0].data)) incremtag = card.getElementsByTagName("increase")[0] incrementval = int(incremtag.childNodes[0].data) board.set_turn_in_values(cardvals) board.set_increment_value(incrementval) createCards(board)
def loadTerritories(
zfile, board)
Load territory (and other) information from a file.
def loadTerritories(zfile, board): """Load territory (and other) information from a file.""" terr = xml.dom.minidom.parseString(zfile.read("territory.xml")) terr_structs = terr.getElementsByTagName("territory") #Create and add territories for t in terr_structs: name = t.getAttribute("name") nt = RiskTerritory(name, len(board.territories)) board.add_territory(nt) #Create continents, add in neighbors for terrs in terr_structs: name = terrs.getAttribute("name") cname = terrs.getAttribute("continent") tidx = board.territory_to_id[name] ncon = RiskContinent(cname, 0) board.add_continent(ncon) continent = board.continents[cname] continent.add_territory(tidx) neighbors = terrs.getElementsByTagName("neighbor") for neigh in neighbors: nidx = board.territory_to_id[neigh.childNodes[0].data] board.territories[tidx].add_neighbor(nidx) cont_structs = terr.getElementsByTagName("continent") for con in cont_structs: continent = board.continents[con.getAttribute("name")] continent.reward = int(con.getAttribute("value")) loadCards(terr, board)
def nextPlayer(
state)
Moves the state's current_player to the next player in the order Also ensures that when it exits, the new state.current_player is still in the game Input: RiskState object
def nextPlayer(state): """ Moves the state's current_player to the next player in the order Also ensures that when it exits, the new state.current_player is still in the game Input: RiskState object """ incrementPlayer(state) while state.current_player not in state.owners and None not in state.owners: incrementPlayer(state)
def nextType(
state, action)
Sets a state's turn type after an action is performed
def nextType(state, action): """Sets a state's turn type after an action is performed""" if action.type == 'PreAssign': #Move to PrePlace if all countries are owned if not None in state.owners: state.turn_type = 'PrePlace' elif action.type == 'PrePlace': #Move to Place if no one has any free armies left done = True for p in state.players: if p.free_armies > 0: done = False if done: state.turn_type = 'Place' elif action.type == 'Place': if state.players[state.current_player].free_armies == 0: state.turn_type = 'Attack' elif action.type == 'TurnInCards': if len(state.players[state.current_player].cards) <= 3 or action.to_territory is None: state.turn_type = 'Place' if state.players[state.current_player].conquered_territory and len(state.players[state.current_player].cards) < 5: state.turn_type = 'Place' elif action.type == 'Attack': #If attack is over, change to Fortify if action.to_territory is None: state.turn_type = 'Fortify' elif state.armies[state.board.territory_to_id[action.to_territory]] == 0: #The attack was successful state.turn_type = 'Occupy' state.players[state.current_player].conquered_territory = True if max(state.owners) == min(state.owners): #GAME OVER IN THIS STATE state.turn_type = 'GameOver' elif action.type == 'Occupy': #Moved troops in, still attacking state.turn_type = 'Attack' if len(state.players[state.current_player].cards) > 5: #If this player has too many cards from defeating someone, must turn them in now (THIS Needs to happen after occupy) state.turn_type = 'TurnInCards' elif action.type == 'Fortify': #Done with turn, now next players turn state.turn_type = 'TurnInCards' else: print 'ILLEGAL ACTION TYPE!'
def pause(
)
Utility if you ever want to pause to see output at some point. For debugging
def pause(): """Utility if you ever want to pause to see output at some point. For debugging""" i = raw_input('Paused. Press a key to continue . . . ')
def simulateAction(
input_state, action)
Returns a list of all possible future states that this action could lead to, along with the probability of each. For all but attack actions, there is only a single possibility.
This function makes a copy of the input_state, so it is not modified
Returns: [state0, state1, ....], [probability0, probability1]
def simulateAction(input_state, action): """ Returns a list of all possible future states that this action could lead to, along with the probability of each. For all but attack actions, there is only a single possibility. This function makes a copy of the input_state, so it is not modified Returns: [state0, state1, ....], [probability0, probability1] """ advance_player = False #Handle attacks differently, because there are multiple possible outcomes rstates = [] rsprobs = [] if action.type == 'Attack': rstates,rsprobs = simulateAttack(input_state, action) else: s = input_state.copy_state() if action.type == 'PreAssign': simulatePreAssignAction(s, action) advance_player = True elif action.type == 'PrePlace': simulatePrePlaceAction(s, action) advance_player = True elif action.type == 'TurnInCards': simulateTurnInCardsAction(s, action) elif action.type == 'Place': simulatePlaceAction(s, action) elif action.type == 'Occupy': simulateOccupyAction(s, action) elif action.type == 'Fortify': simulateFortifyAction(s, action) advance_player = True else: print 'ILLEGAL ACTION TYPE!' rstates = [s] rsprobs = [1] for state in rstates: nextType(state, action) if advance_player: nextPlayer(state) #If it is the beginning of the turn, do the beginning of the turn stuff for the next player if advance_player and (state.turn_type == 'Place' or state.turn_type == 'TurnInCards'): beginTurn(state) return rstates, rsprobs
def simulateAttack(
input_state, action)
Simulates attack action. Returns a list of possible successor states and a list of their probabilities. This copies the input_state, so it is not modified.
def simulateAttack(input_state, action): """ Simulates attack action. Returns a list of possible successor states and a list of their probabilities. This copies the input_state, so it is not modified. """ if action.from_territory is None: #This is the end of attack action. Give the player a card if they earned it cur_state = input_state.copy_state() if cur_state.players[cur_state.current_player].conquered_territory: not_duplicate = False card = random.choice(cur_state.cards) while not_duplicate: #Randomly choose a card and give it to this player card = random.choice(cur_state.cards) if card not in cur_state.players[cur_state.current_player].cards: not_duplicate = True cur_state.players[cur_state.current_player].cards.append(card) #Reset the players conquered_territory flag cur_state.players[cur_state.current_player].conquered_territory = False return [cur_state], [1] #How many possible attack outcomes are there num_successors = getNumAttackSuccessors(input_state, action) successor_states = [] successor_probs = [] for i in range(num_successors): cur_state = input_state.copy_state() cur_prob = 0 if action.type == 'Attack': cur_prob = simulateAttackAction(cur_state, action, i) else: print 'ILLEGAL ACTION TYPE' nextType(cur_state, action) successor_states.append(cur_state) successor_probs.append(cur_prob) return successor_states, successor_probs
def simulateAttackAction(
state, action, outcome_index)
Execute the given action in the given state, assuming that the outcome of the battle is given by outcome_index. This will modify the state to reflect the outcome of the state.
def simulateAttackAction(state, action, outcome_index): """ Execute the given action in the given state, assuming that the outcome of the battle is given by outcome_index. This will modify the state to reflect the outcome of the state. """ #a is attacker and d is defender #Get indices of involved territories a_idx = state.board.territory_to_id[action.from_territory] d_idx = state.board.territory_to_id[action.to_territory] #MAKE SURE THIS IS VALID if state.owners[a_idx] != state.current_player or state.owners[d_idx] == state.current_player or state.armies[a_idx] <= 1: print 'INVALID ATTACK ACTION: ' action.print_action() print 'NO ATTACK ACTION WILL BE TAKEN' return #Set last attacker and defender variables in the state state.last_attacker = a_idx state.last_defender = d_idx #Player id of defender defender = state.owners[d_idx] #Get number of dice that will be involved (This assumes attacker always attacks with all dice) a_num_dice = min(3, state.armies[a_idx]-1) d_num_dice = min(2, state.armies[d_idx]) if state.armies[d_idx] == 0: print 'THERE ARE NO ARMIES IN TERRITORY: ', state.board.territories[d_idx].name print 'ACTION: ', action.print_action() print 'State: ', state.print_state() #Get the outcome and probability for the input outcome index a_loss, d_loss, outcome_probability = getAttackOutcome(a_num_dice, d_num_dice, outcome_index) state.armies[d_idx] = max(state.armies[d_idx] - d_loss, 0) state.armies[a_idx] = state.armies[a_idx] - a_loss #Check if the defender is at zero, then change owner (armies will be moved in with the next action (Occupy) if state.armies[d_idx] == 0: state.owners[d_idx] = state.current_player #Check to see if the defender is now done completely, in which case we need to give her cards to the attacker. if defender not in state.owners: #The defender is done state.players[state.current_player].cards.extend(state.players[defender].cards) state.players[defender].cards = [] return outcome_probability
def simulateFortifyAction(
state, action)
Execute the given action in the given state. This will modify the state to reflect the outcome of the state.
def simulateFortifyAction(state, action): """ Execute the given action in the given state. This will modify the state to reflect the outcome of the state. """ if action.to_territory is None: return to_idx = state.board.territory_to_id[action.to_territory] from_idx = state.board.territory_to_id[action.from_territory] #Make sure that the fortify action is correctly constructed if state.armies[from_idx] - action.troops < 1 or state.owners[from_idx] != state.current_player or state.owners[to_idx] != state.current_player or to_idx not in state.board.territories[from_idx].neighbors: #This move is invalid print 'INVALID FORTIFY ACTION: ' action.print_action() print 'NO FORTIFY ACTION WILL BE TAKEN' return state.armies[to_idx] = state.armies[to_idx] + action.troops state.armies[from_idx] = state.armies[from_idx] - action.troops
def simulateOccupyAction(
state, action)
Execute the given action in the given state. This will modify the state to reflect the outcome of the state.
def simulateOccupyAction(state, action): """ Execute the given action in the given state. This will modify the state to reflect the outcome of the state. """ to_idx = state.board.territory_to_id[action.to_territory] from_idx = state.board.territory_to_id[action.from_territory] #Make sure that the occupy action is correctly constructed if to_idx != state.last_defender or from_idx != state.last_attacker or state.armies[from_idx] - action.troops < 1 or state.owners[from_idx] != state.current_player or state.owners[to_idx] != state.current_player: #This move is invalid print 'INVALID OCCUPY ACTION: ' action.print_action() print 'To index = ', to_idx print 'Last defender = ', state.last_defender print 'From index = ', from_idx print 'Last attacker = ', state.last_attacker print 'NO OCCUPY ACTION WILL BE TAKEN' pause() return state.armies[to_idx] = state.armies[to_idx] + action.troops state.armies[from_idx] = state.armies[from_idx] - action.troops
def simulatePlaceAction(
state, action)
Execute the given action in the given state. This will modify the state to reflect the outcome of the state.
def simulatePlaceAction(state, action): """ Execute the given action in the given state. This will modify the state to reflect the outcome of the state. """ idx = state.board.territory_to_id[action.to_territory] if state.owners[idx] == state.current_player and state.players[state.current_player].free_armies >= 1: state.armies[idx] = state.armies[idx] + 1 state.players[state.current_player].free_armies -= 1 else: print 'INVALID PLACE ACTION: ' action.print_action() print 'NO PLACE ACTION WILL OCCUR'
def simulatePreAssignAction(
state, action)
Execute the given action in the given state. This will modify the state to reflect the outcome of the state.
def simulatePreAssignAction(state, action): """Execute the given action in the given state. This will modify the state to reflect the outcome of the state.""" idx = state.board.territory_to_id[action.to_territory] if state.owners[idx] != None or state.armies[idx] != 0 or state.players[state.current_player].free_armies < 1: print 'INVALID PREASSIGN ACTION!' state.owners[idx] = state.current_player state.armies[idx] = 1 state.players[state.current_player].free_armies -= 1
def simulatePrePlaceAction(
state, action)
Execute the given action in the given state. This will modify the state to reflect the outcome of the state.
def simulatePrePlaceAction(state, action): """Execute the given action in the given state. This will modify the state to reflect the outcome of the state.""" idx = state.board.territory_to_id[action.to_territory] if state.owners[idx] == state.current_player and state.players[state.current_player].free_armies > 0: state.armies[idx] = state.armies[idx] + 1 state.players[state.current_player].free_armies -= 1 else: print 'INVALID PREPLACE ACTION:' print 'NO PREPLACE ACTION WILL OCCUR'
def simulateTurnInCardsAction(
state, action)
Execute the given action in the given state. This will modify the state to reflect the outcome of the action.
def simulateTurnInCardsAction(state, action): """ Execute the given action in the given state. This will modify the state to reflect the outcome of the action. """ #See if they want to turn in cards if action.to_territory is None: return if (not isCardSet(state, action.to_territory, action.from_territory, action.troops)) or action.to_territory not in state.players[state.current_player].cards or action.from_territory not in state.players[state.current_player].cards or action.troops not in state.players[state.current_player].cards: print 'INVALID TURN-IN-CARDS ACTION!' action.print_action() print 'NO TURN-IN-CARDS ACTION WILL BE TAKEN' #Modify cards appropriately (cards indices are stored in to_territory, from_territory, and troops #Add turn in value to players free_armies turn_in_troops = getTurnInValue(state) state.players[state.current_player].free_armies += turn_in_troops #Add two troops to any territory that appears on one of the cards owned by this player for i in [action.from_territory, action.to_territory, action.troops]: t = state.board.cards[i].territory if state.owners[t] == state.current_player: state.armies[t] = state.armies[t] + 2 #Remove these cards from the player's hand state.players[state.current_player].cards.remove(i)
def translateAction(
state, action)
Takes a RiskAction and converts it into what needs to be returned by the AI to compete in the interactive risk game. Don't modify
def translateAction(state, action): """ Takes a RiskAction and converts it into what needs to be returned by the AI to compete in the interactive risk game. Don't modify """ if action.type == 'PreAssign' or action.type == 'PrePlace' or action.type == 'Place': return filter(lambda x:x.name == action.to_territory, riskengine.territories.values())[0] elif action.type == 'TurnInCards': if action.to_territory is None: return None,None,None else: return filter(lambda x:x.territory == state.board.territories[state.board.cards[action.to_territory].territory].name, riskengine.currentplayer.cards)[0],filter(lambda x:x.territory == state.board.territories[state.board.cards[action.from_territory].territory].name, riskengine.currentplayer.cards)[0],filter(lambda x:x.territory == state.board.territories[state.board.cards[action.troops].territory].name, riskengine.currentplayer.cards)[0] elif action.type == 'Attack': if action.to_territory is None: return None,None return filter(lambda x:x.name == action.from_territory, riskengine.territories.values())[0], filter(lambda x:x.name == action.to_territory, riskengine.territories.values())[0] elif action.type == 'Occupy': return action.troops elif action.type == 'Fortify': if action.to_territory is None: return None,None,0 return filter(lambda x:x.name == action.from_territory, riskengine.territories.values())[0], filter(lambda x:x.name == action.to_territory, riskengine.territories.values())[0], action.troops else: print 'ILLEGAL ACTION TYPE!'
Classes
class RiskAction
Stores the information about an action in a risk game
class RiskAction(): """Stores the information about an action in a risk game""" def __init__(self, type, to_territory, from_territory, troops): """Initializes a RiskAction""" self.type = type """ What kind of action this is? Should be a string possible types = (PreAssign, PrePlace, Place, TurnInCards, Attack, Occupy, Fortify) """ self.to_territory = to_territory """ This stores the territory id of the place the action is going For Each type it contains: PreAssign: The territory id being chosen by player PrePlace: The territory id of the territory where troop is being placed Place: The territory id of the territory where troop is being placed TurnInCards: The card id of first of the cards being turned in, or None, if there are no cards to turn in Attack: The territory id of the territory being attacked Occupy: The territory id of the territory into which troops are moving Fortify: The territory id of the territory into which troops are moving """ self.from_territory = from_territory """ This stores the territory id of the place the action is coming from For Each type it contains: PreAssign: None PrePlace: None Place: None TurnInCards: The card id of second of the cards being turned in, or None, if there are no cards to turn in Attack: The territory id of the territory doing the attacking Occupy: The territory id of the territory from which troops are moving Fortify: The territory id of the territory from which troops are moving """ self.troops = troops """ This stores the number of troops involved in the action For Each type it contains: PreAssign: None PrePlace: None Place: None TurnInCards: The card id of third of the cards being turned in, or None, if there are no cards to turn in Attack: None Occupy: Number of troops moving into conquered territory Fortify: Number of troops moving to other territory """ def print_action(self): """Displays information about this action to the output""" print self.description() def description(self, newline=False): """returns string description of this action, useful for display""" d = "" d = d + str(self.type) if newline: d = d + "\n" if self.type == 'TurnInCards': d = d + ' CARD 1: ' + str(self.from_territory) elif self.type == 'Attack' or self.type == 'Occupy' or self.type == 'Fortify': d = d + ' FROM: ' + str(self.from_territory) if newline: d = d + "\n" if self.type == 'TurnInCards': d = d + ' CARD 2: ' + str(self.to_territory) elif self.type == 'Place' or self.type == 'PrePlace': d = d + ' IN: ' + str(self.to_territory) else: d = d + ' TO: ' + str(self.to_territory) if newline: d = d + "\n" if self.type == 'TurnInCards': d = d + ' CARD 3: ' + str(self.troops) elif self.type == 'Occupy' or self.type == 'Fortify': d = d + ' NUM: ' + str(self.troops) return d def to_string(self): """Saves this action to a string""" s = 'RISKACTION|' + json.dumps(self.type) + '|' + json.dumps(self.from_territory) + '|' + json.dumps(self.to_territory) + '|' + json.dumps(self.troops) return s def from_string(self, s): """Loads this action from a string""" ss = s.split('|') if ss[0] != 'RISKACTION': print 'THIS IS NOT A RISK ACTION STRING!' self.type = json.loads(ss[1]) self.from_territory = json.loads(ss[2]) self.to_territory = json.loads(ss[3]) self.troops = json.loads(ss[4])
Ancestors (in MRO)
Instance variables
var from_territory
This stores the territory id of the place the action is coming from For Each type it contains:
PreAssign: None PrePlace: None Place: None TurnInCards: The card id of second of the cards being turned in, or None, if there are no cards to turn in Attack: The territory id of the territory doing the attacking Occupy: The territory id of the territory from which troops are moving Fortify: The territory id of the territory from which troops are moving
var to_territory
This stores the territory id of the place the action is going For Each type it contains:
PreAssign: The territory id being chosen by player PrePlace: The territory id of the territory where troop is being placed Place: The territory id of the territory where troop is being placed TurnInCards: The card id of first of the cards being turned in, or None, if there are no cards to turn in Attack: The territory id of the territory being attacked Occupy: The territory id of the territory into which troops are moving Fortify: The territory id of the territory into which troops are moving
var troops
This stores the number of troops involved in the action For Each type it contains:
PreAssign: None PrePlace: None Place: None TurnInCards: The card id of third of the cards being turned in, or None, if there are no cards to turn in Attack: None Occupy: Number of troops moving into conquered territory Fortify: Number of troops moving to other territory
var type
What kind of action this is? Should be a string possible types = (PreAssign, PrePlace, Place, TurnInCards, Attack, Occupy, Fortify)
Methods
def __init__(
self, type, to_territory, from_territory, troops)
Initializes a RiskAction
def __init__(self, type, to_territory, from_territory, troops): """Initializes a RiskAction""" self.type = type """ What kind of action this is? Should be a string possible types = (PreAssign, PrePlace, Place, TurnInCards, Attack, Occupy, Fortify) """ self.to_territory = to_territory """ This stores the territory id of the place the action is going For Each type it contains: PreAssign: The territory id being chosen by player PrePlace: The territory id of the territory where troop is being placed Place: The territory id of the territory where troop is being placed TurnInCards: The card id of first of the cards being turned in, or None, if there are no cards to turn in Attack: The territory id of the territory being attacked Occupy: The territory id of the territory into which troops are moving Fortify: The territory id of the territory into which troops are moving """ self.from_territory = from_territory """ This stores the territory id of the place the action is coming from For Each type it contains: PreAssign: None PrePlace: None Place: None TurnInCards: The card id of second of the cards being turned in, or None, if there are no cards to turn in Attack: The territory id of the territory doing the attacking Occupy: The territory id of the territory from which troops are moving Fortify: The territory id of the territory from which troops are moving """ self.troops = troops """ This stores the number of troops involved in the action For Each type it contains: PreAssign: None PrePlace: None Place: None TurnInCards: The card id of third of the cards being turned in, or None, if there are no cards to turn in Attack: None Occupy: Number of troops moving into conquered territory Fortify: Number of troops moving to other territory """
def description(
self, newline=False)
returns string description of this action, useful for display
def description(self, newline=False): """returns string description of this action, useful for display""" d = "" d = d + str(self.type) if newline: d = d + "\n" if self.type == 'TurnInCards': d = d + ' CARD 1: ' + str(self.from_territory) elif self.type == 'Attack' or self.type == 'Occupy' or self.type == 'Fortify': d = d + ' FROM: ' + str(self.from_territory) if newline: d = d + "\n" if self.type == 'TurnInCards': d = d + ' CARD 2: ' + str(self.to_territory) elif self.type == 'Place' or self.type == 'PrePlace': d = d + ' IN: ' + str(self.to_territory) else: d = d + ' TO: ' + str(self.to_territory) if newline: d = d + "\n" if self.type == 'TurnInCards': d = d + ' CARD 3: ' + str(self.troops) elif self.type == 'Occupy' or self.type == 'Fortify': d = d + ' NUM: ' + str(self.troops) return d
def from_string(
self, s)
Loads this action from a string
def from_string(self, s): """Loads this action from a string""" ss = s.split('|') if ss[0] != 'RISKACTION': print 'THIS IS NOT A RISK ACTION STRING!' self.type = json.loads(ss[1]) self.from_territory = json.loads(ss[2]) self.to_territory = json.loads(ss[3]) self.troops = json.loads(ss[4])
def print_action(
self)
Displays information about this action to the output
def print_action(self): """Displays information about this action to the output""" print self.description()
def to_string(
self)
Saves this action to a string
def to_string(self): """Saves this action to a string""" s = 'RISKACTION|' + json.dumps(self.type) + '|' + json.dumps(self.from_territory) + '|' + json.dumps(self.to_territory) + '|' + json.dumps(self.troops) return s
class RiskBoard
Stores all of the information about the current Risk game that doesn't change over the course of the game, things like:
Which territories make up the map Which territories are connected to which What the continents are and which territories they are composed of What the sequence of card turn-in values is What players are in the game What pictures are on the cards What the set of all cards is
class RiskBoard(): """ Stores all of the information about the current Risk game that doesn't change over the course of the game, things like: Which territories make up the map Which territories are connected to which What the continents are and which territories they are composed of What the sequence of card turn-in values is What players are in the game What pictures are on the cards What the set of all cards is """ def __init__(self): """ Initialize a Risk Board. All of the variables are initially empty. They should be filled in through other function calls """ self.players = [] """ An array of RiskPlayer objects """ self.player_to_id = dict() """ A dictionary which is indexed by player name and stores the id number of that player """ self.id_to_player = dict() """ A dictionary which is indexed by player id and stores the player name of that player """ self.territories = [] """ An array of RiskTerritory objects """ self.territory_to_id = dict() """ A dictionary which is indexed by territory name and stores the id number of that territory """ self.cards = [] """ An array of RiskCard objects """ self.card_to_id = dict() """ A dictionary which is indexed by card name (territory name of pictured territory, or wildcard) Stores the id of that card """ self.pictures = [] """ An array of string descriptions of the different card pictures """ self.continents = dict() """ A dictionary of RiskContinent objects indexed by continent name, stores corresponding RiskContinent object """ self.turn_in_values = [] """ An array of the card turn-in-values turn_in_values[i] gives the number of troops received for the i-th card turn-in """ self.increment_value = 0 """ A number specifying the incremental gain in received troops for card turn-ins beyond the length of turn-in-values array """ def from_string(self, s): """ Load RiskBoard information from a string. We assume the string was created by the to_string function. """ ss = s.split('|') #Players ssp = ss[1].split(';') for p in ssp: if len(p) > 0: np = RiskPlayer(None,None,None,None) np.from_string(p) self.add_player(np) #Territories sst = ss[2].split(';') for t in sst: if t: nt = RiskTerritory(None,None) nt.from_string(t) self.add_territory(nt) #Continents ssc = ss[3].split(';') for c in ssc: if c: nc = RiskContinent(None,None) nc.from_string(c) self.add_continent(nc) #Cards ssd = ss[4].split(';') for d in ssd: if d: nd = RiskCard(None,None,None) nd.from_string(d) self.add_card(nd) #Turn in values self.turn_in_values = json.loads(ss[5]) #Increment value self.increment_value = json.loads(ss[6]) #Pictures self.pictures = json.loads(ss[7]) def to_string(self): """Save the current RiskBoard to a string. This is used to save games.""" output_string = 'RISKBOARD|' #Players for p in self.players: output_string = output_string + p.to_string() output_string = output_string + ';' output_string = output_string + '|' #Territories for t in self.territories: output_string = output_string + t.to_string() output_string = output_string + ';' output_string = output_string + '|' #Continents for n,c in self.continents.iteritems(): output_string = output_string + c.to_string() + ';' output_string = output_string + '|' #Cards for c in self.cards: output_string = output_string + c.to_string() output_string = output_string + ';' output_string = output_string + '|' #Turn In Values output_string = output_string + json.dumps(self.turn_in_values) output_string = output_string + '|' #Increment value output_string = output_string + json.dumps(self.increment_value) output_string = output_string + '|' #Pictures output_string = output_string + json.dumps(self.pictures) return output_string # def shuffle_players(self): # """ # Randomize the player order. Make sure everything is right after that # """ # player_order = range(len(self.players)) # random.shuffle(player_order) # shuffled_players = [] # player_counter = 0 # for i in player_order: # p = self.players[i] # p.id = player_counter # shuffled_players.append(p) # self.player_to_id[p.name] = p.id # self.id_to_player[p.id] = p.name # player_counter += 1 # self.players = shuffled_players def add_player(self, player): """ Add a player object to the list of players. Modify player_to_id and id_to_player to include new player """ self.players.append(player) self.player_to_id[player.name] = player.id self.id_to_player[player.id] = player.name def add_territory(self, territory): """ Add a territory object to the list of territories. Modify territory_to_id to include new territory """ self.territories.append(territory) self.territory_to_id[territory.name] = territory.id def add_card(self, card): """ Add a card object to the list of cards Modify card_to_id to include the new card """ self.cards.append(card) self.card_to_id[self.territories[card.territory].name] = card.id def shuffle_cards(self): """Randomly shuffle the array of cards and reassign the card ids to be consistent""" random.shuffle(self.cards) for i in range(len(self.cards)): self.cards[i].id = i self.card_to_id[self.territories[self.cards[i].territory].name] = i def add_continent(self, continent): """Add a continent object to the list of continents""" if continent.name not in self.continents: self.continents[continent.name] = continent def set_turn_in_values(self, tiv): """Set the array of turn-in values (for card turn-ins)""" self.turn_in_values = tiv def set_increment_value(self, iv): """Set the increment value for card turn-ins beyond the end of turn_in_values""" self.increment_value = iv def print_board(self): """Display the Risk Board to the output.""" print 'RISK BOARD' #PRINT TERRITORIES BY CONTINENT print 'CONTINENTS' for c in self.continents.values(): c.print_continent(self) #PRINT CARDS print 'CARDS' for c in self.cards: c.print_card(self) #PRINT PLAYERS print 'PLAYERS' for p in self.players: p.print_player(self)
Ancestors (in MRO)
Instance variables
var card_to_id
A dictionary which is indexed by card name (territory name of pictured territory, or wildcard) Stores the id of that card
var cards
An array of RiskCard objects
var continents
A dictionary of RiskContinent objects indexed by continent name, stores corresponding RiskContinent object
var id_to_player
A dictionary which is indexed by player id and stores the player name of that player
var increment_value
A number specifying the incremental gain in received troops for card turn-ins beyond the length of turn-in-values array
var pictures
An array of string descriptions of the different card pictures
var player_to_id
A dictionary which is indexed by player name and stores the id number of that player
var players
An array of RiskPlayer objects
var territories
An array of RiskTerritory objects
var territory_to_id
A dictionary which is indexed by territory name and stores the id number of that territory
var turn_in_values
An array of the card turn-in-values turn_in_values[i] gives the number of troops received for the i-th card turn-in
Methods
def __init__(
self)
Initialize a Risk Board.
All of the variables are initially empty.
They should be filled in through other function calls
def __init__(self): """ Initialize a Risk Board. All of the variables are initially empty. They should be filled in through other function calls """ self.players = [] """ An array of RiskPlayer objects """ self.player_to_id = dict() """ A dictionary which is indexed by player name and stores the id number of that player """ self.id_to_player = dict() """ A dictionary which is indexed by player id and stores the player name of that player """ self.territories = [] """ An array of RiskTerritory objects """ self.territory_to_id = dict() """ A dictionary which is indexed by territory name and stores the id number of that territory """ self.cards = [] """ An array of RiskCard objects """ self.card_to_id = dict() """ A dictionary which is indexed by card name (territory name of pictured territory, or wildcard) Stores the id of that card """ self.pictures = [] """ An array of string descriptions of the different card pictures """ self.continents = dict() """ A dictionary of RiskContinent objects indexed by continent name, stores corresponding RiskContinent object """ self.turn_in_values = [] """ An array of the card turn-in-values turn_in_values[i] gives the number of troops received for the i-th card turn-in """ self.increment_value = 0 """ A number specifying the incremental gain in received troops for card turn-ins beyond the length of turn-in-values array """
def add_card(
self, card)
Add a card object to the list of cards Modify card_to_id to include the new card
def add_card(self, card): """ Add a card object to the list of cards Modify card_to_id to include the new card """ self.cards.append(card) self.card_to_id[self.territories[card.territory].name] = card.id
def add_continent(
self, continent)
Add a continent object to the list of continents
def add_continent(self, continent): """Add a continent object to the list of continents""" if continent.name not in self.continents: self.continents[continent.name] = continent
def add_player(
self, player)
Add a player object to the list of players. Modify player_to_id and id_to_player to include new player
def add_player(self, player): """ Add a player object to the list of players. Modify player_to_id and id_to_player to include new player """ self.players.append(player) self.player_to_id[player.name] = player.id self.id_to_player[player.id] = player.name
def add_territory(
self, territory)
Add a territory object to the list of territories. Modify territory_to_id to include new territory
def add_territory(self, territory): """ Add a territory object to the list of territories. Modify territory_to_id to include new territory """ self.territories.append(territory) self.territory_to_id[territory.name] = territory.id
def from_string(
self, s)
Load RiskBoard information from a string.
We assume the string was created by the to_string
function.
def from_string(self, s): """ Load RiskBoard information from a string. We assume the string was created by the to_string function. """ ss = s.split('|') #Players ssp = ss[1].split(';') for p in ssp: if len(p) > 0: np = RiskPlayer(None,None,None,None) np.from_string(p) self.add_player(np) #Territories sst = ss[2].split(';') for t in sst: if t: nt = RiskTerritory(None,None) nt.from_string(t) self.add_territory(nt) #Continents ssc = ss[3].split(';') for c in ssc: if c: nc = RiskContinent(None,None) nc.from_string(c) self.add_continent(nc) #Cards ssd = ss[4].split(';') for d in ssd: if d: nd = RiskCard(None,None,None) nd.from_string(d) self.add_card(nd) #Turn in values self.turn_in_values = json.loads(ss[5]) #Increment value self.increment_value = json.loads(ss[6]) #Pictures self.pictures = json.loads(ss[7])
def print_board(
self)
Display the Risk Board to the output.
def print_board(self): """Display the Risk Board to the output.""" print 'RISK BOARD' #PRINT TERRITORIES BY CONTINENT print 'CONTINENTS' for c in self.continents.values(): c.print_continent(self) #PRINT CARDS print 'CARDS' for c in self.cards: c.print_card(self) #PRINT PLAYERS print 'PLAYERS' for p in self.players: p.print_player(self)
def set_increment_value(
self, iv)
Set the increment value for card turn-ins beyond the end of turn_in_values
def set_increment_value(self, iv): """Set the increment value for card turn-ins beyond the end of turn_in_values""" self.increment_value = iv
def set_turn_in_values(
self, tiv)
Set the array of turn-in values (for card turn-ins)
def set_turn_in_values(self, tiv): """Set the array of turn-in values (for card turn-ins)""" self.turn_in_values = tiv
def shuffle_cards(
self)
Randomly shuffle the array of cards and reassign the card ids to be consistent
def shuffle_cards(self): """Randomly shuffle the array of cards and reassign the card ids to be consistent""" random.shuffle(self.cards) for i in range(len(self.cards)): self.cards[i].id = i self.card_to_id[self.territories[self.cards[i].territory].name] = i
def to_string(
self)
Save the current RiskBoard to a string. This is used to save games.
def to_string(self): """Save the current RiskBoard to a string. This is used to save games.""" output_string = 'RISKBOARD|' #Players for p in self.players: output_string = output_string + p.to_string() output_string = output_string + ';' output_string = output_string + '|' #Territories for t in self.territories: output_string = output_string + t.to_string() output_string = output_string + ';' output_string = output_string + '|' #Continents for n,c in self.continents.iteritems(): output_string = output_string + c.to_string() + ';' output_string = output_string + '|' #Cards for c in self.cards: output_string = output_string + c.to_string() output_string = output_string + ';' output_string = output_string + '|' #Turn In Values output_string = output_string + json.dumps(self.turn_in_values) output_string = output_string + '|' #Increment value output_string = output_string + json.dumps(self.increment_value) output_string = output_string + '|' #Pictures output_string = output_string + json.dumps(self.pictures) return output_string
class RiskCard
Stores all the information for a risk card
class RiskCard(): """Stores all the information for a risk card""" def __init__(self, territory, picture, id): """Initializes card information""" self.territory = territory """ The territory id number of the territory pictured on the card """ self.picture = picture """ The picture on the card """ self.id = id """ The id number of this card """ def print_card(self, board, indent=0): """Displays information about this card to the output""" for i in range(indent): print ' ', print board.territories[self.territory].name, ' : ', self.picture def to_string(self): """Saves card information to a string""" s = str(self.territory) + '.' + str(self.picture) + '.' + str(self.id) return s def from_string(self, s): """Loads card information from a string""" ss = s.split('.') self.territory = json.loads(ss[0]) self.picture = json.loads(ss[1]) self.id = json.loads(ss[2])
Ancestors (in MRO)
Instance variables
var id
The id number of this card
var picture
The picture on the card
var territory
The territory id number of the territory pictured on the card
Methods
def __init__(
self, territory, picture, id)
Initializes card information
def __init__(self, territory, picture, id): """Initializes card information""" self.territory = territory """ The territory id number of the territory pictured on the card """ self.picture = picture """ The picture on the card """ self.id = id """ The id number of this card """
def from_string(
self, s)
Loads card information from a string
def from_string(self, s): """Loads card information from a string""" ss = s.split('.') self.territory = json.loads(ss[0]) self.picture = json.loads(ss[1]) self.id = json.loads(ss[2])
def print_card(
self, board, indent=0)
Displays information about this card to the output
def print_card(self, board, indent=0): """Displays information about this card to the output""" for i in range(indent): print ' ', print board.territories[self.territory].name, ' : ', self.picture
def to_string(
self)
Saves card information to a string
def to_string(self): """Saves card information to a string""" s = str(self.territory) + '.' + str(self.picture) + '.' + str(self.id) return s
class RiskContinent
Stores all information related to a continent
class RiskContinent(): """Stores all information related to a continent""" def __init__(self, name, reward): """Initializes the continent information""" self.name = name """The name of this continent (string) """ self.reward = reward """ The reward for this continent This is the number of troops bonus that a player gets if the player owns all of the territories in this continent """ self.territories = [] """ A list of the territory id numbers of the territories in this continent""" def add_territory(self, territory): """Adds a territory to this continent""" if territory not in self.territories: self.territories.append(territory) def print_continent(self,board,indent=0): """Displays information about continent to output""" for i in range(indent): print ' ', print '{', self.name, '} : ', self.reward for t in self.territories: board.territories[t].print_territory(board, indent+2) def to_string(self): """Save continent information to string""" s = json.dumps(self.name) + '&' + json.dumps(self.reward) + '&' + json.dumps(self.territories) return s def from_string(self, s): """Load continent information from string""" ss = s.split('&') self.name = json.loads(ss[0]) self.reward = json.loads(ss[1]) self.territories = json.loads(ss[2])
Ancestors (in MRO)
Instance variables
var name
The name of this continent (string)
var reward
The reward for this continent This is the number of troops bonus that a player gets if the player owns all of the territories in this continent
var territories
A list of the territory id numbers of the territories in this continent
Methods
def __init__(
self, name, reward)
Initializes the continent information
def __init__(self, name, reward): """Initializes the continent information""" self.name = name """The name of this continent (string) """ self.reward = reward """ The reward for this continent This is the number of troops bonus that a player gets if the player owns all of the territories in this continent """ self.territories = [] """ A list of the territory id numbers of the territories in this continent"""
def add_territory(
self, territory)
Adds a territory to this continent
def add_territory(self, territory): """Adds a territory to this continent""" if territory not in self.territories: self.territories.append(territory)
def from_string(
self, s)
Load continent information from string
def from_string(self, s): """Load continent information from string""" ss = s.split('&') self.name = json.loads(ss[0]) self.reward = json.loads(ss[1]) self.territories = json.loads(ss[2])
def print_continent(
self, board, indent=0)
Displays information about continent to output
def print_continent(self,board,indent=0): """Displays information about continent to output""" for i in range(indent): print ' ', print '{', self.name, '} : ', self.reward for t in self.territories: board.territories[t].print_territory(board, indent+2)
def to_string(
self)
Save continent information to string
def to_string(self): """Save continent information to string""" s = json.dumps(self.name) + '&' + json.dumps(self.reward) + '&' + json.dumps(self.territories) return s
class RiskPlayer
Stores all information about a player in the game
class RiskPlayer(): """Stores all information about a player in the game""" def __init__(self, name, id, free_armies, conquered_territory): """Initializes the various components of this RiskPlayer object""" self.name = name """ The Player's name (string) """ self.id = id """ The Player's id (integer) """ self.cards = [] """ A list of card id numbers that this player holds (integers) """ self.free_armies = free_armies """ The number of armies this player has in hand, waiting to be placed on the board """ self.conquered_territory = conquered_territory """ A boolean stating whether or not this player conquered a territory on their current turn """ def add_card(self, card): """Adds a card (card id number) to the player's list of card ids""" self.cards.append(card) def add_armies(self, n): """Adds a number (n) of armies to the player's free_armies count""" self.free_armies += n def print_player(self, board, indent=0): """Displays information about this player to the output""" for i in range(indent): print ' ', print '<', self.name, '[', self.id, ']> (', self.free_armies, ' free armies )' for i in range(indent+2): print ' ', print 'Cards:' for c in self.cards: board.cards[c].print_card(board, indent+3) def copy_player(self): """Creates a copy of this player and returns it""" np = RiskPlayer(self.name, self.id, self.free_armies, self.conquered_territory) np.cards = self.cards[:] return np def to_string(self): """Saves this player to a string""" s = json.dumps(self.name) + '.' + json.dumps(self.id) + '.' + json.dumps(self.cards) + '.' + json.dumps(self.free_armies) + '.' + json.dumps(self.conquered_territory) return s def from_string(self, s): """Loads player information from a string""" ss = s.split('.') self.name = json.loads(ss[0]) self.id = json.loads(ss[1]) self.cards = json.loads(ss[2]) self.free_armies = json.loads(ss[3]) self.conquered_territory = json.loads(ss[4])
Ancestors (in MRO)
Instance variables
var cards
A list of card id numbers that this player holds (integers)
var conquered_territory
A boolean stating whether or not this player conquered a territory on their current turn
var free_armies
The number of armies this player has in hand, waiting to be placed on the board
var id
The Player's id (integer)
var name
The Player's name (string)
Methods
def __init__(
self, name, id, free_armies, conquered_territory)
Initializes the various components of this RiskPlayer object
def __init__(self, name, id, free_armies, conquered_territory): """Initializes the various components of this RiskPlayer object""" self.name = name """ The Player's name (string) """ self.id = id """ The Player's id (integer) """ self.cards = [] """ A list of card id numbers that this player holds (integers) """ self.free_armies = free_armies """ The number of armies this player has in hand, waiting to be placed on the board """ self.conquered_territory = conquered_territory """ A boolean stating whether or not this player conquered a territory on their current turn """
def add_armies(
self, n)
Adds a number (n) of armies to the player's free_armies count
def add_armies(self, n): """Adds a number (n) of armies to the player's free_armies count""" self.free_armies += n
def add_card(
self, card)
Adds a card (card id number) to the player's list of card ids
def add_card(self, card): """Adds a card (card id number) to the player's list of card ids""" self.cards.append(card)
def copy_player(
self)
Creates a copy of this player and returns it
def copy_player(self): """Creates a copy of this player and returns it""" np = RiskPlayer(self.name, self.id, self.free_armies, self.conquered_territory) np.cards = self.cards[:] return np
def from_string(
self, s)
Loads player information from a string
def from_string(self, s): """Loads player information from a string""" ss = s.split('.') self.name = json.loads(ss[0]) self.id = json.loads(ss[1]) self.cards = json.loads(ss[2]) self.free_armies = json.loads(ss[3]) self.conquered_territory = json.loads(ss[4])
def print_player(
self, board, indent=0)
Displays information about this player to the output
def print_player(self, board, indent=0): """Displays information about this player to the output""" for i in range(indent): print ' ', print '<', self.name, '[', self.id, ']> (', self.free_armies, ' free armies )' for i in range(indent+2): print ' ', print 'Cards:' for c in self.cards: board.cards[c].print_card(board, indent+3)
def to_string(
self)
Saves this player to a string
def to_string(self): """Saves this player to a string""" s = json.dumps(self.name) + '.' + json.dumps(self.id) + '.' + json.dumps(self.cards) + '.' + json.dumps(self.free_armies) + '.' + json.dumps(self.conquered_territory) return s
class RiskState
Stores all the information about a state of a Risk game
class RiskState(): """Stores all the information about a state of a Risk game""" def __init__(self, players, armies, owners, current_player, turn_type, turn_in_number, last_attacker, last_defender, cards, board): """Initializes a RiskState object""" self.players = players """An array of RiskPlayer objects (the players in the game)""" self.armies = armies """ An array of integers, indexed by territory id number, that stores the number of armies on that territory """ self.owners = owners """ An array of integers, indexed by territory id number, that stores the player id number of the player who owns that territory """ self.current_player = current_player """ The player id number of the current player (player whose turn it is) """ self.turn_type = turn_type """ What kind of turn is it currently Choices = (PreAssign, PrePlace, Place, TurnInCards, Attack, Occupy, Fortify, GameOver) """ self.cards = cards """ A list of all the card id numbers of the cards still in the deck """ self.turn_in_number = turn_in_number """ How many card sets have been turned in? """ self.last_attacker = last_attacker """ What is the territory id of the last territory that was attacking Important to determine troop movements after territory is conquered """ self.last_defender = last_defender """ What is the territory id of the last territory that was defending Important to determine troop movements after territory is conquered """ self.board = board """ A RiskBoard object that stores all the non-changing information about this risk game """ def to_string(self): """Saves this state to a string""" s = 'RISKSTATE|' #players for p in self.players: s = s + p.to_string() s = s + ';' s = s + '|' + json.dumps(self.armies) + '|' + json.dumps(self.owners) + '|' s = s + json.dumps(self.current_player) + '|' + json.dumps(self.turn_type) + '|' s = s + json.dumps(self.cards) + '|' + json.dumps(self.turn_in_number) + '|' s = s + json.dumps(self.last_attacker) + '|' + json.dumps(self.last_defender) return s def from_string(self, s, board): """Loads this state from a string""" ss = s.split('|') if ss[0] != 'RISKSTATE': print 'THIS IS AN INVALID RISKSTATE' ps = ss[1].split(';') self.players = [] for p in ps: if p:#len(p) > 0: np = RiskPlayer(None, None, None, None) np.from_string(p) self.players.append(np) self.armies = json.loads(ss[2]) self.owners = json.loads(ss[3]) self.current_player = json.loads(ss[4]) self.turn_type = json.loads(ss[5]) self.cards = json.loads(ss[6]) self.turn_in_number = json.loads(ss[7]) self.last_attacker = json.loads(ss[8]) self.last_defender = json.loads(ss[9]) self.board = board def print_state(self): """Displays information about this state to the output""" print 'PLAYERS' for p in self.players: p.print_player(self.board) print 'OWNERS/ARMIES' for i in range(len(self.armies)): if self.owners[i] in self.board.id_to_player: print self.board.territories[i].name, '[', self.board.id_to_player[self.owners[i]], '] : ', self.armies[i] else: print self.board.territories[i].name, '[', self.owners[i], '] : ', self.armies[i] if self.current_player < len(self.players): print 'CURRENT PLAYER: ', self.players[self.current_player].name, '[', self.current_player, ']' else: print 'CURRENT PLAYER: ??? [', self.current_player, ']' print len(self.cards), ' CARDS LEFT' def copy_state(self): """ Creates a (deep) copy of this state and return it. Modifications to new state won't change original, except for the risk board, of which there is only one (it shouldn't be modified!) """ copy_players = [] for p in self.players: copy_players.append(p.copy_player()) return RiskState(copy_players,self.armies[:],self.owners[:],self.current_player,self.turn_type,self.turn_in_number, self.last_attacker, self.last_defender, self.cards[:],self.board)
Ancestors (in MRO)
Instance variables
var armies
An array of integers, indexed by territory id number, that stores the number of armies on that territory
var board
A RiskBoard object that stores all the non-changing information about this risk game
var cards
A list of all the card id numbers of the cards still in the deck
var current_player
The player id number of the current player (player whose turn it is)
var last_attacker
What is the territory id of the last territory that was attacking Important to determine troop movements after territory is conquered
var last_defender
What is the territory id of the last territory that was defending Important to determine troop movements after territory is conquered
var owners
An array of integers, indexed by territory id number, that stores the player id number of the player who owns that territory
var players
An array of RiskPlayer objects (the players in the game)
var turn_in_number
How many card sets have been turned in?
var turn_type
What kind of turn is it currently Choices = (PreAssign, PrePlace, Place, TurnInCards, Attack, Occupy, Fortify, GameOver)
Methods
def __init__(
self, players, armies, owners, current_player, turn_type, turn_in_number, last_attacker, last_defender, cards, board)
Initializes a RiskState object
def __init__(self, players, armies, owners, current_player, turn_type, turn_in_number, last_attacker, last_defender, cards, board): """Initializes a RiskState object""" self.players = players """An array of RiskPlayer objects (the players in the game)""" self.armies = armies """ An array of integers, indexed by territory id number, that stores the number of armies on that territory """ self.owners = owners """ An array of integers, indexed by territory id number, that stores the player id number of the player who owns that territory """ self.current_player = current_player """ The player id number of the current player (player whose turn it is) """ self.turn_type = turn_type """ What kind of turn is it currently Choices = (PreAssign, PrePlace, Place, TurnInCards, Attack, Occupy, Fortify, GameOver) """ self.cards = cards """ A list of all the card id numbers of the cards still in the deck """ self.turn_in_number = turn_in_number """ How many card sets have been turned in? """ self.last_attacker = last_attacker """ What is the territory id of the last territory that was attacking Important to determine troop movements after territory is conquered """ self.last_defender = last_defender """ What is the territory id of the last territory that was defending Important to determine troop movements after territory is conquered """ self.board = board """ A RiskBoard object that stores all the non-changing information about this risk game """
def copy_state(
self)
Creates a (deep) copy of this state and return it. Modifications to new state won't change original, except for the risk board, of which there is only one (it shouldn't be modified!)
def copy_state(self): """ Creates a (deep) copy of this state and return it. Modifications to new state won't change original, except for the risk board, of which there is only one (it shouldn't be modified!) """ copy_players = [] for p in self.players: copy_players.append(p.copy_player()) return RiskState(copy_players,self.armies[:],self.owners[:],self.current_player,self.turn_type,self.turn_in_number, self.last_attacker, self.last_defender, self.cards[:],self.board)
def from_string(
self, s, board)
Loads this state from a string
def from_string(self, s, board): """Loads this state from a string""" ss = s.split('|') if ss[0] != 'RISKSTATE': print 'THIS IS AN INVALID RISKSTATE' ps = ss[1].split(';') self.players = [] for p in ps: if p:#len(p) > 0: np = RiskPlayer(None, None, None, None) np.from_string(p) self.players.append(np) self.armies = json.loads(ss[2]) self.owners = json.loads(ss[3]) self.current_player = json.loads(ss[4]) self.turn_type = json.loads(ss[5]) self.cards = json.loads(ss[6]) self.turn_in_number = json.loads(ss[7]) self.last_attacker = json.loads(ss[8]) self.last_defender = json.loads(ss[9]) self.board = board
def print_state(
self)
Displays information about this state to the output
def print_state(self): """Displays information about this state to the output""" print 'PLAYERS' for p in self.players: p.print_player(self.board) print 'OWNERS/ARMIES' for i in range(len(self.armies)): if self.owners[i] in self.board.id_to_player: print self.board.territories[i].name, '[', self.board.id_to_player[self.owners[i]], '] : ', self.armies[i] else: print self.board.territories[i].name, '[', self.owners[i], '] : ', self.armies[i] if self.current_player < len(self.players): print 'CURRENT PLAYER: ', self.players[self.current_player].name, '[', self.current_player, ']' else: print 'CURRENT PLAYER: ??? [', self.current_player, ']' print len(self.cards), ' CARDS LEFT'
def to_string(
self)
Saves this state to a string
def to_string(self): """Saves this state to a string""" s = 'RISKSTATE|' #players for p in self.players: s = s + p.to_string() s = s + ';' s = s + '|' + json.dumps(self.armies) + '|' + json.dumps(self.owners) + '|' s = s + json.dumps(self.current_player) + '|' + json.dumps(self.turn_type) + '|' s = s + json.dumps(self.cards) + '|' + json.dumps(self.turn_in_number) + '|' s = s + json.dumps(self.last_attacker) + '|' + json.dumps(self.last_defender) return s
class RiskTerritory
Stores all of the information for a territory
class RiskTerritory(): """Stores all of the information for a territory""" def __init__(self, name, id): self.name = name """ The name of the territory (a string) """ self.id = id """ The id number of the territory (an integer) """ self.neighbors = [] """ An list of the id numbers of all territories neighboring this one """ def add_neighbor(self, neighbor): """Add a neighbor id to the neighbor list""" self.neighbors.append(neighbor) def print_territory(self, board, indent=0): """Display information about this territory to the output""" for i in range(indent): print ' ', print '[', self.name, '] (', self.id, ')' for i in range(indent+2): print ' ', print 'Neighbors:' for n in self.neighbors: for i in range(indent+3): print ' ', print board.territories[n].name def to_string(self): """Save the current Territory information to a string""" s = json.dumps(self.name) + '&' + json.dumps(self.id) + '&' + json.dumps(self.neighbors) return s def from_string(self, s): """Load information about this territory from a string""" ss = s.split('&') self.name = json.loads(ss[0]) self.id = json.loads(ss[1]) self.neighbors = json.loads(ss[2])
Ancestors (in MRO)
Instance variables
var id
The id number of the territory (an integer)
var name
The name of the territory (a string)
var neighbors
An list of the id numbers of all territories neighboring this one
Methods
def __init__(
self, name, id)
def __init__(self, name, id): self.name = name """ The name of the territory (a string) """ self.id = id """ The id number of the territory (an integer) """ self.neighbors = [] """ An list of the id numbers of all territories neighboring this one """
def add_neighbor(
self, neighbor)
Add a neighbor id to the neighbor list
def add_neighbor(self, neighbor): """Add a neighbor id to the neighbor list""" self.neighbors.append(neighbor)
def from_string(
self, s)
Load information about this territory from a string
def from_string(self, s): """Load information about this territory from a string""" ss = s.split('&') self.name = json.loads(ss[0]) self.id = json.loads(ss[1]) self.neighbors = json.loads(ss[2])
def print_territory(
self, board, indent=0)
Display information about this territory to the output
def print_territory(self, board, indent=0): """Display information about this territory to the output""" for i in range(indent): print ' ', print '[', self.name, '] (', self.id, ')' for i in range(indent+2): print ' ', print 'Neighbors:' for n in self.neighbors: for i in range(indent+3): print ' ', print board.territories[n].name
def to_string(
self)
Save the current Territory information to a string
def to_string(self): """Save the current Territory information to a string""" s = json.dumps(self.name) + '&' + json.dumps(self.id) + '&' + json.dumps(self.neighbors) return s