Пример #1
0
 def adjustScoresForAttack(card, attackCounts, priorities):
   # Discarding the associated remedy is a better move, and an
   # auto-discard if all three have been played now
   # Also that safety is less useful as a coup fourre
   attackCounts[card] += 1
   if attackCounts[card] == 3:
     priorities[Move(Move.PLAY, Cards.attackToSafety(card))] = 100
     priorities[Move(Move.DISCARD, Cards.attackToRemedy(card))] = 0
   else:
     priorities[Move(Move.PLAY, Cards.attackToSafety(card))] += 1
     priorities[Move(Move.DISCARD, Cards.attackToRemedy(card))] += .5
Пример #2
0
 def adjustScoresForAttack(card, attackCounts, priorities):
     # Discarding the associated remedy is a better move, and an
     # auto-discard if all three have been played now
     # Also that safety is less useful as a coup fourre
     attackCounts[card] += 1
     if attackCounts[card] == 3:
         priorities[Move(Move.PLAY, Cards.attackToSafety(card))] = 100
         priorities[Move(Move.DISCARD, Cards.attackToRemedy(card))] = 0
     else:
         priorities[Move(Move.PLAY, Cards.attackToSafety(card))] += 1
         priorities[Move(Move.DISCARD, Cards.attackToRemedy(card))] += .5
Пример #3
0
    def chanceOpponentHasProtection(self, team, attack):
        # Chance that a particular opponent has protection from a particular attack in their hand.
        safety = Cards.attackToSafety(attack)
        remedy = Cards.attackToRemedy(attack)

        if safety in team.safeties:
            self.debug("Team %d has already played safety v. %s", team.number,
                       Cards.cardToString(attack))
            return 1.0

        # Odds based on number of the card lurking out there somewhere.
        odds = self.percentOfCardsRemaining(safety, remedy)

        # Boost likelihood by 50% for each remedy they've discarded.
        remedyDiscards = 0
        for player in team.playerNumbers:
            for _ in xrange(
                    self.interestingRemedyDiscardsByPlayer[player][remedy]):
                remedyDiscards += 1
                odds *= self.__class__.constants.remedy_discard_boost

        self.debug(
            "Team %d protection odds %r v. %s, based on %d safety %d remedy %d discards.",
            team.number, odds, Cards.cardToString(attack),
            self.cardsUnseen[safety], self.cardsUnseen[remedy], remedyDiscards)
        return odds
Пример #4
0
  def chanceOpponentHasProtection(self, team, attack):
    # Chance that a particular opponent has protection from a particular attack in their hand.
    safety = Cards.attackToSafety(attack)
    remedy = Cards.attackToRemedy(attack)

    if safety in team.safeties:
      self.debug("Team %d has already played safety v. %s",
                 team.number,
                 Cards.cardToString(attack))
      return 1.0

    # Odds based on number of the card lurking out there somewhere.
    odds = self.percentOfCardsRemaining(safety, remedy)

    # Boost likelihood by 50% for each remedy they've discarded.
    remedyDiscards = 0
    for player in team.playerNumbers:
      for _ in xrange(self.interestingRemedyDiscardsByPlayer[player][remedy]):
        remedyDiscards += 1
        odds *= self.__class__.constants.remedy_discard_boost

    self.debug("Team %d protection odds %r v. %s, based on %d safety %d remedy %d discards.",
               team.number,
               odds,
               Cards.cardToString(attack),
               self.cardsUnseen[safety],
               self.cardsUnseen[remedy],
               remedyDiscards)
    return odds
Пример #5
0
 def getDangerCards(self, gameState):
   #Danger cards are cards that can actually stop us
   dangerCards = 0
   if len(gameState.us.safeties) == 4:
     #If we have all of the safeties, there are no danger cards.
     return 0
   else:
     for card in self.cardsLeft:
       if Cards.cardToType(card) == Cards.ATTACK and Cards.attackToSafety(card) not in gameState.us.safeties and card != Cards.ATTACK_SPEED_LIMIT:
       #For each card that's an attack and we don't have the safety
         if Cards.attackToRemedy(card) not in gameState.hand:
           #If the remedy isn't in our hand, all cards are danger cards
           dangerCards+=self.cardsLeft[card]
         elif gameState.hand.count(Cards.attackToRemedy(card)) < self.cardsLeft[card]:
           #If The number of remedies we have is less than the number of attacks out there
           dangerCards+=(self.cardsLeft[card] - gameState.hand.count(Cards.attackToRemedy(card)))
Пример #6
0
    def cardValue(self, card, cardIdx, cards):
        # cardIdx and cards let us disambiguate between two equal cards in our hand.
        #
        # All equally worthless:
        # * Safeties in play or elsewhere in our hand
        # * Remedies for safeties in play or elsewhere in our hand
        # * 200mi if we've already maxed out
        # * Mileage > distance remaining (assuming extension will be played)
        #   TODO: ...but what if an extension *won't* be played?
        # * Safeties we have in our hand

        # How many of this card do we already have in our hand?
        numDuplicates = len([c for c in cards if c == card])
        # Make this card less valuable if we have more if it on our hand.
        # The obvious thing to do is a straight divisor:
        # If we have 2 dupes, value each at 1/2; if 3, value each at 1/3.
        # But that's too severe, having 2 200km is more valuable than having
        # 1 200km!  So, we take the "duplicate fraction", 1/numDuplicates,
        # and we want to scale down *the inverse of that* to bring it
        # nearer to 1 (to reduce the severity of the penalty), and then
        # invert again to cancel out the inversion.
        dupeFrac = 1 / numDuplicates
        dupePenaltyFactor = self.__class__.constants.dupe_penalty_factor
        dupeCoefficient = 1 - (1 - dupeFrac) / dupePenaltyFactor

        cardType = Cards.cardToType(card)
        if cardType == Cards.MILEAGE:
            mileage = Cards.cardToMileage(card)
            mileageRemaining = 1000 - self.gameState.us.mileage
            if mileage > mileageRemaining:
                return 0.0 * dupeCoefficient
            elif mileage == 200 and self.gameState.us.twoHundredsPlayed >= 2:
                return 0.0 * dupeCoefficient
            elif mileage == 25 and cards.index(card) == cardIdx:
                # Try to hold onto a single 25km card in case we need it to finish.
                return 1.0 * dupeCoefficient
            else:
                return self.mileageCardValue(card) * dupeCoefficient
        elif cardType == Cards.REMEDY:

            # Attacks that could necessitate this card.
            if card == Cards.REMEDY_GO:
                relevantAttacks = Cards.ATTACKS[:]
            else:
                relevantAttacks = [Cards.remedyToAttack(card)]

            relevantAttacks = [
                c for c in relevantAttacks
                if not Cards.attackToSafety(c) in self.gameState.us.safeties +
                cards
            ]

            # Factor in:
            # 1. Value of taking a turn.
            # 2. Likelihood of getting hit with a relevant attack.
            #    (Number of attacks remaining / number of teams in game.)
            #    NB: If no attacks are relevant, this will be 0.
            # TODO: Also add likelihood of drawing another remedy (or the safety.)
            turnPoints = self.expectedTurnPoints(self.gameState.us)
            turnPointValue = self.valueOfPoints(turnPoints, self.gameState.us)
            # Hold onto at least one of the card if we already know we need it!
            if (cards.index(card) == cardIdx and
                ((not self.gameState.us.moving and card == Cards.REMEDY_GO) or
                 (self.gameState.us.speedLimit
                  and card == Cards.REMEDY_END_OF_LIMIT) or
                 (self.gameState.us.needRemedy
                  and card == self.gameState.us.needRemedy))):
                self.debug("We need %s, attack on us odds == 1.0!",
                           Cards.cardToString(card))
                attackOnUsOdds = 1.0
            else:
                attackOdds = self.percentOfCardsRemaining(
                    *relevantAttacks) * self.expectedTurnsLeft()
                attackOnUsOdds = attackOdds / (len(self.gameState.opponents) +
                                               1)
            value = turnPointValue * attackOnUsOdds
            self.debug(
                "Card %s val: %r = %r * %r = val(%d) * pctLeft(%s)/#teams",
                Cards.cardToString(card), value, turnPointValue,
                attackOnUsOdds, turnPoints,
                ",".join([Cards.cardToString(c) for c in relevantAttacks]))
            return value * dupeCoefficient
        elif cardType == Cards.SAFETY:
            # Never discard a safety!
            # (Assuming default deck composition of 1 of each type...)
            return 1.0 * dupeCoefficient
        elif cardType == Cards.ATTACK:
            safety = Cards.attackToSafety(card)
            remedy = Cards.attackToRemedy(card)

            valuesPerTarget = []
            for target in self.gameState.opponents:
                valuesPerTarget.append(
                    (1 - self.chanceOpponentHasProtection(target, card)) *
                    (1 - self.percentOfCardsRemaining(safety, remedy)) *
                    self.valueOfPoints(self.expectedTurnPoints(target),
                                       target))
            return sum(valuesPerTarget) / len(
                valuesPerTarget) * dupeCoefficient
        else:
            raise Exception("Unknown card type for %r: %r" % (card, cardType))
Пример #7
0
    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)
Пример #8
0
  def cardValue(self, card, cardIdx, cards):
    # cardIdx and cards let us disambiguate between two equal cards in our hand.
    #
    # All equally worthless:
    # * Safeties in play or elsewhere in our hand
    # * Remedies for safeties in play or elsewhere in our hand
    # * 200mi if we've already maxed out
    # * Mileage > distance remaining (assuming extension will be played)
    #   TODO: ...but what if an extension *won't* be played?
    # * Safeties we have in our hand

    # How many of this card do we already have in our hand?
    numDuplicates = len([c for c in cards if c == card])
    # Make this card less valuable if we have more if it on our hand.
    # The obvious thing to do is a straight divisor:
    # If we have 2 dupes, value each at 1/2; if 3, value each at 1/3.
    # But that's too severe, having 2 200km is more valuable than having
    # 1 200km!  So, we take the "duplicate fraction", 1/numDuplicates,
    # and we want to scale down *the inverse of that* to bring it
    # nearer to 1 (to reduce the severity of the penalty), and then
    # invert again to cancel out the inversion.
    dupeFrac = 1/numDuplicates
    dupePenaltyFactor = self.__class__.constants.dupe_penalty_factor
    dupeCoefficient = 1-(1-dupeFrac)/dupePenaltyFactor

    cardType = Cards.cardToType(card)
    if cardType == Cards.MILEAGE:
      mileage = Cards.cardToMileage(card)
      mileageRemaining = 1000 - self.gameState.us.mileage
      if mileage > mileageRemaining:
        return 0.0 * dupeCoefficient
      elif mileage == 200 and self.gameState.us.twoHundredsPlayed >= 2:
        return 0.0 * dupeCoefficient
      elif mileage == 25 and cards.index(card) == cardIdx:
        # Try to hold onto a single 25km card in case we need it to finish.
        return 1.0 * dupeCoefficient
      else:
        return self.mileageCardValue(card) * dupeCoefficient
    elif cardType == Cards.REMEDY:
      
      # Attacks that could necessitate this card.
      if card == Cards.REMEDY_GO:
        relevantAttacks = Cards.ATTACKS[:]
      else:
        relevantAttacks = [Cards.remedyToAttack(card)]

      relevantAttacks = [c for c in relevantAttacks
                         if not Cards.attackToSafety(c) in self.gameState.us.safeties + cards]

      # Factor in:
      # 1. Value of taking a turn.
      # 2. Likelihood of getting hit with a relevant attack.
      #    (Number of attacks remaining / number of teams in game.)
      #    NB: If no attacks are relevant, this will be 0.
      # TODO: Also add likelihood of drawing another remedy (or the safety.)
      turnPoints = self.expectedTurnPoints(self.gameState.us)
      turnPointValue = self.valueOfPoints(turnPoints, self.gameState.us)
      # Hold onto at least one of the card if we already know we need it!
      if (cards.index(card) == cardIdx and
          ((not self.gameState.us.moving and card == Cards.REMEDY_GO) or
           (self.gameState.us.speedLimit and card == Cards.REMEDY_END_OF_LIMIT) or
           (self.gameState.us.needRemedy and card == self.gameState.us.needRemedy))):
        self.debug("We need %s, attack on us odds == 1.0!", Cards.cardToString(card))
        attackOnUsOdds = 1.0
      else:
        attackOdds = self.percentOfCardsRemaining(*relevantAttacks) * self.expectedTurnsLeft()
        attackOnUsOdds = attackOdds / (len(self.gameState.opponents) + 1)
      value = turnPointValue * attackOnUsOdds
      self.debug("Card %s val: %r = %r * %r = val(%d) * pctLeft(%s)/#teams",
                 Cards.cardToString(card),
                 value,
                 turnPointValue, attackOnUsOdds,
                 turnPoints,
                 ",".join([Cards.cardToString(c) for c in relevantAttacks]))
      return value * dupeCoefficient
    elif cardType == Cards.SAFETY:
      # Never discard a safety!
      # (Assuming default deck composition of 1 of each type...)
      return 1.0 * dupeCoefficient
    elif cardType == Cards.ATTACK:
      safety = Cards.attackToSafety(card)
      remedy = Cards.attackToRemedy(card)

      valuesPerTarget = []
      for target in self.gameState.opponents:
        valuesPerTarget.append(
          (1-self.chanceOpponentHasProtection(target, card)) *
          (1-self.percentOfCardsRemaining(safety, remedy)) *
          self.valueOfPoints(self.expectedTurnPoints(target), target))
      return sum(valuesPerTarget)/len(valuesPerTarget) * dupeCoefficient
    else:
      raise Exception("Unknown card type for %r: %r" % (card, cardType))
Пример #9
0
  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]
Пример #10
0
  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)