GithubHelp home page GithubHelp logo

jleclanche / fireplace Goto Github PK

View Code? Open in Web Editor NEW
660.0 660.0 145.0 10.85 MB

A Hearthstone simulator in Python

Home Page: https://hearthsim.info

License: GNU Affero General Public License v3.0

Python 100.00%
hearthstone hearthstone-simulator python

fireplace's People

Contributors

alebahn avatar amw2104 avatar astradamus avatar beheh avatar edk0 avatar idn2104 avatar jleclanche avatar laiqu avatar liujimj avatar manuel-delverme avatar meerkov avatar nightkev avatar oftc-ftw avatar rgascons avatar robert-nix avatar rxgottlieb avatar shinoi2 avatar smallnamespace avatar snowypowers avatar synap5e avatar tonyyyye avatar vudjun avatar zombie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fireplace's Issues

OWN_MINION_SUMMON() missing 1 required positional argument: 'minion'

Deck 1: ['EX1_508', 'CS2_226', 'CS2_142', 'EX1_011', 'CS1_042', 'EX1_011', 'CS2_197', 'EX1_399', 'CS2_173', 'CS2_023', 'CS1_042', 'CS2_122', 'CS2_120', 'DS1_055', 'CS2_162', 'CS2_187', 'CS2_120', 'CS2_141', 'DS1_055', 'CS2_032', 'CS2_127', 'CS2_029', 'CS2_173', 'EX1_399', 'EX1_066', 'EX1_508', 'CS2_026', 'CS2_122', 'CS2_171', 'CS2_187']

Deck 2: ['CS2_182', 'CS2_118', 'GVG_067', 'GVG_071', 'CS2_119', 'EX1_509', 'BRM_007', 'GVG_025', 'EX1_008', 'BRM_028', 'EX1_059', 'GVG_013', 'EX1_009', 'EX1_134', 'EX1_021', 'NEW1_004', 'GVG_084', 'GVG_013', 'EX1_563', 'EX1_080', 'GVG_103', 'NEW1_040', 'GVG_082', 'CS2_131', 'CS2_161', 'EX1_067', 'CS2_226', 'EX1_017', 'GVG_089', 'GVG_016']

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 530, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 500, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 145, in DoMove
    card.play()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 277, in play
    self.controller.play(self, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 212, in play
    self.game.action(PowSubType.PLAY, self, card, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 54, in action
    args[0]._play(*args[1:])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 230, in _play
    self.summon(card)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 208, in summon
    card.summon()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 593, in summon
    self.game.broadcast("MINION_SUMMON", self.controller, self)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 177, in broadcast
    super().broadcast(event, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\entity.py", line 24, in broadcast
    f(*args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 342, in MINION_SUMMON
    minion.controller.broadcast("OWN_MINION_SUMMON", minion)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 273, in broadcast
    super().broadcast(event, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\entity.py", line 24, in broadcast
    f(*args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 60, in <lambda>
    _func = lambda *args: func(self, *args)
TypeError: OWN_MINION_SUMMON() missing 1 required positional argument: 'minion'
INFO:root:two begins turn 12
INFO:root:two is now at 6 mana crystals
DEBUG:root:<Minion ('Cogmaster')> moves from <Zone.DECK: 2> to <Zone.HAND: 3>
INFO:root:two draws <Minion ('Cogmaster')>
INFO:root:<Minion ('Frostwolf Warlord')> attacks <Minion ('Raid Leader')>
INFO:root:<Minion ('Frostwolf Warlord')> hits <Minion ('Raid Leader')> for 6
INFO:root:<Minion ('Raid Leader')> damaged for 6 health
INFO:root:<Minion ('Raid Leader')> hits <Minion ('Frostwolf Warlord')> for 2
INFO:root:<Minion ('Frostwolf Warlord')> damaged for 2 health
INFO:root:<Minion ('Raid Leader')> dies
INFO:root:<Minion ('Raid Leader')> is removed from the field
INFO:root:Removing <fireplace.card.Aura object at 0x00000000065E4080> affecting [<Minion ('Darkscale Healer')>]
INFO:root:Destroying buff <Enchantment ('Enhanced')> from <Minion ('Darkscale Healer')>
INFO:root:<Minion ('Raid Leader')> healed for 6 health
DEBUG:root:<Minion ('Raid Leader')> moves from <Zone.PLAY: 1> to <Zone.GRAVEYARD: 4>
INFO:root:two plays <Minion ('One-eyed Cheat')> from their hand
DEBUG:root:two summons <Minion ('One-eyed Cheat')>
INFO:root:Summoning <Minion ('One-eyed Cheat')>
DEBUG:root:<Minion ('One-eyed Cheat')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>

issue with enrage mechanic

Hi, I am new to your code but I am having some issues getting the attack from enrages from showing up. For example, I was creating an Amani Berserker (CardID=EX1_393) for player1 and player2 and having one attack the other.

    game = prepare_game(WARLOCK, WARLOCK)
    amani1 = game.currentPlayer.give("EX1_393")
    amani1.play()
    game.currentPlayer.field.append(amani1)
    game.endTurn()

    amani2 = game.currentPlayer.give("EX1_393")
    amani2.play()
    game.currentPlayer.field.append(amani2)
    game.endTurn()        

    print(amani2.atk,amani2.extraAtk,amani2.health)
>> 2 0 3
    amani1.attack(amani2)
    print(amani2.atk,amani2.extraAtk,amani2.health)
>> 2 0 1
    print(amani2.slots)
>> [<Enrage ('Enrage Buff')>]

    game.endTurn()

I would have expected the amani2.extraAtk to pick up the Atk=3 in the Enrage class.

Crazed Alchemist + Damage + silence

[16:33:53] Xinhuan: did u remember the thread about how silence works on a minion that had been crazed alchemist and took damage?
[16:34:03] Xinhuan: and how the current HP gets adjusted
[16:35:15] Xinhuan: for example, a 2/5 gets swapped to a 5/2, takes 1 damage becomes a 5/1, if its silenced then, it becomes a 2/4

knife juggler issue

Knife juggler should not hit when it is summoned. In addition, the potential targets has a typo

# Knife Juggler
class NEW1_019:
    def OWN_MINION_SUMMON(self, minion):
        if minion is not self :
            self.hit(random.choice(self.controller.getTargets(TARGET_ENEMY_CHARACTERS)), 1)

On the same note, mass dispell also has a typo

# Mass Dispel
class EX1_626:
    def action(self):
        for target in self.controller.getTargets(TARGET_ENEMY_MINIONS):
            target.silence()
        self.controller.draw()

tests/full_game.py doesn't run

  1. Import errors:
./full_game.py
Traceback (most recent call last):
  File "./full_game.py", line 48, in <module>
    main()
  File "./full_game.py", line 11, in main
    deck1 = randomDraft(hero=fireplace.heroes.MAGE)
AttributeError: 'module' object has no attribute 'heroes'
  1. line 17: 'game' variable shadows the module name
  2. lines 13,14: Player constructor doesn't accept a deck

I'm submitting a pull request that fixes these problems. The test now runs for 5 turns before throwing this exception. (I'm brand new to the codebase - haven't dug deeper.)

Traceback (most recent call last):
  File "full_game.py", line 53, in <module>
    main()
  File "full_game.py", line 49, in main
    gm.endTurn()
  File "../fireplace/game.py", line 186, in endTurn
    self.broadcast("TURN_END", self.currentPlayer)
  File "../fireplace/game.py", line 209, in broadcast
    super().broadcast(event, *args)
  File "../fireplace/entity.py", line 24, in broadcast
    f(*args)
  File "../fireplace/game.py", line 231, in TURN_END
    player.broadcast("OWN_TURN_END")
  File "../fireplace/player.py", line 260, in broadcast
    super().broadcast(event, *args)
  File "../fireplace/entity.py", line 24, in broadcast
    f(*args)
  File "../fireplace/card.py", line 66, in _func
    self.game.queueActions(self, actions)
  File "../fireplace/game.py", line 138, in queueActions
    action.trigger(source, self)
  File "../fireplace/actions.py", line 31, in trigger
    targets = self.eval(self.target, source, game)
  File "../fireplace/actions.py", line 28, in eval
    return selector.eval(game, source)
  File "../fireplace/targeting.py", line 156, in eval
    result += [e for e in entities if self.test(e, source)]
  File "../fireplace/targeting.py", line 156, in <listcomp>
    result += [e for e in entities if self.test(e, source)]
  File "../fireplace/targeting.py", line 202, in test
    op(self, stack)
  File "../fireplace/targeting.py", line 211, in _and
    b = stack.pop()
IndexError: pop from empty list

'NoneType' object has no attribute 'type'

Deck 1: ['EX1_508', 'CS2_226', 'CS2_142', 'EX1_011', 'CS1_042', 'EX1_011', 'CS2_197', 'EX1_399', 'CS2_173', 'CS2_023', 'CS1_042', 'CS2_122', 'CS2_120', 'DS1_055', 'CS2_162', 'CS2_187', 'CS2_120', 'CS2_141', 'DS1_055', 'CS2_032', 'CS2_127', 'CS2_029', 'CS2_173', 'EX1_399', 'EX1_066', 'EX1_508', 'CS2_026', 'CS2_122', 'CS2_171', 'CS2_187']

Deck 2: ['NEW1_022', 'EX1_616', 'EX1_170', 'CS2_119', 'EX1_144', 'GVG_093', 'FP1_001', 'EX1_025', 'GVG_090', 'NEW1_019', 'EX1_507', 'BRM_008', 'GVG_079', 'EX1_131', 'FP1_017', 'GVG_115', 'CS2_186', 'BRM_008', 'CS2_141', 'CS2_221', 'FP1_009', 'EX1_066', 'CS2_142', 'CS2_186', 'CS2_074', 'GVG_075', 'BRM_025', 'EX1_283', 'CS2_131', 'EX1_509']

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 530, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 500, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 144, in DoMove
    card.play()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 274, in play
    self.controller.play(self, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 204, in play
    self.game.action(PowSubType.PLAY, self, card, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 54, in action
    args[0]._play(*args[1:])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 237, in _play
    card.action()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 219, in action
    func(self, **kwargs)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\cards\classic\rogue.py", line 52, in action
    self.buff(self.controller.weapon, "CS2_074e")
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 148, in buff
    ret.apply(target)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 651, in apply
    if target.type == CardType.WEAPON and getattr(self, "durability", 0):
AttributeError: 'NoneType' object has no attribute 'type'

TypeError: unsupported operand type(s) for *=: 'int' and 'Minion'

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 527, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 497, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 145, in DoMove
    card.play()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 283, in play
    self.controller.play(self, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 178, in play
    return self.game.queueActions(self, [Play(card, target, choose)])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 132, in queueActions
    ret.append(action.trigger(source, self))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\actions.py", line 87, in trigger
    self.do(source, game, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\actions.py", line 190, in do
    source._play(card)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 196, in _play
    self.summon(card)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 174, in summon
    self.game.queueActions(self, [Summon(self, card)])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 132, in queueActions
    ret.append(action.trigger(source, self))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\actions.py", line 235, in trigger
    ret.append(self.do(source, game, *extra_args))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\actions.py", line 512, in do
    card.summon()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 580, in summon
    super().summon()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 371, in summon
    super().summon()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 188, in summon
    self.action()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 214, in action
    self.game.queueActions(self, actions)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 132, in queueActions
    ret.append(action.trigger(source, self))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\actions.py", line 235, in trigger
    ret.append(self.do(source, game, *extra_args))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\actions.py", line 317, in do
    target.draw(card)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 121, in draw
    ret = self.game.queueActions(self, [Draw(self) * count])[0]
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\actions.py", line 38, in __mul__
    self.times *= value
TypeError: unsupported operand type(s) for *=: 'int' and 'Minion'
INFO:root:one begins turn 11
INFO:root:one is now at 6 mana crystals
INFO:root:Player(name='one', hero=<Hero ('Jaina Proudmoore')>) triggering <TargetedAction: Draw()> targeting [Player(name='one', hero=<Hero ('Jaina Proudmoore')>)]
INFO:root:one draws <Minion ('Gurubashi Berserker')>
DEBUG:root:<Minion ('Gurubashi Berserker')> moves from <Zone.DECK: 2> to <Zone.HAND: 3>
INFO:root:one plays <Minion ('Goldshire Footman')> from their hand
INFO:root:Player(name='one', hero=<Hero ('Jaina Proudmoore')>) triggering <TargetedAction: Summon(card=<Minion ('Goldshire Footman')>)> targeting [Player(name='one', hero=<Hero ('Jaina Proudmoore')>)]
INFO:root:one summons <Minion ('Goldshire Footman')>
INFO:root:Summoning <Minion ('Goldshire Footman')>
DEBUG:root:<Minion ('Goldshire Footman')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
DEBUG:root:<Enchantment ('Enhanced')> moves from <Zone.INVALID: 0> to <Zone.SETASIDE: 6>
INFO:root:Applying <Enchantment ('Enhanced')> to <Minion ('Goldshire Footman')>
DEBUG:root:<Enchantment ('Enhanced')> moves from <Zone.SETASIDE: 6> to <Zone.PLAY: 1>
INFO:root:<Minion ('Raid Leader')> attacks <Hero ('Valeera Sanguinar')>
INFO:root:<Minion ('Raid Leader')> triggering <TargetedAction: Damage(amount=2)> targeting [<Hero ('Valeera Sanguinar')>]
INFO:root:<Hero ('Valeera Sanguinar')> damaged for 2 health
INFO:root:one plays <Minion ('Kobold Geomancer')> from their hand
INFO:root:Player(name='one', hero=<Hero ('Jaina Proudmoore')>) triggering <TargetedAction: Summon(card=<Minion ('Kobold Geomancer')>)> targeting [Player(name='one', hero=<Hero ('Jaina Proudmoore')>)]
INFO:root:one summons <Minion ('Kobold Geomancer')>
INFO:root:Summoning <Minion ('Kobold Geomancer')>
DEBUG:root:<Minion ('Kobold Geomancer')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
DEBUG:root:<Enchantment ('Enhanced')> moves from <Zone.INVALID: 0> to <Zone.SETASIDE: 6>
INFO:root:Applying <Enchantment ('Enhanced')> to <Minion ('Kobold Geomancer')>
DEBUG:root:<Enchantment ('Enhanced')> moves from <Zone.SETASIDE: 6> to <Zone.PLAY: 1>
INFO:root:one plays hero power <HeroPower ('Fireblast')>
INFO:root:<HeroPower ('Fireblast')> triggering <TargetedAction: Hit(amount=1)> targeting [<Hero ('Jaina Proudmoore')>]
INFO:root:<HeroPower ('Fireblast')> triggering <TargetedAction: Damage(amount=1)> targeting [<Hero ('Jaina Proudmoore')>]
INFO:root:<Hero ('Jaina Proudmoore')> damaged for 1 health
INFO:root:one ends turn 11
INFO:root:two begins turn 12
INFO:root:two is now at 6 mana crystals
INFO:root:Player(name='two', hero=<Hero ('Valeera Sanguinar')>) triggering <TargetedAction: Draw()> targeting [Player(name='two', hero=<Hero ('Valeera Sanguinar')>)]
INFO:root:two draws <Minion ('Magma Rager')>
DEBUG:root:<Minion ('Magma Rager')> moves from <Zone.DECK: 2> to <Zone.HAND: 3>
INFO:root:two plays <Minion ('Piloted Shredder')> from their hand
INFO:root:Player(name='two', hero=<Hero ('Valeera Sanguinar')>) triggering <TargetedAction: Summon(card=<Minion ('Piloted Shredder')>)> targeting [Player(name='two', hero=<Hero ('Valeera Sanguinar')>)]
INFO:root:two summons <Minion ('Piloted Shredder')>
INFO:root:Summoning <Minion ('Piloted Shredder')>
DEBUG:root:<Minion ('Piloted Shredder')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:two plays <Minion ("Captain's Parrot")> from their hand
INFO:root:Player(name='two', hero=<Hero ('Valeera Sanguinar')>) triggering <TargetedAction: Summon(card=<Minion ("Captain's Parrot")>)> targeting [Player(name='two', hero=<Hero ('Valeera Sanguinar')>)]
INFO:root:two summons <Minion ("Captain's Parrot")>
INFO:root:Summoning <Minion ("Captain's Parrot")>
DEBUG:root:<Minion ("Captain's Parrot")> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:Activating <Minion ("Captain's Parrot")> action targeting None
INFO:root:<Minion ("Captain's Parrot")> triggering <TargetedAction: ForceDraw(cards=<Selector: DECK FRIENDLY and PIRATE and>)> targeting [Player(name='two', hero=<Hero ('Valeera Sanguinar')>)]

'Spell' object has no attribute 'race'

Deck 1: ['EX1_508', 'CS2_226', 'CS2_142', 'EX1_011', 'CS1_042', 'EX1_011', 'CS2_197', 'EX1_399', 'CS2_173', 'CS2_023', 'CS1_042', 'CS2_122', 'CS2_120', 'DS1_055', 'CS2_162', 'CS2_187', 'CS2_120', 'CS2_141', 'DS1_055', 'CS2_032', 'CS2_127', 'CS2_029', 'CS2_173', 'EX1_399', 'EX1_066', 'EX1_508', 'CS2_026', 'CS2_122', 'CS2_171', 'CS2_187']

Deck 2: ['EX1_009', 'CS2_226', 'GVG_064', 'EX1_556', 'CS2_118', 'EX1_586', 'EX1_572', 'NEW1_016', 'EX1_507', 'GVG_111', 'NEW1_040', 'GVG_092', 'EX1_128', 'EX1_561', 'CS2_120', 'NEW1_025', 'GVG_013', 'EX1_015', 'GVG_098', 'CS2_155', 'NEW1_029', 'CS2_181', 'GVG_112', 'GVG_013', 'GVG_107', 'CS2_196', 'EX1_021', 'EX1_043', 'CS2_189', 'GVG_093']

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 527, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 497, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 145, in DoMove
    card.play()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 277, in play
    self.controller.play(self, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 212, in play
    self.game.action(PowSubType.PLAY, self, card, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 54, in action
    args[0]._play(*args[1:])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 245, in _play
    card.action()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 219, in action
    func(self, **kwargs)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\cards\classic\neutral_epic.py", line 39, in action
    pirates = self.controller.deck.filter(race=Race.PIRATE)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\utils.py", line 35, in filter
    return self.__class__(e for k, v in kwargs.items() for e in self if getattr(e, k) == v)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\deck.py", line 15, in __init__
    super().__init__(cards or [])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\utils.py", line 35, in <genexpr>
    return self.__class__(e for k, v in kwargs.items() for e in self if getattr(e, k) == v)
AttributeError: 'Spell' object has no attribute 'race'
INFO:root:two begins turn 12
INFO:root:two is now at 6 mana crystals
DEBUG:root:<Minion ('Gnomish Experimenter')> moves from <Zone.DECK: 2> to <Zone.HAND: 3>
INFO:root:two draws <Minion ('Gnomish Experimenter')>
INFO:root:two plays <Minion ('Enhance-o Mechano')> from their hand
DEBUG:root:two summons <Minion ('Enhance-o Mechano')>
INFO:root:Summoning <Minion ('Enhance-o Mechano')>
DEBUG:root:<Minion ('Enhance-o Mechano')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:<Minion ('Harvest Golem')> attacks <Hero ('Jaina Proudmoore')>
INFO:root:<Minion ('Harvest Golem')> hits <Hero ('Jaina Proudmoore')> for 2
INFO:root:<Hero ('Jaina Proudmoore')> damaged for 2 health
INFO:root:two plays <Minion ("Captain's Parrot")> from their hand
DEBUG:root:two summons <Minion ("Captain's Parrot")>
INFO:root:Summoning <Minion ("Captain's Parrot")>
DEBUG:root:<Minion ("Captain's Parrot")> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:Activating <Minion ("Captain's Parrot")> action targeting None

Implement Weapon sheathing

Hi,

I ran the following test:

    game = prepare_game()
    waraxe = game.player1.give("CS2_106")
    waraxe.play()
    game.endTurn()
    tinker = game.player2.give("GVG_102")
    print(tinker.health)
    tinker.attack(game.player1.hero)
    print(tinker.health)

tinker health is 3 and then 0. It should be 3 and then 3 as player2's minion should not receive damage from player1s weapon. However, the weapon correctly does not lose durability due to the tinker's attack.

aura destroy issues

Hi,

I am currently running the following test case as I was hitting some issues with aura removes. In the test case, I created two Timber Wolfs for player1 and had player2 moonfire both.

    game = prepare_game()
    timber1 = game.player1.give("DS1_175")
    timber2 = game.player1.give("DS1_175")
    timber1.play()
    timber2.play()
    game.endTurn();
    moonfire1 = game.player2.give("CS2_008") 
    moonfire2 = game.player2.give("CS2_008")
    print([card.atk for card in game.player1.field])
    moonfire1.play(timber1)
    print([card.atk for card in game.player1.field])
    moonfire2.play(timber2)

During the second moonfire, I get a ValueError during the aura destroy. Currently trying to see if I can characterize the error further. Let me know if you need more information or if I may have set up the test case incorrectly. Thanks!

buff() missing 1 required positional argument: 'buff'

Deck 1: ['EX1_508', 'CS2_226', 'CS2_142', 'EX1_011', 'CS1_042', 'EX1_011', 'CS2_197', 'EX1_399', 'CS2_173', 'CS2_023', 'CS1_042', 'CS2_122', 'CS2_120', 'DS1_055', 'CS2_162', 'CS2_187', 'CS2_120', 'CS2_141', 'DS1_055', 'CS2_032', 'CS2_127', 'CS2_029', 'CS2_173', 'EX1_399', 'EX1_066', 'EX1_508', 'CS2_026', 'CS2_122', 'CS2_171', 'CS2_187']

Deck 2: ['CS2_074', 'NEW1_023', 'EX1_059', 'CS2_117', 'CS2_189', 'FP1_008', 'EX1_564', 'EX1_128', 'EX1_126', 'CS2_120', 'GVG_013', 'CS2_169', 'GVG_067', 'EX1_145', 'CS2_073', 'EX1_561', 'CS2_161', 'CS2_169', 'FP1_016', 'EX1_048', 'CS2_124', 'CS2_196', 'BRM_019', 'CS2_161', 'FP1_024', 'GVG_102', 'FP1_001', 'EX1_100', 'CS2_172', 'EX1_029']

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 530, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 500, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 145, in DoMove
    card.play()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 277, in play
    self.controller.play(self, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 212, in play
    self.game.action(PowSubType.PLAY, self, card, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 54, in action
    args[0]._play(*args[1:])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 220, in _play
    self.game.broadcast("CARD_PLAYED", self, card)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 177, in broadcast
    super().broadcast(event, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\entity.py", line 24, in broadcast
    f(*args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 60, in <lambda>
    _func = lambda *args: func(self, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\cards\gvg\neutral_common.py", line 19, in CARD_PLAYED
    self.buff("GVG_067a")
TypeError: buff() missing 1 required positional argument: 'buff'
INFO:root:one begins turn 25
INFO:root:one is now at 11 mana crystals
DEBUG:root:<Spell ('Frost Nova')> moves from <Zone.DECK: 2> to <Zone.HAND: 3>
INFO:root:one draws <Spell ('Frost Nova')>
INFO:root:one plays hero power <HeroPower ('Fireblast')>
INFO:root:Activating <HeroPower ('Fireblast')> action targeting <Minion ('Alexstrasza')>
INFO:root:<HeroPower ('Fireblast')> hits <Minion ('Alexstrasza')> for 1
INFO:root:<Minion ('Alexstrasza')> damaged for 1 health
INFO:root:<Minion ('Voodoo Doctor')> attacks <Minion ('Alexstrasza')>
INFO:root:<Minion ('Voodoo Doctor')> hits <Minion ('Alexstrasza')> for 2
INFO:root:<Minion ('Alexstrasza')> damaged for 2 health
INFO:root:<Minion ('Alexstrasza')> hits <Minion ('Voodoo Doctor')> for 8
INFO:root:<Minion ('Voodoo Doctor')> damaged for 8 health
INFO:root:<Minion ('Voodoo Doctor')> dies
INFO:root:<Minion ('Voodoo Doctor')> is removed from the field
INFO:root:<Minion ('Voodoo Doctor')> healed for 8 health
DEBUG:root:<Minion ('Voodoo Doctor')> moves from <Zone.PLAY: 1> to <Zone.GRAVEYARD: 4>
INFO:root:<Minion ('Alexstrasza')> dies
INFO:root:<Minion ('Alexstrasza')> is removed from the field
INFO:root:<Minion ('Alexstrasza')> healed for 10 health
DEBUG:root:<Minion ('Alexstrasza')> moves from <Zone.PLAY: 1> to <Zone.GRAVEYARD: 4>
INFO:root:one plays <Spell ('Frost Nova')> from their hand

issue with adjacent minions

Hey,

I'll look into joining the irc channel. I was running the following test and hit "AssertionError: Zone.GRAVEYARD". The error looks like it has to do with the zone assertion in the adjacentMinions function.

    game = prepare_game()
    dire1 = game.player1.give("EX1_162")
    dire2 = game.player1.give("EX1_162")
    dire1.play()
    dire2.play()
    game.endTurn();
    yeti = game.player2.give("GVG_078")
    yeti.play()
    yeti.attack(dire2)

Interestingly enough, if the yeti attack dire1, the assert is not triggered.

'Weapon' object has no attribute 'race'

Deck 1: ['EX1_508', 'CS2_226', 'CS2_142', 'EX1_011', 'CS1_042', 'EX1_011', 'CS2_197', 'EX1_399', 'CS2_173', 'CS2_023', 'CS1_042', 'CS2_122', 'CS2_120', 'DS1_055', 'CS2_162', 'CS2_187', 'CS2_120', 'CS2_141', 'DS1_055', 'CS2_032', 'CS2_127', 'CS2_029', 'CS2_173', 'EX1_399', 'EX1_066', 'EX1_508', 'CS2_026', 'CS2_122', 'CS2_171', 'CS2_187']

Deck 2: ['GVG_097', 'FP1_024', 'EX1_522', 'EX1_021', 'EX1_067', 'GVG_111', 'EX1_080', 'CS2_222', 'FP1_005', 'EX1_002', 'EX1_009', 'NEW1_041', 'EX1_577', 'CS2_162', 'EX1_089', 'EX1_032', 'CS2_151', 'BRM_007', 'EX1_508', 'EX1_028', 'GVG_095', 'CS2_127', 'GVG_070', 'CS2_151', 'NEW1_017', 'EX1_563', 'EX1_019', 'EX1_110', 'EX1_586', 'EX1_017']

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 532, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 502, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 146, in DoMove
    card.play()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 277, in play
    self.controller.play(self, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 212, in play
    self.game.action(PowSubType.PLAY, self, card, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 54, in action
    args[0]._play(*args[1:])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 230, in _play
    self.summon(card)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 208, in summon
    card.summon()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 593, in summon
    self.game.broadcast("MINION_SUMMON", self.controller, self)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 177, in broadcast
    super().broadcast(event, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\entity.py", line 31, in broadcast
    self.broadcast("UPDATE")
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 177, in broadcast
    super().broadcast(event, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\entity.py", line 24, in broadcast
    f(*args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 181, in UPDATE
    aura.update()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 733, in update
    if self.isValidTarget(target):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 695, in isValidTarget
    return targeting.isValidTarget(self.source, target, requirements=self.requirements)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\targeting.py", line 83, in isValidTarget
    if target.race != param:
AttributeError: 'Weapon' object has no attribute 'race'
DEBUG:root:two summons <Enchantment ('Might of Stormwind')>
INFO:root:Summoning <Enchantment ('Might of Stormwind')>
DEBUG:root:<Enchantment ('Might of Stormwind')> moves from <Zone.INVALID: 0> to <Zone.PLAY: 1>
INFO:root:Applying <Enchantment ('Might of Stormwind')> to <Minion ('Sunwalker')>
INFO:root:<Minion ('Stormwind Champion')> attacks <Hero ('Jaina Proudmoore')>
INFO:root:<Minion ('Stormwind Champion')> hits <Hero ('Jaina Proudmoore')> for 6
INFO:root:<Hero ('Jaina Proudmoore')> damaged for 6 health
INFO:root:<Minion ('Goblin Sapper')> attacks <Hero ('Jaina Proudmoore')>
INFO:root:<Minion ('Goblin Sapper')> hits <Hero ('Jaina Proudmoore')> for 3
INFO:root:<Hero ('Jaina Proudmoore')> damaged for 3 health
INFO:root:two plays <Minion ('Grimscale Oracle')> from their hand
DEBUG:root:two summons <Minion ('Grimscale Oracle')>
INFO:root:Summoning <Minion ('Grimscale Oracle')>
DEBUG:root:<Minion ('Grimscale Oracle')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:Summoning Aura <fireplace.card.Aura object at 0x0000000004D319B0>
DEBUG:root:two summons <Enchantment ('Might of Stormwind')>
INFO:root:Summoning <Enchantment ('Might of Stormwind')>
DEBUG:root:<Enchantment ('Might of Stormwind')> moves from <Zone.INVALID: 0> to <Zone.PLAY: 1>
INFO:root:Applying <Enchantment ('Might of Stormwind')> to <Minion ('Grimscale Oracle')>

ValueError: list.remove(x): x not in list

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 527, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 497, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 150, in DoMove
    minion.attack(minion.targets[move[3]])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 368, in attack
    self.game.attack(self, target)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 72, in attack
    return self.queueActions(source, [Attack(source, target)])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 136, in queueActions
    self._processDeaths()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 119, in _processDeaths
    self.queueActions(self, actions)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 132, in queueActions
    ret.append(action.trigger(source, self))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\actions.py", line 87, in trigger
    self.do(source, game, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\actions.py", line 139, in do
    target.zone = Zone.GRAVEYARD
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 75, in zone
    self._setZone(value)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 518, in _setZone
    super()._setZone(value)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 363, in _setZone
    super()._setZone(zone)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 181, in _setZone
    super()._setZone(zone)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 97, in _setZone
    aura.destroy()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 736, in destroy
    self.game.auras.remove(self)
ValueError: list.remove(x): x not in list
INFO:root:two ends turn 2
INFO:root:one begins turn 3
INFO:root:one is now at 11 mana crystals
INFO:root:Player(name='one', hero=<Hero ('Jaina Proudmoore')>) triggering <TargetedAction: Draw()> targeting [Player(name='one', hero=<Hero ('Jaina Proudmoore')>)]
INFO:root:one draws <Minion ('Bluegill Warrior')>
DEBUG:root:<Minion ('Bluegill Warrior')> moves from <Zone.DECK: 2> to <Zone.HAND: 3>
INFO:root:<Minion ('Raid Leader')> attacks <Minion ('Faerie Dragon')>
INFO:root:<Minion ('Raid Leader')> triggering <TargetedAction: Damage(amount=3)> targeting [<Minion ('Faerie Dragon')>]
INFO:root:<Minion ('Faerie Dragon')> damaged for 3 health
INFO:root:<Minion ('Faerie Dragon')> triggering <TargetedAction: Damage(amount=3)> targeting [<Minion ('Raid Leader')>]
INFO:root:<Minion ('Raid Leader')> damaged for 3 health
INFO:root:Destroying buff <Enchantment ('Enhanced')> from <Minion ('Raid Leader')>
DEBUG:root:<Enchantment ('Enhanced')> moves from <Zone.PLAY: 1> to <Zone.GRAVEYARD: 4>
INFO:root:<Minion ('Raid Leader')> is removed from the field
INFO:root:<Minion ('Raid Leader')> healed for 3 health
DEBUG:root:<Minion ('Raid Leader')> moves from <Zone.PLAY: 1> to <Zone.GRAVEYARD: 4>
INFO:root:Removing <Aura ('CS2_122e')> affecting [<Minion ('Gurubashi Berserker')>, <Minion ('Frostwolf Warlord')>]

webspinner deathrattle issue

Webspinner FP1_011 in the enUS.xml does not contain an entourage list for its deathrattle, so the random summon will error out on selecting random from an empty list. I think the webspinner needs to generate a list of "collectible" beasts instead.

'Aura' object has no attribute '_buffed'

Deck 1: ['EX1_508', 'CS2_226', 'CS2_142', 'EX1_011', 'CS1_042', 'EX1_011', 'CS2_197', 'EX1_399', 'CS2_173', 'CS2_023', 'CS1_042', 'CS2_122', 'CS2_120', 'DS1_055', 'CS2_162', 'CS2_187', 'CS2_120', 'CS2_141', 'DS1_055', 'CS2_032', 'CS2_127', 'CS2_029', 'CS2_173', 'EX1_399', 'EX1_066', 'EX1_508', 'CS2_026', 'CS2_122', 'CS2_171', 'CS2_187']

Deck 2: ['EX1_399', 'EX1_506', 'EX1_508', 'CS2_075', 'NEW1_017', 'EX1_562', 'EX1_112', 'BRM_008', 'CS2_146', 'GVG_102', 'CS2_221', 'CS2_077', 'NEW1_024', 'EX1_509', 'GVG_078', 'EX1_004', 'EX1_405', 'CS2_182', 'GVG_090', 'GVG_081', 'EX1_144', 'GVG_044', 'EX1_522', 'EX1_405', 'GVG_006', 'GVG_023', 'GVG_082', 'GVG_095', 'EX1_595', 'CS2_182']

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 527, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 497, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 147, in DoMove
    card.play(target=card.targets[move[3]])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 277, in play
    self.controller.play(self, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 212, in play
    self.game.action(PowSubType.PLAY, self, card, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 54, in action
    args[0]._play(*args[1:])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 245, in _play
    card.action()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 219, in action
    func(self, **kwargs)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\cards\utils.py", line 35, in <lambda>
    bounceTarget = lambda self, target: target.bounce()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 544, in bounce
    self.zone = Zone.HAND
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 87, in zone
    self._setZone(value)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 530, in _setZone
    aura.destroy()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 747, in destroy
    logging.info("Removing %r affecting %r" % (self, self._buffed))
AttributeError: 'Aura' object has no attribute '_buffed'
INFO:root:two begins turn 20
INFO:root:two is now at 10 mana crystals
DEBUG:root:<Minion ('Madder Bomber')> moves from <Zone.DECK: 2> to <Zone.HAND: 3>
INFO:root:two draws <Minion ('Madder Bomber')>
INFO:root:two plays <Minion ('Grimscale Oracle')> from their hand
DEBUG:root:two summons <Minion ('Grimscale Oracle')>
INFO:root:Summoning <Minion ('Grimscale Oracle')>
DEBUG:root:<Minion ('Grimscale Oracle')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:Summoning Aura <fireplace.card.Aura object at 0x00000000047ADDD8>
INFO:root:two plays hero power <HeroPower ('Dagger Mastery')>
INFO:root:Activating <HeroPower ('Dagger Mastery')> action targeting None
DEBUG:root:two summons <Weapon ('Wicked Knife')>
INFO:root:Summoning <Weapon ('Wicked Knife')>
DEBUG:root:<Weapon ('Wicked Knife')> moves from <Zone.INVALID: 0> to <Zone.PLAY: 1>
INFO:root:two plays <Minion ('Madder Bomber')> from their hand
DEBUG:root:two summons <Minion ('Madder Bomber')>
INFO:root:Summoning <Minion ('Madder Bomber')>
DEBUG:root:<Minion ('Madder Bomber')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:<Minion ('Southsea Deckhand')> attacks <Minion ('Booty Bay Bodyguard')>
INFO:root:<Minion ('Southsea Deckhand')> hits <Minion ('Booty Bay Bodyguard')> for 2
INFO:root:<Minion ('Booty Bay Bodyguard')> damaged for 2 health
INFO:root:<Minion ('Booty Bay Bodyguard')> hits <Minion ('Southsea Deckhand')> for 5
INFO:root:<Minion ('Southsea Deckhand')> damaged for 5 health
INFO:root:<Minion ('Booty Bay Bodyguard')> dies
INFO:root:<Minion ('Booty Bay Bodyguard')> is removed from the field
INFO:root:<Minion ('Booty Bay Bodyguard')> healed for 4 health
DEBUG:root:<Minion ('Booty Bay Bodyguard')> moves from <Zone.PLAY: 1> to <Zone.GRAVEYARD: 4>
INFO:root:<Minion ('Southsea Deckhand')> dies
INFO:root:<Minion ('Southsea Deckhand')> is removed from the field
INFO:root:<Minion ('Southsea Deckhand')> healed for 5 health
DEBUG:root:<Minion ('Southsea Deckhand')> moves from <Zone.PLAY: 1> to <Zone.GRAVEYARD: 4>
INFO:root:two plays <Minion ('Patient Assassin')> from their hand
DEBUG:root:two summons <Minion ('Patient Assassin')>
INFO:root:Summoning <Minion ('Patient Assassin')>
DEBUG:root:<Minion ('Patient Assassin')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:two plays <Spell ('Time Rewinder')> from their hand
DEBUG:root:two summons <Spell ('Time Rewinder')>
INFO:root:Summoning <Spell ('Time Rewinder')>
DEBUG:root:<Spell ('Time Rewinder')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:Activating <Spell ('Time Rewinder')> action targeting <Minion ('Grimscale Oracle')>
INFO:root:<Minion ('Grimscale Oracle')> is bounced back to two's hand
INFO:root:<Minion ('Grimscale Oracle')> is removed from the field

buff() missing 1 required positional argument: 'buff'

Deck 1: ['EX1_508', 'CS2_226', 'CS2_142', 'EX1_011', 'CS1_042', 'EX1_011', 'CS2_197', 'EX1_399', 'CS2_173', 'CS2_023', 'CS1_042', 'CS2_122', 'CS2_120', 'DS1_055', 'CS2_162', 'CS2_187', 'CS2_120', 'CS2_141', 'DS1_055', 'CS2_032', 'CS2_127', 'CS2_029', 'CS2_173', 'EX1_399', 'EX1_066', 'EX1_508', 'CS2_026', 'CS2_122', 'CS2_171', 'CS2_187']

Deck 2: ['EX1_412', 'GVG_089', 'GVG_074', 'GVG_117', 'EX1_004', 'GVG_121', 'GVG_110', 'GVG_079', 'CS2_117', 'EX1_145', 'BRM_030', 'EX1_043', 'EX1_390', 'CS2_213', 'GVG_022', 'EX1_004', 'GVG_090', 'EX1_043', 'CS2_227', 'EX1_005', 'EX1_562', 'GVG_109', 'EX1_032', 'GVG_090', 'EX1_028', 'FP1_008', 'CS2_146', 'GVG_084', 'EX1_007', 'CS2_197']

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 537, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 507, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 142, in DoMove
    self.game.endTurn()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 153, in endTurn
    self.broadcast("TURN_END", self.currentPlayer)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 177, in broadcast
    super().broadcast(event, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\entity.py", line 24, in broadcast
    f(*args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 211, in TURN_END
    player.broadcast("OWN_TURN_END")
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 265, in broadcast
    super().broadcast(event, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\entity.py", line 24, in broadcast
    f(*args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 60, in <lambda>
    _func = lambda *args: func(self, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\cards\classic\neutral_rare.py", line 16, in OWN_TURN_END
    target.buff("EX1_004e")
TypeError: buff() missing 1 required positional argument: 'buff'

battlecry with empty targets

Minions with battlecries that target minions can be played without a target if none are present.

def test_empty_shattered() :
    game = prepare_game()
    shattered = game.player1.give("EX1_019")
    shattered.play()
    game.endTurn()

However, the following test leads to the error:

AttributeError: 'NoneType' object has no attribute 'buffs'

I think having a target == None check in the utils.py for buffTarget would enable these battlecry minions to be played with an empty target.

Missing Auras

Hi,

I think there may be a few auras that are not implemented yet (not sure if it was intentional). I ran the following code to check for cards with AURA=1 without an associated aura card.

    import fireplace.cards
    game = prepare_game()
    for cardid in fireplace.cards.filter(collectible=True) :
        if Card(cardid).aura and not Card(cardid).data.auras:
            card = Card(cardid)
            print(cardid)   

It looks like most are there except for the following :

GVG_095 = Goblin Sapper
EX1_591 = Auchenai Soulpriest
GVG_024 = Cogmaster's Wrench
GVG_122 = Wee Spellstopper
GVG_013 = Cogmaster

It seems like the Soulpriest aura may require some work to rebroadcast DAMAGE instead of HEAL (i.e., infinite death combo with soulpriest + mistress of pain). In addition, "+ spell damage" appears to boost the heal "damage" when this aura is active.

animal companion entourage issue

Hey,

I get the following error playing Animal Companion (NEW1_031):

  File "C:\Python34\lib\site-packages\fireplace-0.1-py3.4.egg\fireplace\cards\classic\hunter.py", line 120, in action
    self.controller.summon(random.choice(self.entourage))
AttributeError: 'Spell' object has no attribute 'entourage'

Seems like entourage cards are there in the xml files.

KeyError: <GameTag.Collectible: 321>

Deck 1: ['EX1_508', 'CS2_226', 'CS2_142', 'EX1_011', 'CS1_042', 'EX1_011', 'CS2_197', 'EX1_399', 'CS2_173', 'CS2_023', 'CS1_042', 'CS2_122', 'CS2_120', 'DS1_055', 'CS2_162', 'CS2_187', 'CS2_120', 'CS2_141', 'DS1_055', 'CS2_032', 'CS2_127', 'CS2_029', 'CS2_173', 'EX1_399', 'EX1_066', 'EX1_508', 'CS2_026', 'CS2_122', 'CS2_171', 'CS2_187']

Deck 2: ['CS2_187', 'PRO_001', 'NEW1_037', 'EX1_043', 'EX1_009', 'EX1_283', 'GVG_091', 'CS2_173', 'EX1_096', 'NEW1_027', 'EX1_020', 'CS2_125', 'FP1_007', 'EX1_103', 'EX1_562', 'FP1_009', 'EX1_062', 'CS2_161', 'GVG_109', 'FP1_003', 'GVG_084', 'BRM_007', 'CS2_233', 'EX1_032', 'EX1_506', 'GVG_024', 'EX1_616', 'GVG_104', 'BRM_022', 'EX1_508']

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 527, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 497, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 135, in DoMove
    self.game.endTurn()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 153, in endTurn
    self.broadcast("TURN_END", self.currentPlayer)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 177, in broadcast
    super().broadcast(event, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\entity.py", line 24, in broadcast
    f(*args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 211, in TURN_END
    player.broadcast("OWN_TURN_END")
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 273, in broadcast
    super().broadcast(event, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\entity.py", line 24, in broadcast
    f(*args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 60, in <lambda>
    _func = lambda *args: func(self, *args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\cards\classic\neutral_rare.py", line 186, in OWN_TURN_END
    self.buff(target, "NEW1_037e")
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 147, in buff
    ret = self.controller.summon(buff)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 205, in summon
    card = self.game.card(card)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 100, in card
    card = Card(id)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 29, in Card
    return subclass(id, data)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 640, in __init__
    super().__init__(*args)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 51, in __init__
    self.tags.update(data.tags)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\managers.py", line 32, in update
    if self.map[k]:
KeyError: <GameTag.Collectible: 321>
DEBUG:root:two summons <Minion ('Nerubian Egg')>
INFO:root:Summoning <Minion ('Nerubian Egg')>
DEBUG:root:<Minion ('Nerubian Egg')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:two plays <Minion ('Loot Hoarder')> from their hand
DEBUG:root:two summons <Minion ('Loot Hoarder')>
INFO:root:Summoning <Minion ('Loot Hoarder')>
DEBUG:root:<Minion ('Loot Hoarder')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:two plays <Minion ('Twilight Drake')> from their hand
DEBUG:root:two summons <Minion ('Twilight Drake')>
INFO:root:Summoning <Minion ('Twilight Drake')>
DEBUG:root:<Minion ('Twilight Drake')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:Activating <Minion ('Twilight Drake')> action targeting None
DEBUG:root:two summons <Enchantment ('Hour of Twilight')>
INFO:root:Summoning <Enchantment ('Hour of Twilight')>
DEBUG:root:<Enchantment ('Hour of Twilight')> moves from <Zone.INVALID: 0> to <Zone.PLAY: 1>
INFO:root:Applying <Enchantment ('Hour of Twilight')> to <Minion ('Twilight Drake')>
DEBUG:root:two summons <Enchantment ('Hour of Twilight')>
INFO:root:Summoning <Enchantment ('Hour of Twilight')>
DEBUG:root:<Enchantment ('Hour of Twilight')> moves from <Zone.INVALID: 0> to <Zone.PLAY: 1>
INFO:root:Applying <Enchantment ('Hour of Twilight')> to <Minion ('Twilight Drake')>
DEBUG:root:two summons <Enchantment ('Hour of Twilight')>
INFO:root:Summoning <Enchantment ('Hour of Twilight')>
DEBUG:root:<Enchantment ('Hour of Twilight')> moves from <Zone.INVALID: 0> to <Zone.PLAY: 1>
INFO:root:Applying <Enchantment ('Hour of Twilight')> to <Minion ('Twilight Drake')>
INFO:root:two plays <Minion ('Master Swordsmith')> from their hand
DEBUG:root:two summons <Minion ('Master Swordsmith')>
INFO:root:Summoning <Minion ('Master Swordsmith')>
DEBUG:root:<Minion ('Master Swordsmith')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:<Minion ('Flying Machine')> attacks <Hero ('Jaina Proudmoore')>
INFO:root:<Minion ('Flying Machine')> hits <Hero ('Jaina Proudmoore')> for 1
INFO:root:<Hero ('Jaina Proudmoore')> damaged for 1 health
INFO:root:two ends turn

lightwell

Lightwell needs a targets empty check so that you do not random.choice with none.

# Lightwell
class EX1_341:
    def OWN_TURN_BEGIN(self):
        targets = [t for t in self.controller.getTargets(TARGET_FRIENDLY_CHARACTERS) if t.damage]
        if targets :
            self.heal(random.choice(targets), 3)

druid morph issues

Hey,

I was seeing some issues with druid choice cards such as Druid of the Claw (EX1_165). It looks like once you make the choice to like EX1_165b which is a Spell card, you then call MORPH on the spell card. However, Spells do not have the morph attribute only Minions do. I encountered this issue on an earlier commit but I think it should still happen on the latest build. Let me know thanks!

unsupported operand type(s) for -=: 'Minion' and 'int'

Deck 1: ['EX1_508', 'CS2_226', 'CS2_142', 'EX1_011', 'CS1_042', 'EX1_011', 'CS2_197', 'EX1_399', 'CS2_173', 'CS2_023', 'CS1_042', 'CS2_122', 'CS2_120', 'DS1_055', 'CS2_162', 'CS2_187', 'CS2_120', 'CS2_141', 'DS1_055', 'CS2_032', 'CS2_127', 'CS2_029', 'CS2_173', 'EX1_399', 'EX1_066', 'EX1_508', 'CS2_026', 'CS2_122', 'CS2_171', 'CS2_187']

Deck 2: ['FP1_031', 'CS2_171', 'EX1_093', 'FP1_007', 'NEW1_016', 'EX1_017', 'EX1_283', 'GVG_093', 'GVG_094', 'GVG_108', 'tt_004', 'CS2_118', 'CS2_146', 'FP1_024', 'CS2_072', 'FP1_014', 'GVG_116', 'EX1_562', 'BRM_026', 'CS2_222', 'CS2_227', 'GVG_025', 'EX1_062', 'EX1_014', 'GVG_099', 'GVG_097', 'CS2_169', 'BRM_025', 'EX1_023', 'EX1_043']

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 532, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 502, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 146, in DoMove
    card.play()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 277, in play
    self.controller.play(self, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 204, in play
    self.game.action(PowSubType.PLAY, self, card, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 54, in action
    args[0]._play(*args[1:])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 237, in _play
    card.action()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 219, in action
    func(self, **kwargs)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\cards\classic\neutral_epic.py", line 41, in action
    self.controller.draw(random.choice(pirates))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 149, in draw
    count -= 1
TypeError: unsupported operand type(s) for -=: 'Minion' and 'int'
DEBUG:root:two summons <Weapon ('Wicked Knife')>
INFO:root:Summoning <Weapon ('Wicked Knife')>
DEBUG:root:<Weapon ('Wicked Knife')> moves from <Zone.INVALID: 0> to <Zone.PLAY: 1>
INFO:root:<Weapon ('Wicked Knife')> dies
DEBUG:root:<Weapon ('Wicked Knife')> moves from <Zone.PLAY: 1> to <Zone.GRAVEYARD: 4>
INFO:root:<Hero ('Valeera Sanguinar')> attacks <Hero ('Jaina Proudmoore')>
INFO:root:<Weapon ('Wicked Knife')> loses 1 point of durability
INFO:root:<Hero ('Valeera Sanguinar')> hits <Hero ('Jaina Proudmoore')> for 1
INFO:root:<Hero ('Jaina Proudmoore')> damaged for 1 health
INFO:root:two plays <Minion ('Jeeves')> from their hand
DEBUG:root:two summons <Minion ('Jeeves')>
INFO:root:Summoning <Minion ('Jeeves')>
DEBUG:root:<Minion ('Jeeves')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:two plays <Minion ("Captain's Parrot")> from their hand
DEBUG:root:two summons <Minion ("Captain's Parrot")>
INFO:root:Summoning <Minion ("Captain's Parrot")>
DEBUG:root:<Minion ("Captain's Parrot")> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:Activating <Minion ("Captain's Parrot")> action targeting None
DEBUG:root:<Minion ('Southsea Deckhand')> moves from <Zone.DECK: 2> to <Zone.HAND: 3>
INFO:root:two draws <Minion ('Southsea Deckhand')>

infinite loop

Hey,

Was able to create an infinite loop case using reversing switch and printing attack. Here's the test case:

    GOLDSHIRE = "CS1_042"
    REVERSE = "PART_006"
    game = prepare_game()
    minion1 = game.player1.give(GOLDSHIRE)
    spell1 = game.player1.give(REVERSE)
    minion1.play()
    spell1.play(minion1)
    print(minion1.atk)

felcannon

Hey,

I think the felcannon has a 2 hit left out.

# Fel Cannon
class GVG_020:
    OWN_TURN_END = [Hit(RANDOM_MINION - MECH)]

Thanks!

card played error

Hi,

Running a test with secretkeeper and seems to run into a CARD_PLAYED error:

    game = prepare_game()
    secretkeeper = game.player1.give("EX1_080")
    secretkeeper.play()
    mirror = game.player2.give("CS2_027")
    mirror.play()

TypeError: CARD_PLAYED() takes 2 positional arguments but 3 were given.

floating watcher issue

Hey,

I was testing floating watcher and hit the following error using this test case:

TypeError: <lambda>() takes 3 positional arguments but 4 were given

WATCHER = "GVG_100"
def test_watcher() :
        game = prepare_game(WARLOCK,WARLOCK)
        watcher = game.player1.give(WATCHER)
        watcher.play()
        game.endTurn();game.endTurn()
        assert(watcher.atk==4)
        game.player1.hero.power.play()
        assert(watcher.atk==6)
        assert(watcher.atk==6)

I think the lambda function requires 4 arguments (aka lambda self, target, amount, source:). Thanks!

'int' object is not callable

Deck 1: ['EX1_508', 'CS2_226', 'CS2_142', 'EX1_011', 'CS1_042', 'EX1_011', 'CS2_197', 'EX1_399', 'CS2_173', 'CS2_023', 'CS1_042', 'CS2_122', 'CS2_120', 'DS1_055', 'CS2_162', 'CS2_187', 'CS2_120', 'CS2_141', 'DS1_055', 'CS2_032', 'CS2_127', 'CS2_029', 'CS2_173', 'EX1_399', 'EX1_066', 'EX1_508', 'CS2_026', 'CS2_122', 'CS2_171', 'CS2_187']

Deck 2: ['NEW1_004', 'EX1_033', 'CS2_147', 'GVG_091', 'EX1_096', 'EX1_126', 'PRO_001', 'NEW1_041', 'EX1_085', 'GVG_108', 'EX1_396', 'EX1_562', 'GVG_113', 'EX1_025', 'BRM_019', 'GVG_047', 'EX1_059', 'EX1_009', 'FP1_028', 'CS2_155', 'EX1_581', 'GVG_024', 'GVG_084', 'EX1_059', 'EX1_393', 'GVG_097', 'EX1_067', 'tt_004', 'NEW1_021', 'EX1_009']

Traceback (most recent call last):
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 538, in UCTPlayGame
    m = UCT(rootstate = state, seconds = 10, verbose = False)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 508, in UCT
    state.DoMove(random.choice(state.GetMoves()))
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\mcts\uct.py", line 155, in DoMove
    card.play(target=card.targets[move[3]])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 277, in play
    self.controller.play(self, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 212, in play
    self.game.action(PowSubType.PLAY, self, card, target, choose)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\game.py", line 54, in action
    args[0]._play(*args[1:])
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\player.py", line 245, in _play
    card.action()
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\card.py", line 219, in action
    func(self, **kwargs)
  File "C:\Users\Ragowit\Documents\GitHub\fireplace\fireplace\cards\classic\neutral_legendary.py", line 222, in action
    target.damage(4)
TypeError: 'int' object is not callable
INFO:root:two begins turn 16
INFO:root:two is now at 8 mana crystals
DEBUG:root:<Minion ('Doomsayer')> moves from <Zone.DECK: 2> to <Zone.HAND: 3>
INFO:root:two draws <Minion ('Doomsayer')>
INFO:root:two plays <Minion ('Arcane Nullifier X-21')> from their hand
DEBUG:root:two summons <Minion ('Arcane Nullifier X-21')>
INFO:root:Summoning <Minion ('Arcane Nullifier X-21')>
DEBUG:root:<Minion ('Arcane Nullifier X-21')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:two plays <Spell ('Rogues Do It...')> from their hand
DEBUG:root:two summons <Spell ('Rogues Do It...')>
INFO:root:Summoning <Spell ('Rogues Do It...')>
DEBUG:root:<Spell ('Rogues Do It...')> moves from <Zone.HAND: 3> to <Zone.PLAY: 1>
INFO:root:Activating <Spell ('Rogues Do It...')> action targeting <Hero ('Jaina Proudmoore')>

some minor issues

Hey,

The new tag push seems to be pretty good although I hit a few minor issues during my testing. Below, I posted some quick fixes that seemed to make my code work again as a couple of the attributes seem to be missing in the latest commit:

  1. In card.py, the Character class should probably be initialized with self.numAttacks = 0 (hit a few errors not finding that attribute during Minion attacks). The BaseCard class should probably have self.secret = False (secretkeeper will error when a non-secret was played)

  2. In player.py the Player class should probably be initialized with self.usedMana = 0, self.combo = False, self.minionsPlayedThisTurn = 0.

  3. In targeting.py, I was hitting an issue where a weapon's race was being checked so maybe checking that the 'race' attribute is available or the target is a Minion before checking for race is needed.

  4. In cards/utils.py, healHero(amount) should be applied to self.controller.hero rather than hero.

  5. In cards/classic/druid.py, the buffSelf(self, "**") should be changed to buffSelf("**") for Claw and Bite; and replace character to target in self.hit(target,5) in Starfire.

  6. In cards/classic/warrior.py, the Frothing Beserker action should be OWN_DAMAGE not DAMAGE

  7. In cards/gvg/hunter.py, metaltooth leaper is buffing all entities, should only do mechs

    def action(self) :
        for minion in self.controller.field:
            if minion is not self and minion.race == Race.MECHANICAL :
                self.buff(minion, "GVG_048e")

I also implemented a few cards (probably not in the most ideal way), so if you would like to use them as a starting point, please feel free:

  1. In cards/classic/paladin.py, the repentance enchantment was missing
# Repentance
class EX1_379e:
       HEALTH = lambda i: 1
  1. In cards/gvg/hunter.py, maybe you can add Call Pet. It also required an enchantment (similar to Summoning Portal that is in the extras/missing_cards.py) to reduce cost by 4 on the beast draw.
# Call Pet
class GVG_017:
    def action(self):
        card = self.controller.draw()
            if card.type == CardType.MINION and card.race == Race.BEAST :
                self.buff(card, "GVG_017e")

Missing buff for call pet

GVG_017e = {
    GameTag.CARDNAME: "Call Pet Buff",
    GameTag.CARDTYPE: CardType.ENCHANTMENT,
    GameTag.COST: -4,
}
  1. In cards/gvg/neutral_common.py, maybe you can add tinkertown technician.
# Tinkertown Technician
class GVG_102:
    def action(self):
        if self.poweredUp:
            self.buff(self, "GVG_102e")
            self.controller.give(random.choice(self.data.entourage))
  1. Added a few gvg rogue card implementations
from ..utils import *


##
# Minions

# Goblin Auto-Barber
class GVG_023:
    def action(self):
        if self.controller.hero.weapon:
            self.buff(self.controller.hero.weapon, "GVG_023a")

# One-eyed Cheat
class GVG_025:
    def OWN_MINION_SUMMON(self, minion):
        if minion.race == Race.PIRATE:
            self.stealthed = True

# Iron Sensei
class GVG_027:
    def OWN_TURN_END(self):
        own_mechs = [minion for minion in self.controller.field if minion.race == Race.MECHANICAL] 
        if own_mechs :
            self.buff(random.choice(own_mechs),"GVG_027e")


# Trade Prince Gallywix
class GVG_028:
    def CARD_PLAYED(self, player, card):
        if player == self.controller.opponent and card.type == CardType.SPELL:
            self.controller.give(card.id)
            player.give("GVG_028t")

# Gallywix's Coin
class GVG_028t:
    def action(self):
        self.controller.tempMana += 1

##
# Spells

# Sabotage
class GVG_047:
    def action(self):
        if self.controller.opponent.field :
            random.choice(self.controller.opponent.field).destroy()
    def combo(self):
        if self.controller.opponent.field :
            random.choice(self.controller.opponent.field).destroy()
        if self.controller.opponent.hero.weapon:
            self.controller.opponent.hero.weapon.destroy()


# Tinker's Sharpsword Oil
class GVG_022:
    def action(self):
        if self.controller.hero.weapon :
            self.buff(self.controller.hero.weapon, "GVG_022a")
    def combo(self):
        if self.controller.hero.weapon :
            self.buff(self.controller.hero.weapon, "GVG_022a")
        if self.controller.field :
            self.buff(random.choice(self.controller.field), "GVG_022b")

which required these additions in buffs.py

# Iron Sensei
GVG_027e = {
    GameTag.ATK: 2,
    GameTag.HEALTH: 2
}

# Tinker's Sharpsword Oil
GVG_022a = buff(atk=3)
GVG_022b = buff(atk=3)

# Goblin Auto-Barber
GVG_023a = buff(atk=1)

Jaraxxus + Commanding Shout + Wisp

Commanding shout is an aura. Need to check if it's on the hero.

[13:09:01] <jleclanche> cho on the board
[13:09:16] <jleclanche> opponent warrior plays commanding shout
[13:09:25] <jleclanche> then millhouse
[13:09:32] <jleclanche> you (warlock) play jaraxxus, commanding shout, wisp

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.