def resetHandState(self): self.winner = -1 self.tripComplete = False self.delayedAction = False self.extension = False self.discardPile = [] if len(self.players) == 4: self.target = 1000 self.extensionPossible = False else: self.target = 700 self.extensionPossible = True self.deck = Deck() # No hands yet. for player in self.players: player.hand = [] # Reset per-hand team variables for team in self.teams: team.reset()
def playHand(self): self.resetHandState() # Fresh deck self.deck = Deck() # Deal hands for player in self.players: player.hand = self.draw(player, 6) if self.transcriptWriter: self.transcriptWriter.writeHandStart() currentPlayerNumber = 0 # Normally this is currentPlayerNumber + 1, but in the case of a coup # fourre it gets changed to give the player another turn nextPlayerNumber = 1 while True: currentPlayer = self.players[currentPlayerNumber] currentTeam = self.teams[currentPlayer.teamNumber] drewCardThisMove = None try: drewCardThisMove = self.draw(currentPlayer) currentPlayer.hand.append(drewCardThisMove) except IndexError: self.delayedAction = True if self.debug: print 'Hand contents:', print Cards.cardsToStrings(currentPlayer.hand) if len(currentPlayer.hand) == 0: totalHandSize = 0 for player in self.players: totalHandSize += len(player.hand) if totalHandSize == 0: # Everyone's out of cards - game over break else: # Pass and let someone else go currentPlayerNumber = nextPlayerNumber nextPlayerNumber += 1 if nextPlayerNumber >= len(self.players): nextPlayerNumber = 0 continue state = self.makeState(currentPlayer) state.findValidPlays() # Store a copy of this locally, just in case the AI changes it validMoves = state.validMoves move = currentPlayer.ai.makeMove(state) # Replace invalid moves with a random discard if move not in validMoves: print 'Warning: invalid play' move = Move(Move.DISCARD, currentPlayer.hand[0]) if self.debug: print currentPlayer, print move sanitizedPlayer = copy(currentPlayer) sanitizedPlayer.hand = [] sanitizedPlayer.ai = None self.notifyPlayers(sanitizedPlayer, move) oldTarget = self.target self.handleMove(currentPlayer, currentTeam, move) if self.transcriptWriter: extensionWasDeclared = self.target > oldTarget self.transcriptWriter.writeMove(currentPlayer.number, drewCardThisMove, move, extensionWasDeclared) if self.tripComplete: break # Remove the card from the player's hand del currentPlayer.hand[currentPlayer.hand.index(move.card)] if self.debug: for team in self.teams: print team print '' # Go on to the next player. If a safety got played, nextPlayerNumber # got changed and will cause us to break out of the normal rotation currentPlayerNumber = nextPlayerNumber nextPlayerNumber += 1 if nextPlayerNumber >= len(self.players): nextPlayerNumber = 0 if self.debug: print 'Hand complete' if self.transcriptWriter: self.transcriptWriter.writeHandEnd() self.computeHandScores()
class Game: # How many points to win a game, as opposed to a single hand pointsToWin = 5000 def __init__(self, AIs, debug=False, teams=None, players=None): self.teams = [] self.players = [] self.debug = debug self.transcriptWriter = None if not (bool(AIs) ^ bool(teams and players)): raise Exception( "Must specify one and only one of (AIs), (teams, players).") if AIs is None: self.teams = teams self.players = players else: self.generateTeams(AIs) # Some initialization logic lives in its own routine so it can be called # separately if we're going to re-use this object for a new game self.reset() def generateTeams(self, AIs): # Seat players in a random order shuffle(AIs) # Set up teams count = len(AIs) if count == 2 or count == 4: teams = 2 elif count == 3 or count == 6: teams = 3 else: raise ValueError('Invalid number of players') for i in range(teams): self.teams.append(Team(i)) team = 0 for playerNumber in range(count): self.teams[team].playerNumbers.append(playerNumber) self.players.append(Player()) self.players[playerNumber].number = playerNumber self.players[playerNumber].teamNumber = team self.players[playerNumber].ai = AIs[playerNumber] team = team + 1 if team >= teams: team = 0 # Prepare this object for a new game def reset(self): for team in self.teams: team.totalScore = 0 # Prepare this object for a new hand def resetHandState(self): self.winner = -1 self.tripComplete = False self.delayedAction = False self.extension = False self.discardPile = [] if len(self.players) == 4: self.target = 1000 self.extensionPossible = False else: self.target = 700 self.extensionPossible = True self.deck = Deck() # No hands yet. for player in self.players: player.hand = [] # Reset per-hand team variables for team in self.teams: team.reset() # Run a complete game # # Returns a list of winning player numbers def play(self): gameOver = False winningscore = 0 winningteam = -1 self.resetHandState() for player in self.players: player.ai.gameStarted(self.makeState(player)) if self.transcriptWriter: self.transcriptWriter.writeGameStart() while not gameOver: # Run a single hand self.playHand() if self.debug: # Show the hand's results for team in self.teams: print 'Team ' + str(team.number) + ' has ' + str( team.totalScore) + ' points' # Adjust total scores for team in self.teams: if team.totalScore > winningscore: winningscore = team.totalScore winningteam = team.number if team.totalScore >= self.pointsToWin: gameOver = True if self.debug: print 'Team ' + str(winningteam) + ' wins' if self.transcriptWriter: self.transcriptWriter.writeGameEnd() # Return the winners indexed by name ret = {} for playerNumber in self.teams[winningteam].playerNumbers: name = str(self.players[playerNumber].ai) if not ret.has_key(name): ret[name] = 0 ret[name] += 1 return ret # Play a single hand def playHand(self): self.resetHandState() # Fresh deck self.deck = Deck() # Deal hands for player in self.players: player.hand = self.draw(player, 6) if self.transcriptWriter: self.transcriptWriter.writeHandStart() currentPlayerNumber = 0 # Normally this is currentPlayerNumber + 1, but in the case of a coup # fourre it gets changed to give the player another turn nextPlayerNumber = 1 while True: currentPlayer = self.players[currentPlayerNumber] currentTeam = self.teams[currentPlayer.teamNumber] drewCardThisMove = None try: drewCardThisMove = self.draw(currentPlayer) currentPlayer.hand.append(drewCardThisMove) except IndexError: self.delayedAction = True if self.debug: print 'Hand contents:', print Cards.cardsToStrings(currentPlayer.hand) if len(currentPlayer.hand) == 0: totalHandSize = 0 for player in self.players: totalHandSize += len(player.hand) if totalHandSize == 0: # Everyone's out of cards - game over break else: # Pass and let someone else go currentPlayerNumber = nextPlayerNumber nextPlayerNumber += 1 if nextPlayerNumber >= len(self.players): nextPlayerNumber = 0 continue state = self.makeState(currentPlayer) state.findValidPlays() # Store a copy of this locally, just in case the AI changes it validMoves = state.validMoves move = currentPlayer.ai.makeMove(state) # Replace invalid moves with a random discard if move not in validMoves: print 'Warning: invalid play' move = Move(Move.DISCARD, currentPlayer.hand[0]) if self.debug: print currentPlayer, print move sanitizedPlayer = copy(currentPlayer) sanitizedPlayer.hand = [] sanitizedPlayer.ai = None self.notifyPlayers(sanitizedPlayer, move) oldTarget = self.target self.handleMove(currentPlayer, currentTeam, move) if self.transcriptWriter: extensionWasDeclared = self.target > oldTarget self.transcriptWriter.writeMove(currentPlayer.number, drewCardThisMove, move, extensionWasDeclared) if self.tripComplete: break # Remove the card from the player's hand del currentPlayer.hand[currentPlayer.hand.index(move.card)] if self.debug: for team in self.teams: print team print '' # Go on to the next player. If a safety got played, nextPlayerNumber # got changed and will cause us to break out of the normal rotation currentPlayerNumber = nextPlayerNumber nextPlayerNumber += 1 if nextPlayerNumber >= len(self.players): nextPlayerNumber = 0 if self.debug: print 'Hand complete' if self.transcriptWriter: self.transcriptWriter.writeHandEnd() self.computeHandScores() def handleMove(self, currentPlayer, currentTeam, move, forceExtension=False): if True: # To keep the indent level of all this the same as in upstream and make the diff prettier. :( currentPlayerNumber = currentPlayer.number # Handle moves if move.type == Move.PLAY: card = move.card type = Cards.cardToType(card) if type == Cards.MILEAGE: currentTeam.mileage += Cards.cardToMileage(card) currentTeam.mileagePile.append(card) if card == Cards.MILEAGE_200: currentTeam.safeTrip = False currentTeam.twoHundredsPlayed += 1 if currentTeam.mileage == self.target: tempState = self.makeState(currentPlayer) if self.extensionPossible and ( forceExtension or currentPlayer.ai.goForExtension(tempState)): if self.debug: print 'Player ' + str( currentPlayerNumber ) + ' goes for the extension' self.extension = True self.extensionPossible = False self.target = 1000 else: if self.debug: print 'Race complete' self.winner = currentPlayer.teamNumber self.tripComplete = True return elif type == Cards.REMEDY: currentTeam.battlePile.append(card) if card == Cards.REMEDY_END_OF_LIMIT: currentTeam.speedLimit = False else: currentTeam.needRemedy = Cards.REMEDY_GO if (card == Cards.REMEDY_GO or Cards.SAFETY_RIGHT_OF_WAY in currentTeam.safeties): currentTeam.needRemedy = None currentTeam.moving = True elif type == Cards.ATTACK: targetTeam = self.teams[(move.target)] # Check for coup fourre neededSafety = Cards.attackToSafety(card) coupFourrePlayerNumber = -1 for targetPlayerNumber in targetTeam.playerNumbers: targetPlayer = self.players[targetPlayerNumber] if neededSafety in targetPlayer.hand: tempState = self.makeState(targetPlayer) if targetPlayer.ai and targetPlayer.ai.playCoupFourre( card, tempState): coupFourrePlayerNumber = targetPlayerNumber # There's only one of each safety, so if we found it, we don't # need to keep looking break if coupFourrePlayerNumber == -1: # The attack resolves targetTeam.battlePile.append(card) if card == Cards.ATTACK_SPEED_LIMIT: self.teams[move.target].speedLimit = True else: self.teams[move.target].moving = False self.teams[ move.target].needRemedy = Cards.attackToRemedy( card) else: # Coup fourre self.playSafety(targetTeam, neededSafety) nextPlayerNumber = coupFourrePlayerNumber # Remove the safety from the player's hand del self.players[coupFourrePlayerNumber].hand[ self.players[coupFourrePlayerNumber].hand.index( neededSafety)] # Draw an extra card to replace the one just played try: player = self.players[coupFourrePlayerNumber] cfCard = self.draw(player) player.hand.append(cfCard) except IndexError: cfCard = None pass targetTeam.coupFourres += 1 cfMove = Move(Move.PLAY, neededSafety, None, True) if self.debug: print self.players[nextPlayerNumber], print cfMove cfPlayer = copy(self.players[nextPlayerNumber]) cfPlayer.hand = [] cfPlayer.ai = None self.notifyPlayers(cfPlayer, cfMove) if self.transcriptWriter: self.transcriptWriter.writeMove( cfPlayer.number, cfCard, cfMove, False) elif type == Cards.SAFETY: self.playSafety(currentTeam, card) nextPlayerNumber = currentPlayerNumber else: raise ValueError('Unknown card type!') elif move.type == Move.DISCARD: self.discardPile.append(move.card) def computeHandScores(self): # Look for a shut out teamsWithMileage = 0 for team in self.teams: if team.mileage > 0: teamsWithMileage += 1 # Now actually figure scores scoreSummary = '' handScoresByTeam = [] totalScoresByTeam = [] for team in self.teams: scoreSummary += 'Team ' + str(team.number) + "\n" team.handScore = team.mileage scoreSummary += ' ' + str(team.mileage) + " miles\n" team.handScore += 100 * len(team.safeties) scoreSummary += ' ' + str(len( team.safeties)) + ' safeties = ' + str( 100 * len(team.safeties)) + "\n" team.handScore += 300 * team.coupFourres scoreSummary += ' ' + str( team.coupFourres) + ' coup fourres = ' + str( 300 * team.coupFourres) + "\n" if len(team.safeties) == 4: scoreSummary += " All four safeties = 500\n" team.handScore += 700 if self.winner == team.number: team.handScore += 400 scoreSummary += " Trip complete = 400\n" if self.delayedAction: scoreSummary += " Delayed action = 300\n" team.handScore += 300 if team.safeTrip: scoreSummary += " Safe trip = 300\n" team.handScore += 300 if self.extension: scoreSummary += " Extension = 200\n" team.handScore += 200 if teamsWithMileage == 1: team.handScore += 500 scoreSummary += " Shut out = 500\n" scoreSummary += ' Total: ' + str(team.handScore) + "\n" team.totalScore += team.handScore handScoresByTeam.append(team.handScore) totalScoresByTeam.append(team.totalScore) if self.debug: print scoreSummary # Notify the players that the hand is over for player in self.players: if player.ai: player.ai.handEnded(scoreSummary) player.ai.handEnded2(handScoresByTeam, totalScoresByTeam) def notifyPlayers(self, movingPlayer, move): for player in self.players: player.ai.playerPlayed(movingPlayer, move) # This is a separate method since it gets called from two places in # playHand() def playSafety(self, team, card): team.safeties.append(card) if card == Cards.SAFETY_RIGHT_OF_WAY: team.speedLimit = False if Cards.remedyToSafety(team.needRemedy) == card: if (card == Cards.SAFETY_RIGHT_OF_WAY or Cards.SAFETY_RIGHT_OF_WAY in team.safeties): team.needRemedy = None team.moving = True else: team.needRemedy = Cards.REMEDY_GO # Make a customized state object for the player that includes # everything needed to make a decision, but doesn't expose private # information about other players def makeState(self, player): state = GameState() state.debug = self.debug # Pass copies, not the original objects, so the canonical ones can't # "accidentally" get modified by the AIs state.hand = copy(player.hand) state.discardPile = copy(self.discardPile) state.teams = deepcopy(self.teams) state.us = copy(self.teams[player.teamNumber]) state.opponents = deepcopy(self.teams) del state.opponents[(player.teamNumber)] # Passed by value, doesn't need copying state.target = self.target state.cardsLeft = self.deck.cardsLeft() state.playerCount = len(self.players) state.playerNumber = player.number return state def draw(self, player, count=1): cards = self.deck.draw(count) if count == 1: player.ai.cardDrawn(cards) else: for card in cards: player.ai.cardDrawn(card) return cards
class Game: # How many points to win a game, as opposed to a single hand pointsToWin = 5000 def __init__(self, AIs, debug = False, teams = None, players = None): self.teams = [] self.players = [] self.debug = debug self.transcriptWriter = None if not (bool(AIs) ^ bool(teams and players)): raise Exception("Must specify one and only one of (AIs), (teams, players).") if AIs is None: self.teams = teams self.players = players else: self.generateTeams(AIs) # Some initialization logic lives in its own routine so it can be called # separately if we're going to re-use this object for a new game self.reset() def generateTeams(self, AIs): # Seat players in a random order shuffle(AIs) # Set up teams count = len(AIs) if count == 2 or count == 4: teams = 2 elif count == 3 or count == 6: teams = 3 else: raise ValueError('Invalid number of players') for i in range(teams): self.teams.append(Team(i)) team = 0 for playerNumber in range(count): self.teams[team].playerNumbers.append(playerNumber) self.players.append(Player()) self.players[playerNumber].number = playerNumber self.players[playerNumber].teamNumber = team self.players[playerNumber].ai = AIs[playerNumber] team = team + 1 if team >= teams: team = 0 # Prepare this object for a new game def reset(self): for team in self.teams: team.totalScore = 0 # Prepare this object for a new hand def resetHandState(self): self.winner = -1 self.tripComplete = False self.delayedAction = False self.extension = False self.discardPile = [] if len(self.players) == 4: self.target = 1000 self.extensionPossible = False else: self.target = 700 self.extensionPossible = True self.deck = Deck() # No hands yet. for player in self.players: player.hand = [] # Reset per-hand team variables for team in self.teams: team.reset() # Run a complete game # # Returns a list of winning player numbers def play(self): gameOver = False winningscore = 0 winningteam = -1 self.resetHandState() for player in self.players: player.ai.gameStarted(self.makeState(player)) if self.transcriptWriter: self.transcriptWriter.writeGameStart() while not gameOver: # Run a single hand self.playHand() if self.debug: # Show the hand's results for team in self.teams: print 'Team ' + str(team.number) + ' has ' + str(team.totalScore) + ' points' # Adjust total scores for team in self.teams: if team.totalScore > winningscore: winningscore = team.totalScore winningteam = team.number if team.totalScore >= self.pointsToWin: gameOver = True if self.debug: print 'Team ' + str(winningteam) + ' wins' if self.transcriptWriter: self.transcriptWriter.writeGameEnd() # Return the winners indexed by name ret = {} for playerNumber in self.teams[winningteam].playerNumbers: name = str(self.players[playerNumber].ai) if not ret.has_key(name): ret[name] = 0 ret[name] += 1 return ret # Play a single hand def playHand(self): self.resetHandState() # Fresh deck self.deck = Deck() # Deal hands for player in self.players: player.hand = self.draw(player, 6) if self.transcriptWriter: self.transcriptWriter.writeHandStart() currentPlayerNumber = 0 # Normally this is currentPlayerNumber + 1, but in the case of a coup # fourre it gets changed to give the player another turn nextPlayerNumber = 1 while True: currentPlayer = self.players[currentPlayerNumber] currentTeam = self.teams[currentPlayer.teamNumber] drewCardThisMove = None try: drewCardThisMove = self.draw(currentPlayer) currentPlayer.hand.append(drewCardThisMove) except IndexError: self.delayedAction = True if self.debug: print 'Hand contents:', print Cards.cardsToStrings(currentPlayer.hand) if len(currentPlayer.hand) == 0: totalHandSize = 0 for player in self.players: totalHandSize += len(player.hand) if totalHandSize == 0: # Everyone's out of cards - game over break else: # Pass and let someone else go currentPlayerNumber = nextPlayerNumber nextPlayerNumber += 1 if nextPlayerNumber >= len(self.players): nextPlayerNumber = 0 continue state = self.makeState(currentPlayer) state.findValidPlays() # Store a copy of this locally, just in case the AI changes it validMoves = state.validMoves move = currentPlayer.ai.makeMove(state) # Replace invalid moves with a random discard if move not in validMoves: print 'Warning: invalid play' move = Move(Move.DISCARD, currentPlayer.hand[0]) if self.debug: print currentPlayer, print move sanitizedPlayer = copy(currentPlayer) sanitizedPlayer.hand = [] sanitizedPlayer.ai = None self.notifyPlayers(sanitizedPlayer, move) oldTarget = self.target self.handleMove(currentPlayer, currentTeam, move) if self.transcriptWriter: extensionWasDeclared = self.target > oldTarget self.transcriptWriter.writeMove(currentPlayer.number, drewCardThisMove, move, extensionWasDeclared) if self.tripComplete: break # Remove the card from the player's hand del currentPlayer.hand[currentPlayer.hand.index(move.card)] if self.debug: for team in self.teams: print team print '' # Go on to the next player. If a safety got played, nextPlayerNumber # got changed and will cause us to break out of the normal rotation currentPlayerNumber = nextPlayerNumber nextPlayerNumber += 1 if nextPlayerNumber >= len(self.players): nextPlayerNumber = 0 if self.debug: print 'Hand complete' if self.transcriptWriter: self.transcriptWriter.writeHandEnd() self.computeHandScores() def handleMove(self, currentPlayer, currentTeam, move, forceExtension = False): if True: # To keep the indent level of all this the same as in upstream and make the diff prettier. :( currentPlayerNumber = currentPlayer.number # Handle moves if move.type == Move.PLAY: card = move.card type = Cards.cardToType(card) if type == Cards.MILEAGE: currentTeam.mileage += Cards.cardToMileage(card) currentTeam.mileagePile.append(card) if card == Cards.MILEAGE_200: currentTeam.safeTrip = False currentTeam.twoHundredsPlayed += 1 if currentTeam.mileage == self.target: tempState = self.makeState(currentPlayer) if self.extensionPossible and (forceExtension or currentPlayer.ai.goForExtension(tempState)): if self.debug: print 'Player ' + str(currentPlayerNumber) + ' goes for the extension' self.extension = True self.extensionPossible = False self.target = 1000 else: if self.debug: print 'Race complete' self.winner = currentPlayer.teamNumber self.tripComplete = True return elif type == Cards.REMEDY: currentTeam.battlePile.append(card) if card == Cards.REMEDY_END_OF_LIMIT: currentTeam.speedLimit = False else: currentTeam.needRemedy = Cards.REMEDY_GO if (card == Cards.REMEDY_GO or Cards.SAFETY_RIGHT_OF_WAY in currentTeam.safeties): currentTeam.needRemedy = None currentTeam.moving = True elif type == Cards.ATTACK: targetTeam = self.teams[(move.target)] # Check for coup fourre neededSafety = Cards.attackToSafety(card) coupFourrePlayerNumber = -1 for targetPlayerNumber in targetTeam.playerNumbers: targetPlayer = self.players[targetPlayerNumber] if neededSafety in targetPlayer.hand: tempState = self.makeState(targetPlayer) if targetPlayer.ai and targetPlayer.ai.playCoupFourre(card, tempState): coupFourrePlayerNumber = targetPlayerNumber # There's only one of each safety, so if we found it, we don't # need to keep looking break if coupFourrePlayerNumber == -1: # The attack resolves targetTeam.battlePile.append(card) if card == Cards.ATTACK_SPEED_LIMIT: self.teams[move.target].speedLimit = True else: self.teams[move.target].moving = False self.teams[move.target].needRemedy = Cards.attackToRemedy(card) else: # Coup fourre self.playSafety(targetTeam, neededSafety) nextPlayerNumber = coupFourrePlayerNumber # Remove the safety from the player's hand del self.players[coupFourrePlayerNumber].hand[self.players[coupFourrePlayerNumber].hand.index(neededSafety)] # Draw an extra card to replace the one just played try: player = self.players[coupFourrePlayerNumber] cfCard = self.draw(player) player.hand.append(cfCard) except IndexError: cfCard = None pass targetTeam.coupFourres += 1 cfMove = Move(Move.PLAY, neededSafety, None, True) if self.debug: print self.players[nextPlayerNumber], print cfMove cfPlayer = copy(self.players[nextPlayerNumber]) cfPlayer.hand = [] cfPlayer.ai = None self.notifyPlayers(cfPlayer, cfMove) if self.transcriptWriter: self.transcriptWriter.writeMove(cfPlayer.number, cfCard, cfMove, False) elif type == Cards.SAFETY: self.playSafety(currentTeam, card) nextPlayerNumber = currentPlayerNumber else: raise ValueError('Unknown card type!') elif move.type == Move.DISCARD: self.discardPile.append(move.card) def computeHandScores(self): # Look for a shut out teamsWithMileage = 0 for team in self.teams: if team.mileage > 0: teamsWithMileage += 1 # Now actually figure scores scoreSummary = '' handScoresByTeam = [] totalScoresByTeam = [] for team in self.teams: scoreSummary += 'Team ' + str(team.number) + "\n" team.handScore = team.mileage scoreSummary += ' ' + str(team.mileage) + " miles\n" team.handScore += 100 * len(team.safeties) scoreSummary += ' ' + str(len(team.safeties)) + ' safeties = ' + str(100 * len(team.safeties)) + "\n" team.handScore += 300 * team.coupFourres scoreSummary += ' ' + str(team.coupFourres) + ' coup fourres = ' + str(300 * team.coupFourres) + "\n" if len(team.safeties) == 4: scoreSummary += " All four safeties = 500\n" team.handScore += 700 if self.winner == team.number: team.handScore += 400 scoreSummary += " Trip complete = 400\n" if self.delayedAction: scoreSummary += " Delayed action = 300\n" team.handScore += 300 if team.safeTrip: scoreSummary += " Safe trip = 300\n" team.handScore += 300 if self.extension: scoreSummary += " Extension = 200\n" team.handScore += 200 if teamsWithMileage == 1: team.handScore += 500 scoreSummary += " Shut out = 500\n" scoreSummary += ' Total: ' + str(team.handScore) + "\n" team.totalScore += team.handScore handScoresByTeam.append(team.handScore) totalScoresByTeam.append(team.totalScore) if self.debug: print scoreSummary # Notify the players that the hand is over for player in self.players: if player.ai: player.ai.handEnded(scoreSummary) player.ai.handEnded2(handScoresByTeam, totalScoresByTeam) def notifyPlayers(self, movingPlayer, move): for player in self.players: player.ai.playerPlayed(movingPlayer, move) # This is a separate method since it gets called from two places in # playHand() def playSafety(self, team, card): team.safeties.append(card) if card == Cards.SAFETY_RIGHT_OF_WAY: team.speedLimit = False if Cards.remedyToSafety(team.needRemedy) == card: if (card == Cards.SAFETY_RIGHT_OF_WAY or Cards.SAFETY_RIGHT_OF_WAY in team.safeties): team.needRemedy = None team.moving = True else: team.needRemedy = Cards.REMEDY_GO # Make a customized state object for the player that includes # everything needed to make a decision, but doesn't expose private # information about other players def makeState(self, player): state = GameState() state.debug = self.debug # Pass copies, not the original objects, so the canonical ones can't # "accidentally" get modified by the AIs state.hand = copy(player.hand) state.discardPile = copy(self.discardPile) state.teams = deepcopy(self.teams) state.us = copy(self.teams[player.teamNumber]) state.opponents = deepcopy(self.teams) del state.opponents[(player.teamNumber)] # Passed by value, doesn't need copying state.target = self.target state.cardsLeft = self.deck.cardsLeft() state.playerCount = len(self.players) state.playerNumber = player.number return state def draw(self, player, count = 1): cards = self.deck.draw(count) if count == 1: player.ai.cardDrawn(cards) else: for card in cards: player.ai.cardDrawn(card) return cards