package net.demilich.metastone.game.behaviour.threat;
import java.util.ArrayList;
import java.util.List;
import net.demilich.metastone.game.Attribute;
import net.demilich.metastone.game.GameContext;
import net.demilich.metastone.game.Player;
import net.demilich.metastone.game.behaviour.heuristic.IGameStateHeuristic;
import net.demilich.metastone.game.cards.Card;
import net.demilich.metastone.game.entities.heroes.Hero;
import net.demilich.metastone.game.entities.heroes.HeroClass;
import net.demilich.metastone.game.entities.minions.Minion;
public class ThreatBasedHeuristic implements IGameStateHeuristic {
private static List<String> hardRemoval;
static {
hardRemoval = new ArrayList<String>();
hardRemoval.add("spell_polymorph");
hardRemoval.add("spell_execute");
hardRemoval.add("spell_crush");
hardRemoval.add("spell_assassinate");
hardRemoval.add("spell_siphon_soul");
hardRemoval.add("spell_shadow_word_death");
hardRemoval.add("spell_naturalize");
hardRemoval.add("spell_hex");
hardRemoval.add("spell_humility");
hardRemoval.add("spell_equality");
hardRemoval.add("spell_deadly_shot");
hardRemoval.add("spell_sap");
hardRemoval.add("minion_doomsayer");
hardRemoval.add("minion_big_game_hunter");
}
private static ThreatLevel calcuateThreatLevel(GameContext context, int playerId) {
int damageOnBoard = 0;
Player player = context.getPlayer(playerId);
Player opponent = context.getOpponent(player);
for (Minion minion : opponent.getMinions()) {
damageOnBoard += minion.getAttack() * minion.getAttributeValue(Attribute.NUMBER_OF_ATTACKS);
}
damageOnBoard += getHeroDamage(opponent.getHero());
int remainingHp = player.getHero().getEffectiveHp() - damageOnBoard;
if (remainingHp < 1) {
return ThreatLevel.RED;
} else if (remainingHp < 15) {
return ThreatLevel.YELLOW;
}
return ThreatLevel.GREEN;
}
private static int getHeroDamage(Hero hero) {
int heroDamage = 0;
if (hero.getHeroClass() == HeroClass.MAGE) {
heroDamage += 1;
} else if (hero.getHeroClass() == HeroClass.HUNTER) {
heroDamage += 2;
} else if (hero.getHeroClass() == HeroClass.DRUID) {
heroDamage += 1;
} else if (hero.getHeroClass() == HeroClass.ROGUE) {
heroDamage += 1;
}
if (hero.getWeapon() != null) {
heroDamage += hero.getWeapon().getWeaponDamage();
}
return heroDamage;
}
private static boolean isHardRemoval(Card card) {
return hardRemoval.contains(card.getCardId());
}
private final FeatureVector weights;
public ThreatBasedHeuristic(FeatureVector vector) {
this.weights = vector;
}
private double calculateMinionScore(Minion minion, ThreatLevel threatLevel) {
if (minion.hasAttribute(Attribute.MARKED_FOR_DEATH)) {
return 0;
}
double minionScore = weights.get(WeightedFeature.MINION_INTRINSIC_VALUE);
minionScore += weights.get(WeightedFeature.MINION_ATTACK_FACTOR)
* (minion.getAttack() - minion.getAttributeValue(Attribute.TEMPORARY_ATTACK_BONUS));
minionScore += weights.get(WeightedFeature.MINION_HP_FACTOR) * minion.getHp();
if (minion.hasAttribute(Attribute.TAUNT)) {
switch (threatLevel) {
case RED:
minionScore += weights.get(WeightedFeature.MINION_RED_TAUNT_MODIFIER);
break;
case YELLOW:
minionScore += weights.get(WeightedFeature.MINION_YELLOW_TAUNT_MODIFIER);
break;
default:
minionScore += weights.get(WeightedFeature.MINION_DEFAULT_TAUNT_MODIFIER);
break;
}
}
if (minion.hasAttribute(Attribute.WINDFURY)) {
minionScore += weights.get(WeightedFeature.MINION_WINDFURY_MODIFIER);
} else if (minion.hasAttribute(Attribute.MEGA_WINDFURY)) {
minionScore += 2 * weights.get(WeightedFeature.MINION_WINDFURY_MODIFIER);
}
if (minion.hasAttribute(Attribute.DIVINE_SHIELD)) {
minionScore += weights.get(WeightedFeature.MINION_DIVINE_SHIELD_MODIFIER);
}
if (minion.hasAttribute(Attribute.SPELL_DAMAGE)) {
minionScore += minion.getAttributeValue(Attribute.SPELL_DAMAGE) * weights.get(WeightedFeature.MINION_SPELL_POWER_MODIFIER);
}
if (minion.hasAttribute(Attribute.STEALTH)) {
minionScore += weights.get(WeightedFeature.MINION_STEALTHED_MODIFIER);
}
if (minion.hasAttribute(Attribute.UNTARGETABLE_BY_SPELLS)) {
minionScore += weights.get(WeightedFeature.MINION_UNTARGETABLE_BY_SPELLS_MODIFIER);
}
return minionScore;
}
@Override
public double getScore(GameContext context, int playerId) {
Player player = context.getPlayer(playerId);
Player opponent = context.getOpponent(player);
if (player.getHero().isDestroyed()) {
return Float.NEGATIVE_INFINITY;
}
if (opponent.getHero().isDestroyed()) {
return Float.POSITIVE_INFINITY;
}
double score = 0;
ThreatLevel threatLevel = calcuateThreatLevel(context, playerId);
switch (threatLevel) {
case RED:
score += weights.get(WeightedFeature.RED_MODIFIER);
break;
case YELLOW:
score += weights.get(WeightedFeature.YELLOW_MODIFIER);
break;
default:
break;
}
score += player.getHero().getEffectiveHp() * weights.get(WeightedFeature.OWN_HP_FACTOR);
score += opponent.getHero().getEffectiveHp() * weights.get(WeightedFeature.OPPONENT_HP_FACTOR);
for (Card card : player.getHand()) {
if (isHardRemoval(card)) {
score += weights.get(WeightedFeature.HARD_REMOVAL_VALUE);
}
}
score += player.getHand().getCount() * weights.get(WeightedFeature.OWN_CARD_COUNT);
score += opponent.getHand().getCount() * weights.get(WeightedFeature.OPPONENT_CARD_COUNT);
for (Minion minion : player.getMinions()) {
score += calculateMinionScore(minion, threatLevel);
}
for (Minion minion : opponent.getMinions()) {
score -= calculateMinionScore(minion, threatLevel);
}
return score;
}
@Override
public void onActionSelected(GameContext context, int playerId) {
}
}