예제 #1
0
class PlayerState:
    num = 0
    foodbag = 0
    species = []
    hand = []
    """ 
		Internal representation of a json player
		@param num: Nat+ representing this player's unique ID number
		@param foodbag: Nat representing this player's foodbag
		@param species: A List of this player's Species boards
		@param hand: A List of TraitCards in this player's hand (not on boards/haven't been traded in)
		@param player: the external player with strategic functionality
		Nat, Nat, ListOf(Species), ListOf(TraitCard), Player -> PlayerState
	"""
    def __init__(self, id, bag, speciesList, cards, player=None):
        self.num = id
        self.foodbag = bag
        self.species = speciesList
        self.hand = cards
        self.player = SillyPlayer()

    """ 
		override equality
		Any -> Boolean
	"""

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.__dict__ == other.__dict__
        else:
            return False

    """ 
		override inequality
		Any -> Boolean
	"""

    def __ne__(self, other):
        return not self.__eq__(other)

    """
		start a game (step 1) -- if given a new species, add it, and inform external player of current state
		@param spec: an OptSpecies (given if this player didn't already have a species; otherwise False)
		OptSpecies -> Void
	"""

    def start(self, spec):
        if spec is not False:
            self.species.append(spec)
        self.player.start(self)

    """
		Do post-turn cleanup:
		- square up species' populations with what they ate
		- remove starving species
		- move food from species boards to player foodbags
		@return the number of species we removed for being extinct (so that dealer can give us cards)
		Void -> Nat
	"""

    def endOfTurn(self):
        numExtinct = 0
        speciesToRemove = []
        for specIdx in range(len(self.species)):
            self.foodbag += self.species[specIdx].cullStarving()
            if self.isExtinct(specIdx):
                speciesToRemove.append(specIdx)
                numExtinct += 1

        self.species = [
            self.species[i] for i in range(len(self.species))
            if i not in speciesToRemove
        ]
        return numExtinct

    """
		Check if an action from our external player would be a cheating move
		Following conditions must pass:
			- All card indexes acted on must exist in our hand and be unique 
			- The highest species index referenced should exist after addition of new species boards
			- Should only be able to replace trait cards that already exist (ie no adding of new trait cards)
		@param action: the action the external player wishes to take
		@return the action or, if it was cheating, False
		Action4 -> Action4 or False
	"""

    def checkCheatAction(self, action):
        if (self.checkLegalCards(action.getAllCardIdcs())
                and self.checkLegalSpecies(action.getAllSpecIdcs(),
                                           len(action.BT))
                and self.checkTraitReplacement(action.RT, action.BT)):
            return action
        else:
            return False

    """
		Check that all species indexes passed correspond to species that
		the player will own by the end of the action
		Used to check for cheating in Action4
		@param specIdcs: indexes of the species the external player asked to modify
		@param numBT: the number of BuyTrait actions being requested
		@return True if moves are legal, False otherwise
		ListOf(Nat) -> Boolean
	"""

    def checkLegalSpecies(self, specIdcs, numBT):
        return not ((max(specIdcs) >
                     (numBT + len(self.species) - 1)) or (min(specIdcs) < 0))

    """
		Check that trait replacement uses legal cards and tries to replace existing traits
		Used to check for cheating in Action4
		@param RTs: the ReplaceTrait actions to check
		@param BTs: the BuySpeciesBoards
		@return True if moves are legal, False otherwise
		ListOf(ReplaceTrait) -> Boolean
	"""

    def checkTraitReplacement(self, RTs, BTs):
        for rt in RTs:
            if (rt.oldTraitIdx < 0) or (rt.specIdx >
                                        (len(self.species) + len(BTs) - 1)):
                return False

            traitLen = False
            if rt.specIdx < len(self.species):
                traitLen = len(self.species[rt.specIdx].traits)
            elif rt.specIdx < (len(self.species) + len(BTs)):
                traitLen = len(BTs[rt.specIdx - len(self.species) -
                                   1].traitList)

            if traitLen is False or rt.oldTraitIdx >= traitLen:
                return False

        return True

    """
		Check that the card indexes passed all correspond to cards we own
		and that each index is unique
		Used to check for cheating in an Action4
		@param cardIdcs: the indexes of the cards the external player asked to use
		@return True if the moves are legal, False otherwise
		ListOf(Nat) -> Boolean
	"""

    def checkLegalCards(self, cardIdcs):
        if len(cardIdcs) != len(set(cardIdcs)):  # no duplicates
            return False
        if (max(cardIdcs) >
            (len(self.hand) -
             1)) or min(cardIdcs) < 0:  # only use cards we have
            return False
        return True

    """
		Choose actions for watering hole tributes and trading and check for cheating
		assumption: list of players is ordered such that players before this one in the list
					play before us, and those after this one in the list play after us
		@param allPlayers: the states of all players currently in the game
		@return an Action4 from the external player or, if that move was cheating, False
		ListOf(PlayerState) -> Action4 or False
	"""

    def choose(self, allPlayers):
        for i in range(len(allPlayers)):
            if allPlayers[i] is self:
                splitIdx = i
        befores = [play.species for play in allPlayers[:splitIdx]]
        afters = [play.species for play in allPlayers[splitIdx + 1:]]

        return self.checkCheatAction(self.player.choose(befores, afters))

    """
		create dictionary 
		None -> Dict
	"""

    def toDict(self):
        return {
            "num": self.num,
            "species": [species.toDict() for species in self.species],
            "hand": [card.toDict() for card in self.hand],
            "foodbag": self.foodbag
        }

    """
		Display the essence of a player
		Void -> Void
	"""

    def display(self):
        Drawing(player=self.toDict())

    """ 
	   creates a json array of a PlayerState object
	   Void -> JsonArray
	"""

    def playerStateToJson(self):
        species = [Species.speciesToJson(animal) for animal in self.species]
        cards = [TraitCard.traitCardToJson(card) for card in self.hand]

        result = [["id", self.num], ["species", species],
                  ["bag", self.foodbag]]

        if cards:
            result.append(["cards", cards])

        return result

    """
	   creates a PlayerState from a json array
	   JsonArray -> PlayerState
	"""

    @staticmethod
    def playerStateFromJson(state):
        try:
            cards = []
            if state[0][0] == "id":
                num = state[0][1]
            if state[1][0] == "species":
                speciesList = [
                    Species.speciesFromJson(species) for species in state[1][1]
                ]
            if state[2][0] == "bag":
                bag = state[2][1]
            if len(state) == 4 and state[3][0] == "cards":
                cards = [
                    TraitCard.traitCardFromJson(card) for card in state[3][1]
                ]
            if num > 0 and bag >= 0:
                return PlayerState(num, bag, speciesList, cards)

        except Exception as e:
            print state
            raise e
            #quit()

    """
		Proxy to call the feed method in the external player
		@param wateringHole: the amount of food that can be eaten
		@param players: all the players in this round
		@return FeedingAction -One of:
			False - no feeding at this time
			Nat - index of Species fed
			[Nat, Nat] - index of fat-tissue Species fed, amount of fatFood
			[Nat, Nat, Nat] - index of carnivore, index of player to attack, index of species to attack
		Nat, ListOf(PlayerState) -> FeedingAction
	"""

    def feed(self, wateringHole, player):
        return self.player.feed(self, wateringHole, player)

    """
		Get this player's score -- a combo of their foodbag, total population 
		for all species, and total trait cards placed on species
		@return the player's score based on the above
		Void -> Nat
	"""

    def getScore(self):
        score = self.foodbag
        for spec in self.species:
            score += spec.population
            score += len(spec.traits)

        return score

    """
		Number of cards this player needs dealt at the beginning of a turn
		Based on their number of species
		If they have no species, then they should ask for 1 card, as they'll be receiving a new species
		at the beginning of the round
		@return num cards needed
		Void -> Nat
	"""

    def numCardsNeeded(self):
        return 3 + max(1, len(self.species))

    """
		Filter out all fed species to get a list of species that can be fed
		@return a list of (Species' index, Species) for hungry species this player has
		Void -> ListOf((Nat, Species))
	"""

    def getHungrySpecies(self):
        hungry = []
        for i in range(len(self.species)):
            if self.species[i].isHungry():
                hungry.append((i, self.species[i]))

        return hungry

    """
		Gets the indexes of the species neighboring the given species
		@param speciesIdx: index of the species to get neighbors for
		Nat -> [OptSpecies, OptSpecies] where an OptSpecies is Species or False
	"""

    def getNeighbors(self, speciesIdx):
        if speciesIdx > 0:
            lNeighbor = self.species[speciesIdx - 1]
        else:
            lNeighbor = False

        if speciesIdx < len(self.species) - 1:
            rNeighbor = self.species[speciesIdx + 1]
        else:
            rNeighbor = False

        return [lNeighbor, rNeighbor]

    """
		Verify that this player can use their own chosen species to attack the other given species
		@param defPlayer: PlayerState of the defending player
		@param defIdx: index of the defending species
		@param attIdx: index of the attacking species: should be one of our own
		PlayerState, Nat, Nat -> Boolean
	"""

    def verifyAttack(self, defPlayer, attIdx, defIdx):
        left, right = defPlayer.getNeighbors(defIdx)
        return Species.isAttackable(defPlayer.species[defIdx],
                                    self.species[attIdx], left, right)

    """
		Tell whether or not the given species is extinct (i.e. that its population <= 0)
		@param specIdx: index of the species to check
		Nat -> Boolean
	"""

    def isExtinct(self, specIdx):
        return self.species[specIdx].isExtinct()

    """
		Add one population to a species at the given index
		@param specIdx: index of the species to modify
		Nat -> Void
	"""

    def addPopulation(self, specIdx):
        self.species[specIdx].addPopulation()

    """
		Add one body size to a species at the given index
		@param specIdx: index of the species to modify
		Nat -> Void
	"""

    def addBody(self, specIdx):
        self.species[specIdx].addBody()

    """
		Give this player a new species with the given traits
		@param traitIdcs: indices of the trait cards this new species will have, up to 3
		ListOf(Nat) -> Void
	"""

    def addSpecies(self, traitIdcs):
        self.species.append(
            Species(0, 0, 1, [self.hand[i] for i in traitIdcs], 0))

    """
		Remove the given species from this player's list of species
		@param specIdx: index of the species to remove
		Nat -> Void
	"""

    def removeSpecies(self, specIdx):
        del self.species[specIdx]

    """
		Tell whether or not this player has any species
		Void -> Boolean
	"""

    def hasNoSpecies(self):
        return not (len(self.species) > 0)

    """
		Deletes the cards at the given indices from this player's hand
		@param cardIdcs: the indexes of the cards to remove
		ListOf(Nat) -> Void
	"""

    def discardFromHand(self, cardIdcs):
        self.hand = [
            self.hand[i] for i in range(len(self.hand)) if i not in cardIdcs
        ]

    """
		Adds the given TraitCards to the end of this player's hand
		@param cards: the cards to add
		ListOf(TraitCard) -> Void 

	"""

    def addCards(self, cards):
        self.hand = cards + self.hand

    """
		Tell whether the species at the given index has the given trait
		@param specIdx: the index of the species to check
		@param traitName: the name of the trait to check for
		Nat, String -> Boolean
	"""

    def speciesHasTrait(self, specIdx, traitName):
        return self.species[specIdx].hasTrait(traitName)

    """
		Drop the population of the given species as in a carnivore attack
		If the species' food > its population, drop the food as well
		@param specIdx: the index of the species to have its population decremented
		Nat -> Void
	"""

    def executeAttack(self, specIdx):
        self.species[specIdx].executeAttack()

    """
		Feed the given species the given amount of fat food that it requested
		@param specIdx: index of the species to feed
		@param foodCount: amount of food to try to feed
		@return the amount of food this species was given
		Nat, Nat -> Nat
	"""

    def feedFatFood(self, specIdx, foodCount):
        spec = self.species[specIdx]
        if spec.hasTrait("fat-tissue"):
            foodCount = min(foodCount, spec.body - spec.fatFood)
            spec.eatFatFood(foodCount)
            return foodCount
        else:
            raise ValueError(
                "Tried to feed fat food to a species without fat tissue")

    """
		Feed a given species the amount of food that it requested
		@param specIdx: index of the species to feed
		@param foodCount: amount of food to try to feed
		@param wateringHole: the amount of food in the dealer's watering hole
		@return the total amount of food eaten during this feeding
		Nat, Nat, Nat -> Nat
	"""

    def feedSpecies(self, specIdx, foodCount, wateringHole):
        spec = self.species[specIdx]
        foodCount = min(wateringHole, foodCount, spec.population - spec.food)
        spec.eatFood(foodCount)
        wateringHole -= foodCount

        forageAmount = self.forage(specIdx, wateringHole)
        wateringHole -= forageAmount
        foodCount += forageAmount

        foodCount += self.cooperate(specIdx, foodCount, wateringHole)

        return foodCount

    # TODO: abstract everything. all of it
    """
		If the given species has the foraging trait, give it one more food
		@param specIdx: index of the species to feed
		@param wateringHole: the amount of food in the dealer's watering hole
		@return the amount of food this species ate
	"""

    def forage(self, specIdx, wateringHole):
        spec = self.species[specIdx]
        amountFed = 0
        if wateringHole > 0 and spec.hasTrait(
                "foraging") and spec.population > spec.food:
            spec.eatFood(1)
            amountFed += 1

        return amountFed

    """
		Cooperate
		@param specIdx: index of the species to feed
		@param foodCount: amount of food to try to feed
		@param wateringHole: the amount of food in the dealer's watering hole
		@return the amount of food all species in the cooperation chain ate
		Nat, Nat, Nat -> Nat
	"""

    def cooperate(self, specIdx, foodCount, wateringHole):
        spec = self.species[specIdx]
        amountFed = 0
        left, right = self.getNeighbors(specIdx)
        if spec.hasTrait("cooperation") and right is not False:
            for i in range(foodCount):
                if wateringHole > 0:
                    fedThisSpecies = self.feedSpecies(specIdx + 1, 1,
                                                      wateringHole)
                    amountFed += fedThisSpecies
                    wateringHole -= fedThisSpecies

        return amountFed

    """
		After a carnivore attack, have all species with the scavenge trait eat from left to right
		@param wateringHole: the amount of food that can be handed out amongst the scavengers
		@return the amount of food eaten amongst our scavengers
		Nat -> Nat
	"""

    def scavenge(self, wateringHole):
        amountFed = 0
        scavengers = self.getSpeciesWithTrait("scavenger")
        for scav in scavengers:
            if wateringHole > 0:
                fedThisSpecies = self.feedSpecies(scav, 1, wateringHole)
                amountFed += fedThisSpecies
                wateringHole -= fedThisSpecies
        return amountFed

    """
		Get all species this player owns that has the given trait 
		@param trait: the name of the trait to be checked
		String -> ListOf(SpecIdx)
	"""

    def getSpeciesWithTrait(self, trait):
        return [
            i for i in range(len(self.species))
            if self.speciesHasTrait(i, trait)
        ]

    """
		Give all fertile species this player owns one population
		Void -> Void
	"""

    def fertile(self):
        fertile = self.getSpeciesWithTrait("fertile")
        for spec in fertile:
            self.species[spec].addPopulation()

    """
		Give all long-necked species this player owns one food, as long as the watering hole
		is large enough to feed them
		@param wateringHole: the dealer's watering hole
		@return the amount of food eaten by the longnecks
		Nat -> Nat
	"""

    def longNeck(self, wateringHole):
        amountFed = 0
        longNeck = self.getSpeciesWithTrait("long-neck")
        for ln in longNeck:
            if wateringHole > 0:
                fedThisSpecies = self.feedSpecies(ln, 1, wateringHole)
                amountFed += fedThisSpecies
                wateringHole -= fedThisSpecies

        return amountFed

    """
		Transfer fat food that a species stored last turn to its actual food 
		Void -> Void
	"""

    def transferFatFood(self):
        fatTissue = self.getSpeciesWithTrait("fat-tissue")
        for ft in fatTissue:
            self.species[ft].transferFatFood()

    """
		Replace a TraitCard on the given species
		@param specIdx: the index of the species whose traits need switched out
		@param oldTraitIdx: the index of the old trait card
		@param newTraitIdx: the index of the trait card replacing the old one
		Nat, Nat, Nat -> Void
	"""

    def replaceTrait(self, specIdx, oldTraitIdx, newTraitIdx):
        self.species[specIdx].replaceTrait(oldTraitIdx, self.hand[newTraitIdx])
예제 #2
0
class PlayerState:
    num = 0
    foodbag = 0
    species = []
    hand = []
    """ 
		Internal representation of a json player
		@param num: ID number
		@param foodbag: yep
		@param species: this player's species boards
		@param hand: traitcards in this player's hand (not on boards/haven't been traded in)
		@param player: the external player with strategic functionality
		Nat, Nat, ListOf(Species), ListOf(TraitCard), Player -> PlayerState
	"""
    def __init__(self, id, bag, speciesList, cards, player=None):
        self.num = id
        self.foodbag = bag
        self.species = speciesList
        self.hand = cards
        self.player = SillyPlayer()

    """ 
		override equality
		Any -> Boolean
	"""

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.__dict__ == other.__dict__
        else:
            return False

    """ 
		override inequality
		Any -> Boolean
	"""

    def __ne__(self, other):
        return not self.__eq__(other)

    """
		create dictionary 
		None -> Dict
	"""

    def toDict(self):
        return {
            "num": self.num,
            "species": [species.toDict() for species in self.species],
            "hand": [card.toDict() for card in self.hand],
            "foodbag": self.foodbag
        }

    """
		Display the essence of a player
		Void -> Void
	"""

    def display(self):
        Drawing(player=self.toDict())

    """ 
	   creates a json array of a PlayerState object
	   Void -> JsonArray
	"""

    def playerStateToJson(self):
        species = [Species.speciesToJson(animal) for animal in self.species]
        cards = [TraitCard.traitCardToJson(card) for card in self.hand]

        result = [["id", self.num], ["species", species],
                  ["bag", self.foodbag]]

        if cards:
            result.append(["cards", cards])

        return result

    """
	   creates a PlayerState from a json array
	   JsonArray -> PlayerState
	"""

    @staticmethod
    def playerStateFromJson(state):
        try:
            cards = []
            if state[0][0] == "id":
                num = state[0][1]
            if state[1][0] == "species":
                speciesList = [
                    Species.speciesFromJson(species) for species in state[1][1]
                ]
            if state[2][0] == "bag":
                bag = state[2][1]
            if len(state) == 4 and state[3][0] == "cards":
                cards = [
                    TraitCard.traitCardFromJson(card) for card in state[3][1]
                ]
            if num > 0 and bag >= 0:
                return PlayerState(num, bag, speciesList, cards)

        except:
            quit()

    """
		Proxy to call the feed method in the external player
		@param wateringHole: the amount of food that can be eaten
		@param players: all the players in this round
		@return FeedingAction -One of:
			False - no feeding at this time
			Nat - index of Species fed
			[Nat, Nat] - index of fat-tissue Species fed, amount of fatFood
			[Nat, Nat, Nat] - index of carnivore, index of player to attack, index of species to attack
		Nat, ListOf(PlayerState) -> FeedingAction
	"""

    def feed(self, wateringHole, player):
        return self.player.feed(self, wateringHole, player)

    """
		Filter out all fed species to get a list of species that can be fed
		@return a list of (Species' index, Species) for hungry species this player has
		Void -> ListOf((Nat, Species))
	"""

    def getHungrySpecies(self):
        hungry = []
        for i in range(len(self.species)):
            if self.species[i].isHungry():
                hungry.append((i, self.species[i]))

        return hungry

    """
		Gets the indexes of the species neighboring the given species
		@param speciesIdx: index of the species to get neighbors for
		Nat -> [OptSpecies, OptSpecies] where an OptSpecies is Species or False
	"""

    def getNeighbors(self, speciesIdx):
        if speciesIdx > 0:
            lNeighbor = self.species[speciesIdx - 1]
        else:
            lNeighbor = False

        if speciesIdx < len(self.species) - 1:
            rNeighbor = self.species[speciesIdx + 1]
        else:
            rNeighbor = False

        return [lNeighbor, rNeighbor]

    """
		Verify that this player can use their own chosen species to attack the other given species
		@param defPlayer: PlayerState of the defending player
		@param defIdx: index of the defending species
		@param attIdx: index of the attacking species: should be one of our own
		PlayerState, Nat, Nat -> Boolean
	"""

    def verifyAttack(self, defPlayer, attIdx, defIdx):
        left, right = defPlayer.getNeighbors(defIdx)
        return Species.isAttackable(defPlayer.species[defIdx],
                                    self.species[attIdx], left, right)

    """
		Tell whether or not the given species is extinct (i.e. that its population <= 0)
		@param specIdx: index of the species to check
		Nat -> Boolean
	"""

    def isExtinct(self, specIdx):
        return self.species[specIdx].isExtinct()

    """
		Remove the given species from this player's list of species
		@param specIdx: index of the species to remove
		Nat -> Void
	"""

    def removeSpecies(self, specIdx):
        del self.species[specIdx]

    """
		Tell whether or not this player has any species
		Void -> Boolean
	"""

    def hasNoSpecies(self):
        return not (len(self.species) > 0)

    """
		Tell whether the species at the given index has the given trait
		@param specIdx: the index of the species to check
		@param traitName: the name of the trait to check for
		Nat, String -> Boolean
	"""

    def speciesHasTrait(self, specIdx, traitName):
        return self.species[specIdx].hasTrait(traitName)

    """
		Drop the population of the given species as in a carnivore attack
		If the species' food > its population, drop the food as well
		@param specIdx: the index of the species to have its population decremented
		Nat -> Void
	"""

    def executeAttack(self, specIdx):
        self.species[specIdx].executeAttack()

    """
		Feed the given species the given amount of fat food that it requested
		@param specIdx: index of the species to feed
		@param foodCount: amount of food to try to feed
		@return the amount of food this species was given
		Nat, Nat -> Nat
	"""

    def feedFatFood(self, specIdx, foodCount):
        spec = self.species[specIdx]
        if spec.hasTrait("fat-tissue"):
            foodCount = min(foodCount, spec.body - spec.fatFood)
            spec.eatFatFood(foodCount)
            return foodCount
        else:
            raise ValueError(
                "Tried to feed fat food to a species without fat tissue")

    """
		Feed a given species the amount of food that it requested
		@param specIdx: index of the species to feed
		@param foodCount: amount of food to try to feed
		@param wateringHole: the amount of food in the dealer's watering hole
		@return the total amount of food eaten during this feeding
		Nat, Nat, Nat -> Nat
	"""

    def feedSpecies(self, specIdx, foodCount, wateringHole):
        spec = self.species[specIdx]
        foodCount = min(wateringHole, foodCount, spec.population - spec.food)
        spec.eatFood(foodCount)
        wateringHole -= foodCount

        forageAmount = self.forage(specIdx, wateringHole)
        wateringHole -= forageAmount
        foodCount += forageAmount

        foodCount += self.cooperate(specIdx, foodCount, wateringHole)

        return foodCount

    """
		If the given species has the foraging trait, give it one more food
		@param specIdx: index of the species to feed
		@param wateringHole: the amount of food in the dealer's watering hole
		@return the amount of food this species ate
	"""

    def forage(self, specIdx, wateringHole):
        spec = self.species[specIdx]
        amountFed = 0
        if wateringHole > 0 and spec.hasTrait(
                "foraging") and spec.population > spec.food:
            spec.eatFood(1)
            amountFed += 1

        return amountFed

    """
		Cooperate
		@param specIdx: index of the species to feed
		@param foodCount: amount of food to try to feed
		@param wateringHole: the amount of food in the dealer's watering hole
		@return the amount of food all species in the cooperation chain ate
		Nat, Nat, Nat -> Nat
	"""

    def cooperate(self, specIdx, foodCount, wateringHole):
        spec = self.species[specIdx]
        amountFed = 0
        left, right = self.getNeighbors(specIdx)
        if spec.hasTrait("cooperation") and right is not False:
            for i in range(foodCount):
                if wateringHole > 0:
                    fedThisSpecies = self.feedSpecies(specIdx + 1, 1,
                                                      wateringHole)
                    amountFed += fedThisSpecies
                    wateringHole -= fedThisSpecies

        return amountFed

    """
		After a carnivore attack, have all species with the scavenge trait eat from left to right
		@param wateringHole: the amount of food that can be handed out amongst the scavengers
		@return the amount of food eaten amongst our scavengers
		Nat -> Nat
	"""

    def scavenge(self, wateringHole):
        amountFed = 0
        for i in range(len(self.species)):
            if self.speciesHasTrait(i, "scavenger"):
                fedThisSpecies = self.feedSpecies(i, 1, wateringHole)
                amountFed += fedThisSpecies
                wateringHole -= fedThisSpecies

        return amountFed