async def doPhaseActions(game): currPhase = game.currPhase activePlayer = game.activePlayer print(currPhase) if currPhase == Turn.UNTAP: phaseIn(game, activePlayer) # checkSBA(game) untapAll(game, activePlayer) elif currPhase == Turn.UPKEEP: phaseIn(game, activePlayer) elif currPhase == Turn.DRAW: evaluate(game, drawCard, player=activePlayer) elif currPhase == Turn.DECLARE_ATTACKS: await chooseAttackers(game, activePlayer) elif currPhase == Turn.DECLARE_BLOCKS: for player in game.getOpponents(activePlayer): if player.isDefending: await chooseBlockers(game, player) elif currPhase == Turn.FIRST_COMBAT_DAMAGE: resolveCombatMatrix_FS(game) elif currPhase == Turn.SECOND_COMBAT_DAMAGE: resolveCombatMatrix(game) elif currPhase == Turn.CLEANUP: discardToHandSize(game, activePlayer) removeAllDamage(game)
def setLife(game, source, player, newTotal): """Sets the life total of the selected player to the specified amount Args: game (Game): Game Object source (Object): Source that is setting the player's life total player (Player): Player is having their life total set newTotal (Int): New life total Returns: None """ if player.getLife() == newTotal: pass elif (player.getLife() > newTotal): evaluate(game, loseLife, source=source, player=player, amountToLose=(player.getLife() - newTotal)) else: evaluate(game, gainLife, source=source, player=player, amountToGain=(newTotal - player.getLife()))
async def doAction(self, player): # waits for the player to make a choice player.passed = False player.chosenAction = None self.waitingOn = player while True: player.chosenAction = None while player.chosenAction == None and player.passed == False: await asyncio.sleep(0) if player.passed: self.notify("Lose Priority", { "selfID": self.gameID }, player) break else: self.passedInSuccession = False choice = player.chosenAction if choice[0] == 'C': card = self.allCards[choice] if card.hasType(Type.LAND): evaluate(self, playLand, card=card, player=player) else: await self.declareCast(self, choice, player) elif choice[0] == 'A': await self.declareActivation(self, choice)
def declareBlockers(game, declaredBlocks): """Used to check if all chosen blocks are legal Args: game (Game): Game state object listOfBlockers (List): List of tuples of type Card * Card where Card 1 is the declared blocker and Card 2 is the Card being blocked. Returns: True if all declared blocks are legal. False if any declared blocks are illegal. """ for blocker in declaredBlocks: for blocked in declaredBlocks[blocker]: if not isLegal(block, blocker, blocked): return False for blocker in declaredBlocks: blocker.isBlocking = True for blocked in declaredBlocks[blocker]: blocker.blocking.append(blocked) for blocker in declaredBlocks: for blocked in declaredBlocks[blocker]: evaluate(game, block, blocker=blocker, blocked=blocked) return True
def destroy(game, source, card): """Destroy target card. Args: game (Game): Game Object source (Object): Spell or Ability source target (Card): Card to destroy Returns: None """ evaluate(game, moveToZone, card=card, newZoneName=Zone.GRAVE)
def untapAll(game, activePlayer): """Untap all cards controlled by the active player during the Untap step Args: game (Game): Game Object activePlayer (Player): Player whose cards need to be untapped Returns: None """ for card in activePlayer.getField(): evaluate(game, untap, card=card)
async def declareActivation(self, abilityID): ability = self.GAT[abilityID] result = Effect() result.effect = ability.effect.copy() result.sourceAbility = ability result.sourceCard = ability.source result.cost = addCosts(self, ability, ability.cost[0], ability.cost[1]) result.rulesText = ability.rulesText if result.cost.canBePaid(self, result.sourceCard.controller): if await result.cost.pay(self, result.sourceCard.controller): evaluate(self, activateAbility, effect=result)
def tapCards(game, cardsToTap): """Tap multiple cards Args: game (Game): Game Object cardsToTap (List(Card)): Cards to tap Returns: None """ for card in cardsToTap: evaluate(game, tap, card=card)
def drawCard(game, player): """Selected player draws a card Args: game (Game): Game Object player (Player): Player that needs more gas Returns: None """ card = player.getTopOfDeck() evaluate(game, moveToZone, card=card, newZoneName=Zone.HAND)
def mill(game, player): """Selected player mills one card Args: game (Game): Game Object player (Player): Player that is milling cards Returns: None """ card = player.getTopOfDeck() evaluate(game, moveToZone, card=card, newZoneName=Zone.GRAVE)
def sacrifice(game, source, card): """Sacrifices the target card. Args: game (Game): Game Object source (Object): Spell or Ability source target (Card): Card to sacrifice Returns: None """ evaluate(game, dies, card=card) evaluate(game, moveToZone, card=card, newZoneName=Zone.GRAVE)
def millCards(game, player, numToMill): """Mill multiple cards Args: game (Game): Game Object player (Player): Player that is milling cards numToDraw (Int): Number of cards to mill Returns: None """ for _ in range(numToMill): evaluate(game, mill, player=player)
def drawCards(game, player, numToDraw): """Draw multiple cards Args: game (Game): Game Object player (Player): Player that is drawing the cards numToDraw (Int): Number of cards to draw Returns: None """ for _ in range(numToDraw): evaluate(game, drawCard, player=player)
def dealCombatDamage(game, source, target, amountToDeal): """Deals combat damage to the target. Args: game (Game): Game state object source (Card): Card that is dealing the combat damage target (Card or Player): Target that is taking the damage amountToDeal (Int): Amount of combat damage to deal to target """ evaluate(game, dealDamage, source=source, target=target, amountToDeal=amountToDeal)
def resolveCombatMatrix(game): matrix = game.COMBAT_MATRIX for attacker in matrix: blockers = game.COMBAT_MATRIX[attacker]["Blockers"] defender = game.COMBAT_MATRIX[attacker]["Defender"] if blockers != []: if not attacker.hasKeyword( Keyword.FIRST_STRIKE) or attacker.hasKeyword( Keyword.DOUBLE_STRIKE): for blocker in blockers: damageToDeal = assignDamage(game, attacker, blocker) if damageToDeal > 0: evaluate(game, dealCombatDamage, source=attacker, target=blocker, amountToDeal=damageToDeal) for blocker in blockers: if not blocker.hasKeyword( Keyword.FIRST_STRIKE) or attacker.hasKeyword( Keyword.DOUBLE_STRIKE): evaluate(game, dealCombatDamage, source=blocker, target=attacker, amountToDeal=calculatePossibleDamage( game, blocker)) if game.COMBAT_MATRIX[attacker][ "Assignable Damage"] > 0 and attacker.hasKeyword( Keyword.TRAMPLE) and ( not blocker.hasKeyword(Keyword.FIRST_STRIKE) or attacker.hasKeyword(Keyword.DOUBLE_STRIKE)): evaluate(game, dealCombatDamage, source=attacker, target=defender, amountToDeal=game.COMBAT_MATRIX[attacker] ["Assignable Damage"]) elif not attacker.hasKeyword( Keyword.FIRST_STRIKE) or attacker.hasKeyword( Keyword.DOUBLE_STRIKE): evaluate( game, dealCombatDamage, source=attacker, target=defender, amountToDeal=game.COMBAT_MATRIX[attacker]["Assignable Damage"]) game.COMBAT_MATRIX = {}
async def run(self): self.currPhase = Turn.UNTAP self.activePlayer = self.players[0] # Use try except to check for win while not self.won: # main selfplay loop await doPhaseActions(self) self.passedInSuccession = False while not self.passedInSuccession and (self.currPhase != Turn.UNTAP or (self.currPhase == Turn.CLEANUP and self.zones[Zone.STACK] != [])): self.passedInSuccession = True for player in self.getRelativePlayerList(self.activePlayer): # checkSBA(self) givePriority(self, player) await self.doAction(self, player) if self.zones[Zone.STACK] != []: self.pop() else: evaluate(self, goToNextPhase)
def destroyCards(game, source, cardsToBeDestroyed): """Destroy multiple cards. Args: game (Game): Game Object source (Object): Spell or Ability source target (Card): Cards to destroy Returns: None """ legalCards = set() for card in cardsToBeDestroyed: if isLegal(game, destroy, source, card) and not isReplaced(game, destroy, source, card): legalCards.add(card) for card in legalCards: evaluate(game, dies, card=card) for card in legalCards: evaluate(game, destroy, source=source, card=card)
def dealDamage(game, source, target, amountToDeal): """Deals Damage to target depending in its type Args: game (Game): Game state object source (Card): Creature source that is dealing the damage target (Object): Creature or Player that is being dealt damage amountToDeal (int): The amount of damage to deal to the target Returns: None. """ if isinstance(target, Card): markDamage(game, source, target, amountToDeal) elif isinstance(target, Player): evaluate(game, loseLife, source=source, player=target, amountToLose=amountToDeal)
def declareAttackers(game, declaredAttacks): """Used to check if all chosen attacks are legal. If attacks are legal, set all the attackers as attacking and sets the declared defender. Tiggers "on attack" abilities Args: game (Game): Game state object listOfAttackers (List): List of tuples of type Card * Object, where Card is the declared attacker and Object is the defending Object. Returns: True if all declared attacks are legal. False if any declared attacks are illegal. """ # Check if the declared attacks are legal for attacker in declaredAttacks: defender = declaredAttacks[attacker] if not isLegal(game, attack, attacker, defender): return False # Set combat statuses for creatures and players for attacker in declaredAttacks: defender = declaredAttacks[attacker] if isinstance(defender, Player): defender.isDefending = True else: defender.getController().isDefending = True attacker.isAttacking = True attacker.attacking = defender for attacker in declaredAttacks: defender = declaredAttacks[attacker] evaluate(game, attack, attacker=attacker, defender=defender) return True
def resolve(self, obj): if isinstance(obj, Card) and obj.isPermanent: evaluate(self, moveToZone, obj, Zone.FIELD, None) else: for effect in obj.effect: evaluate(self, **effect)
async def pay(self, game, player): convertStringToColorEnum = { "Color.WHITE": Color.WHITE, "Color.BLUE": Color.BLUE, "Color.BLACK": Color.BLACK, "Color.RED": Color.RED, "Color.GREEN": Color.GREEN, "Color.COLORLESS": Color.COLORLESS } if self.manaCost != {}: while True: player.answer = None game.notify( "Pay Mana", { "gameID": game.gameID, "status": "PAYING_MANA", "cost": { "ManaType.GENERIC": self.manaCost[ManaType.GENERIC] if ManaType.GENERIC in self.manaCost else 0, "ManaType.WHITE": self.manaCost[ManaType.WHITE] if ManaType.WHITE in self.manaCost else 0, "ManaType.BLUE": self.manaCost[ManaType.BLUE] if ManaType.BLUE in self.manaCost else 0, "ManaType.BLACK": self.manaCost[ManaType.BLACK] if ManaType.BLACK in self.manaCost else 0, "ManaType.RED": self.manaCost[ManaType.RED] if ManaType.RED in self.manaCost else 0, "ManaType.GREEN": self.manaCost[ManaType.GREEN] if ManaType.GREEN in self.manaCost else 0, "ManaType.COLORLESS": self.manaCost[ManaType.COLORLESS] if ManaType.COLORLESS in self.manaCost else 0, } }, player) while player.answer == None: await asyncio.sleep(0) # TODO: implement validation of payment for color in player.answer: if player.answer[color] > 0: evaluate(game, removeMana, player, convertStringToColorEnum[color], player.answer[color]) break if self.additional != []: for cost in self.additional: evaluate(game, **cost) return True
async def declareCast(self, instanceID, player): card = self.getCard(instanceID) allEffects = card.effects.copy() # copy of all effects on the card chosenMainCost = {} chosenAddedCosts = [] chosenEffects = [] # chosen effects effectIndexesAdded = [] # indexes of the effects added from allEffects effectTypes = set() # Effect result = Effect() result.sourceCard = card # # Used for modal spells # if card.isModal: # effectIndexesAdded.append(0) # num = card.maxNumOfChoices # effectList = [] # for effect in allEffects[0]: # if card.repeatableChoice: # for _ in range(num): # effectList.append(effect) # else: # effectList.append(effect) # chosenEffects.append( # choose(self, effectList, player, InquiryType.MODAL, effectList.append(effect))) # # Chose what x should be # if Keyword.DECLARE_VAR in card.specialTypes: # card.property["X"] = choose( # self, None, player, InquiryType.VARIABLE, 1) # Choose main cost or alt cost if applicable and add their respective effect if len(card.alternativeCosts) > 0: pass else: chosenMainCost = card.manaCost # Add chosen additional costs and their respective effects if len(card.additionalCosts) > 0: pass # Add all chosen effect to result.effect for effect in chosenEffects: result.addEffect(effect[1]) # Add all the rules text for the chosen effects to result.rulesText for effect in chosenEffects: result.rulesText += " " + effect[0] # Instantiate a cost object with the chosen costs and set it in result.cost card.cost = addCosts(self, card, chosenMainCost, chosenAddedCosts) # Add cost types to card properties like Flashback for costType in effectTypes: card.property[costType] = True # Evaluate cast if card.cost.canBePaid(self, card.controller): if await card.cost.pay(self, card.controller): evaluate(self, cast, card=card)
def goToNextPhase(game): currPhase = game.currPhase activePlayer = game.activePlayer nextPhase = { Turn.UNTAP: Turn.UPKEEP, Turn.UPKEEP: Turn.DRAW, Turn.DRAW: Turn.FIRST_MAIN, Turn.FIRST_MAIN: Turn.BEGIN_COMBAT, Turn.BEGIN_COMBAT: Turn.DECLARE_ATTACKS, Turn.DECLARE_ATTACKS: Turn.DECLARE_BLOCKS, Turn.DECLARE_BLOCKS: Turn.FIRST_COMBAT_DAMAGE, Turn.FIRST_COMBAT_DAMAGE: Turn.SECOND_COMBAT_DAMAGE, Turn.SECOND_COMBAT_DAMAGE: Turn.END_COMBAT, Turn.END_COMBAT: Turn.SECOND_MAIN, Turn.SECOND_MAIN: Turn.BEGIN_END, Turn.BEGIN_END: Turn.CLEANUP } evaluate(game, endPhase, activePlayer=activePlayer, phase=currPhase) if currPhase == Turn.CLEANUP and Turn.EXTRA in activePlayer.property and activePlayer.property[ Turn.EXTRA] > 0: activePlayer.property[Turn.EXTRA] -= 1 evaluate(game, beginPhase, activePlayer=activePlayer, phase=Turn.UNTAP) elif currPhase == Turn.CLEANUP: nextPlayer = game.getNextPlayer(activePlayer) evaluate(game, beginPhase, activePlayer=nextPlayer, phase=Turn.UNTAP) elif currPhase in activePlayer.property and activePlayer.property[ currPhase] > 0: activePlayer.property[currPhase] -= 1 evaluate(game, beginPhase, activePlayer=activePlayer, phase=currPhase) elif currPhase == Turn.DECLARE_ATTACKS and game.COMBAT_MATRIX == {}: evaluate(game, beginPhase, activePlayer=activePlayer, phase=Turn.END_COMBAT) else: evaluate(game, beginPhase, activePlayer=activePlayer, phase=nextPhase[currPhase]) # pylint: disable=unsubscriptable-object
def cast(game, card): evaluate(game, moveToZone, card=card, newZoneName=Zone.STACK, IndexToInsert=0)
def playLand(game, card, player): evaluate(game, moveToZone, card=card, newZoneName=Zone.FIELD)