def ConsiderPlaySafety(self, state, safeties): # If I'm stuck and the safety resolves it, play it if (not state.us.moving): needRemedy = state.us.needRemedy needSafe = Cards.remedyToSafety(needRemedy) for safety in safeties: if (safety.card == needSafe): return safety # If all the hazards are visible, just play the safety for safety in safeties: if (safety.card == Cards.SAFETY_PUNCTURE_PROOF): flats = self.CountCard(state, Cards.ATTACK_FLAT_TIRE) if (flats >= 3): return safety elif (safety.card == Cards.SAFETY_EXTRA_TANK): outs = self.CountCard(state, Cards.ATTACK_OUT_OF_GAS) if (outs >= 3): return safety elif (safety.card == Cards.SAFETY_DRIVING_ACE): acc = self.CountCard(state, Cards.ATTACK_ACCIDENT) if (acc >= 3): return safety # Out of cards, just play the safety if (len(state.hand) == len(safeties)): return safety # Been stuck for too long, have to do something if (self.needRoll >= 10): return safeties[0]
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
def GetDiscard(self, state, discards): milesLeft = state.target - state.us.mileage milesInHand = 0 for discard in discards: # Excess mileage; discard the card that puts me over the top if (Cards.cardToType(discard.card) == Cards.MILEAGE): milesInHand = milesInHand + Cards.cardToMileage(discard.card) if (milesInHand > milesLeft): return discard # Too many 200s if (discard.card == Cards.MILEAGE_200): total200 = state.us.twoHundredsPlayed + state.hand.count(Cards.MILEAGE_200) if (total200 > 2): return discard # Attack that opponent has a safety for, if only 1 opponent if (len(state.opponents) == 1) & (Cards.cardToType(discard.card) == Cards.ATTACK): safety = Cards.attackToSafety(discard.card) if safety in state.opponents[0].safeties: return discard # Remedy for a safety that I have if Cards.cardToType(discard.card) == Cards.REMEDY: safety = Cards.remedyToSafety(discard.card) if (safety in state.hand) | (safety in state.us.safeties): return discard # Now that we've gone through the best options, try second-tier discards for discard in discards: # 2 of the same remedy, or 3 if it's GO if Cards.cardToType(discard.card) == Cards.REMEDY: count = state.hand.count(discard.card) if (count > 1) & (discard.card != Cards.REMEDY_GO): return discard if count > 2: return discard # Weak mileage, unless near the end of the game if (milesLeft > 100): if discard.card == Cards.MILEAGE_25: return discard if discard.card == Cards.MILEAGE_50: return discard if discard.card == Cards.MILEAGE_75: return discard # Nothing good to discard; just toss something that's not attack or safety for d in discards: if ((Cards.cardToType(d.card) != Cards.SAFETY) & (Cards.cardToType(d.card) != Cards.ATTACK)): return d;
def monteCarloMileageSimulation(self): # Returns a list of many (turns elapsed, team 0 trip mileage remaining, team 1 trip remaining, ...) # TODO: Assumes extension. results = [] for _ in xrange(self.perfConstants.monteCarloIterations): needMileage = dict( (team.number, team.mileage) for team in self.gameState.teams) moving = dict( (team.number, team.moving) for team in self.gameState.teams) needRemedy = dict((team.number, team.needRemedy) for team in self.gameState.teams) twoHundredsPlayed = dict((team.number, team.twoHundredsPlayed) for team in self.gameState.teams) tripCompletedBy = None deck = collections.deque() for (card, qty) in self.cardsUnseen.iteritems(): for _ in xrange(qty): deck.append(card) random.shuffle(deck) turnsElapsed = 1 while deck: for currentTurnTeam in self.gameState.teams: if not deck: break teamNo = currentTurnTeam.number teamNeedMileage = needMileage[teamNo] teamNeedRemedy = needRemedy[teamNo] teamMoving = moving[teamNo] teamTwoHundredsPlayed = twoHundredsPlayed[teamNo] if teamNeedRemedy: teamNeedSafety = Cards.remedyToSafety(teamNeedRemedy) else: teamNeedSafety = None for playerNum in currentTurnTeam.playerNumbers: if tripCompletedBy == playerNum: deck = None break if not deck: break card = deck.pop() cardType = Cards.cardToType(card) if Cards.cardToType(card) != Cards.MILEAGE: if card == Cards.REMEDY_GO: teamMoving = True if teamNeedRemedy == Cards.REMEDY_GO: teamNeedRemedy = None elif teamNeedRemedy: if ((cardType == Cards.SAFETY and teamNeedSafety == card) or (cardType == Cards.REMEDY and needRemedy[teamNo] == card)): teamNeedRemedy = None else: mileage = Cards.cardToMileage(card) if mileage == 200 and teamTwoHundredsPlayed >= 2: continue elif mileage > teamNeedMileage: continue elif mileage == 200: teamTwoHundredsPlayed += 1 teamNeedMileage -= mileage if teamNeedMileage == 0 and teamMoving and not teamNeedRemedy: tripCompletedBy = playerNum break needMileage[teamNo] = teamNeedMileage needRemedy[teamNo] = teamNeedRemedy moving[teamNo] = teamMoving twoHundredsPlayed[teamNo] = teamTwoHundredsPlayed turnsElapsed += 1 result = [turnsElapsed] for i in xrange(len(self.gameState.opponents) + 1): if i == self.gameState.us.number: team = self.gameState.us else: team = self.gameState.teamNumberToTeam(i) if needRemedy[i] or not moving[i]: mileage = 1000 - team.mileage else: mileage = needMileage[i] result.append(mileage) results.append(result) return results
def chanceTeamWillCompleteTrip(self, team): if self.useMonteCarloSimulation(): self.debug( "Using monte carlo method for team trip completion estimate.") teamResults = [ result[team.number + 1] for result in self.monteCarloMileageSimulation() ] completionCount = 0 for result in teamResults: if result == 0: completionCount += 1 ret = completionCount / len(teamResults) self.debug("%r chance that %d will complete trip (%d/%d)", ret, team.number, completionCount, len(teamResults)) else: self.debug( "Using card-counting method for team trip completion estimate." ) turnsLeft = self.deckExhaustionTurnsLeft() playersOnTeam = len(team.playerNumbers) teamMovesLeft = turnsLeft * playersOnTeam if team.moving: goCoeff = 1.0 else: goCoeff = min( 1.0, self.percentOfCardsRemaining(Cards.REMEDY_GO) * teamMovesLeft) if team.needRemedy and team.needRemedy != Cards.REMEDY_GO: remedyCoeff = min( 1.0, self.percentOfCardsRemaining( team.needRemedy, Cards.remedyToSafety(team.needRemedy)) * teamMovesLeft) else: remedyCoeff = 1.0 if goCoeff == 0.0 or remedyCoeff == 0.0: self.debug( "Team %d can't complete trip due to remedy unavailable.", team.number) return 0.0 needMileage = 1000 - team.mileage validMileageCards = [ card for card in Cards.MILEAGE_CARDS if Cards.cardToMileage(card) <= needMileage ] validMileageCards.sort(reverse=True) validMileagePct = self.percentOfCardsRemaining(*validMileageCards) unseenTotalMileage = sum([ Cards.cardToMileage(card) * self.cardsUnseen[card] for card in validMileageCards ]) if unseenTotalMileage < needMileage: self.debug( "Not enough mileage left in deck for team %d to complete trip.", team.number) ret = 0.0 else: ret = min(1.0, (validMileagePct * (unseenTotalMileage / needMileage) * teamMovesLeft * remedyCoeff * goCoeff)) self.debug( "Team %d has %r of trip completion, based on crude card count: %r * (%r / %r) * %r * %r * %r", team.number, ret, validMileagePct, unseenTotalMileage, needMileage, teamMovesLeft, remedyCoeff, goCoeff) return ret
def makeMove(self, gameState): discards = [] mileage = [] attacks = [] remedies = [] safeties = [] cardsPlayed = [] us = gameState.us cardsPlayed = cardsPlayed + us.mileagePile cardsPlayed = cardsPlayed + us.speedPile cardsPlayed = cardsPlayed + us.battlePile cardsPlayed = cardsPlayed + us.safeties MyMileage = us.mileage MyRunningTotal = MyMileage + us.totalScore + len( us.safeties) * 100 + us.coupFourres * 300 opponents = gameState.opponents ourSafeties = us.safeties playedSafeties = ourSafeties opponentsCount = 0 opponentsSafeties = [] for opponent in opponents: opponentsCount = opponentsCount + 1 opponentsSafeties = opponentsSafeties + opponent.safeties cardsPlayed = cardsPlayed + opponent.mileagePile cardsPlayed = cardsPlayed + opponent.speedPile cardsPlayed = cardsPlayed + opponent.battlePile cardsPlayed = cardsPlayed + opponent.safeties playedSafeties = playedSafeties + opponentsSafeties cardsPlayed = cardsPlayed + gameState.discardPile cardPlayedByType = {} for x in xrange(0, 19): cardPlayedByType[x] = 0 for card in cardsPlayed: cardPlayedByType[card] += 1 target = gameState.target target_minus_25 = target - 25 target_minus_50 = target - 50 target_minus_75 = target - 75 target_minus_100 = target - 100 target_minus_200 = target - 200 for play in gameState.validMoves: if (play.type == Move.DISCARD): discards.append(play) else: type = Cards.cardToType(play.card) if type == Cards.MILEAGE: mileage.append(play) elif type == Cards.REMEDY: remedies.append(play) elif type == Cards.ATTACK: attacks.append(play) elif type == Cards.SAFETY: safeties.append(play) ####################### # IF CAN GO FOR WIN DO IT! ####################### if len(mileage) > 0: if us.mileage == target_minus_25: for mi in mileage: if mi.card == Cards.MILEAGE_25: return mi elif us.mileage == target_minus_50: for mi in mileage: if mi.card == Cards.MILEAGE_50: return mi elif us.mileage == target_minus_75: for mi in mileage: if mi.card == Cards.MILEAGE_75: return mi elif us.mileage == target_minus_100: for mi in mileage: if mi.card == Cards.MILEAGE_100: return mi elif us.mileage == target_minus_200: for mi in mileage: if mi.card == Cards.MILEAGE_200: return mi ######################## # play a red card based on weighted factors # but check to see if the corrosponding safety is known in play or in your hand # if not known dont play if less then X cards in deck ##################################### numberOfCardsLeftToNotAttack = 10 if len(attacks) > 0: highestWeight = -10 weightedAttacks = {} for attack in attacks: opponent = gameState.teamNumberToTeam(attack.target) opponentMileage = opponent.mileage opponentRunningTotal = opponentMileage + opponent.totalScore + len( opponent.safeties) * 100 + opponent.coupFourres * 300 weight = 0 if opponentRunningTotal >= 3000: # print "opponentRunningTotal >= 3500" weight += 1 if opponentRunningTotal >= 3500: # print "opponentRunningTotal >= 3500" weight += 1 if opponentRunningTotal >= 4000: # print "opponentRunningTotal >= 4000" weight += 1 if opponentRunningTotal >= 4500: # print "opponentRunningTotal >= 4500" weight += 2 if opponentRunningTotal >= MyRunningTotal + 1000: # print "opponentRunningTotal >= myRunningTotal+2000" weight += 1 if opponentRunningTotal >= MyRunningTotal + 2000: # print "opponentRunningTotal >= myRunningTotal+2000" weight += 3 if opponentMileage >= MyMileage + 400: # print "opponentMileage >= my mileage + 400" weight += 1 if opponentMileage >= target_minus_200: if opponentMileage >= target_minus_50 and attack.card == Cards.ATTACK_SPEED_LIMIT: weight = -10 # print "opponet withi 50 of end and attack=limit" else: weight += 1 # print "opponent within 200 of end" if attack.card != Cards.ATTACK_SPEED_LIMIT and attack.card != Cards.ATTACK_STOP: weight += 0.6 if attack.card == Cards.ATTACK_SPEED_LIMIT: weight += 0 if attack.card == Cards.ATTACK_STOP: weight += 0.01 # print "attack card not limit or stop" corrospondingSafetyisNotKnown = True corrospondingSafety = Cards.attackToSafety(attack.card) if cardPlayedByType[corrospondingSafety] == 1: corrospondingSafetyisNotKnown = False for safet in safeties: if Cards.attackToSafety(attack.card) == safet.card: corrospondingSafetyisNotKnown = False if corrospondingSafetyisNotKnown: if gameState.cardsLeft < numberOfCardsLeftToNotAttack: weight = -10 #nop # print "safety NOT known and gameState.cardsLeft < numberOfCardsLeftToNotAttack" else: # print "safety not known" weight -= 0.5 #print "proceed with attack!!!!!!!!!!" else: # print "safety KNOWN!" weight += 0.5 #print "weight="+str(weight) if weight > highestWeight: highestWeight = weight weightedAttacks[weight] = attack if highestWeight > -1: #print "=============highest Weight="+str(highestWeight) #print weightedAttacks[highestWeight] return weightedAttacks[highestWeight] ##################3 # play a remedy ################## if len(remedies) > 0: remedies.sort(key=lambda x: x.card) if remedies[ 0].card == Cards.REMEDY_END_OF_LIMIT and us.mileage >= target_minus_50 and len( mileage) > 0: mileage.sort(key=lambda x: x.card, reverse=True) #print "remedy= EOL AND us.mileage="+str(us.mileage)+" mileage[0]="+str(mileage[0].card) return mileage[0] else: return remedies[0] ##################### # play a mileage: # first check to see if you are 200 or less away, if any 2 card combonation in hand will finish race # if not, play highest mileage ###################### if len(mileage) > 0: mileage.sort(key=lambda x: x.card, reverse=True) if len(mileage) > 2 and us.mileage > target_minus_200: num = 0 for mi in mileage: num += 1 mileageCopy = mileage[num:] for mi2 in mileageCopy: mivalue = Cards.cardToMileage(mi.card) mi2value = Cards.cardToMileage(mi2.card) total = us.mileage + mivalue + mi2value if total == target: return mi return mileage[0] ############################## # If have safety that solves a problem we have... use it ############################ remedyNeeded = us.needRemedy if remedyNeeded > 0: for safet in safeties: if safet.card == Cards.remedyToSafety(remedyNeeded): return safet if us.speedLimit == True: for safet in safeties: if safet.card == Cards.SAFETY_RIGHT_OF_WAY: return safet #################### # ENTER EASY DISCARD PHASE ############## # reuse these because this used to be in a seperate function mileage = [] attacks = [] remedies = [] for play in discards: type = Cards.cardToType(play.card) if type == Cards.MILEAGE: mileage.append(play) elif type == Cards.REMEDY: remedies.append(play) elif type == Cards.ATTACK: attacks.append(play) ################## # Remedies we have safeties for in play, in hand or for all attacks have been played ################ if len(remedies) > 0: for remedy in remedies: for safet in safeties: if Cards.remedyToSafety(remedy.card) == safet.card: return remedy if Cards.remedyToSafety(remedy.card) in ourSafeties: return remedy corrospondingAttack = Cards.remedyToAttack(remedy.card) if corrospondingAttack == Cards.ATTACK_FLAT_TIRE: if cardPlayedByType[Cards.ATTACK_FLAT_TIRE] == 3: # print "discard spare due to all flats played" return remedy elif corrospondingAttack == Cards.ATTACK_OUT_OF_GAS: if cardPlayedByType[Cards.ATTACK_OUT_OF_GAS] == 3: # print "discard gas due to all out of gas played" return remedy elif corrospondingAttack == Cards.ATTACK_ACCIDENT: if cardPlayedByType[Cards.ATTACK_ACCIDENT] == 3: # print "discard repairs due to all accidents played" return remedy elif corrospondingAttack == Cards.ATTACK_SPEED_LIMIT: if cardPlayedByType[Cards.ATTACK_SPEED_LIMIT] == 4: # print "discard end of limit due to all spedlimits played" return remedy ######################## # Attacks that opponents have safeties for (if not 3 or 6 person game) ###################### if len(attacks) > 0: if opponentsCount == 1: for attack in attacks: if Cards.attackToSafety(attack.card) in opponentsSafeties: return attack ################### # mileage discard due to limit: # if an opponent has right of way and all EOL played and we have speed limit discard mileage over 50 ###################### mileage.sort(key=lambda x: x.card) if len(mileage) > 0: opponentRightOfWay = False for safet in opponentsSafeties: if safet == Cards.SAFETY_RIGHT_OF_WAY: opponentRightOfWay = True if cardPlayedByType[ Cards. REMEDY_END_OF_LIMIT] == 6 and us.speedLimit and opponentRightOfWay: for mi in mileage: if mi.card > 1: #print "discarding mi:" + Cards.cardToString(mi.card) return mi ######################## # milage that we can no longer play due to being to close to the end # or playing 2 2 hundreds ######################### thp = us.twoHundredsPlayed for mi in mileage: if thp == 2 or us.mileage > target_minus_200: if mi.card == Cards.MILEAGE_200: return mi if us.mileage > target_minus_100: if mi.card == Cards.MILEAGE_100: return mi if us.mileage > target_minus_75: if mi.card == Cards.MILEAGE_75: return mi if us.mileage > target_minus_50: if mi.card == Cards.MILEAGE_50: return mi ######################### # Enter "Smart" discard phases ######################## ######################## # Remedy smart discard: remedies we have dupes of (save 1 of each, except GO save 2) ########################### remedyDupes = {} for x in xrange(5, 10): remedyDupes[x] = 0 if len(remedies) > 0: for remedy in remedies: remedyDupes[remedy.card] += 1 if remedyDupes[Cards.REMEDY_SPARE_TIRE] > 1: for remedy in remedies: if remedy.card == Cards.REMEDY_SPARE_TIRE: return remedy elif remedyDupes[Cards.REMEDY_GASOLINE] > 1: for remedy in remedies: if remedy.card == Cards.REMEDY_GASOLINE: return remedy elif remedyDupes[Cards.REMEDY_REPAIRS] > 1: for remedy in remedies: if remedy.card == Cards.REMEDY_REPAIRS: return remedy elif remedyDupes[Cards.REMEDY_GO] > 2: for remedy in remedies: if remedy.card == Cards.REMEDY_GO: return remedy elif remedyDupes[Cards.REMEDY_END_OF_LIMIT] > 1: for remedy in remedies: if remedy.card == Cards.REMEDY_END_OF_LIMIT: return remedy #################### # play a safety as no more easy discards are available ################### # Play a safety rather than discard if len(safeties) > 0: return safeties[0] #################### # attack smart discard: if have more then 2 attack cards discard stops/ speedlimits #################### attackDupes = {} for x in xrange(10, 16): attackDupes[x] = 0 if len(attacks) > 0: totalAttackCardsInHand = 0 for attack in attacks: totalAttackCardsInHand += 1 attackDupes[attack.card] += 1 if totalAttackCardsInHand > 2: for attack in attacks: if attack.card == Cards.ATTACK_SPEED_LIMIT: return attack if attack.card == Cards.ATTACK_STOP: return attack ##################### # final "smart" discard: if more then one mileage in hand discard lowest mileage # if exactly 1 mileage in hand first discard a non GO remedy(starting with EOL, then an attack (order EOL, stop, then others) ######################## if len(mileage) > 0: if len(mileage) == 1: remedies.sort(key=lambda x: x.card, reverse=True) for remedy in remedies: if remedy.card != Cards.REMEDY_GO: return remedy attacks.sort(key=lambda x: x.card, reverse=True) for attack in attacks: return attack else: return mileage[0] ################## # discard whatever is left ################## return discards[0] #toDiscard
def monteCarloMileageSimulation(self): # Returns a list of many (turns elapsed, team 0 trip mileage remaining, team 1 trip remaining, ...) # TODO: Assumes extension. results = [] for _ in xrange(self.perfConstants.monteCarloIterations): needMileage = dict((team.number, team.mileage) for team in self.gameState.teams) moving = dict((team.number, team.moving) for team in self.gameState.teams) needRemedy = dict((team.number, team.needRemedy) for team in self.gameState.teams) twoHundredsPlayed = dict((team.number, team.twoHundredsPlayed) for team in self.gameState.teams) tripCompletedBy = None deck = collections.deque() for (card, qty) in self.cardsUnseen.iteritems(): for _ in xrange(qty): deck.append(card) random.shuffle(deck) turnsElapsed = 1 while deck: for currentTurnTeam in self.gameState.teams: if not deck: break teamNo = currentTurnTeam.number teamNeedMileage = needMileage[teamNo] teamNeedRemedy = needRemedy[teamNo] teamMoving = moving[teamNo] teamTwoHundredsPlayed = twoHundredsPlayed[teamNo] if teamNeedRemedy: teamNeedSafety = Cards.remedyToSafety(teamNeedRemedy) else: teamNeedSafety = None for playerNum in currentTurnTeam.playerNumbers: if tripCompletedBy == playerNum: deck = None break if not deck: break card = deck.pop() cardType = Cards.cardToType(card) if Cards.cardToType(card) != Cards.MILEAGE: if card == Cards.REMEDY_GO: teamMoving = True if teamNeedRemedy == Cards.REMEDY_GO: teamNeedRemedy = None elif teamNeedRemedy: if ((cardType == Cards.SAFETY and teamNeedSafety == card) or (cardType == Cards.REMEDY and needRemedy[teamNo] == card)): teamNeedRemedy = None else: mileage = Cards.cardToMileage(card) if mileage == 200 and teamTwoHundredsPlayed >= 2: continue elif mileage > teamNeedMileage: continue elif mileage == 200: teamTwoHundredsPlayed += 1 teamNeedMileage -= mileage if teamNeedMileage == 0 and teamMoving and not teamNeedRemedy: tripCompletedBy = playerNum break needMileage[teamNo] = teamNeedMileage needRemedy[teamNo] = teamNeedRemedy moving[teamNo] = teamMoving twoHundredsPlayed[teamNo] = teamTwoHundredsPlayed turnsElapsed += 1 result = [turnsElapsed] for i in xrange(len(self.gameState.opponents) + 1): if i == self.gameState.us.number: team = self.gameState.us else: team = self.gameState.teamNumberToTeam(i) if needRemedy[i] or not moving[i]: mileage = 1000 - team.mileage else: mileage = needMileage[i] result.append(mileage) results.append(result) return results
def chanceTeamWillCompleteTrip(self, team): if self.useMonteCarloSimulation(): self.debug("Using monte carlo method for team trip completion estimate.") teamResults = [result[team.number + 1] for result in self.monteCarloMileageSimulation()] completionCount = 0 for result in teamResults: if result == 0: completionCount += 1 ret = completionCount/len(teamResults) self.debug("%r chance that %d will complete trip (%d/%d)", ret, team.number, completionCount, len(teamResults)) else: self.debug("Using card-counting method for team trip completion estimate.") turnsLeft = self.deckExhaustionTurnsLeft() playersOnTeam = len(team.playerNumbers) teamMovesLeft = turnsLeft * playersOnTeam if team.moving: goCoeff = 1.0 else: goCoeff = min(1.0, self.percentOfCardsRemaining(Cards.REMEDY_GO) * teamMovesLeft) if team.needRemedy and team.needRemedy != Cards.REMEDY_GO: remedyCoeff = min(1.0, self.percentOfCardsRemaining( team.needRemedy, Cards.remedyToSafety(team.needRemedy)) * teamMovesLeft) else: remedyCoeff = 1.0 if goCoeff == 0.0 or remedyCoeff == 0.0: self.debug("Team %d can't complete trip due to remedy unavailable.", team.number) return 0.0 needMileage = 1000 - team.mileage validMileageCards = [card for card in Cards.MILEAGE_CARDS if Cards.cardToMileage(card) <= needMileage] validMileageCards.sort(reverse=True) validMileagePct = self.percentOfCardsRemaining(*validMileageCards) unseenTotalMileage = sum([Cards.cardToMileage(card) * self.cardsUnseen[card] for card in validMileageCards]) if unseenTotalMileage < needMileage: self.debug("Not enough mileage left in deck for team %d to complete trip.", team.number) ret = 0.0 else: ret = min(1.0, (validMileagePct * (unseenTotalMileage / needMileage) * teamMovesLeft * remedyCoeff * goCoeff)) self.debug("Team %d has %r of trip completion, based on crude card count: %r * (%r / %r) * %r * %r * %r", team.number, ret, validMileagePct, unseenTotalMileage, needMileage, teamMovesLeft, remedyCoeff, goCoeff) return ret
def makeMove(self, gameState): discards = [] mileage = [] attacks = [] remedies = [] safeties = [] cardsPlayed = [] us = gameState.us cardsPlayed = cardsPlayed + us.mileagePile cardsPlayed = cardsPlayed + us.speedPile cardsPlayed = cardsPlayed + us.battlePile cardsPlayed = cardsPlayed + us.safeties MyMileage = us.mileage MyRunningTotal = MyMileage + us.totalScore + len(us.safeties) * 100 + us.coupFourres * 300 opponents = gameState.opponents ourSafeties = us.safeties playedSafeties = ourSafeties opponentsCount = 0 opponentsSafeties = [] for opponent in opponents: opponentsCount = opponentsCount + 1 opponentsSafeties = opponentsSafeties + opponent.safeties cardsPlayed = cardsPlayed + opponent.mileagePile cardsPlayed = cardsPlayed + opponent.speedPile cardsPlayed = cardsPlayed + opponent.battlePile cardsPlayed = cardsPlayed + opponent.safeties playedSafeties = playedSafeties + opponentsSafeties cardsPlayed = cardsPlayed + gameState.discardPile cardPlayedByType = {} for x in xrange(0, 19): cardPlayedByType[x] = 0 for card in cardsPlayed: cardPlayedByType[card] += 1 target = gameState.target target_minus_25 = target - 25 target_minus_50 = target - 50 target_minus_75 = target - 75 target_minus_100 = target - 100 target_minus_200 = target - 200 for play in gameState.validMoves: if play.type == Move.DISCARD: discards.append(play) else: type = Cards.cardToType(play.card) if type == Cards.MILEAGE: mileage.append(play) elif type == Cards.REMEDY: remedies.append(play) elif type == Cards.ATTACK: attacks.append(play) elif type == Cards.SAFETY: safeties.append(play) ####################### # IF CAN GO FOR WIN DO IT! ####################### if len(mileage) > 0: if us.mileage == target_minus_25: for mi in mileage: if mi.card == Cards.MILEAGE_25: return mi elif us.mileage == target_minus_50: for mi in mileage: if mi.card == Cards.MILEAGE_50: return mi elif us.mileage == target_minus_75: for mi in mileage: if mi.card == Cards.MILEAGE_75: return mi elif us.mileage == target_minus_100: for mi in mileage: if mi.card == Cards.MILEAGE_100: return mi elif us.mileage == target_minus_200: for mi in mileage: if mi.card == Cards.MILEAGE_200: return mi ######################## # play a red card based on weighted factors # but check to see if the corrosponding safety is known in play or in your hand # if not known dont play if less then X cards in deck ##################################### numberOfCardsLeftToNotAttack = 10 if len(attacks) > 0: highestWeight = -10 weightedAttacks = {} for attack in attacks: opponent = gameState.teamNumberToTeam(attack.target) opponentMileage = opponent.mileage opponentRunningTotal = ( opponentMileage + opponent.totalScore + len(opponent.safeties) * 100 + opponent.coupFourres * 300 ) weight = 0 if opponentRunningTotal >= 3000: # print "opponentRunningTotal >= 3500" weight += 1 if opponentRunningTotal >= 3500: # print "opponentRunningTotal >= 3500" weight += 1 if opponentRunningTotal >= 4000: # print "opponentRunningTotal >= 4000" weight += 1 if opponentRunningTotal >= 4500: # print "opponentRunningTotal >= 4500" weight += 2 if opponentRunningTotal >= MyRunningTotal + 1000: # print "opponentRunningTotal >= myRunningTotal+2000" weight += 1 if opponentRunningTotal >= MyRunningTotal + 2000: # print "opponentRunningTotal >= myRunningTotal+2000" weight += 3 if opponentMileage >= MyMileage + 400: # print "opponentMileage >= my mileage + 400" weight += 1 if opponentMileage >= target_minus_200: if opponentMileage >= target_minus_50 and attack.card == Cards.ATTACK_SPEED_LIMIT: weight = -10 # print "opponet withi 50 of end and attack=limit" else: weight += 1 # print "opponent within 200 of end" if attack.card != Cards.ATTACK_SPEED_LIMIT and attack.card != Cards.ATTACK_STOP: weight += 0.6 if attack.card == Cards.ATTACK_SPEED_LIMIT: weight += 0 if attack.card == Cards.ATTACK_STOP: weight += 0.01 # print "attack card not limit or stop" corrospondingSafetyisNotKnown = True corrospondingSafety = Cards.attackToSafety(attack.card) if cardPlayedByType[corrospondingSafety] == 1: corrospondingSafetyisNotKnown = False for safet in safeties: if Cards.attackToSafety(attack.card) == safet.card: corrospondingSafetyisNotKnown = False if corrospondingSafetyisNotKnown: if gameState.cardsLeft < numberOfCardsLeftToNotAttack: weight = -10 # nop # print "safety NOT known and gameState.cardsLeft < numberOfCardsLeftToNotAttack" else: # print "safety not known" weight -= 0.5 # print "proceed with attack!!!!!!!!!!" else: # print "safety KNOWN!" weight += 0.5 # print "weight="+str(weight) if weight > highestWeight: highestWeight = weight weightedAttacks[weight] = attack if highestWeight > -1: # print "=============highest Weight="+str(highestWeight) # print weightedAttacks[highestWeight] return weightedAttacks[highestWeight] ##################3 # play a remedy ################## if len(remedies) > 0: remedies.sort(key=lambda x: x.card) if remedies[0].card == Cards.REMEDY_END_OF_LIMIT and us.mileage >= target_minus_50 and len(mileage) > 0: mileage.sort(key=lambda x: x.card, reverse=True) # print "remedy= EOL AND us.mileage="+str(us.mileage)+" mileage[0]="+str(mileage[0].card) return mileage[0] else: return remedies[0] ##################### # play a mileage: # first check to see if you are 200 or less away, if any 2 card combonation in hand will finish race # if not, play highest mileage ###################### if len(mileage) > 0: mileage.sort(key=lambda x: x.card, reverse=True) if len(mileage) > 2 and us.mileage > target_minus_200: num = 0 for mi in mileage: num += 1 mileageCopy = mileage[num:] for mi2 in mileageCopy: mivalue = Cards.cardToMileage(mi.card) mi2value = Cards.cardToMileage(mi2.card) total = us.mileage + mivalue + mi2value if total == target: return mi return mileage[0] ############################## # If have safety that solves a problem we have... use it ############################ remedyNeeded = us.needRemedy if remedyNeeded > 0: for safet in safeties: if safet.card == Cards.remedyToSafety(remedyNeeded): return safet if us.speedLimit == True: for safet in safeties: if safet.card == Cards.SAFETY_RIGHT_OF_WAY: return safet #################### # ENTER EASY DISCARD PHASE ############## # reuse these because this used to be in a seperate function mileage = [] attacks = [] remedies = [] for play in discards: type = Cards.cardToType(play.card) if type == Cards.MILEAGE: mileage.append(play) elif type == Cards.REMEDY: remedies.append(play) elif type == Cards.ATTACK: attacks.append(play) ################## # Remedies we have safeties for in play, in hand or for all attacks have been played ################ if len(remedies) > 0: for remedy in remedies: for safet in safeties: if Cards.remedyToSafety(remedy.card) == safet.card: return remedy if Cards.remedyToSafety(remedy.card) in ourSafeties: return remedy corrospondingAttack = Cards.remedyToAttack(remedy.card) if corrospondingAttack == Cards.ATTACK_FLAT_TIRE: if cardPlayedByType[Cards.ATTACK_FLAT_TIRE] == 3: # print "discard spare due to all flats played" return remedy elif corrospondingAttack == Cards.ATTACK_OUT_OF_GAS: if cardPlayedByType[Cards.ATTACK_OUT_OF_GAS] == 3: # print "discard gas due to all out of gas played" return remedy elif corrospondingAttack == Cards.ATTACK_ACCIDENT: if cardPlayedByType[Cards.ATTACK_ACCIDENT] == 3: # print "discard repairs due to all accidents played" return remedy elif corrospondingAttack == Cards.ATTACK_SPEED_LIMIT: if cardPlayedByType[Cards.ATTACK_SPEED_LIMIT] == 4: # print "discard end of limit due to all spedlimits played" return remedy ######################## # Attacks that opponents have safeties for (if not 3 or 6 person game) ###################### if len(attacks) > 0: if opponentsCount == 1: for attack in attacks: if Cards.attackToSafety(attack.card) in opponentsSafeties: return attack ################### # mileage discard due to limit: # if an opponent has right of way and all EOL played and we have speed limit discard mileage over 50 ###################### mileage.sort(key=lambda x: x.card) if len(mileage) > 0: opponentRightOfWay = False for safet in opponentsSafeties: if safet == Cards.SAFETY_RIGHT_OF_WAY: opponentRightOfWay = True if cardPlayedByType[Cards.REMEDY_END_OF_LIMIT] == 6 and us.speedLimit and opponentRightOfWay: for mi in mileage: if mi.card > 1: # print "discarding mi:" + Cards.cardToString(mi.card) return mi ######################## # milage that we can no longer play due to being to close to the end # or playing 2 2 hundreds ######################### thp = us.twoHundredsPlayed for mi in mileage: if thp == 2 or us.mileage > target_minus_200: if mi.card == Cards.MILEAGE_200: return mi if us.mileage > target_minus_100: if mi.card == Cards.MILEAGE_100: return mi if us.mileage > target_minus_75: if mi.card == Cards.MILEAGE_75: return mi if us.mileage > target_minus_50: if mi.card == Cards.MILEAGE_50: return mi ######################### # Enter "Smart" discard phases ######################## ######################## # Remedy smart discard: remedies we have dupes of (save 1 of each, except GO save 2) ########################### remedyDupes = {} for x in xrange(5, 10): remedyDupes[x] = 0 if len(remedies) > 0: for remedy in remedies: remedyDupes[remedy.card] += 1 if remedyDupes[Cards.REMEDY_SPARE_TIRE] > 1: for remedy in remedies: if remedy.card == Cards.REMEDY_SPARE_TIRE: return remedy elif remedyDupes[Cards.REMEDY_GASOLINE] > 1: for remedy in remedies: if remedy.card == Cards.REMEDY_GASOLINE: return remedy elif remedyDupes[Cards.REMEDY_REPAIRS] > 1: for remedy in remedies: if remedy.card == Cards.REMEDY_REPAIRS: return remedy elif remedyDupes[Cards.REMEDY_GO] > 2: for remedy in remedies: if remedy.card == Cards.REMEDY_GO: return remedy elif remedyDupes[Cards.REMEDY_END_OF_LIMIT] > 1: for remedy in remedies: if remedy.card == Cards.REMEDY_END_OF_LIMIT: return remedy #################### # play a safety as no more easy discards are available ################### # Play a safety rather than discard if len(safeties) > 0: return safeties[0] #################### # attack smart discard: if have more then 2 attack cards discard stops/ speedlimits #################### attackDupes = {} for x in xrange(10, 16): attackDupes[x] = 0 if len(attacks) > 0: totalAttackCardsInHand = 0 for attack in attacks: totalAttackCardsInHand += 1 attackDupes[attack.card] += 1 if totalAttackCardsInHand > 2: for attack in attacks: if attack.card == Cards.ATTACK_SPEED_LIMIT: return attack if attack.card == Cards.ATTACK_STOP: return attack ##################### # final "smart" discard: if more then one mileage in hand discard lowest mileage # if exactly 1 mileage in hand first discard a non GO remedy(starting with EOL, then an attack (order EOL, stop, then others) ######################## if len(mileage) > 0: if len(mileage) == 1: remedies.sort(key=lambda x: x.card, reverse=True) for remedy in remedies: if remedy.card != Cards.REMEDY_GO: return remedy attacks.sort(key=lambda x: x.card, reverse=True) for attack in attacks: return attack else: return mileage[0] ################## # discard whatever is left ################## return discards[0] # toDiscard
def makeMove(self, gameState): discards = [] mileage = [] attacks = [] remedies = [] safeties = [] worthlessCards = [] #These are cards that cannot ever help us and are first to be discarded. milesToGo = gameState.target - gameState.us.mileage numMiles = [] targetTeam = self.priorityTarget(gameState) for play in gameState.validMoves: type = Cards.cardToType(play.card) if (play.type == Move.DISCARD): #Never discard a safety. if type != Cards.SAFETY: if type == Cards.MILEAGE: #Mileage cards are worthless if they put us over 1000 miles. if Cards.cardToMileage(play.card) + gameState.us.mileage > 1000: worthlessCards.append(play) #200 miles is worthless if we've played 2 of them, or if # played + # in hand > 2, the surplus is worthless else: discards.append(play) elif type == Cards.ATTACK: #Attack cards are worthless if we only have one opponent and they have played the safety if targetTeam == -1: for opponent in gameState.opponents: if Cards.attackToSafety(play.card) in opponent.safeties: worthlessCards.append(play) else: discards.append(play) else: discards.append(play) elif type == Cards.REMEDY: #Remedies are worthless if the we have played the safety if Cards.remedyToSafety(play.card) in gameState.us.safeties: worthlessCards.append(play) #Remedies are worthless if we have the safety for it in our hand elif Cards.remedyToSafety(play.card) in gameState.hand: worthlessCards.append(play) #Remedies are worthless if all of the appropriate attack cards have been played and we do not need it right now. elif self.cardsLeft[Cards.remedyToAttack(play.card)] == 0 and gameState.us.needRemedy != Cards.remedyToAttack(play.card): worthlessCards.append(play) else: discards.append(play) else: if type == Cards.MILEAGE: #Sort as we insert, biggest mileage at the front of the list if len(mileage) == 0: numMiles.append(Cards.cardToMileage(play.card)) mileage.append(play) elif len(mileage) == 1: if Cards.cardToMileage(play.card) > numMiles[0]: numMiles.insert(0, Cards.cardToMileage(play.card)) mileage.insert(0, play) else: numMiles.append(Cards.cardToMileage(play.card)) mileage.append(play) elif len(mileage) == 2: if Cards.cardToMileage(play.card) > numMiles[0]: numMiles.insert(0, Cards.cardToMileage(play.card)) mileage.insert(0, play) elif Cards.cardToMileage(play.card) > numMiles[1]: numMiles.insert(1, Cards.cardToMileage(play.card)) mileage.insert(1, play) else: numMiles.append(Cards.cardToMileage(play.card)) mileage.append(play) else: #If it's biggest, insert it first if Cards.cardToMileage(play.card) > numMiles[0]: numMiles.insert(0, Cards.cardToMileage(play.card)) mileage.insert(0, play) #If it's smallest, insert it last elif Cards.cardToMileage(play.card) < numMiles[len(numMiles) - 1]: numMiles.insert((1 - len(numMiles)), Cards.cardToMileage(play.card)) mileage.insert((1 - len(mileage)), play) #Otherwise Insert it at index 1 else: numMiles.insert(1, Cards.cardToMileage(play.card)) mileage.insert(1, play) elif type == Cards.ATTACK: if targetTeam == -1: #No priority target attacks.append(play) #Sort as we insert, priority targets in front elif play.target == targetTeam: attacks.insert(0, play) else: attacks.append(play) elif type == Cards.REMEDY: remedies.append(play) elif type == Cards.SAFETY: safeties.append(play) #If there are less than 10 cards left, play any safeties in our hand # NOTE: Investigate how much this changes AI results if gameState.cardsLeft <= 10: if len(safeties) > 0: return safeties[0] #If we can win the game, play a safety if we can, then check to see if we are safe to go for delayed action, otherwise win the game. if milesToGo in numMiles: if len(safeties) > 0: return safeties[0] elif gameState.cardsLeft == 0: #Win the game if we already have a delayed action. return mileage[numMiles.index(milesToGo)] elif self.getDangerCards(gameState) == 0: #If we are safe, attack if we can, pitch a worthless card if we can't, win the game otherwise. if len(attacks) > 0: return attacks[0] elif len(worthlessCards) > 0: return worthlessCards[0] else: return mileage[numMiles.index(milesToGo)] else: #For the time being, consider even one potential attack too deadly, and end the game. #TINKER HERE LATER return mileage[numMiles.index(milesToGo)] if len(attacks) > 0: attacks = self.sortAttacks(attacks, targetTeam) #Attack if we can in a two player game always. if targetTeam == -1 : return attacks[0] #If we can't move in a 3 player game, attack first. elif len(mileage) == 0: return attacks[0] #If we have a valid move for 100 miles or more and we've got a ways to go, move, otherwise attack. elif Cards.cardToMileage(mileage[0].card) >= 100 and milesToGo >= 400: return mileage[0] else: return attacks[0] #Fix what needs fixing in a two player game, attack in a 3 player game (since we aren't moving) if len(remedies) > 0: return remedies[0] #If we need a remedy and we have the safety for it after move 10, just play it. if gameState.us.needRemedy != None and len(safeties) > 0: for s in safeties: if Cards.remedyToSafety(gameState.us.needRemedy) == s.card and len(self.gameHistory) >= 10: return s # If we can move if len(mileage) > 0: #Move as fast as we can if we haven't gone too far if milesToGo > 400: return mileage[0] #If we've already played a 200 and our biggest mileage card leaves us at or over 100 miles, play it if (milesToGo - Cards.cardToMileage(mileage[0].card) >= 100): if (gameState.us.twoHundredsPlayed > 0): return mileage[0] #If we have more than 200 miles to go, haven't played a 200, have a 100, and we're not under a speed limit, play the 100 if milesToGo > 200 and gameState.us.twoHundredsPlayed == 0 and Cards.MILEAGE_100 in gameState.hand and not(gameState.us.speedLimit): for i in mileage: if numMiles[mileage.index(i)] == 100: return i #If we're over 100 miles away and we can get to 100 miles away, do it if milesToGo > 100: for i in numMiles: if milesToGo - i == 100: return mileage[numMiles.index(i)] #If we're under a speed limit, play the biggest mileage we have if gameState.us.speedLimit: return mileage[0] #If we're at 200 miles or less, play our smallest mileage (if we could win the game, we already would have done so) if milesToGo <= 200: mileage.reverse() return mileage[0] #Return our biggest mileage return mileage[0] # Discard something worthless if we have it if len(worthlessCards) > 0: return worthlessCards[0] #If we are at the end of the game, discard high mileage, then remedies, then attacks, then low mileage. if gameState.cardsLeft == 0: for d in discards: if d.card <= 4: if d.card >= 2: return d for d in discards: if d.card <= 9: if d.card >= 2: return d for d in discards: if d.card <= 14: if d.card >= 2: return d for d in discards: if d.card <= 2: return d for d in discards: #If we have more of a remedy than there are attacks in the game, discard it. if Cards.cardToType(d.card) == Cards.REMEDY and discards.count(d) > self.cardsLeft[Cards.remedyToAttack(d.card)]: return d #If we have 3 of any given card, discard it. if discards.count(d) >= 3: return d #If we're under a speed limit, discard 75 if we have it, 100 if we already popped our safeTrip cherry, 200 if we haven't and are halfway through the race, the rest of the mileage. if gameState.us.speedLimit: if d.card == Cards.MILEAGE_75: return d if gameState.us.safeTrip and ((gameState.target == 1000 and gameState.us.handScore >= 600) or (gameState.target == 700 and gameState.us.handScore > 400)) and d.card == Cards.MILEAGE_200: return d if d.card == Cards.MILEAGE_100: return d if d.card == Cards.MILEAGE_200: return d else: if d.card == Cards.MILEAGE_25: return d #Pitch doubles next - low mileage first, then high mileage, then remedies for d in discards: if discards.count(d) > 1: if d.card <= 1: return d for d in discards: if discards.count(d) > 1: if d.card <= 4: return d for d in discards: if discards.count(d) > 1: if d.card <= 9: return d #Pitch crappy single cards. if Cards.MILEAGE_25 in gameState.hand: return Move(Move.DISCARD, Cards.MILEAGE_25) if Cards.MILEAGE_75 in gameState.hand: return Move(Move.DISCARD, Cards.MILEAGE_75) if Cards.MILEAGE_50 in gameState.hand: return Move(Move.DISCARD, Cards.MILEAGE_50) if Cards.REMEDY_END_OF_LIMIT in gameState.hand: return Move(Move.DISCARD, Cards.REMEDY_END_OF_LIMIT) #Pitch any mileage that puts us over 700 if the target is 700 for d in discards: if Cards.cardToType(d.card) == Cards.MILEAGE: if gameState.target == 700 and Cards.cardToMileage(d.card) + gameState.us.mileage > 700: return d #If we have an attack and the remedy for the attack, pitch the remedy for d in discards: if Cards.cardToType(d.card) == Cards.ATTACK: if Cards.attackToRemedy(d.card) in gameState.hand: return Move(Move.DISCARD, Cards.attackToRemedy(d.card)) #Pitch duplicate attack cards now, but only if we don't have the safety for it already played. for d in discards: if discards.count(d) > 1: if d.card <= 14 and Cards.attackToSafety(d.card) not in gameState.us.safeties: return d #If we've gotten here we have a pretty good hand - so play a safety if we have one if len(safeties) > 0: return safeties[0] #Pitch attacks we don't have the safety for for d in discards: if Cards.cardToType(d.card) == Cards.ATTACK and Cards.attackToSafety(d.card) not in gameState.us.safeties: return d #Pitch any duplicates at this point for d in discards: if discards.count(d) > 1: return d #Pitch Speed Limit next for d in discards: if d.card == 14: return d #Pitch Stop next for d in discards: if d.card == 13: return d #In nearly a million runs total this hasn't ever triggered. But it seems best to leave it here. return discards[0]