Code:
using HREngine.API;
using HREngine.API.Utilities;
using System;
using System.Collections.Generic;
namespace HREngine.Bots
{
internal class PossibleTurnAttack
{
public int Cost;
public int Attack;
public int NeededAttack;
public List<HRCard> Cards;
}
/// <summary>
/// [EN]
/// This Bot implements a simple AI to fight against enemies.
/// Features:
/// - Attacking Taunts
/// - Spawn Taunts
/// - Attack Hero if available.
///
/// [DE]
/// Dieser Bot implementiert eine einfache KI um gegen Gegner
/// zu kämpfen.
/// Funktionen:
/// - Kämpfe gegen Gegner mit Spott
/// - Lege Karten mit Spott
/// - Kämpfe gegen Held
/// </summary>
public class Bot : API.IBot
{
/// <summary>
/// [EN]
/// Initializes a new instance of the <see cref="Bot"/> class.
///
/// [DE]
/// Initialisiert eine neue Instanz dieses Bots.
/// </summary>
public Bot()
{
// [EN]
// Setting required event handler to implement bot logic.
OnBattleLocalPlayerTurn = HandleBattleLocalPlayerTurnHandler;
}
private PossibleTurnAttack GetPossibleTurnAttack(int NeededAttackPower, int MaxCards = -1)
{
PossibleTurnAttack result = new PossibleTurnAttack();
result.NeededAttack = NeededAttackPower;
result.Cards = new List<HRCard>();
try
{
var p = HRPlayer.GetLocalPlayer();
// Loop through all minions that can attack...
List<HRCard> playerBattleField = HRCard.GetCards(HRPlayer.GetLocalPlayer(), HRCardZone.PLAY);
foreach (var card in playerBattleField)
{
if (HRBattle.CanUseCard(card.GetEntity()))
{
if (MaxCards == -1)
{
result.Attack += card.GetEntity().GetATK();
result.Cards.Add(card);
}
else
{
if (result.Cards.Count + 1 == MaxCards)
{
if (result.Attack + card.GetEntity().GetATK() >= NeededAttackPower)
{
result.Attack += card.GetEntity().GetATK();
result.Cards.Add(card);
}
}
else
{
result.Attack += card.GetEntity().GetATK();
result.Cards.Add(card);
}
}
if (result.Attack >= result.NeededAttack || result.Cards.Count == MaxCards)
break;
}
}
if (result.Attack < result.NeededAttack)
{
// Try with hero power?
if (p.GetHeroPower().GetCost() <= p.GetNumAvailableResources())
{
if (p.GetHeroPower().GetATK() > 0)
{
result.Attack += p.GetHeroPower().GetATK();
result.Cost += p.GetHeroPower().GetCost();
result.Cards.Add(p.GetHeroCard());
}
}
// Try with remaining cards..
if (result.Attack < result.NeededAttack)
{
List<HRCard> playerHandField = HRCard.GetCards(p, HRCardZone.HAND);
foreach (var card in playerBattleField)
{
int leftResources = p.GetNumAvailableResources() - result.Cost;
if (card.GetEntity().IsSpell() && card.GetEntity().GetATK() > 0)
{
if (card.GetEntity().GetCost() <= leftResources)
{
result.Attack += card.GetEntity().GetATK();
result.Cost += card.GetEntity().GetCost();
result.Cards.Add(card);
}
}
else if (card.GetEntity().IsMinion() && card.GetEntity().GetATK() > 0)
{
if (card.GetEntity().HasBattlecry())
{
if (card.GetEntity().GetCost() <= leftResources)
{
result.Attack += card.GetEntity().GetATK();
result.Cost += card.GetEntity().GetCost();
result.Cards.Add(card);
}
}
}
if (result.Attack >= result.NeededAttack)
break;
}
}
}
}
catch (Exception)
{
HRLog.Write("GetPossibleTurnAttack caused an exception");
throw;
}
return result;
}
/// <summary>
/// [EN]
/// This handler is executed when the local player turn is active.
///
/// [DE]
/// Dieses Event wird ausgelöst wenn der Spieler am Zug ist.
/// </summary>
private void HandleBattleLocalPlayerTurnHandler()
{
try
{
SafeHandleBattleLocalPlayerTurnHandler();
}
catch (Exception Exception)
{
HRLog.Write(Exception.Message);
HRLog.Write(Environment.StackTrace);
}
HRBattle.FinishRound();
}
private void SafeHandleBattleLocalPlayerTurnHandler()
{
bool didHeroPower = false;
// PRIEST: Heal if Health < 18
if (HRPlayer.GetLocalPlayer().GetRemainingHP() < 18 &&
HRPlayer.GetLocalPlayer().GetClass() == HRClass.PRIEST)
{
try
{
TryHeroPower();
}
catch (Exception)
{
HRLog.Write("TryHeroPower caused an exception");
throw;
}
didHeroPower = true;
}
try
{
TryPossibleWin();
}
catch (Exception)
{
HRLog.Write("TryPossibleWin caused an exception.");
throw;
}
try
{
TrySpawnBattleCry();
}
catch (Exception)
{
HRLog.Write("TrySpawnBattleCry caused an exception.");
}
try
{
TrySpawnRest();
}
catch (Exception)
{
HRLog.Write("TrySpawnRest caused an exception");
throw;
}
try
{
TryPossibleOneShots();
}
catch (Exception)
{
HRLog.Write("TryPossibleOneShots caused an exception.");
throw;
}
// TryFight
try
{
TryFight();
}
catch (Exception)
{
HRLog.Write("TryFight caused an exception");
throw;
}
if (!didHeroPower)
{
try
{
TryHeroPower();
}
catch (Exception)
{
HRLog.Write("TryHeroPower caused an exception");
throw;
}
}
}
private void TryHeroPower()
{
var nextMinion = GetNextMinion(new List<int>());
if (nextMinion == null)
nextMinion = HRPlayer.GetEnemyPlayer().GetHero().GetCard();
HRBattle.UseHeroPower(nextMinion.GetEntity());
}
private void TrySpawnRest()
{
var p = HRPlayer.GetLocalPlayer();
List<HRCard> hand = HRCard.GetCards(p, HRCardZone.HAND);
foreach (var card in hand)
{
if (card.GetEntity().IsMinion() || card.GetEntity().IsSpell())
{
if (HRBattle.CanPlayCard(card.GetEntity()))
{
if (!HRBattle.Push(card))
HRLog.Write(String.Format("Pushing card failed with card {0}", card.GetEntity().GetName()));
else
{
if (HRBattle.IsInTargetMode())
{
var nextMinion = GetNextMinion(new List<int>());
if (nextMinion != null)
HRBattle.Target(nextMinion.GetEntity());
else
HRBattle.Target(HRPlayer.GetEnemyPlayer().GetHero());
}
}
}
}
else
{
HRLog.Write(
String.Format("Card ({0}) is not a minion or spell and cannot be used.",
card.GetEntity().GetName()));
}
}
}
private void TrySpawnBattleCry()
{
var p = HRPlayer.GetLocalPlayer();
List<HRCard> hand = HRCard.GetCards(p, HRCardZone.HAND);
foreach (var card in hand)
{
if (card.GetEntity().HasBattlecry() && HRBattle.CanUseCard(card.GetEntity()))
{
if (!HRBattle.Push(card))
HRLog.Write(String.Format("Pushing battlecry card failed with card {0}", card.GetEntity().GetName()));
}
}
}
private void TryFight()
{
List<int> lsCheckedEntities = new List<int>();
HRCard nextMinion = null;
using (var imp = new HRDeadlockBypass("TryFight"))
{
do
{
nextMinion = GetNextMinion(lsCheckedEntities);
if (nextMinion != null)
{
var possibleAttack = GetPossibleTurnAttack(nextMinion.GetEntity().GetRemainingHP());
if (possibleAttack.Attack >= nextMinion.GetEntity().GetRemainingHP() &&
possibleAttack.Cost <= HRPlayer.GetLocalPlayer().GetNumAvailableResources() &&
possibleAttack.Cards.Count > 0)
{
HRLog.Write(String.Format("Fighting with {0}", nextMinion.GetEntity().GetName()));
PerformAttackFromPossibleTurnAttack(possibleAttack, nextMinion.GetEntity());
}
lsCheckedEntities.Add(nextMinion.GetEntity().GetEntityId());
}
if (imp.HasDeadlock && nextMinion != null)
{
HRLog.Write(String.Format("Deadlock occured with entity {0} and ID {1}",
nextMinion.GetEntity().GetName(),
nextMinion.GetEntity().GetEntityId()));
}
} while (!imp.HasDeadlock && nextMinion != null);
}
// Try to attack his hero...?
var e = HRPlayer.GetEnemyPlayer();
if (!e.HasATauntMinion())
{
var possibleAttack = GetPossibleTurnAttack(e.GetHero().GetRemainingHP());
if (possibleAttack.Cost <= HRPlayer.GetLocalPlayer().GetNumAvailableResources())
{
PerformAttackFromPossibleTurnAttack(possibleAttack, e.GetHero());
}
}
}
private void TryPossibleWin()
{
var e = HRPlayer.GetEnemyPlayer();
if (e.HasATauntMinion() || !e.GetHeroCard().GetEntity().CanBeAttacked())
return;
var p = HRPlayer.GetLocalPlayer();
PossibleTurnAttack possiblePower = GetPossibleTurnAttack(e.GetHero().GetRemainingHP());
if (possiblePower.Attack >= e.GetHero().GetRemainingHP() && possiblePower.Cost <= p.GetNumAvailableResources())
{
HRLog.Write(String.Format("Prepare next win, killing {0}", e.GetHero().GetName()));
PerformAttackFromPossibleTurnAttack(possiblePower, e.GetHero());
}
}
private void PerformAttackFromPossibleTurnAttack(PossibleTurnAttack possiblePower, HREntity entity)
{
if (entity == null)
return;
if (!entity.CanBeAttacked())
{
HRLog.Write(String.Format("Entity {0} cannot be attacked", entity.GetName()));
return;
}
if (possiblePower.Cards == null)
return;
foreach (var item in possiblePower.Cards)
{
if (item.GetEntity().IsHero())
HRBattle.UseHeroPower(entity);
else
{
HRCardZone zone = item.GetEntity().GetZone();
switch (zone)
{
case HRCardZone.PLAY:
HRBattle.Attack(item.GetEntity(), entity);
break;
case HRCardZone.HAND:
if (item.GetEntity().IsMinion() && item.GetEntity().HasBattlecry())
{
HRBattle.Push(item);
HRBattle.Attack(item.GetEntity(), entity);
}
else if (item.GetEntity().IsSpell())
{
HRBattle.Push(item);
if (HRBattle.IsInTargetMode())
HRBattle.Target(entity);
}
break;
default:
break;
}
}
}
}
private void TryPossibleOneShots()
{
List<int> lsCheckedEntities = new List<int>();
HRCard nextMinion = null;
using (var imp = new HRDeadlockBypass("TryPossibleOneShots"))
{
do
{
nextMinion = GetNextMinion(lsCheckedEntities);
if (nextMinion != null)
{
var possibleAttack = GetPossibleTurnAttack(nextMinion.GetEntity().GetRemainingHP(), 1);
if (possibleAttack.Attack >= nextMinion.GetEntity().GetRemainingHP() &&
possibleAttack.Cost <= HRPlayer.GetLocalPlayer().GetNumAvailableResources() && possibleAttack.Cards.Count == 1)
{
HRLog.Write(String.Format("Perform 1 Hit to {0}", nextMinion.GetEntity().GetName()));
PerformAttackFromPossibleTurnAttack(possibleAttack, nextMinion.GetEntity());
}
lsCheckedEntities.Add(nextMinion.GetEntity().GetEntityId());
}
if (imp.HasDeadlock && nextMinion != null)
{
HRLog.Write(String.Format("Deadlock occured with entity {0} and ID {1}",
nextMinion.GetEntity().GetName(),
nextMinion.GetEntity().GetEntityId()));
}
} while (!imp.HasDeadlock && nextMinion != null);
}
}
private HRCard GetNextMinion(List<int> lsCheckedEntities)
{
HRCard nextMinion = null;
var e = HRPlayer.GetEnemyPlayer();
List<HRCard> battleField = HRCard.GetCards(e, HRCardZone.PLAY);
foreach (var card in battleField)
{
if (lsCheckedEntities.Contains(card.GetEntity().GetEntityId()))
continue;
if (card.GetEntity().CanBeAttacked())
{
if (nextMinion == null)
{
if (!e.HasATauntMinion() || card.GetEntity().HasTaunt())
nextMinion = card;
}
else
{
if (card.GetEntity().GetRemainingHP() < nextMinion.GetEntity().GetRemainingHP())
{
if (!e.HasATauntMinion() || card.GetEntity().HasTaunt())
nextMinion = card;
}
else if (card.GetEntity().GetRemainingHP() == nextMinion.GetEntity().GetRemainingHP())
{
// kill minions with higher attack first
if (card.GetEntity().GetATK() > nextMinion.GetEntity().GetATK())
{
if (!e.HasATauntMinion() || card.GetEntity().HasTaunt())
nextMinion = card;
}
}
}
}
}
return nextMinion;
}
}
}