demilich1 / metastone

Hearthstone simulator written in Java with full GUI support
GNU General Public License v2.0
132 stars 80 forks source link

AI can't play Kazakus due to null source for spells #297

Closed doctorpangloss closed 7 years ago

doctorpangloss commented 7 years ago

I'm using the Renolock deck, which has Kazakus. This exception kils the match. It looks like source is null, and trying to create a PreDamageEvent with a null source throws a NullPointerException.

The stack:

java.lang.NullPointerException
    at net.demilich.metastone.game.events.PreDamageEvent.<init>(PreDamageEvent.java:12)
    at net.demilich.metastone.game.logic.GameLogic.damage(GameLogic.java:416)
    at net.demilich.metastone.game.spells.DamageSpell.onCast(DamageSpell.java:62)
    at net.demilich.metastone.game.spells.Spell.castForPlayer(Spell.java:58)
    at net.demilich.metastone.game.spells.Spell.cast(Spell.java:36)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:317)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:271)
    at net.demilich.metastone.game.actions.DiscoverAction.execute(DiscoverAction.java:74)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1253)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:43)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.requestAction(GameStateValueBehaviour.java:117)
    at net.demilich.metastone.game.spells.SpellUtils.getSpellDiscover(SpellUtils.java:134)
    at net.demilich.metastone.game.spells.CreateCardSpell.discoverCardParts(CreateCardSpell.java:62)
    at net.demilich.metastone.game.spells.CreateCardSpell.onCast(CreateCardSpell.java:84)
    at net.demilich.metastone.game.spells.Spell.castForPlayer(Spell.java:58)
    at net.demilich.metastone.game.spells.Spell.cast(Spell.java:19)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:317)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:271)
    at net.demilich.metastone.game.actions.DiscoverAction.execute(DiscoverAction.java:74)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1253)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:43)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.requestAction(GameStateValueBehaviour.java:117)
    at net.demilich.metastone.game.spells.SpellUtils.getSpellDiscover(SpellUtils.java:134)
    at net.demilich.metastone.game.spells.DiscoverOptionSpell.onCast(DiscoverOptionSpell.java:62)
    at net.demilich.metastone.game.spells.Spell.castForPlayer(Spell.java:58)
    at net.demilich.metastone.game.spells.Spell.cast(Spell.java:19)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:317)
    at net.demilich.metastone.game.actions.BattlecryAction.execute(BattlecryAction.java:68)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1253)
    at net.demilich.metastone.game.logic.GameLogic.performBattlecryAction(GameLogic.java:1565)
    at net.demilich.metastone.game.logic.GameLogic.resolveBattlecry(GameLogic.java:1539)
    at net.demilich.metastone.game.logic.GameLogic.summon(GameLogic.java:1700)
    at net.demilich.metastone.game.actions.PlayMinionCardAction.play(PlayMinionCardAction.java:49)
    at net.demilich.metastone.game.actions.PlayCardAction.execute(PlayCardAction.java:50)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1253)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:43)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:53)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.requestAction(GameStateValueBehaviour.java:117)
    at net.demilich.metastone.game.GameContext.playTurn(GameContext.java:487)
    at net.demilich.metastone.game.GameContext.play(GameContext.java:460)

There's another exception that source being null causes:

java.lang.NullPointerException
    at net.demilich.metastone.game.spells.CreateCardSpell.onCast(CreateCardSpell.java:85)
    at net.demilich.metastone.game.spells.Spell.castForPlayer(Spell.java:58)
    at net.demilich.metastone.game.spells.Spell.cast(Spell.java:19)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:317)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:271)
    at net.demilich.metastone.game.actions.DiscoverAction.execute(DiscoverAction.java:74)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1256)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:43)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.requestAction(GameStateValueBehaviour.java:117)
    at net.demilich.metastone.game.spells.SpellUtils.getSpellDiscover(SpellUtils.java:134)
    at net.demilich.metastone.game.spells.DiscoverOptionSpell.onCast(DiscoverOptionSpell.java:63)
    at net.demilich.metastone.game.spells.Spell.castForPlayer(Spell.java:58)
    at net.demilich.metastone.game.spells.Spell.cast(Spell.java:19)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:317)
    at net.demilich.metastone.game.actions.BattlecryAction.execute(BattlecryAction.java:68)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1256)
    at net.demilich.metastone.game.logic.GameLogic.performBattlecryAction(GameLogic.java:1568)
    at net.demilich.metastone.game.logic.GameLogic.resolveBattlecry(GameLogic.java:1542)
    at net.demilich.metastone.game.logic.GameLogic.summon(GameLogic.java:1703)
    at net.demilich.metastone.game.actions.PlayMinionCardAction.play(PlayMinionCardAction.java:49)
    at net.demilich.metastone.game.actions.PlayCardAction.execute(PlayCardAction.java:50)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1256)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:43)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:53)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:53)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.requestAction(GameStateValueBehaviour.java:117)
    at net.demilich.metastone.game.GameContext.playTurn(GameContext.java:492)
    at net.demilich.metastone.game.GameContext.play(GameContext.java:465)

Here's a sample game context. I don't believe anything special is going on, I reproduce every time whenever we use the Renolock deck.

GameContext hashCode: 1132318042
Player: [Test Player] - Valeera Sanguinar Mana: 0/7 HP: 28(0)
Behaviour: Game state value opponentPlayer 
Minions:
    [MINION 'Edwin VanCleef'id:75 8/4 hashCode: 716744331]
    [MINION 'Questing Adventurer'id:74 4/4 hashCode: 676660077]
Cards (hand):
    [SPELL 'Fan of Knives' [EntityReference id:22] Manacost:3]
Secrets:
[Opponent Player] - Jaina Proudmoore Mana: 1/7 HP: 28(0)
Behaviour: Game state value opponentPlayer 
Minions:
    [MINION 'Kazakus'id:76 3/3 hashCode: 742758095]
Cards (hand):
    [SPELL 'Firelands Portal' [EntityReference id:61] Manacost:7]
    [SPELL 'Ice Barrier' [EntityReference id:44] Manacost:3]
    [MINION 'Alexstrasza' [EntityReference id:65] Manacost:9]
    [SPELL 'Frost Nova' [EntityReference id:47] Manacost:3]
    [SPELL 'Polymorph' [EntityReference id:53] Manacost:4]
    [MINION 'Emperor Thaurissan' [EntityReference id:58] Manacost:6]
Secrets:
Turn: 14
Result: RUNNING
Winner: tbd
Environment:
{PENDING_CARD=[EntityReference id:50], EVENT_TARGET_REFERENCE_STACK=[], SPELL_TARGET=[EntityReference id:75], DAMAGE_STACK=[2], TARGET=null, SUMMON_REFERENCE_STACK=[[EntityReference id:76]], LAST_MANA_COST=4}
doctorpangloss commented 7 years ago

Also target is null:

java.lang.NullPointerException
    at net.demilich.metastone.game.logic.GameLogic.damage(GameLogic.java:396)
    at net.demilich.metastone.game.spells.DamageSpell.onCast(DamageSpell.java:62)
    at net.demilich.metastone.game.spells.Spell.castForPlayer(Spell.java:58)
    at net.demilich.metastone.game.spells.Spell.cast(Spell.java:19)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:317)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:271)
    at net.demilich.metastone.game.actions.DiscoverAction.execute(DiscoverAction.java:74)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1253)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:43)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.requestAction(GameStateValueBehaviour.java:117)
    at net.demilich.metastone.game.spells.SpellUtils.getSpellDiscover(SpellUtils.java:134)
    at net.demilich.metastone.game.spells.CreateCardSpell.discoverCardParts(CreateCardSpell.java:62)
    at net.demilich.metastone.game.spells.CreateCardSpell.onCast(CreateCardSpell.java:84)
    at net.demilich.metastone.game.spells.Spell.castForPlayer(Spell.java:58)
    at net.demilich.metastone.game.spells.Spell.cast(Spell.java:19)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:317)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:271)
    at net.demilich.metastone.game.actions.DiscoverAction.execute(DiscoverAction.java:74)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1253)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:43)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.requestAction(GameStateValueBehaviour.java:117)
    at net.demilich.metastone.game.spells.SpellUtils.getSpellDiscover(SpellUtils.java:134)
    at net.demilich.metastone.game.spells.DiscoverOptionSpell.onCast(DiscoverOptionSpell.java:63)
    at net.demilich.metastone.game.spells.Spell.castForPlayer(Spell.java:58)
    at net.demilich.metastone.game.spells.Spell.cast(Spell.java:19)
    at net.demilich.metastone.game.logic.GameLogic.castSpell(GameLogic.java:317)
    at net.demilich.metastone.game.actions.BattlecryAction.execute(BattlecryAction.java:68)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1253)
    at net.demilich.metastone.game.logic.GameLogic.performBattlecryAction(GameLogic.java:1565)
    at net.demilich.metastone.game.logic.GameLogic.resolveBattlecry(GameLogic.java:1539)
    at net.demilich.metastone.game.logic.GameLogic.summon(GameLogic.java:1700)
    at net.demilich.metastone.game.actions.PlayMinionCardAction.play(PlayMinionCardAction.java:49)
    at net.demilich.metastone.game.actions.PlayCardAction.execute(PlayCardAction.java:50)
    at net.demilich.metastone.game.logic.GameLogic.performGameAction(GameLogic.java:1253)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:43)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:53)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.alphaBeta(GameStateValueBehaviour.java:53)
    at net.demilich.metastone.game.behaviour.threat.GameStateValueBehaviour.requestAction(GameStateValueBehaviour.java:117)
    at net.demilich.metastone.game.GameContext.playTurn(GameContext.java:492)
    at net.demilich.metastone.game.GameContext.play(GameContext.java:465)
webadict commented 7 years ago

Ah... I see the issue. The AI doesn't really like the way I've programmed the spell, since it tries to perform this the way an AI would want it performed: It takes the actions, and then tries to verify if it's the best action (See what the aftermath is after performing said issue...) The most obvious problem is that, well, this has multiple actions in a row, so that method fails anyway. The second most obvious problem is that then it tries to cast that spell without any targets or source, since it's a crazy AI on the loose trying to decide how good Kazakus' potions really are.

So, I've done a small lobotomy on the AI. It now simply picks the first option in a Discover action. This doesn't actually fix the problem, but... Well, the random chance that is modified when it happens seems to make that pretty difficult.

doctorpangloss commented 7 years ago

This issue was resolved thank you.