diff --git a/ExecutorBase/ExecutorBase.csproj b/ExecutorBase/ExecutorBase.csproj new file mode 100644 index 00000000..3062e851 --- /dev/null +++ b/ExecutorBase/ExecutorBase.csproj @@ -0,0 +1,89 @@ + + + + + Debug + AnyCPU + {A1583FD7-7985-47DD-A835-8134DBF5811C} + Library + Properties + ExecutorBase + ExecutorBase + v4.0 + 512 + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\Mono.Data.Sqlite.dll + + + + + + + + + + + + ..\YGOSharp.OCGWrapper.dll + + + ..\YGOSharp.OCGWrapper.Enums.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Game/AI/AIUtil.cs b/ExecutorBase/Game/AI/AIUtil.cs similarity index 97% rename from Game/AI/AIUtil.cs rename to ExecutorBase/Game/AI/AIUtil.cs index eeeb8bf0..ab9f50f9 100644 --- a/Game/AI/AIUtil.cs +++ b/ExecutorBase/Game/AI/AIUtil.cs @@ -1,446 +1,446 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using YGOSharp.OCGWrapper.Enums; -namespace WindBot.Game.AI -{ - public class AIUtil - { - public Duel Duel { get; private set; } - public ClientField Bot { get; private set; } - public ClientField Enemy { get; private set; } - - public AIUtil(Duel duel) - { - Duel = duel; - Bot = Duel.Fields[0]; - Enemy = Duel.Fields[1]; - } - - /// - /// Get the total ATK Monster of the player. - /// - public int GetTotalAttackingMonsterAttack(int player) - { - return Duel.Fields[player].GetMonsters().Where(m => m.IsAttack()).Sum(m => (int?)m.Attack) ?? 0; - } - /// - /// Get the best ATK or DEF power of the field. - /// - /// Bot or Enemy. - /// Only calculate attack. - public int GetBestPower(ClientField field, bool onlyATK = false) - { - return field.MonsterZone.GetMonsters() - .Where(card => !onlyATK || card.IsAttack()) - .Max(card => (int?)card.GetDefensePower()) ?? -1; - } - - public int GetBestAttack(ClientField field) - { - return GetBestPower(field, true); - } - - public bool IsOneEnemyBetterThanValue(int value, bool onlyATK) - { - return Enemy.MonsterZone.GetMonsters() - .Any(card => card.GetDefensePower() > value && (!onlyATK || card.IsAttack())); - } - - public bool IsAllEnemyBetterThanValue(int value, bool onlyATK) - { - List monsters = Enemy.MonsterZone.GetMonsters(); - return monsters.Count > 0 && monsters - .All(card => card.GetDefensePower() > value && (!onlyATK || card.IsAttack())); - } - - /// - /// Deprecated, use IsOneEnemyBetter and IsAllEnemyBetter instead. - /// - public bool IsEnemyBetter(bool onlyATK, bool all) - { - if (all) - return IsAllEnemyBetter(onlyATK); - else - return IsOneEnemyBetter(onlyATK); - } - - /// - /// Is there an enemy monster who has better power than the best power of the bot's? - /// - /// Only calculate attack. - public bool IsOneEnemyBetter(bool onlyATK = false) - { - int bestBotPower = GetBestPower(Bot, onlyATK); - return IsOneEnemyBetterThanValue(bestBotPower, onlyATK); - } - - /// - /// Do all enemy monsters have better power than the best power of the bot's? - /// - /// Only calculate attack. - public bool IsAllEnemyBetter(bool onlyATK = false) - { - int bestBotPower = GetBestPower(Bot, onlyATK); - return IsAllEnemyBetterThanValue(bestBotPower, onlyATK); - } - - public ClientCard GetBestBotMonster(bool onlyATK = false) - { - return Bot.MonsterZone.GetMonsters() - .Where(card => !onlyATK || card.IsAttack()) - .OrderByDescending(card => card.GetDefensePower()) - .FirstOrDefault(); - } - - public ClientCard GetWorstBotMonster(bool onlyATK = false) - { - return Bot.MonsterZone.GetMonsters() - .Where(card => !onlyATK || card.IsAttack()) - .OrderBy(card => card.GetDefensePower()) - .FirstOrDefault(); - } - - public ClientCard GetOneEnemyBetterThanValue(int value, bool onlyATK = false, bool canBeTarget = false) - { - return Enemy.MonsterZone.GetMonsters() - .FirstOrDefault(card => card.GetDefensePower() >= value && (!onlyATK || card.IsAttack()) && (!canBeTarget || !card.IsShouldNotBeTarget())); - } - - public ClientCard GetOneEnemyBetterThanMyBest(bool onlyATK = false, bool canBeTarget = false) - { - int bestBotPower = GetBestPower(Bot, onlyATK); - return GetOneEnemyBetterThanValue(bestBotPower, onlyATK, canBeTarget); - } - - public ClientCard GetProblematicEnemyCard(int attack = 0, bool canBeTarget = false) - { - ClientCard card = Enemy.MonsterZone.GetFloodgate(canBeTarget); - if (card != null) - return card; - - card = Enemy.SpellZone.GetFloodgate(canBeTarget); - if (card != null) - return card; - - card = Enemy.MonsterZone.GetDangerousMonster(canBeTarget); - if (card != null) - return card; - - card = Enemy.MonsterZone.GetInvincibleMonster(canBeTarget); - if (card != null) - return card; - - if (attack == 0) - attack = GetBestAttack(Bot); - return GetOneEnemyBetterThanValue(attack, true, canBeTarget); - } - - public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false) - { - ClientCard card = Enemy.MonsterZone.GetFloodgate(canBeTarget); - if (card != null) - return card; - - card = Enemy.MonsterZone.GetDangerousMonster(canBeTarget); - if (card != null) - return card; - - card = Enemy.MonsterZone.GetInvincibleMonster(canBeTarget); - if (card != null) - return card; - - if (attack == 0) - attack = GetBestAttack(Bot); - return GetOneEnemyBetterThanValue(attack, true, canBeTarget); - } - - public ClientCard GetProblematicEnemySpell() - { - ClientCard card = Enemy.SpellZone.GetFloodgate(); - return card; - } - - public ClientCard GetBestEnemyCard(bool onlyFaceup = false, bool canBeTarget = false) - { - ClientCard card = GetBestEnemyMonster(onlyFaceup, canBeTarget); - if (card != null) - return card; - - card = GetBestEnemySpell(onlyFaceup); - if (card != null) - return card; - - return null; - } - - public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false) - { - ClientCard card = GetProblematicEnemyMonster(0, canBeTarget); - if (card != null) - return card; - - card = Enemy.MonsterZone.GetHighestAttackMonster(canBeTarget); - if (card != null) - return card; - - List monsters = Enemy.GetMonsters(); - - // after GetHighestAttackMonster, the left monsters must be face-down. - if (monsters.Count > 0 && !onlyFaceup) - return monsters[0]; - - return null; - } - - public ClientCard GetWorstEnemyMonster(bool onlyATK = false) - { - return Enemy.MonsterZone.GetMonsters() - .Where(card => !onlyATK || card.IsAttack()) - .OrderBy(card => card.GetDefensePower()) - .FirstOrDefault(); - } - - public ClientCard GetBestEnemySpell(bool onlyFaceup = false) - { - ClientCard card = GetProblematicEnemySpell(); - if (card != null) - return card; - - var spells = Enemy.GetSpells(); - - card = spells.FirstOrDefault(ecard => ecard.IsFaceup() && (ecard.HasType(CardType.Continuous) || ecard.HasType(CardType.Field))); - if (card != null) - return card; - - if (spells.Count > 0 && !onlyFaceup) - return spells[0]; - - return null; - } - - public ClientCard GetPZone(int player, int id) - { - if (Duel.IsNewRule) - { - return Duel.Fields[player].SpellZone[id * 4]; - } - else - { - return Duel.Fields[player].SpellZone[6 + id]; - } - } - - public int GetStringId(int id, int option) - { - return id * 16 + option; - } - - public bool IsTurn1OrMain2() - { - return Duel.Turn == 1 || Duel.Phase == DuelPhase.Main2; - } - - public int GetBotAvailZonesFromExtraDeck(IList remove) - { - ClientCard[] BotMZone = (ClientCard[])Bot.MonsterZone.Clone(); - ClientCard[] EnemyMZone = (ClientCard[])Enemy.MonsterZone.Clone(); - for (int i = 0; i < 7; i++) - { - if (remove.Contains(BotMZone[i])) BotMZone[i] = null; - if (remove.Contains(EnemyMZone[i])) EnemyMZone[i] = null; - } - - if (!Duel.IsNewRule || Duel.IsNewRule2020) - return Zones.MainMonsterZones; - - int result = 0; - - if (BotMZone[5] == null && BotMZone[6] == null) - { - if (EnemyMZone[5] == null) - result |= Zones.z6; - if (EnemyMZone[6] == null) - result |= Zones.z5; - } - - if (BotMZone[0] == null && - ((BotMZone[1]?.HasLinkMarker(CardLinkMarker.Left) ?? false) || - (BotMZone[5]?.HasLinkMarker(CardLinkMarker.BottomLeft) ?? false) || - (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.TopRight) ?? false))) - result |= Zones.z0; - - if (BotMZone[1] == null && - ((BotMZone[0]?.HasLinkMarker(CardLinkMarker.Right) ?? false) || - (BotMZone[2]?.HasLinkMarker(CardLinkMarker.Left) ?? false) || - (BotMZone[5]?.HasLinkMarker(CardLinkMarker.Bottom) ?? false) || - (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.Top) ?? false))) - result |= Zones.z1; - - if (BotMZone[2] == null && - ((BotMZone[1]?.HasLinkMarker(CardLinkMarker.Right) ?? false) || - (BotMZone[3]?.HasLinkMarker(CardLinkMarker.Left) ?? false) || - (BotMZone[5]?.HasLinkMarker(CardLinkMarker.BottomRight) ?? false) || - (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.TopLeft) ?? false) || - (BotMZone[6]?.HasLinkMarker(CardLinkMarker.BottomLeft) ?? false) || - (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.TopRight) ?? false))) - result |= Zones.z2; - - if (BotMZone[3] == null && - ((BotMZone[2]?.HasLinkMarker(CardLinkMarker.Right) ?? false) || - (BotMZone[4]?.HasLinkMarker(CardLinkMarker.Left) ?? false) || - (BotMZone[6]?.HasLinkMarker(CardLinkMarker.Bottom) ?? false) || - (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.Top) ?? false))) - result |= Zones.z3; - - if (BotMZone[4] == null && - ((BotMZone[3]?.HasLinkMarker(CardLinkMarker.Right) ?? false) || - (BotMZone[6]?.HasLinkMarker(CardLinkMarker.BottomRight) ?? false) || - (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.TopLeft) ?? false))) - result |= Zones.z4; - - return result; - } - - public int GetBotAvailZonesFromExtraDeck(ClientCard remove) - { - return GetBotAvailZonesFromExtraDeck(new[] { remove }); - } - - public int GetBotAvailZonesFromExtraDeck() - { - return GetBotAvailZonesFromExtraDeck(new List()); - } - - public bool IsChainTarget(ClientCard card) - { - return Duel.ChainTargets.Any(card.Equals); - } - - public bool IsChainTargetOnly(ClientCard card) - { - return Duel.ChainTargetOnly.Count == 1 && card.Equals(Duel.ChainTargetOnly[0]); - } - - public bool ChainContainsCard(int id) - { - return Duel.CurrentChain.Any(card => card.IsCode(id)); - } - - public bool ChainContainsCard(int[] ids) - { - return Duel.CurrentChain.Any(card => card.IsCode(ids)); - } - - public int ChainCountPlayer(int player) - { - return Duel.CurrentChain.Count(card => card.Controller == player); - } - - public bool ChainContainPlayer(int player) - { - return Duel.CurrentChain.Any(card => card.Controller == player); - } - - public bool HasChainedTrap(int player) - { - return Duel.CurrentChain.Any(card => card.Controller == player && card.HasType(CardType.Trap)); - } - - public ClientCard GetLastChainCard() - { - return Duel.CurrentChain.LastOrDefault(); - } - - /// - /// Select cards listed in preferred. - /// - public IList SelectPreferredCards(ClientCard preferred, IList cards, int min, int max) - { - IList selected = new List(); - if (cards.IndexOf(preferred) > 0 && selected.Count < max) - { - selected.Add(preferred); - } - - return selected; - } - - /// - /// Select cards listed in preferred. - /// - public IList SelectPreferredCards(int preferred, IList cards, int min, int max) - { - IList selected = new List(); - foreach (ClientCard card in cards) - { - if (card.IsCode(preferred) && selected.Count < max) - selected.Add(card); - } - - return selected; - } - - /// - /// Select cards listed in preferred. - /// - public IList SelectPreferredCards(IList preferred, IList cards, int min, int max) - { - IList selected = new List(); - IList avail = cards.ToList(); // clone - while (preferred.Count > 0 && avail.IndexOf(preferred[0]) > 0 && selected.Count < max) - { - ClientCard card = preferred[0]; - preferred.Remove(card); - avail.Remove(card); - selected.Add(card); - } - - return selected; - } - - /// - /// Select cards listed in preferred. - /// - public IList SelectPreferredCards(IList preferred, IList cards, int min, int max) - { - IList selected = new List(); - foreach (int id in preferred) - { - foreach (ClientCard card in cards) - { - if (card.IsCode(id) && selected.Count < max && selected.IndexOf(card) <= 0) - selected.Add(card); - } - if (selected.Count >= max) - break; - } - - return selected; - } - - /// - /// Check and fix selected to make sure it meet the count requirement. - /// - public IList CheckSelectCount(IList _selected, IList cards, int min, int max) - { - var selected = _selected.ToList(); - if (selected.Count < min) - { - foreach (ClientCard card in cards) - { - if (!selected.Contains(card)) - selected.Add(card); - if (selected.Count >= max) - break; - } - } - while (selected.Count > max) - { - selected.RemoveAt(selected.Count - 1); - } - - return selected; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using YGOSharp.OCGWrapper.Enums; +namespace WindBot.Game.AI +{ + public class AIUtil + { + public Duel Duel { get; private set; } + public ClientField Bot { get; private set; } + public ClientField Enemy { get; private set; } + + public AIUtil(Duel duel) + { + Duel = duel; + Bot = Duel.Fields[0]; + Enemy = Duel.Fields[1]; + } + + /// + /// Get the total ATK Monster of the player. + /// + public int GetTotalAttackingMonsterAttack(int player) + { + return Duel.Fields[player].GetMonsters().Where(m => m.IsAttack()).Sum(m => (int?)m.Attack) ?? 0; + } + /// + /// Get the best ATK or DEF power of the field. + /// + /// Bot or Enemy. + /// Only calculate attack. + public int GetBestPower(ClientField field, bool onlyATK = false) + { + return field.MonsterZone.GetMonsters() + .Where(card => !onlyATK || card.IsAttack()) + .Max(card => (int?)card.GetDefensePower()) ?? -1; + } + + public int GetBestAttack(ClientField field) + { + return GetBestPower(field, true); + } + + public bool IsOneEnemyBetterThanValue(int value, bool onlyATK) + { + return Enemy.MonsterZone.GetMonsters() + .Any(card => card.GetDefensePower() > value && (!onlyATK || card.IsAttack())); + } + + public bool IsAllEnemyBetterThanValue(int value, bool onlyATK) + { + List monsters = Enemy.MonsterZone.GetMonsters(); + return monsters.Count > 0 && monsters + .All(card => card.GetDefensePower() > value && (!onlyATK || card.IsAttack())); + } + + /// + /// Deprecated, use IsOneEnemyBetter and IsAllEnemyBetter instead. + /// + public bool IsEnemyBetter(bool onlyATK, bool all) + { + if (all) + return IsAllEnemyBetter(onlyATK); + else + return IsOneEnemyBetter(onlyATK); + } + + /// + /// Is there an enemy monster who has better power than the best power of the bot's? + /// + /// Only calculate attack. + public bool IsOneEnemyBetter(bool onlyATK = false) + { + int bestBotPower = GetBestPower(Bot, onlyATK); + return IsOneEnemyBetterThanValue(bestBotPower, onlyATK); + } + + /// + /// Do all enemy monsters have better power than the best power of the bot's? + /// + /// Only calculate attack. + public bool IsAllEnemyBetter(bool onlyATK = false) + { + int bestBotPower = GetBestPower(Bot, onlyATK); + return IsAllEnemyBetterThanValue(bestBotPower, onlyATK); + } + + public ClientCard GetBestBotMonster(bool onlyATK = false) + { + return Bot.MonsterZone.GetMonsters() + .Where(card => !onlyATK || card.IsAttack()) + .OrderByDescending(card => card.GetDefensePower()) + .FirstOrDefault(); + } + + public ClientCard GetWorstBotMonster(bool onlyATK = false) + { + return Bot.MonsterZone.GetMonsters() + .Where(card => !onlyATK || card.IsAttack()) + .OrderBy(card => card.GetDefensePower()) + .FirstOrDefault(); + } + + public ClientCard GetOneEnemyBetterThanValue(int value, bool onlyATK = false, bool canBeTarget = false) + { + return Enemy.MonsterZone.GetMonsters() + .FirstOrDefault(card => card.GetDefensePower() >= value && (!onlyATK || card.IsAttack()) && (!canBeTarget || !card.IsShouldNotBeTarget())); + } + + public ClientCard GetOneEnemyBetterThanMyBest(bool onlyATK = false, bool canBeTarget = false) + { + int bestBotPower = GetBestPower(Bot, onlyATK); + return GetOneEnemyBetterThanValue(bestBotPower, onlyATK, canBeTarget); + } + + public ClientCard GetProblematicEnemyCard(int attack = 0, bool canBeTarget = false) + { + ClientCard card = Enemy.MonsterZone.GetFloodgate(canBeTarget); + if (card != null) + return card; + + card = Enemy.SpellZone.GetFloodgate(canBeTarget); + if (card != null) + return card; + + card = Enemy.MonsterZone.GetDangerousMonster(canBeTarget); + if (card != null) + return card; + + card = Enemy.MonsterZone.GetInvincibleMonster(canBeTarget); + if (card != null) + return card; + + if (attack == 0) + attack = GetBestAttack(Bot); + return GetOneEnemyBetterThanValue(attack, true, canBeTarget); + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false) + { + ClientCard card = Enemy.MonsterZone.GetFloodgate(canBeTarget); + if (card != null) + return card; + + card = Enemy.MonsterZone.GetDangerousMonster(canBeTarget); + if (card != null) + return card; + + card = Enemy.MonsterZone.GetInvincibleMonster(canBeTarget); + if (card != null) + return card; + + if (attack == 0) + attack = GetBestAttack(Bot); + return GetOneEnemyBetterThanValue(attack, true, canBeTarget); + } + + public ClientCard GetProblematicEnemySpell() + { + ClientCard card = Enemy.SpellZone.GetFloodgate(); + return card; + } + + public ClientCard GetBestEnemyCard(bool onlyFaceup = false, bool canBeTarget = false) + { + ClientCard card = GetBestEnemyMonster(onlyFaceup, canBeTarget); + if (card != null) + return card; + + card = GetBestEnemySpell(onlyFaceup); + if (card != null) + return card; + + return null; + } + + public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false) + { + ClientCard card = GetProblematicEnemyMonster(0, canBeTarget); + if (card != null) + return card; + + card = Enemy.MonsterZone.GetHighestAttackMonster(canBeTarget); + if (card != null) + return card; + + List monsters = Enemy.GetMonsters(); + + // after GetHighestAttackMonster, the left monsters must be face-down. + if (monsters.Count > 0 && !onlyFaceup) + return monsters[0]; + + return null; + } + + public ClientCard GetWorstEnemyMonster(bool onlyATK = false) + { + return Enemy.MonsterZone.GetMonsters() + .Where(card => !onlyATK || card.IsAttack()) + .OrderBy(card => card.GetDefensePower()) + .FirstOrDefault(); + } + + public ClientCard GetBestEnemySpell(bool onlyFaceup = false) + { + ClientCard card = GetProblematicEnemySpell(); + if (card != null) + return card; + + var spells = Enemy.GetSpells(); + + card = spells.FirstOrDefault(ecard => ecard.IsFaceup() && (ecard.HasType(CardType.Continuous) || ecard.HasType(CardType.Field))); + if (card != null) + return card; + + if (spells.Count > 0 && !onlyFaceup) + return spells[0]; + + return null; + } + + public ClientCard GetPZone(int player, int id) + { + if (Duel.IsNewRule) + { + return Duel.Fields[player].SpellZone[id * 4]; + } + else + { + return Duel.Fields[player].SpellZone[6 + id]; + } + } + + public int GetStringId(int id, int option) + { + return id * 16 + option; + } + + public bool IsTurn1OrMain2() + { + return Duel.Turn == 1 || Duel.Phase == DuelPhase.Main2; + } + + public int GetBotAvailZonesFromExtraDeck(IList remove) + { + ClientCard[] BotMZone = (ClientCard[])Bot.MonsterZone.Clone(); + ClientCard[] EnemyMZone = (ClientCard[])Enemy.MonsterZone.Clone(); + for (int i = 0; i < 7; i++) + { + if (remove.Contains(BotMZone[i])) BotMZone[i] = null; + if (remove.Contains(EnemyMZone[i])) EnemyMZone[i] = null; + } + + if (!Duel.IsNewRule || Duel.IsNewRule2020) + return Zones.MainMonsterZones; + + int result = 0; + + if (BotMZone[5] == null && BotMZone[6] == null) + { + if (EnemyMZone[5] == null) + result |= Zones.z6; + if (EnemyMZone[6] == null) + result |= Zones.z5; + } + + if (BotMZone[0] == null && + ((BotMZone[1]?.HasLinkMarker(CardLinkMarker.Left) ?? false) || + (BotMZone[5]?.HasLinkMarker(CardLinkMarker.BottomLeft) ?? false) || + (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.TopRight) ?? false))) + result |= Zones.z0; + + if (BotMZone[1] == null && + ((BotMZone[0]?.HasLinkMarker(CardLinkMarker.Right) ?? false) || + (BotMZone[2]?.HasLinkMarker(CardLinkMarker.Left) ?? false) || + (BotMZone[5]?.HasLinkMarker(CardLinkMarker.Bottom) ?? false) || + (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.Top) ?? false))) + result |= Zones.z1; + + if (BotMZone[2] == null && + ((BotMZone[1]?.HasLinkMarker(CardLinkMarker.Right) ?? false) || + (BotMZone[3]?.HasLinkMarker(CardLinkMarker.Left) ?? false) || + (BotMZone[5]?.HasLinkMarker(CardLinkMarker.BottomRight) ?? false) || + (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.TopLeft) ?? false) || + (BotMZone[6]?.HasLinkMarker(CardLinkMarker.BottomLeft) ?? false) || + (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.TopRight) ?? false))) + result |= Zones.z2; + + if (BotMZone[3] == null && + ((BotMZone[2]?.HasLinkMarker(CardLinkMarker.Right) ?? false) || + (BotMZone[4]?.HasLinkMarker(CardLinkMarker.Left) ?? false) || + (BotMZone[6]?.HasLinkMarker(CardLinkMarker.Bottom) ?? false) || + (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.Top) ?? false))) + result |= Zones.z3; + + if (BotMZone[4] == null && + ((BotMZone[3]?.HasLinkMarker(CardLinkMarker.Right) ?? false) || + (BotMZone[6]?.HasLinkMarker(CardLinkMarker.BottomRight) ?? false) || + (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.TopLeft) ?? false))) + result |= Zones.z4; + + return result; + } + + public int GetBotAvailZonesFromExtraDeck(ClientCard remove) + { + return GetBotAvailZonesFromExtraDeck(new[] { remove }); + } + + public int GetBotAvailZonesFromExtraDeck() + { + return GetBotAvailZonesFromExtraDeck(new List()); + } + + public bool IsChainTarget(ClientCard card) + { + return Duel.ChainTargets.Any(card.Equals); + } + + public bool IsChainTargetOnly(ClientCard card) + { + return Duel.ChainTargetOnly.Count == 1 && card.Equals(Duel.ChainTargetOnly[0]); + } + + public bool ChainContainsCard(int id) + { + return Duel.CurrentChain.Any(card => card.IsCode(id)); + } + + public bool ChainContainsCard(int[] ids) + { + return Duel.CurrentChain.Any(card => card.IsCode(ids)); + } + + public int ChainCountPlayer(int player) + { + return Duel.CurrentChain.Count(card => card.Controller == player); + } + + public bool ChainContainPlayer(int player) + { + return Duel.CurrentChain.Any(card => card.Controller == player); + } + + public bool HasChainedTrap(int player) + { + return Duel.CurrentChain.Any(card => card.Controller == player && card.HasType(CardType.Trap)); + } + + public ClientCard GetLastChainCard() + { + return Duel.CurrentChain.LastOrDefault(); + } + + /// + /// Select cards listed in preferred. + /// + public IList SelectPreferredCards(ClientCard preferred, IList cards, int min, int max) + { + IList selected = new List(); + if (cards.IndexOf(preferred) > 0 && selected.Count < max) + { + selected.Add(preferred); + } + + return selected; + } + + /// + /// Select cards listed in preferred. + /// + public IList SelectPreferredCards(int preferred, IList cards, int min, int max) + { + IList selected = new List(); + foreach (ClientCard card in cards) + { + if (card.IsCode(preferred) && selected.Count < max) + selected.Add(card); + } + + return selected; + } + + /// + /// Select cards listed in preferred. + /// + public IList SelectPreferredCards(IList preferred, IList cards, int min, int max) + { + IList selected = new List(); + IList avail = cards.ToList(); // clone + while (preferred.Count > 0 && avail.IndexOf(preferred[0]) > 0 && selected.Count < max) + { + ClientCard card = preferred[0]; + preferred.Remove(card); + avail.Remove(card); + selected.Add(card); + } + + return selected; + } + + /// + /// Select cards listed in preferred. + /// + public IList SelectPreferredCards(IList preferred, IList cards, int min, int max) + { + IList selected = new List(); + foreach (int id in preferred) + { + foreach (ClientCard card in cards) + { + if (card.IsCode(id) && selected.Count < max && selected.IndexOf(card) <= 0) + selected.Add(card); + } + if (selected.Count >= max) + break; + } + + return selected; + } + + /// + /// Check and fix selected to make sure it meet the count requirement. + /// + public IList CheckSelectCount(IList _selected, IList cards, int min, int max) + { + var selected = _selected.ToList(); + if (selected.Count < min) + { + foreach (ClientCard card in cards) + { + if (!selected.Contains(card)) + selected.Add(card); + if (selected.Count >= max) + break; + } + } + while (selected.Count > max) + { + selected.RemoveAt(selected.Count - 1); + } + + return selected; + } + } } \ No newline at end of file diff --git a/Game/AI/CardContainer.cs b/ExecutorBase/Game/AI/CardContainer.cs similarity index 97% rename from Game/AI/CardContainer.cs rename to ExecutorBase/Game/AI/CardContainer.cs index 1b263e59..0a09a506 100644 --- a/Game/AI/CardContainer.cs +++ b/ExecutorBase/Game/AI/CardContainer.cs @@ -1,155 +1,155 @@ -using System.Collections.Generic; -using YGOSharp.OCGWrapper.Enums; -using System; -using System.Linq; - -namespace WindBot.Game.AI -{ - public static class CardContainer - { - public static int CompareCardAttack(ClientCard cardA, ClientCard cardB) - { - if (cardA.Attack < cardB.Attack) - return -1; - if (cardA.Attack == cardB.Attack) - return 0; - return 1; - } - - public static int CompareCardLevel(ClientCard cardA, ClientCard cardB) - { - if (cardA.Level < cardB.Level) - return -1; - if (cardA.Level == cardB.Level) - return 0; - return 1; - } - - public static int CompareDefensePower(ClientCard cardA, ClientCard cardB) - { - if (cardA == null && cardB == null) - return 0; - if (cardA == null) - return -1; - if (cardB == null) - return 1; - int powerA = cardA.GetDefensePower(); - int powerB = cardB.GetDefensePower(); - if (powerA < powerB) - return -1; - if (powerA == powerB) - return 0; - return 1; - } - - public static ClientCard GetHighestAttackMonster(this IEnumerable cards, bool canBeTarget = false) - { - return cards - .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget())) - .OrderBy(card => card.Attack).FirstOrDefault(); - } - - public static ClientCard GetHighestDefenseMonster(this IEnumerable cards, bool canBeTarget = false) - { - return cards - .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget())) - .OrderBy(card => card.Defense).FirstOrDefault(); - } - - public static ClientCard GetLowestAttackMonster(this IEnumerable cards, bool canBeTarget = false) - { - return cards - .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget())) - .OrderByDescending(card => card.Attack).FirstOrDefault(); - } - - public static ClientCard GetLowestDefenseMonster(this IEnumerable cards, bool canBeTarget = false) - { - return cards - .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget())) - .OrderByDescending(card => card.Defense).FirstOrDefault(); - } - - public static bool ContainsMonsterWithLevel(this IEnumerable cards, int level) - { - return cards.Where(card => card?.Data != null).Any(card => !card.HasType(CardType.Xyz) && card.Level == level); - } - - public static bool ContainsMonsterWithRank(this IEnumerable cards, int rank) - { - return cards.Where(card => card?.Data != null).Any(card => card.HasType(CardType.Xyz) && card.Rank == rank); - } - - public static bool ContainsCardWithId(this IEnumerable cards, int id) - { - return cards.Where(card => card?.Data != null).Any(card => card.IsCode(id)); - } - - public static int GetCardCount(this IEnumerable cards, int id) - { - return cards.Where(card => card?.Data != null).Count(card => card.IsCode(id)); - } - - public static List GetMonsters(this IEnumerable cards) - { - return cards.Where(card => card?.Data != null && card.HasType(CardType.Monster)).ToList(); - } - - public static List GetFaceupPendulumMonsters(this IEnumerable cards) - { - return cards.Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && card.HasType(CardType.Pendulum)).ToList(); - } - - public static ClientCard GetInvincibleMonster(this IEnumerable cards, bool canBeTarget = false) - { - return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterInvincible() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget())); - } - - public static ClientCard GetDangerousMonster(this IEnumerable cards, bool canBeTarget = false) - { - return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterDangerous() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget())); - } - - public static ClientCard GetFloodgate(this IEnumerable cards, bool canBeTarget = false) - { - return cards.FirstOrDefault(card => card?.Data != null && card.IsFloodgate() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget())); - } - - public static ClientCard GetFirstMatchingCard(this IEnumerable cards, Func filter) - { - return cards.FirstOrDefault(card => card?.Data != null && filter.Invoke(card)); - } - - public static ClientCard GetFirstMatchingFaceupCard(this IEnumerable cards, Func filter) - { - return cards.FirstOrDefault(card => card?.Data != null && card.IsFaceup() && filter.Invoke(card)); - } - - public static IList GetMatchingCards(this IEnumerable cards, Func filter) - { - return cards.Where(card => card?.Data != null && filter.Invoke(card)).ToList(); - } - - public static int GetMatchingCardsCount(this IEnumerable cards, Func filter) - { - return cards.Count(card => card?.Data != null && filter.Invoke(card)); - } - - public static bool IsExistingMatchingCard(this IEnumerable cards, Func filter, int count = 1) - { - return cards.GetMatchingCardsCount(filter) >= count; - } - - public static ClientCard GetShouldBeDisabledBeforeItUseEffectMonster(this IEnumerable cards, bool canBeTarget = true) - { - return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget())); - } - - public static IEnumerable> GetCombinations(this IEnumerable elements, int k) - { - return k == 0 ? new[] { new T[0] } : - elements.SelectMany((e, i) => - elements.Skip(i + 1).GetCombinations(k - 1).Select(c => (new[] { e }).Concat(c))); - } - } +using System.Collections.Generic; +using YGOSharp.OCGWrapper.Enums; +using System; +using System.Linq; + +namespace WindBot.Game.AI +{ + public static class CardContainer + { + public static int CompareCardAttack(ClientCard cardA, ClientCard cardB) + { + if (cardA.Attack < cardB.Attack) + return -1; + if (cardA.Attack == cardB.Attack) + return 0; + return 1; + } + + public static int CompareCardLevel(ClientCard cardA, ClientCard cardB) + { + if (cardA.Level < cardB.Level) + return -1; + if (cardA.Level == cardB.Level) + return 0; + return 1; + } + + public static int CompareDefensePower(ClientCard cardA, ClientCard cardB) + { + if (cardA == null && cardB == null) + return 0; + if (cardA == null) + return -1; + if (cardB == null) + return 1; + int powerA = cardA.GetDefensePower(); + int powerB = cardB.GetDefensePower(); + if (powerA < powerB) + return -1; + if (powerA == powerB) + return 0; + return 1; + } + + public static ClientCard GetHighestAttackMonster(this IEnumerable cards, bool canBeTarget = false) + { + return cards + .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget())) + .OrderBy(card => card.Attack).FirstOrDefault(); + } + + public static ClientCard GetHighestDefenseMonster(this IEnumerable cards, bool canBeTarget = false) + { + return cards + .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget())) + .OrderBy(card => card.Defense).FirstOrDefault(); + } + + public static ClientCard GetLowestAttackMonster(this IEnumerable cards, bool canBeTarget = false) + { + return cards + .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget())) + .OrderByDescending(card => card.Attack).FirstOrDefault(); + } + + public static ClientCard GetLowestDefenseMonster(this IEnumerable cards, bool canBeTarget = false) + { + return cards + .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget())) + .OrderByDescending(card => card.Defense).FirstOrDefault(); + } + + public static bool ContainsMonsterWithLevel(this IEnumerable cards, int level) + { + return cards.Where(card => card?.Data != null).Any(card => !card.HasType(CardType.Xyz) && card.Level == level); + } + + public static bool ContainsMonsterWithRank(this IEnumerable cards, int rank) + { + return cards.Where(card => card?.Data != null).Any(card => card.HasType(CardType.Xyz) && card.Rank == rank); + } + + public static bool ContainsCardWithId(this IEnumerable cards, int id) + { + return cards.Where(card => card?.Data != null).Any(card => card.IsCode(id)); + } + + public static int GetCardCount(this IEnumerable cards, int id) + { + return cards.Where(card => card?.Data != null).Count(card => card.IsCode(id)); + } + + public static List GetMonsters(this IEnumerable cards) + { + return cards.Where(card => card?.Data != null && card.HasType(CardType.Monster)).ToList(); + } + + public static List GetFaceupPendulumMonsters(this IEnumerable cards) + { + return cards.Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && card.HasType(CardType.Pendulum)).ToList(); + } + + public static ClientCard GetInvincibleMonster(this IEnumerable cards, bool canBeTarget = false) + { + return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterInvincible() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget())); + } + + public static ClientCard GetDangerousMonster(this IEnumerable cards, bool canBeTarget = false) + { + return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterDangerous() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget())); + } + + public static ClientCard GetFloodgate(this IEnumerable cards, bool canBeTarget = false) + { + return cards.FirstOrDefault(card => card?.Data != null && card.IsFloodgate() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget())); + } + + public static ClientCard GetFirstMatchingCard(this IEnumerable cards, Func filter) + { + return cards.FirstOrDefault(card => card?.Data != null && filter.Invoke(card)); + } + + public static ClientCard GetFirstMatchingFaceupCard(this IEnumerable cards, Func filter) + { + return cards.FirstOrDefault(card => card?.Data != null && card.IsFaceup() && filter.Invoke(card)); + } + + public static IList GetMatchingCards(this IEnumerable cards, Func filter) + { + return cards.Where(card => card?.Data != null && filter.Invoke(card)).ToList(); + } + + public static int GetMatchingCardsCount(this IEnumerable cards, Func filter) + { + return cards.Count(card => card?.Data != null && filter.Invoke(card)); + } + + public static bool IsExistingMatchingCard(this IEnumerable cards, Func filter, int count = 1) + { + return cards.GetMatchingCardsCount(filter) >= count; + } + + public static ClientCard GetShouldBeDisabledBeforeItUseEffectMonster(this IEnumerable cards, bool canBeTarget = true) + { + return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget())); + } + + public static IEnumerable> GetCombinations(this IEnumerable elements, int k) + { + return k == 0 ? new[] { new T[0] } : + elements.SelectMany((e, i) => + elements.Skip(i + 1).GetCombinations(k - 1).Select(c => (new[] { e }).Concat(c))); + } + } } \ No newline at end of file diff --git a/Game/AI/CardExecutor.cs b/ExecutorBase/Game/AI/CardExecutor.cs similarity index 96% rename from Game/AI/CardExecutor.cs rename to ExecutorBase/Game/AI/CardExecutor.cs index 75509a77..d5ec43dd 100644 --- a/Game/AI/CardExecutor.cs +++ b/ExecutorBase/Game/AI/CardExecutor.cs @@ -1,18 +1,18 @@ -using System; - -namespace WindBot.Game.AI -{ - public class CardExecutor - { - public int CardId { get; private set; } - public ExecutorType Type { get; private set; } - public Func Func { get; private set; } - - public CardExecutor(ExecutorType type, int cardId, Func func) - { - CardId = cardId; - Type = type; - Func = func; - } - } +using System; + +namespace WindBot.Game.AI +{ + public class CardExecutor + { + public int CardId { get; private set; } + public ExecutorType Type { get; private set; } + public Func Func { get; private set; } + + public CardExecutor(ExecutorType type, int cardId, Func func) + { + CardId = cardId; + Type = type; + Func = func; + } + } } \ No newline at end of file diff --git a/Game/AI/CardExtension.cs b/ExecutorBase/Game/AI/CardExtension.cs similarity index 97% rename from Game/AI/CardExtension.cs rename to ExecutorBase/Game/AI/CardExtension.cs index 8b30394b..3e45daf1 100644 --- a/Game/AI/CardExtension.cs +++ b/ExecutorBase/Game/AI/CardExtension.cs @@ -1,80 +1,80 @@ -using System; -using WindBot.Game.AI.Enums; -using YGOSharp.OCGWrapper.Enums; - -namespace WindBot.Game.AI -{ - public static class CardExtension - { - /// - /// Is this monster is invincible to battle? - /// - public static bool IsMonsterInvincible(this ClientCard card) - { - return !card.IsDisabled() && Enum.IsDefined(typeof(InvincibleMonster), card.Id); - } - - /// - /// Is this monster is dangerous to attack? - /// - public static bool IsMonsterDangerous(this ClientCard card) - { - return !card.IsDisabled() && Enum.IsDefined(typeof(DangerousMonster), card.Id); - } - - /// - /// Do this monster prevents activation of opponent's effect monsters in battle? - /// - public static bool IsMonsterHasPreventActivationEffectInBattle(this ClientCard card) - { - return !card.IsDisabled() && Enum.IsDefined(typeof(PreventActivationEffectInBattle), card.Id); - } - - /// - /// Is this card shouldn't be tried to be selected as target? - /// - public static bool IsShouldNotBeTarget(this ClientCard card) - { - return !card.IsDisabled() && !card.HasType(CardType.Normal) && Enum.IsDefined(typeof(ShouldNotBeTarget), card.Id); - } - - /// - /// Is this card shouldn't be tried to be selected as target of monster? - /// - public static bool IsShouldNotBeMonsterTarget(this ClientCard card) - { - return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldNotBeMonsterTarget), card.Id); - } - - /// - /// Is this card shouldn't be tried to be selected as target of spell & trap? - /// - public static bool IsShouldNotBeSpellTrapTarget(this ClientCard card) - { - return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldNotBeSpellTrapTarget), card.Id); - } - - /// - /// Is this monster should be disabled (with Breakthrough Skill) before it use effect and release or banish itself? - /// - public static bool IsMonsterShouldBeDisabledBeforeItUseEffect(this ClientCard card) - { - return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldBeDisabledBeforeItUseEffectMonster), card.Id); - } - - public static bool IsFloodgate(this ClientCard card) - { - return Enum.IsDefined(typeof(Floodgate), card.Id); - } - - public static bool IsOneForXyz(this ClientCard card) - { - return Enum.IsDefined(typeof(OneForXyz), card.Id); - } - - public static bool IsFusionSpell(this ClientCard card) - { - return Enum.IsDefined(typeof(FusionSpell), card.Id); - } - } +using System; +using WindBot.Game.AI.Enums; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game.AI +{ + public static class CardExtension + { + /// + /// Is this monster is invincible to battle? + /// + public static bool IsMonsterInvincible(this ClientCard card) + { + return !card.IsDisabled() && Enum.IsDefined(typeof(InvincibleMonster), card.Id); + } + + /// + /// Is this monster is dangerous to attack? + /// + public static bool IsMonsterDangerous(this ClientCard card) + { + return !card.IsDisabled() && Enum.IsDefined(typeof(DangerousMonster), card.Id); + } + + /// + /// Do this monster prevents activation of opponent's effect monsters in battle? + /// + public static bool IsMonsterHasPreventActivationEffectInBattle(this ClientCard card) + { + return !card.IsDisabled() && Enum.IsDefined(typeof(PreventActivationEffectInBattle), card.Id); + } + + /// + /// Is this card shouldn't be tried to be selected as target? + /// + public static bool IsShouldNotBeTarget(this ClientCard card) + { + return !card.IsDisabled() && !card.HasType(CardType.Normal) && Enum.IsDefined(typeof(ShouldNotBeTarget), card.Id); + } + + /// + /// Is this card shouldn't be tried to be selected as target of monster? + /// + public static bool IsShouldNotBeMonsterTarget(this ClientCard card) + { + return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldNotBeMonsterTarget), card.Id); + } + + /// + /// Is this card shouldn't be tried to be selected as target of spell & trap? + /// + public static bool IsShouldNotBeSpellTrapTarget(this ClientCard card) + { + return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldNotBeSpellTrapTarget), card.Id); + } + + /// + /// Is this monster should be disabled (with Breakthrough Skill) before it use effect and release or banish itself? + /// + public static bool IsMonsterShouldBeDisabledBeforeItUseEffect(this ClientCard card) + { + return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldBeDisabledBeforeItUseEffectMonster), card.Id); + } + + public static bool IsFloodgate(this ClientCard card) + { + return Enum.IsDefined(typeof(Floodgate), card.Id); + } + + public static bool IsOneForXyz(this ClientCard card) + { + return Enum.IsDefined(typeof(OneForXyz), card.Id); + } + + public static bool IsFusionSpell(this ClientCard card) + { + return Enum.IsDefined(typeof(FusionSpell), card.Id); + } + } } \ No newline at end of file diff --git a/Game/AI/CardSelector.cs b/ExecutorBase/Game/AI/CardSelector.cs similarity index 96% rename from Game/AI/CardSelector.cs rename to ExecutorBase/Game/AI/CardSelector.cs index 5a0cf2ba..fda0bb55 100644 --- a/Game/AI/CardSelector.cs +++ b/ExecutorBase/Game/AI/CardSelector.cs @@ -1,104 +1,104 @@ -using System.Collections.Generic; -using YGOSharp.OCGWrapper.Enums; - -namespace WindBot.Game.AI -{ - public class CardSelector - { - private enum SelectType - { - Card, - Cards, - Id, - Ids, - Location - } - - private SelectType _type; - private ClientCard _card; - private IList _cards; - private int _id; - private IList _ids; - private CardLocation _location; - - public CardSelector(ClientCard card) - { - _type = SelectType.Card; - _card = card; - } - - public CardSelector(IList cards) - { - _type = SelectType.Cards; - _cards = cards; - } - - public CardSelector(int cardId) - { - _type = SelectType.Id; - _id = cardId; - } - - public CardSelector(IList ids) - { - _type = SelectType.Ids; - _ids = ids; - } - - public CardSelector(CardLocation location) - { - _type = SelectType.Location; - _location = location; - } - - public IList Select(IList cards, int min, int max) - { - IList result = new List(); - - switch (_type) - { - case SelectType.Card: - if (cards.Contains(_card)) - result.Add(_card); - break; - case SelectType.Cards: - foreach (ClientCard card in _cards) - if (cards.Contains(card) && !result.Contains(card)) - result.Add(card); - break; - case SelectType.Id: - foreach (ClientCard card in cards) - if (card.IsCode(_id)) - result.Add(card); - break; - case SelectType.Ids: - foreach (int id in _ids) - foreach (ClientCard card in cards) - if (card.IsCode(id) && !result.Contains(card)) - result.Add(card); - break; - case SelectType.Location: - foreach (ClientCard card in cards) - if (card.Location == _location) - result.Add(card); - break; - } - - if (result.Count < min) - { - foreach (ClientCard card in cards) - { - if (!result.Contains(card)) - result.Add(card); - if (result.Count >= min) - break; - } - } - - while (result.Count > max) - result.RemoveAt(result.Count - 1); - - return result; - } - } +using System.Collections.Generic; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game.AI +{ + public class CardSelector + { + private enum SelectType + { + Card, + Cards, + Id, + Ids, + Location + } + + private SelectType _type; + private ClientCard _card; + private IList _cards; + private int _id; + private IList _ids; + private CardLocation _location; + + public CardSelector(ClientCard card) + { + _type = SelectType.Card; + _card = card; + } + + public CardSelector(IList cards) + { + _type = SelectType.Cards; + _cards = cards; + } + + public CardSelector(int cardId) + { + _type = SelectType.Id; + _id = cardId; + } + + public CardSelector(IList ids) + { + _type = SelectType.Ids; + _ids = ids; + } + + public CardSelector(CardLocation location) + { + _type = SelectType.Location; + _location = location; + } + + public IList Select(IList cards, int min, int max) + { + IList result = new List(); + + switch (_type) + { + case SelectType.Card: + if (cards.Contains(_card)) + result.Add(_card); + break; + case SelectType.Cards: + foreach (ClientCard card in _cards) + if (cards.Contains(card) && !result.Contains(card)) + result.Add(card); + break; + case SelectType.Id: + foreach (ClientCard card in cards) + if (card.IsCode(_id)) + result.Add(card); + break; + case SelectType.Ids: + foreach (int id in _ids) + foreach (ClientCard card in cards) + if (card.IsCode(id) && !result.Contains(card)) + result.Add(card); + break; + case SelectType.Location: + foreach (ClientCard card in cards) + if (card.Location == _location) + result.Add(card); + break; + } + + if (result.Count < min) + { + foreach (ClientCard card in cards) + { + if (!result.Contains(card)) + result.Add(card); + if (result.Count >= min) + break; + } + } + + while (result.Count > max) + result.RemoveAt(result.Count - 1); + + return result; + } + } } \ No newline at end of file diff --git a/Game/AI/DeckAttribute.cs b/ExecutorBase/Game/AI/DeckAttribute.cs similarity index 96% rename from Game/AI/DeckAttribute.cs rename to ExecutorBase/Game/AI/DeckAttribute.cs index 49bae161..89a87a9c 100644 --- a/Game/AI/DeckAttribute.cs +++ b/ExecutorBase/Game/AI/DeckAttribute.cs @@ -1,22 +1,22 @@ -using System; - -namespace WindBot.Game.AI -{ - [AttributeUsage(AttributeTargets.Class)] - public class DeckAttribute : Attribute - { - public string Name { get; private set; } - public string File { get; private set; } - public string Level { get; private set; } - - public DeckAttribute(string name, string file = null, string level = "Normal") - { - if (String.IsNullOrEmpty(file)) - file = name; - - Name = name; - File = file; - Level = level; - } - } -} +using System; + +namespace WindBot.Game.AI +{ + [AttributeUsage(AttributeTargets.Class)] + public class DeckAttribute : Attribute + { + public string Name { get; private set; } + public string File { get; private set; } + public string Level { get; private set; } + + public DeckAttribute(string name, string file = null, string level = "Normal") + { + if (String.IsNullOrEmpty(file)) + file = name; + + Name = name; + File = file; + Level = level; + } + } +} diff --git a/Game/AI/DefaultExecutor.cs b/ExecutorBase/Game/AI/DefaultExecutor.cs similarity index 97% rename from Game/AI/DefaultExecutor.cs rename to ExecutorBase/Game/AI/DefaultExecutor.cs index 3eb0b663..27580a47 100644 --- a/Game/AI/DefaultExecutor.cs +++ b/ExecutorBase/Game/AI/DefaultExecutor.cs @@ -1,1101 +1,1101 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using YGOSharp.OCGWrapper.Enums; -using WindBot; -using WindBot.Game; -using WindBot.Game.AI; - -namespace WindBot.Game.AI -{ - public abstract class DefaultExecutor : Executor - { - protected class _CardId - { - public const int JizukirutheStarDestroyingKaiju = 63941210; - public const int ThunderKingtheLightningstrikeKaiju = 48770333; - public const int DogorantheMadFlameKaiju = 93332803; - public const int RadiantheMultidimensionalKaiju = 28674152; - public const int GadarlatheMysteryDustKaiju = 36956512; - public const int KumongoustheStickyStringKaiju = 29726552; - public const int GamecieltheSeaTurtleKaiju = 55063751; - public const int SuperAntiKaijuWarMachineMechaDogoran = 84769941; - - public const int UltimateConductorTytanno = 18940556; - public const int ElShaddollConstruct = 20366274; - public const int AllyOfJusticeCatastor = 26593852; - - public const int DupeFrog = 46239604; - public const int MaraudingCaptain = 2460565; - - public const int BlackRoseDragon = 73580471; - public const int JudgmentDragon = 57774843; - public const int TopologicTrisbaena = 72529749; - public const int EvilswarmExcitonKnight = 46772449; - public const int HarpiesFeatherDuster = 18144506; - public const int DarkMagicAttack = 2314238; - public const int MysticalSpaceTyphoon = 5318639; - public const int CosmicCyclone = 8267140; - public const int ChickenGame = 67616300; - - public const int SantaClaws = 46565218; - - public const int CastelTheSkyblasterMusketeer = 82633039; - public const int CrystalWingSynchroDragon = 50954680; - public const int NumberS39UtopiaTheLightning = 56832966; - public const int Number39Utopia = 84013237; - public const int UltimayaTzolkin = 1686814; - public const int MekkKnightCrusadiaAstram = 21887175; - public const int HamonLordofStrikingThunder = 32491822; - - public const int MoonMirrorShield = 19508728; - public const int PhantomKnightsFogBlade = 25542642; - - public const int VampireFraeulein = 6039967; - public const int InjectionFairyLily = 79575620; - - public const int BlueEyesChaosMAXDragon = 55410871; - - public const int AshBlossom = 14558127; - public const int MaxxC = 23434538; - public const int LockBird = 94145021; - public const int GhostOgreAndSnowRabbit = 59438930; - public const int GhostBelle = 73642296; - public const int EffectVeiler = 63845230; - public const int ArtifactLancea = 34267821; - - public const int CalledByTheGrave = 24224830; - public const int InfiniteImpermanence = 10045474; - public const int GalaxySoldier = 46659709; - public const int MacroCosmos = 30241314; - public const int UpstartGoblin = 70368879; - public const int CyberEmergency = 60600126; - - public const int EaterOfMillions = 63845230; - - public const int InvokedPurgatrio = 12307878; - public const int ChaosAncientGearGiant = 51788412; - public const int UltimateAncientGearGolem = 12652643; - - public const int RedDragonArchfiend = 70902743; - - public const int ImperialOrder = 61740673; - public const int NaturiaBeast = 33198837; - public const int AntiSpellFragrance = 58921041; - } - - int HonestEffectCount = 0; - - protected DefaultExecutor(GameAI ai, Duel duel) - : base(ai, duel) - { - AddExecutor(ExecutorType.Activate, _CardId.ChickenGame, DefaultChickenGame); - AddExecutor(ExecutorType.Activate, _CardId.SantaClaws); - } - - /// - /// Decide which card should the attacker attack. - /// - /// Card that attack. - /// Cards that defend. - /// BattlePhaseAction including the target, or null (in this situation, GameAI will check the next attacker) - public override BattlePhaseAction OnSelectAttackTarget(ClientCard attacker, IList defenders) - { - foreach (ClientCard defender in defenders) - { - attacker.RealPower = attacker.Attack; - defender.RealPower = defender.GetDefensePower(); - if (!OnPreBattleBetween(attacker, defender)) - continue; - - if (attacker.RealPower > defender.RealPower || (attacker.RealPower >= defender.RealPower && attacker.IsLastAttacker && defender.IsAttack())) - return AI.Attack(attacker, defender); - } - - if (attacker.CanDirectAttack) - return AI.Attack(attacker, null); - - return null; - } - - /// - /// Decide whether to declare attack between attacker and defender. - /// Can be overrided to update the RealPower of attacker for cards like Honest. - /// - /// Card that attack. - /// Card that defend. - /// false if the attack shouldn't be done. - public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender) - { - if (attacker.RealPower <= 0) - return false; - - if (!attacker.IsMonsterHasPreventActivationEffectInBattle()) - { - if (defender.IsMonsterInvincible() && defender.IsDefense()) - return false; - - if (defender.IsMonsterDangerous()) - { - bool canIgnoreIt = !attacker.IsDisabled() && ( - attacker.IsCode(_CardId.UltimateConductorTytanno) && defender.IsDefense() || - attacker.IsCode(_CardId.ElShaddollConstruct) && defender.IsSpecialSummoned || - attacker.IsCode(_CardId.AllyOfJusticeCatastor) && !defender.HasAttribute(CardAttribute.Dark)); - if (!canIgnoreIt) - return false; - } - - foreach (ClientCard equip in defender.EquipCards) - { - if (equip.IsCode(_CardId.MoonMirrorShield) && !equip.IsDisabled()) - { - return false; - } - } - - if (!defender.IsDisabled()) - { - if (defender.IsCode(_CardId.MekkKnightCrusadiaAstram) && defender.IsAttack() && attacker.IsSpecialSummoned) - return false; - - if (defender.IsCode(_CardId.CrystalWingSynchroDragon) && defender.IsAttack() && attacker.Level >= 5) - return false; - - if (defender.IsCode(_CardId.AllyOfJusticeCatastor) && !attacker.HasAttribute(CardAttribute.Dark)) - return false; - - if (defender.IsCode(_CardId.NumberS39UtopiaTheLightning) && defender.IsAttack() && defender.HasXyzMaterial(2, _CardId.Number39Utopia)) - defender.RealPower = 5000; - - if (defender.IsCode(_CardId.VampireFraeulein)) - defender.RealPower += (Enemy.LifePoints > 3000) ? 3000 : (Enemy.LifePoints - 100); - - if (defender.IsCode(_CardId.InjectionFairyLily) && Enemy.LifePoints > 2000) - defender.RealPower += 3000; - } - } - - if (!defender.IsMonsterHasPreventActivationEffectInBattle()) - { - if (attacker.IsCode(_CardId.NumberS39UtopiaTheLightning) && !attacker.IsDisabled() && attacker.HasXyzMaterial(2, _CardId.Number39Utopia)) - attacker.RealPower = 5000; - - foreach (ClientCard equip in attacker.EquipCards) - { - if (equip.IsCode(_CardId.MoonMirrorShield) && !equip.IsDisabled()) - { - attacker.RealPower = defender.RealPower + 100; - } - } - } - - if (Enemy.HasInMonstersZone(_CardId.MekkKnightCrusadiaAstram, true) && !(defender).IsCode(_CardId.MekkKnightCrusadiaAstram)) - return false; - - if (Enemy.HasInMonstersZone(_CardId.DupeFrog, true) && !(defender).IsCode(_CardId.DupeFrog)) - return false; - - if (Enemy.HasInMonstersZone(_CardId.MaraudingCaptain, true) && !defender.IsCode(_CardId.MaraudingCaptain) && defender.Race == (int)CardRace.Warrior) - return false; - - if (defender.IsCode(_CardId.UltimayaTzolkin) && !defender.IsDisabled() && Enemy.GetMonsters().Any(monster => !monster.Equals(defender) && monster.HasType(CardType.Synchro))) - return false; - - if (Enemy.GetMonsters().Any(monster => !monster.Equals(defender) && monster.IsCode(_CardId.HamonLordofStrikingThunder) && !monster.IsDisabled() && monster.IsDefense())) - return false; - - if (defender.OwnTargets.Any(card => card.IsCode(_CardId.PhantomKnightsFogBlade) && !card.IsDisabled())) - return false; - - return true; - } - - /// - /// Called when the AI has to select a card position. - /// - /// Id of the card to position on the field. - /// List of available positions. - /// Selected position, or 0 if no position is set for this card. - public override CardPosition OnSelectPosition(int cardId, IList positions) - { - YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); - if (cardData != null) - { - if (cardData.Attack == 0) - return CardPosition.FaceUpDefence; - } - return 0; - } - - public override bool OnSelectBattleReplay() - { - if (Bot.BattlingMonster == null) - return false; - List defenders = new List(Duel.Fields[1].GetMonsters()); - defenders.Sort(CardContainer.CompareDefensePower); - defenders.Reverse(); - BattlePhaseAction result = OnSelectAttackTarget(Bot.BattlingMonster, defenders); - if (result != null && result.Action == BattlePhaseAction.BattleAction.Attack) - { - return true; - } - return false; - } - - public override void OnNewTurn() - { - HonestEffectCount = 0; - } - - /// - /// Destroy face-down cards first, in our turn. - /// - protected bool DefaultMysticalSpaceTyphoon() - { - if (Duel.CurrentChain.Any(card => card.IsCode(_CardId.MysticalSpaceTyphoon))) - { - return false; - } - - List spells = Enemy.GetSpells(); - if (spells.Count == 0) - return false; - - ClientCard selected = Enemy.SpellZone.GetFloodgate(); - - if (selected == null) - { - if (Duel.Player == 0) - selected = spells.FirstOrDefault(card => card.IsFacedown()); - if (Duel.Player == 1) - selected = spells.FirstOrDefault(card => card.HasType(CardType.Continuous) || card.HasType(CardType.Equip) || card.HasType(CardType.Field)); - } - - if (selected == null) - return false; - AI.SelectCard(selected); - return true; - } - - /// - /// Destroy face-down cards first, in our turn. - /// - protected bool DefaultCosmicCyclone() - { - foreach (ClientCard card in Duel.CurrentChain) - if (card.IsCode(_CardId.CosmicCyclone)) - return false; - return (Bot.LifePoints > 1000) && DefaultMysticalSpaceTyphoon(); - } - - /// - /// Activate if avail. - /// - protected bool DefaultGalaxyCyclone() - { - List spells = Enemy.GetSpells(); - if (spells.Count == 0) - return false; - - ClientCard selected = null; - - if (Card.Location == CardLocation.Grave) - { - selected = Util.GetBestEnemySpell(true); - } - else - { - selected = spells.FirstOrDefault(card => card.IsFacedown()); - } - - if (selected == null) - return false; - - AI.SelectCard(selected); - return true; - } - - /// - /// Set the highest ATK level 4+ effect enemy monster. - /// - protected bool DefaultBookOfMoon() - { - if (Util.IsAllEnemyBetter(true)) - { - ClientCard monster = Enemy.GetMonsters().GetHighestAttackMonster(true); - if (monster != null && monster.HasType(CardType.Effect) && !monster.HasType(CardType.Link) && (monster.HasType(CardType.Xyz) || monster.Level > 4)) - { - AI.SelectCard(monster); - return true; - } - } - return false; - } - - /// - /// Return problematic monster, and if this card become target, return any enemy monster. - /// - protected bool DefaultCompulsoryEvacuationDevice() - { - ClientCard target = Util.GetProblematicEnemyMonster(0, true); - if (target != null) - { - AI.SelectCard(target); - return true; - } - if (Util.IsChainTarget(Card)) - { - ClientCard monster = Util.GetBestEnemyMonster(false, true); - if (monster != null) - { - AI.SelectCard(monster); - return true; - } - } - return false; - } - - /// - /// Revive the best monster when we don't have better one in field. - /// - protected bool DefaultCallOfTheHaunted() - { - if (!Util.IsAllEnemyBetter(true)) - return false; - ClientCard selected = Bot.Graveyard.GetMatchingCards(card => card.IsCanRevive()).OrderByDescending(card => card.Attack).FirstOrDefault(); - AI.SelectCard(selected); - return true; - } - - /// - /// Default Scapegoat effect - /// - protected bool DefaultScapegoat() - { - if (DefaultSpellWillBeNegated()) return false; - if (Duel.Player == 0) return false; - if (Duel.Phase == DuelPhase.End) return true; - if (DefaultOnBecomeTarget()) return true; - if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) - { - if (Enemy.HasInMonstersZone(new[] - { - _CardId.UltimateConductorTytanno, - _CardId.InvokedPurgatrio, - _CardId.ChaosAncientGearGiant, - _CardId.UltimateAncientGearGolem, - _CardId.RedDragonArchfiend - }, true)) return false; - if (Util.GetTotalAttackingMonsterAttack(1) >= Bot.LifePoints) return true; - } - return false; - } - /// - /// Always active in opponent's turn. - /// - protected bool DefaultMaxxC() - { - return Duel.Player == 1; - } - /// - /// Always disable opponent's effect except some cards like UpstartGoblin - /// - protected bool DefaultAshBlossomAndJoyousSpring() - { - int[] ignoreList = { - _CardId.MacroCosmos, - _CardId.UpstartGoblin, - _CardId.CyberEmergency - }; - if (Util.GetLastChainCard().IsCode(ignoreList)) - return false; - if (Util.GetLastChainCard().HasSetcode(0x11e) && Util.GetLastChainCard().Location == CardLocation.Hand) // Danger! archtype hand effect - return false; - return Duel.LastChainPlayer == 1; - } - /// - /// Always activate unless the activating card is disabled - /// - protected bool DefaultGhostOgreAndSnowRabbit() - { - if (Util.GetLastChainCard() != null && Util.GetLastChainCard().IsDisabled()) - return false; - return DefaultTrap(); - } - /// - /// Always disable opponent's effect - /// - protected bool DefaultGhostBelleAndHauntedMansion() - { - return DefaultTrap(); - } - /// - /// Same as DefaultBreakthroughSkill - /// - protected bool DefaultEffectVeiler() - { - if (Util.GetLastChainCard() != null && Util.GetLastChainCard().IsCode(_CardId.GalaxySoldier) && Enemy.Hand.Count >= 3) return false; - if (Util.ChainContainsCard(_CardId.EffectVeiler)) - return false; - return DefaultBreakthroughSkill(); - } - /// - /// Chain common hand traps - /// - protected bool DefaultCalledByTheGrave() - { - int[] targetList = - { - _CardId.MaxxC, - _CardId.LockBird, - _CardId.GhostOgreAndSnowRabbit, - _CardId.AshBlossom, - _CardId.GhostBelle, - _CardId.EffectVeiler, - _CardId.ArtifactLancea - }; - if (Duel.LastChainPlayer == 1) - { - foreach (int id in targetList) - { - if (Util.GetLastChainCard().IsCode(id)) - { - AI.SelectCard(id); - return UniqueFaceupSpell(); - } - } - } - return false; - } - /// - /// Default InfiniteImpermanence effect - /// - protected bool DefaultInfiniteImpermanence() - { - // TODO: disable s & t - if (!DefaultUniqueTrap()) - return false; - return DefaultDisableMonster(); - } - /// - /// Chain the enemy monster, or disable monster like Rescue Rabbit. - /// - protected bool DefaultBreakthroughSkill() - { - if (!DefaultUniqueTrap()) - return false; - return DefaultDisableMonster(); - } - /// - /// Chain the enemy monster, or disable monster like Rescue Rabbit. - /// - protected bool DefaultDisableMonster() - { - if (Duel.Player == 1) - { - ClientCard target = Enemy.MonsterZone.GetShouldBeDisabledBeforeItUseEffectMonster(); - if (target != null) - { - AI.SelectCard(target); - return true; - } - } - - ClientCard LastChainCard = Util.GetLastChainCard(); - - if (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.MonsterZone && - !LastChainCard.IsDisabled() && !LastChainCard.IsShouldNotBeTarget() && !LastChainCard.IsShouldNotBeSpellTrapTarget()) - { - AI.SelectCard(LastChainCard); - return true; - } - - if (Bot.BattlingMonster != null && Enemy.BattlingMonster != null) - { - if (!Enemy.BattlingMonster.IsDisabled() && Enemy.BattlingMonster.IsCode(_CardId.EaterOfMillions)) - { - AI.SelectCard(Enemy.BattlingMonster); - return true; - } - } - - if (Duel.Phase == DuelPhase.BattleStart && Duel.Player == 1 && - Enemy.HasInMonstersZone(_CardId.NumberS39UtopiaTheLightning, true)) - { - AI.SelectCard(_CardId.NumberS39UtopiaTheLightning); - return true; - } - - return false; - } - - /// - /// Activate only except this card is the target or we summon monsters. - /// - protected bool DefaultSolemnJudgment() - { - return !Util.IsChainTargetOnly(Card) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap(); - } - - /// - /// Activate only except we summon monsters. - /// - protected bool DefaultSolemnWarning() - { - return (Bot.LifePoints > 2000) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap(); - } - - /// - /// Activate only except we summon monsters. - /// - protected bool DefaultSolemnStrike() - { - return (Bot.LifePoints > 1500) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap(); - } - - /// - /// Activate when all enemy monsters have better ATK. - /// - protected bool DefaultTorrentialTribute() - { - return !Util.HasChainedTrap(0) && Util.IsAllEnemyBetter(true); - } - - /// - /// Activate enemy have more S&T. - /// - protected bool DefaultHeavyStorm() - { - return Bot.GetSpellCount() < Enemy.GetSpellCount(); - } - - /// - /// Activate before other winds, if enemy have more than 2 S&T. - /// - protected bool DefaultHarpiesFeatherDusterFirst() - { - return Enemy.GetSpellCount() >= 2; - } - - /// - /// Activate when one enemy monsters have better ATK. - /// - protected bool DefaultHammerShot() - { - return Util.IsOneEnemyBetter(true); - } - - /// - /// Activate when one enemy monsters have better ATK or DEF. - /// - protected bool DefaultDarkHole() - { - return Util.IsOneEnemyBetter(); - } - - /// - /// Activate when one enemy monsters have better ATK or DEF. - /// - protected bool DefaultRaigeki() - { - return Util.IsOneEnemyBetter(); - } - - /// - /// Activate when one enemy monsters have better ATK or DEF. - /// - protected bool DefaultSmashingGround() - { - return Util.IsOneEnemyBetter(); - } - - /// - /// Activate when we have more than 15 cards in deck. - /// - protected bool DefaultPotOfDesires() - { - return Bot.Deck.Count > 15; - } - - /// - /// Set traps only and avoid block the activation of other cards. - /// - protected bool DefaultSpellSet() - { - return (Card.IsTrap() || Card.HasType(CardType.QuickPlay)) && Bot.GetSpellCountWithoutField() < 4; - } - - /// - /// Summon with tributes ATK lower. - /// - protected bool DefaultTributeSummon() - { - if (!UniqueFaceupMonster()) - return false; - int tributecount = (int)Math.Ceiling((Card.Level - 4.0d) / 2.0d); - for (int j = 0; j < 7; ++j) - { - ClientCard tributeCard = Bot.MonsterZone[j]; - if (tributeCard == null) continue; - if (tributeCard.GetDefensePower() < Card.Attack) - tributecount--; - } - return tributecount <= 0; - } - - /// - /// Activate when we have no field. - /// - protected bool DefaultField() - { - return Bot.SpellZone[5] == null; - } - - /// - /// Turn if all enemy is better. - /// - protected bool DefaultMonsterRepos() - { - if (Card.IsFaceup() && Card.IsDefense() && Card.Attack == 0) - return false; - - if (Enemy.HasInMonstersZone(_CardId.BlueEyesChaosMAXDragon, true) && - Card.IsAttack() && (4000 - Card.Defense) * 2 > (4000 - Card.Attack)) - return false; - if (Enemy.HasInMonstersZone(_CardId.BlueEyesChaosMAXDragon, true) && - Card.IsDefense() && Card.IsFaceup() && - (4000 - Card.Defense) * 2 > (4000 - Card.Attack)) - return true; - - bool enemyBetter = Util.IsAllEnemyBetter(true); - if (Card.IsAttack() && enemyBetter) - return true; - if (Card.IsDefense() && !enemyBetter && Card.Attack >= Card.Defense) - return true; - - return false; - } - - /// - /// If spell will be negated - /// - protected bool DefaultSpellWillBeNegated() - { - return Bot.HasInSpellZone(_CardId.ImperialOrder, true, true) || Enemy.HasInSpellZone(_CardId.ImperialOrder, true) || Enemy.HasInMonstersZone(_CardId.NaturiaBeast, true); - } - - /// - /// If spell must set first to activate - /// - protected bool DefaultSpellMustSetFirst() - { - ClientCard card = null; - foreach (ClientCard check in Bot.GetSpells()) - { - if (check.IsCode(_CardId.AntiSpellFragrance) && !check.IsDisabled()) - card = check; - } - if (card != null && card.IsFaceup()) - return true; - return Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true, true) || Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true); - } - - /// - /// if spell/trap is the target or enermy activate HarpiesFeatherDuster - /// - protected bool DefaultOnBecomeTarget() - { - if (Util.IsChainTarget(Card)) return true; - int[] destroyAllList = - { - _CardId.EvilswarmExcitonKnight, - _CardId.BlackRoseDragon, - _CardId.JudgmentDragon, - _CardId.TopologicTrisbaena - }; - int[] destroyAllOpponentList = - { - _CardId.HarpiesFeatherDuster, - _CardId.DarkMagicAttack - }; - - if (Util.ChainContainsCard(destroyAllList)) return true; - if (Enemy.HasInSpellZone(destroyAllOpponentList, true)) return true; - // TODO: ChainContainsCard(id, player) - return false; - } - /// - /// Chain enemy activation or summon. - /// - protected bool DefaultTrap() - { - return (Duel.LastChainPlayer == -1 && Duel.LastSummonPlayer != 0) || Duel.LastChainPlayer == 1; - } - - /// - /// Activate when avail and no other our trap card in this chain or face-up. - /// - protected bool DefaultUniqueTrap() - { - if (Util.HasChainedTrap(0)) - return false; - - return UniqueFaceupSpell(); - } - - /// - /// Check no other our spell or trap card with same name face-up. - /// - protected bool UniqueFaceupSpell() - { - return !Bot.GetSpells().Any(card => card.IsCode(Card.Id) && card.IsFaceup()); - } - - /// - /// Check no other our monster card with same name face-up. - /// - protected bool UniqueFaceupMonster() - { - return !Bot.GetMonsters().Any(card => card.IsCode(Card.Id) && card.IsFaceup()); - } - - /// - /// Dumb way to avoid the bot chain in mess. - /// - protected bool DefaultDontChainMyself() - { - if (Executors.Any(exec => exec.Type == Type && exec.CardId == Card.Id)) - return false; - return Duel.LastChainPlayer != 0; - } - - /// - /// Draw when we have lower LP, or destroy it. Can be overrided. - /// - protected bool DefaultChickenGame() - { - if (Executors.Count(exec => exec.Type == Type && exec.CardId == Card.Id) > 1) - return false; - if (Bot.LifePoints <= 1000) - return false; - if (Bot.LifePoints <= Enemy.LifePoints && ActivateDescription == Util.GetStringId(_CardId.ChickenGame, 0)) - return true; - if (Bot.LifePoints > Enemy.LifePoints && ActivateDescription == Util.GetStringId(_CardId.ChickenGame, 1)) - return true; - return false; - } - - /// - /// Draw when we have Dark monster in hand,and banish random one. Can be overrided. - /// - protected bool DefaultAllureofDarkness() - { - ClientCard target = Bot.Hand.FirstOrDefault(card => card.HasAttribute(CardAttribute.Dark)); - return target != null; - } - - /// - /// Clever enough. - /// - protected bool DefaultDimensionalBarrier() - { - const int RITUAL = 0; - const int FUSION = 1; - const int SYNCHRO = 2; - const int XYZ = 3; - const int PENDULUM = 4; - if (Duel.Player != 0) - { - List monsters = Enemy.GetMonsters(); - int[] levels = new int[13]; - bool tuner = false; - bool nontuner = false; - foreach (ClientCard monster in monsters) - { - if (monster.HasType(CardType.Tuner)) - tuner = true; - else if (!monster.HasType(CardType.Xyz) && !monster.HasType(CardType.Link)) - { - nontuner = true; - levels[monster.Level] = levels[monster.Level] + 1; - } - - if (monster.IsOneForXyz()) - { - AI.SelectOption(XYZ); - return true; - } - } - if (tuner && nontuner) - { - AI.SelectOption(SYNCHRO); - return true; - } - for (int i=1; i<=12; i++) - { - if (levels[i]>1) - { - AI.SelectOption(XYZ); - return true; - } - } - ClientCard l = Enemy.SpellZone[6]; - ClientCard r = Enemy.SpellZone[7]; - if (l != null && r != null && l.LScale != r.RScale) - { - AI.SelectOption(PENDULUM); - return true; - } - } - ClientCard lastchaincard = Util.GetLastChainCard(); - if (Duel.LastChainPlayer == 1 && lastchaincard != null && !lastchaincard.IsDisabled()) - { - if (lastchaincard.HasType(CardType.Ritual)) - { - AI.SelectOption(RITUAL); - return true; - } - if (lastchaincard.HasType(CardType.Fusion)) - { - AI.SelectOption(FUSION); - return true; - } - if (lastchaincard.HasType(CardType.Synchro)) - { - AI.SelectOption(SYNCHRO); - return true; - } - if (lastchaincard.HasType(CardType.Xyz)) - { - AI.SelectOption(XYZ); - return true; - } - if (lastchaincard.IsFusionSpell()) - { - AI.SelectOption(FUSION); - return true; - } - } - if (Util.IsChainTarget(Card)) - { - AI.SelectOption(XYZ); - return true; - } - return false; - } - - /// - /// Clever enough - /// - protected bool DefaultInterruptedKaijuSlumber() - { - if (Card.Location == CardLocation.Grave) - { - AI.SelectCard( - _CardId.GamecieltheSeaTurtleKaiju, - _CardId.KumongoustheStickyStringKaiju, - _CardId.GadarlatheMysteryDustKaiju, - _CardId.RadiantheMultidimensionalKaiju, - _CardId.DogorantheMadFlameKaiju, - _CardId.ThunderKingtheLightningstrikeKaiju, - _CardId.JizukirutheStarDestroyingKaiju - ); - return true; - } - - if (DefaultDarkHole()) - { - AI.SelectCard( - _CardId.JizukirutheStarDestroyingKaiju, - _CardId.ThunderKingtheLightningstrikeKaiju, - _CardId.DogorantheMadFlameKaiju, - _CardId.RadiantheMultidimensionalKaiju, - _CardId.GadarlatheMysteryDustKaiju, - _CardId.KumongoustheStickyStringKaiju, - _CardId.GamecieltheSeaTurtleKaiju - ); - AI.SelectNextCard( - _CardId.SuperAntiKaijuWarMachineMechaDogoran, - _CardId.GamecieltheSeaTurtleKaiju, - _CardId.KumongoustheStickyStringKaiju, - _CardId.GadarlatheMysteryDustKaiju, - _CardId.RadiantheMultidimensionalKaiju, - _CardId.DogorantheMadFlameKaiju, - _CardId.ThunderKingtheLightningstrikeKaiju - ); - return true; - } - - return false; - } - - /// - /// Clever enough. - /// - protected bool DefaultKaijuSpsummon() - { - IList kaijus = new[] { - _CardId.JizukirutheStarDestroyingKaiju, - _CardId.GadarlatheMysteryDustKaiju, - _CardId.GamecieltheSeaTurtleKaiju, - _CardId.RadiantheMultidimensionalKaiju, - _CardId.KumongoustheStickyStringKaiju, - _CardId.ThunderKingtheLightningstrikeKaiju, - _CardId.DogorantheMadFlameKaiju, - _CardId.SuperAntiKaijuWarMachineMechaDogoran - }; - foreach (ClientCard monster in Enemy.GetMonsters()) - { - if (monster.IsCode(kaijus)) - return Card.GetDefensePower() > monster.GetDefensePower(); - } - ClientCard card = Enemy.MonsterZone.GetFloodgate(); - if (card != null) - { - AI.SelectCard(card); - return true; - } - card = Enemy.MonsterZone.GetDangerousMonster(); - if (card != null) - { - AI.SelectCard(card); - return true; - } - card = Util.GetOneEnemyBetterThanValue(Card.GetDefensePower()); - if (card != null) - { - AI.SelectCard(card); - return true; - } - return false; - } - - /// - /// Summon when we don't have monster attack higher than enemy's. - /// - protected bool DefaultNumberS39UtopiaTheLightningSummon() - { - int bestBotAttack = Util.GetBestAttack(Bot); - return Util.IsOneEnemyBetterThanValue(bestBotAttack, false); - } - - /// - /// Activate if the card is attack pos, and its attack is below 5000, when the enemy monster is attack pos or not useless faceup defense pos - /// - protected bool DefaultNumberS39UtopiaTheLightningEffect() - { - return Card.IsAttack() && Card.Attack < 5000 && (Enemy.BattlingMonster.IsAttack() || Enemy.BattlingMonster.IsFacedown() || Enemy.BattlingMonster.GetDefensePower() >= Card.Attack); - } - - /// - /// Summon when it can and should use effect. - /// - protected bool DefaultEvilswarmExcitonKnightSummon() - { - int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount() + Bot.GetHandCount(); - int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount() + Enemy.GetHandCount(); - return (selfCount - 1 < oppoCount) && DefaultEvilswarmExcitonKnightEffect(); - } - - /// - /// Activate when we have less cards than enemy's, or the atk sum of we is lower than enemy's. - /// - protected bool DefaultEvilswarmExcitonKnightEffect() - { - int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount(); - int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount(); - - if (selfCount < oppoCount) - return true; - - int selfAttack = Bot.GetMonsters().Sum(monster => (int?)monster.GetDefensePower()) ?? 0; - int oppoAttack = Enemy.GetMonsters().Sum(monster => (int?)monster.GetDefensePower()) ?? 0; - - return selfAttack < oppoAttack; - } - - /// - /// Summon in main2, or when the attack of we is lower than enemy's, but not when enemy have monster higher than 2500. - /// - protected bool DefaultStardustDragonSummon() - { - int selfBestAttack = Util.GetBestAttack(Bot); - int oppoBestAttack = Util.GetBestPower(Enemy); - return (selfBestAttack <= oppoBestAttack && oppoBestAttack <= 2500) || Util.IsTurn1OrMain2(); - } - - /// - /// Negate enemy's destroy effect, and revive from grave. - /// - protected bool DefaultStardustDragonEffect() - { - return (Card.Location == CardLocation.Grave) || Duel.LastChainPlayer == 1; - } - - /// - /// Summon when enemy have card which we must solve. - /// - protected bool DefaultCastelTheSkyblasterMusketeerSummon() - { - return Util.GetProblematicEnemyCard() != null; - } - - /// - /// Bounce the problematic enemy card. Ignore the 1st effect. - /// - protected bool DefaultCastelTheSkyblasterMusketeerEffect() - { - if (ActivateDescription == Util.GetStringId(_CardId.CastelTheSkyblasterMusketeer, 0)) - return false; - ClientCard target = Util.GetProblematicEnemyCard(); - if (target != null) - { - AI.SelectCard(0); - AI.SelectNextCard(target); - return true; - } - return false; - } - - /// - /// Summon when it should use effect, or when the attack of we is lower than enemy's, but not when enemy have monster higher than 3000. - /// - protected bool DefaultScarlightRedDragonArchfiendSummon() - { - int selfBestAttack = Util.GetBestAttack(Bot); - int oppoBestAttack = Util.GetBestPower(Enemy); - return (selfBestAttack <= oppoBestAttack && oppoBestAttack <= 3000) || DefaultScarlightRedDragonArchfiendEffect(); - } - - /// - /// Activate when we have less monsters than enemy, or when enemy have more than 3 monsters. - /// - protected bool DefaultScarlightRedDragonArchfiendEffect() - { - int selfCount = Bot.GetMonsters().Count(monster => !monster.Equals(Card) && monster.IsSpecialSummoned && monster.HasType(CardType.Effect) && monster.Attack <= Card.Attack); - int oppoCount = Enemy.GetMonsters().Count(monster => monster.IsSpecialSummoned && monster.HasType(CardType.Effect) && monster.Attack <= Card.Attack); - return selfCount <= oppoCount && oppoCount > 0 || oppoCount >= 3; - } - - /// - /// Clever enough. - /// - protected bool DefaultHonestEffect() - { - if (Card.Location == CardLocation.Hand) - { - return Bot.BattlingMonster.IsAttack() && - (((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Attack) || Bot.BattlingMonster.Attack >= Enemy.LifePoints) - || ((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Defense) && (Bot.BattlingMonster.Attack + Enemy.BattlingMonster.Attack > Enemy.BattlingMonster.Defense))); - } - - if (Util.IsTurn1OrMain2() && HonestEffectCount <= 5) - { - HonestEffectCount++; - return true; - } - - return false; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using YGOSharp.OCGWrapper.Enums; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; + +namespace WindBot.Game.AI +{ + public abstract class DefaultExecutor : Executor + { + protected class _CardId + { + public const int JizukirutheStarDestroyingKaiju = 63941210; + public const int ThunderKingtheLightningstrikeKaiju = 48770333; + public const int DogorantheMadFlameKaiju = 93332803; + public const int RadiantheMultidimensionalKaiju = 28674152; + public const int GadarlatheMysteryDustKaiju = 36956512; + public const int KumongoustheStickyStringKaiju = 29726552; + public const int GamecieltheSeaTurtleKaiju = 55063751; + public const int SuperAntiKaijuWarMachineMechaDogoran = 84769941; + + public const int UltimateConductorTytanno = 18940556; + public const int ElShaddollConstruct = 20366274; + public const int AllyOfJusticeCatastor = 26593852; + + public const int DupeFrog = 46239604; + public const int MaraudingCaptain = 2460565; + + public const int BlackRoseDragon = 73580471; + public const int JudgmentDragon = 57774843; + public const int TopologicTrisbaena = 72529749; + public const int EvilswarmExcitonKnight = 46772449; + public const int HarpiesFeatherDuster = 18144506; + public const int DarkMagicAttack = 2314238; + public const int MysticalSpaceTyphoon = 5318639; + public const int CosmicCyclone = 8267140; + public const int ChickenGame = 67616300; + + public const int SantaClaws = 46565218; + + public const int CastelTheSkyblasterMusketeer = 82633039; + public const int CrystalWingSynchroDragon = 50954680; + public const int NumberS39UtopiaTheLightning = 56832966; + public const int Number39Utopia = 84013237; + public const int UltimayaTzolkin = 1686814; + public const int MekkKnightCrusadiaAstram = 21887175; + public const int HamonLordofStrikingThunder = 32491822; + + public const int MoonMirrorShield = 19508728; + public const int PhantomKnightsFogBlade = 25542642; + + public const int VampireFraeulein = 6039967; + public const int InjectionFairyLily = 79575620; + + public const int BlueEyesChaosMAXDragon = 55410871; + + public const int AshBlossom = 14558127; + public const int MaxxC = 23434538; + public const int LockBird = 94145021; + public const int GhostOgreAndSnowRabbit = 59438930; + public const int GhostBelle = 73642296; + public const int EffectVeiler = 63845230; + public const int ArtifactLancea = 34267821; + + public const int CalledByTheGrave = 24224830; + public const int InfiniteImpermanence = 10045474; + public const int GalaxySoldier = 46659709; + public const int MacroCosmos = 30241314; + public const int UpstartGoblin = 70368879; + public const int CyberEmergency = 60600126; + + public const int EaterOfMillions = 63845230; + + public const int InvokedPurgatrio = 12307878; + public const int ChaosAncientGearGiant = 51788412; + public const int UltimateAncientGearGolem = 12652643; + + public const int RedDragonArchfiend = 70902743; + + public const int ImperialOrder = 61740673; + public const int NaturiaBeast = 33198837; + public const int AntiSpellFragrance = 58921041; + } + + int HonestEffectCount = 0; + + protected DefaultExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + AddExecutor(ExecutorType.Activate, _CardId.ChickenGame, DefaultChickenGame); + AddExecutor(ExecutorType.Activate, _CardId.SantaClaws); + } + + /// + /// Decide which card should the attacker attack. + /// + /// Card that attack. + /// Cards that defend. + /// BattlePhaseAction including the target, or null (in this situation, GameAI will check the next attacker) + public override BattlePhaseAction OnSelectAttackTarget(ClientCard attacker, IList defenders) + { + foreach (ClientCard defender in defenders) + { + attacker.RealPower = attacker.Attack; + defender.RealPower = defender.GetDefensePower(); + if (!OnPreBattleBetween(attacker, defender)) + continue; + + if (attacker.RealPower > defender.RealPower || (attacker.RealPower >= defender.RealPower && attacker.IsLastAttacker && defender.IsAttack())) + return AI.Attack(attacker, defender); + } + + if (attacker.CanDirectAttack) + return AI.Attack(attacker, null); + + return null; + } + + /// + /// Decide whether to declare attack between attacker and defender. + /// Can be overrided to update the RealPower of attacker for cards like Honest. + /// + /// Card that attack. + /// Card that defend. + /// false if the attack shouldn't be done. + public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender) + { + if (attacker.RealPower <= 0) + return false; + + if (!attacker.IsMonsterHasPreventActivationEffectInBattle()) + { + if (defender.IsMonsterInvincible() && defender.IsDefense()) + return false; + + if (defender.IsMonsterDangerous()) + { + bool canIgnoreIt = !attacker.IsDisabled() && ( + attacker.IsCode(_CardId.UltimateConductorTytanno) && defender.IsDefense() || + attacker.IsCode(_CardId.ElShaddollConstruct) && defender.IsSpecialSummoned || + attacker.IsCode(_CardId.AllyOfJusticeCatastor) && !defender.HasAttribute(CardAttribute.Dark)); + if (!canIgnoreIt) + return false; + } + + foreach (ClientCard equip in defender.EquipCards) + { + if (equip.IsCode(_CardId.MoonMirrorShield) && !equip.IsDisabled()) + { + return false; + } + } + + if (!defender.IsDisabled()) + { + if (defender.IsCode(_CardId.MekkKnightCrusadiaAstram) && defender.IsAttack() && attacker.IsSpecialSummoned) + return false; + + if (defender.IsCode(_CardId.CrystalWingSynchroDragon) && defender.IsAttack() && attacker.Level >= 5) + return false; + + if (defender.IsCode(_CardId.AllyOfJusticeCatastor) && !attacker.HasAttribute(CardAttribute.Dark)) + return false; + + if (defender.IsCode(_CardId.NumberS39UtopiaTheLightning) && defender.IsAttack() && defender.HasXyzMaterial(2, _CardId.Number39Utopia)) + defender.RealPower = 5000; + + if (defender.IsCode(_CardId.VampireFraeulein)) + defender.RealPower += (Enemy.LifePoints > 3000) ? 3000 : (Enemy.LifePoints - 100); + + if (defender.IsCode(_CardId.InjectionFairyLily) && Enemy.LifePoints > 2000) + defender.RealPower += 3000; + } + } + + if (!defender.IsMonsterHasPreventActivationEffectInBattle()) + { + if (attacker.IsCode(_CardId.NumberS39UtopiaTheLightning) && !attacker.IsDisabled() && attacker.HasXyzMaterial(2, _CardId.Number39Utopia)) + attacker.RealPower = 5000; + + foreach (ClientCard equip in attacker.EquipCards) + { + if (equip.IsCode(_CardId.MoonMirrorShield) && !equip.IsDisabled()) + { + attacker.RealPower = defender.RealPower + 100; + } + } + } + + if (Enemy.HasInMonstersZone(_CardId.MekkKnightCrusadiaAstram, true) && !(defender).IsCode(_CardId.MekkKnightCrusadiaAstram)) + return false; + + if (Enemy.HasInMonstersZone(_CardId.DupeFrog, true) && !(defender).IsCode(_CardId.DupeFrog)) + return false; + + if (Enemy.HasInMonstersZone(_CardId.MaraudingCaptain, true) && !defender.IsCode(_CardId.MaraudingCaptain) && defender.Race == (int)CardRace.Warrior) + return false; + + if (defender.IsCode(_CardId.UltimayaTzolkin) && !defender.IsDisabled() && Enemy.GetMonsters().Any(monster => !monster.Equals(defender) && monster.HasType(CardType.Synchro))) + return false; + + if (Enemy.GetMonsters().Any(monster => !monster.Equals(defender) && monster.IsCode(_CardId.HamonLordofStrikingThunder) && !monster.IsDisabled() && monster.IsDefense())) + return false; + + if (defender.OwnTargets.Any(card => card.IsCode(_CardId.PhantomKnightsFogBlade) && !card.IsDisabled())) + return false; + + return true; + } + + /// + /// Called when the AI has to select a card position. + /// + /// Id of the card to position on the field. + /// List of available positions. + /// Selected position, or 0 if no position is set for this card. + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (cardData.Attack == 0) + return CardPosition.FaceUpDefence; + } + return 0; + } + + public override bool OnSelectBattleReplay() + { + if (Bot.BattlingMonster == null) + return false; + List defenders = new List(Duel.Fields[1].GetMonsters()); + defenders.Sort(CardContainer.CompareDefensePower); + defenders.Reverse(); + BattlePhaseAction result = OnSelectAttackTarget(Bot.BattlingMonster, defenders); + if (result != null && result.Action == BattlePhaseAction.BattleAction.Attack) + { + return true; + } + return false; + } + + public override void OnNewTurn() + { + HonestEffectCount = 0; + } + + /// + /// Destroy face-down cards first, in our turn. + /// + protected bool DefaultMysticalSpaceTyphoon() + { + if (Duel.CurrentChain.Any(card => card.IsCode(_CardId.MysticalSpaceTyphoon))) + { + return false; + } + + List spells = Enemy.GetSpells(); + if (spells.Count == 0) + return false; + + ClientCard selected = Enemy.SpellZone.GetFloodgate(); + + if (selected == null) + { + if (Duel.Player == 0) + selected = spells.FirstOrDefault(card => card.IsFacedown()); + if (Duel.Player == 1) + selected = spells.FirstOrDefault(card => card.HasType(CardType.Continuous) || card.HasType(CardType.Equip) || card.HasType(CardType.Field)); + } + + if (selected == null) + return false; + AI.SelectCard(selected); + return true; + } + + /// + /// Destroy face-down cards first, in our turn. + /// + protected bool DefaultCosmicCyclone() + { + foreach (ClientCard card in Duel.CurrentChain) + if (card.IsCode(_CardId.CosmicCyclone)) + return false; + return (Bot.LifePoints > 1000) && DefaultMysticalSpaceTyphoon(); + } + + /// + /// Activate if avail. + /// + protected bool DefaultGalaxyCyclone() + { + List spells = Enemy.GetSpells(); + if (spells.Count == 0) + return false; + + ClientCard selected = null; + + if (Card.Location == CardLocation.Grave) + { + selected = Util.GetBestEnemySpell(true); + } + else + { + selected = spells.FirstOrDefault(card => card.IsFacedown()); + } + + if (selected == null) + return false; + + AI.SelectCard(selected); + return true; + } + + /// + /// Set the highest ATK level 4+ effect enemy monster. + /// + protected bool DefaultBookOfMoon() + { + if (Util.IsAllEnemyBetter(true)) + { + ClientCard monster = Enemy.GetMonsters().GetHighestAttackMonster(true); + if (monster != null && monster.HasType(CardType.Effect) && !monster.HasType(CardType.Link) && (monster.HasType(CardType.Xyz) || monster.Level > 4)) + { + AI.SelectCard(monster); + return true; + } + } + return false; + } + + /// + /// Return problematic monster, and if this card become target, return any enemy monster. + /// + protected bool DefaultCompulsoryEvacuationDevice() + { + ClientCard target = Util.GetProblematicEnemyMonster(0, true); + if (target != null) + { + AI.SelectCard(target); + return true; + } + if (Util.IsChainTarget(Card)) + { + ClientCard monster = Util.GetBestEnemyMonster(false, true); + if (monster != null) + { + AI.SelectCard(monster); + return true; + } + } + return false; + } + + /// + /// Revive the best monster when we don't have better one in field. + /// + protected bool DefaultCallOfTheHaunted() + { + if (!Util.IsAllEnemyBetter(true)) + return false; + ClientCard selected = Bot.Graveyard.GetMatchingCards(card => card.IsCanRevive()).OrderByDescending(card => card.Attack).FirstOrDefault(); + AI.SelectCard(selected); + return true; + } + + /// + /// Default Scapegoat effect + /// + protected bool DefaultScapegoat() + { + if (DefaultSpellWillBeNegated()) return false; + if (Duel.Player == 0) return false; + if (Duel.Phase == DuelPhase.End) return true; + if (DefaultOnBecomeTarget()) return true; + if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + if (Enemy.HasInMonstersZone(new[] + { + _CardId.UltimateConductorTytanno, + _CardId.InvokedPurgatrio, + _CardId.ChaosAncientGearGiant, + _CardId.UltimateAncientGearGolem, + _CardId.RedDragonArchfiend + }, true)) return false; + if (Util.GetTotalAttackingMonsterAttack(1) >= Bot.LifePoints) return true; + } + return false; + } + /// + /// Always active in opponent's turn. + /// + protected bool DefaultMaxxC() + { + return Duel.Player == 1; + } + /// + /// Always disable opponent's effect except some cards like UpstartGoblin + /// + protected bool DefaultAshBlossomAndJoyousSpring() + { + int[] ignoreList = { + _CardId.MacroCosmos, + _CardId.UpstartGoblin, + _CardId.CyberEmergency + }; + if (Util.GetLastChainCard().IsCode(ignoreList)) + return false; + if (Util.GetLastChainCard().HasSetcode(0x11e) && Util.GetLastChainCard().Location == CardLocation.Hand) // Danger! archtype hand effect + return false; + return Duel.LastChainPlayer == 1; + } + /// + /// Always activate unless the activating card is disabled + /// + protected bool DefaultGhostOgreAndSnowRabbit() + { + if (Util.GetLastChainCard() != null && Util.GetLastChainCard().IsDisabled()) + return false; + return DefaultTrap(); + } + /// + /// Always disable opponent's effect + /// + protected bool DefaultGhostBelleAndHauntedMansion() + { + return DefaultTrap(); + } + /// + /// Same as DefaultBreakthroughSkill + /// + protected bool DefaultEffectVeiler() + { + if (Util.GetLastChainCard() != null && Util.GetLastChainCard().IsCode(_CardId.GalaxySoldier) && Enemy.Hand.Count >= 3) return false; + if (Util.ChainContainsCard(_CardId.EffectVeiler)) + return false; + return DefaultBreakthroughSkill(); + } + /// + /// Chain common hand traps + /// + protected bool DefaultCalledByTheGrave() + { + int[] targetList = + { + _CardId.MaxxC, + _CardId.LockBird, + _CardId.GhostOgreAndSnowRabbit, + _CardId.AshBlossom, + _CardId.GhostBelle, + _CardId.EffectVeiler, + _CardId.ArtifactLancea + }; + if (Duel.LastChainPlayer == 1) + { + foreach (int id in targetList) + { + if (Util.GetLastChainCard().IsCode(id)) + { + AI.SelectCard(id); + return UniqueFaceupSpell(); + } + } + } + return false; + } + /// + /// Default InfiniteImpermanence effect + /// + protected bool DefaultInfiniteImpermanence() + { + // TODO: disable s & t + if (!DefaultUniqueTrap()) + return false; + return DefaultDisableMonster(); + } + /// + /// Chain the enemy monster, or disable monster like Rescue Rabbit. + /// + protected bool DefaultBreakthroughSkill() + { + if (!DefaultUniqueTrap()) + return false; + return DefaultDisableMonster(); + } + /// + /// Chain the enemy monster, or disable monster like Rescue Rabbit. + /// + protected bool DefaultDisableMonster() + { + if (Duel.Player == 1) + { + ClientCard target = Enemy.MonsterZone.GetShouldBeDisabledBeforeItUseEffectMonster(); + if (target != null) + { + AI.SelectCard(target); + return true; + } + } + + ClientCard LastChainCard = Util.GetLastChainCard(); + + if (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.MonsterZone && + !LastChainCard.IsDisabled() && !LastChainCard.IsShouldNotBeTarget() && !LastChainCard.IsShouldNotBeSpellTrapTarget()) + { + AI.SelectCard(LastChainCard); + return true; + } + + if (Bot.BattlingMonster != null && Enemy.BattlingMonster != null) + { + if (!Enemy.BattlingMonster.IsDisabled() && Enemy.BattlingMonster.IsCode(_CardId.EaterOfMillions)) + { + AI.SelectCard(Enemy.BattlingMonster); + return true; + } + } + + if (Duel.Phase == DuelPhase.BattleStart && Duel.Player == 1 && + Enemy.HasInMonstersZone(_CardId.NumberS39UtopiaTheLightning, true)) + { + AI.SelectCard(_CardId.NumberS39UtopiaTheLightning); + return true; + } + + return false; + } + + /// + /// Activate only except this card is the target or we summon monsters. + /// + protected bool DefaultSolemnJudgment() + { + return !Util.IsChainTargetOnly(Card) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap(); + } + + /// + /// Activate only except we summon monsters. + /// + protected bool DefaultSolemnWarning() + { + return (Bot.LifePoints > 2000) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap(); + } + + /// + /// Activate only except we summon monsters. + /// + protected bool DefaultSolemnStrike() + { + return (Bot.LifePoints > 1500) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap(); + } + + /// + /// Activate when all enemy monsters have better ATK. + /// + protected bool DefaultTorrentialTribute() + { + return !Util.HasChainedTrap(0) && Util.IsAllEnemyBetter(true); + } + + /// + /// Activate enemy have more S&T. + /// + protected bool DefaultHeavyStorm() + { + return Bot.GetSpellCount() < Enemy.GetSpellCount(); + } + + /// + /// Activate before other winds, if enemy have more than 2 S&T. + /// + protected bool DefaultHarpiesFeatherDusterFirst() + { + return Enemy.GetSpellCount() >= 2; + } + + /// + /// Activate when one enemy monsters have better ATK. + /// + protected bool DefaultHammerShot() + { + return Util.IsOneEnemyBetter(true); + } + + /// + /// Activate when one enemy monsters have better ATK or DEF. + /// + protected bool DefaultDarkHole() + { + return Util.IsOneEnemyBetter(); + } + + /// + /// Activate when one enemy monsters have better ATK or DEF. + /// + protected bool DefaultRaigeki() + { + return Util.IsOneEnemyBetter(); + } + + /// + /// Activate when one enemy monsters have better ATK or DEF. + /// + protected bool DefaultSmashingGround() + { + return Util.IsOneEnemyBetter(); + } + + /// + /// Activate when we have more than 15 cards in deck. + /// + protected bool DefaultPotOfDesires() + { + return Bot.Deck.Count > 15; + } + + /// + /// Set traps only and avoid block the activation of other cards. + /// + protected bool DefaultSpellSet() + { + return (Card.IsTrap() || Card.HasType(CardType.QuickPlay)) && Bot.GetSpellCountWithoutField() < 4; + } + + /// + /// Summon with tributes ATK lower. + /// + protected bool DefaultTributeSummon() + { + if (!UniqueFaceupMonster()) + return false; + int tributecount = (int)Math.Ceiling((Card.Level - 4.0d) / 2.0d); + for (int j = 0; j < 7; ++j) + { + ClientCard tributeCard = Bot.MonsterZone[j]; + if (tributeCard == null) continue; + if (tributeCard.GetDefensePower() < Card.Attack) + tributecount--; + } + return tributecount <= 0; + } + + /// + /// Activate when we have no field. + /// + protected bool DefaultField() + { + return Bot.SpellZone[5] == null; + } + + /// + /// Turn if all enemy is better. + /// + protected bool DefaultMonsterRepos() + { + if (Card.IsFaceup() && Card.IsDefense() && Card.Attack == 0) + return false; + + if (Enemy.HasInMonstersZone(_CardId.BlueEyesChaosMAXDragon, true) && + Card.IsAttack() && (4000 - Card.Defense) * 2 > (4000 - Card.Attack)) + return false; + if (Enemy.HasInMonstersZone(_CardId.BlueEyesChaosMAXDragon, true) && + Card.IsDefense() && Card.IsFaceup() && + (4000 - Card.Defense) * 2 > (4000 - Card.Attack)) + return true; + + bool enemyBetter = Util.IsAllEnemyBetter(true); + if (Card.IsAttack() && enemyBetter) + return true; + if (Card.IsDefense() && !enemyBetter && Card.Attack >= Card.Defense) + return true; + + return false; + } + + /// + /// If spell will be negated + /// + protected bool DefaultSpellWillBeNegated() + { + return Bot.HasInSpellZone(_CardId.ImperialOrder, true, true) || Enemy.HasInSpellZone(_CardId.ImperialOrder, true) || Enemy.HasInMonstersZone(_CardId.NaturiaBeast, true); + } + + /// + /// If spell must set first to activate + /// + protected bool DefaultSpellMustSetFirst() + { + ClientCard card = null; + foreach (ClientCard check in Bot.GetSpells()) + { + if (check.IsCode(_CardId.AntiSpellFragrance) && !check.IsDisabled()) + card = check; + } + if (card != null && card.IsFaceup()) + return true; + return Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true, true) || Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true); + } + + /// + /// if spell/trap is the target or enermy activate HarpiesFeatherDuster + /// + protected bool DefaultOnBecomeTarget() + { + if (Util.IsChainTarget(Card)) return true; + int[] destroyAllList = + { + _CardId.EvilswarmExcitonKnight, + _CardId.BlackRoseDragon, + _CardId.JudgmentDragon, + _CardId.TopologicTrisbaena + }; + int[] destroyAllOpponentList = + { + _CardId.HarpiesFeatherDuster, + _CardId.DarkMagicAttack + }; + + if (Util.ChainContainsCard(destroyAllList)) return true; + if (Enemy.HasInSpellZone(destroyAllOpponentList, true)) return true; + // TODO: ChainContainsCard(id, player) + return false; + } + /// + /// Chain enemy activation or summon. + /// + protected bool DefaultTrap() + { + return (Duel.LastChainPlayer == -1 && Duel.LastSummonPlayer != 0) || Duel.LastChainPlayer == 1; + } + + /// + /// Activate when avail and no other our trap card in this chain or face-up. + /// + protected bool DefaultUniqueTrap() + { + if (Util.HasChainedTrap(0)) + return false; + + return UniqueFaceupSpell(); + } + + /// + /// Check no other our spell or trap card with same name face-up. + /// + protected bool UniqueFaceupSpell() + { + return !Bot.GetSpells().Any(card => card.IsCode(Card.Id) && card.IsFaceup()); + } + + /// + /// Check no other our monster card with same name face-up. + /// + protected bool UniqueFaceupMonster() + { + return !Bot.GetMonsters().Any(card => card.IsCode(Card.Id) && card.IsFaceup()); + } + + /// + /// Dumb way to avoid the bot chain in mess. + /// + protected bool DefaultDontChainMyself() + { + if (Executors.Any(exec => exec.Type == Type && exec.CardId == Card.Id)) + return false; + return Duel.LastChainPlayer != 0; + } + + /// + /// Draw when we have lower LP, or destroy it. Can be overrided. + /// + protected bool DefaultChickenGame() + { + if (Executors.Count(exec => exec.Type == Type && exec.CardId == Card.Id) > 1) + return false; + if (Bot.LifePoints <= 1000) + return false; + if (Bot.LifePoints <= Enemy.LifePoints && ActivateDescription == Util.GetStringId(_CardId.ChickenGame, 0)) + return true; + if (Bot.LifePoints > Enemy.LifePoints && ActivateDescription == Util.GetStringId(_CardId.ChickenGame, 1)) + return true; + return false; + } + + /// + /// Draw when we have Dark monster in hand,and banish random one. Can be overrided. + /// + protected bool DefaultAllureofDarkness() + { + ClientCard target = Bot.Hand.FirstOrDefault(card => card.HasAttribute(CardAttribute.Dark)); + return target != null; + } + + /// + /// Clever enough. + /// + protected bool DefaultDimensionalBarrier() + { + const int RITUAL = 0; + const int FUSION = 1; + const int SYNCHRO = 2; + const int XYZ = 3; + const int PENDULUM = 4; + if (Duel.Player != 0) + { + List monsters = Enemy.GetMonsters(); + int[] levels = new int[13]; + bool tuner = false; + bool nontuner = false; + foreach (ClientCard monster in monsters) + { + if (monster.HasType(CardType.Tuner)) + tuner = true; + else if (!monster.HasType(CardType.Xyz) && !monster.HasType(CardType.Link)) + { + nontuner = true; + levels[monster.Level] = levels[monster.Level] + 1; + } + + if (monster.IsOneForXyz()) + { + AI.SelectOption(XYZ); + return true; + } + } + if (tuner && nontuner) + { + AI.SelectOption(SYNCHRO); + return true; + } + for (int i=1; i<=12; i++) + { + if (levels[i]>1) + { + AI.SelectOption(XYZ); + return true; + } + } + ClientCard l = Enemy.SpellZone[6]; + ClientCard r = Enemy.SpellZone[7]; + if (l != null && r != null && l.LScale != r.RScale) + { + AI.SelectOption(PENDULUM); + return true; + } + } + ClientCard lastchaincard = Util.GetLastChainCard(); + if (Duel.LastChainPlayer == 1 && lastchaincard != null && !lastchaincard.IsDisabled()) + { + if (lastchaincard.HasType(CardType.Ritual)) + { + AI.SelectOption(RITUAL); + return true; + } + if (lastchaincard.HasType(CardType.Fusion)) + { + AI.SelectOption(FUSION); + return true; + } + if (lastchaincard.HasType(CardType.Synchro)) + { + AI.SelectOption(SYNCHRO); + return true; + } + if (lastchaincard.HasType(CardType.Xyz)) + { + AI.SelectOption(XYZ); + return true; + } + if (lastchaincard.IsFusionSpell()) + { + AI.SelectOption(FUSION); + return true; + } + } + if (Util.IsChainTarget(Card)) + { + AI.SelectOption(XYZ); + return true; + } + return false; + } + + /// + /// Clever enough + /// + protected bool DefaultInterruptedKaijuSlumber() + { + if (Card.Location == CardLocation.Grave) + { + AI.SelectCard( + _CardId.GamecieltheSeaTurtleKaiju, + _CardId.KumongoustheStickyStringKaiju, + _CardId.GadarlatheMysteryDustKaiju, + _CardId.RadiantheMultidimensionalKaiju, + _CardId.DogorantheMadFlameKaiju, + _CardId.ThunderKingtheLightningstrikeKaiju, + _CardId.JizukirutheStarDestroyingKaiju + ); + return true; + } + + if (DefaultDarkHole()) + { + AI.SelectCard( + _CardId.JizukirutheStarDestroyingKaiju, + _CardId.ThunderKingtheLightningstrikeKaiju, + _CardId.DogorantheMadFlameKaiju, + _CardId.RadiantheMultidimensionalKaiju, + _CardId.GadarlatheMysteryDustKaiju, + _CardId.KumongoustheStickyStringKaiju, + _CardId.GamecieltheSeaTurtleKaiju + ); + AI.SelectNextCard( + _CardId.SuperAntiKaijuWarMachineMechaDogoran, + _CardId.GamecieltheSeaTurtleKaiju, + _CardId.KumongoustheStickyStringKaiju, + _CardId.GadarlatheMysteryDustKaiju, + _CardId.RadiantheMultidimensionalKaiju, + _CardId.DogorantheMadFlameKaiju, + _CardId.ThunderKingtheLightningstrikeKaiju + ); + return true; + } + + return false; + } + + /// + /// Clever enough. + /// + protected bool DefaultKaijuSpsummon() + { + IList kaijus = new[] { + _CardId.JizukirutheStarDestroyingKaiju, + _CardId.GadarlatheMysteryDustKaiju, + _CardId.GamecieltheSeaTurtleKaiju, + _CardId.RadiantheMultidimensionalKaiju, + _CardId.KumongoustheStickyStringKaiju, + _CardId.ThunderKingtheLightningstrikeKaiju, + _CardId.DogorantheMadFlameKaiju, + _CardId.SuperAntiKaijuWarMachineMechaDogoran + }; + foreach (ClientCard monster in Enemy.GetMonsters()) + { + if (monster.IsCode(kaijus)) + return Card.GetDefensePower() > monster.GetDefensePower(); + } + ClientCard card = Enemy.MonsterZone.GetFloodgate(); + if (card != null) + { + AI.SelectCard(card); + return true; + } + card = Enemy.MonsterZone.GetDangerousMonster(); + if (card != null) + { + AI.SelectCard(card); + return true; + } + card = Util.GetOneEnemyBetterThanValue(Card.GetDefensePower()); + if (card != null) + { + AI.SelectCard(card); + return true; + } + return false; + } + + /// + /// Summon when we don't have monster attack higher than enemy's. + /// + protected bool DefaultNumberS39UtopiaTheLightningSummon() + { + int bestBotAttack = Util.GetBestAttack(Bot); + return Util.IsOneEnemyBetterThanValue(bestBotAttack, false); + } + + /// + /// Activate if the card is attack pos, and its attack is below 5000, when the enemy monster is attack pos or not useless faceup defense pos + /// + protected bool DefaultNumberS39UtopiaTheLightningEffect() + { + return Card.IsAttack() && Card.Attack < 5000 && (Enemy.BattlingMonster.IsAttack() || Enemy.BattlingMonster.IsFacedown() || Enemy.BattlingMonster.GetDefensePower() >= Card.Attack); + } + + /// + /// Summon when it can and should use effect. + /// + protected bool DefaultEvilswarmExcitonKnightSummon() + { + int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount() + Bot.GetHandCount(); + int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount() + Enemy.GetHandCount(); + return (selfCount - 1 < oppoCount) && DefaultEvilswarmExcitonKnightEffect(); + } + + /// + /// Activate when we have less cards than enemy's, or the atk sum of we is lower than enemy's. + /// + protected bool DefaultEvilswarmExcitonKnightEffect() + { + int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount(); + int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount(); + + if (selfCount < oppoCount) + return true; + + int selfAttack = Bot.GetMonsters().Sum(monster => (int?)monster.GetDefensePower()) ?? 0; + int oppoAttack = Enemy.GetMonsters().Sum(monster => (int?)monster.GetDefensePower()) ?? 0; + + return selfAttack < oppoAttack; + } + + /// + /// Summon in main2, or when the attack of we is lower than enemy's, but not when enemy have monster higher than 2500. + /// + protected bool DefaultStardustDragonSummon() + { + int selfBestAttack = Util.GetBestAttack(Bot); + int oppoBestAttack = Util.GetBestPower(Enemy); + return (selfBestAttack <= oppoBestAttack && oppoBestAttack <= 2500) || Util.IsTurn1OrMain2(); + } + + /// + /// Negate enemy's destroy effect, and revive from grave. + /// + protected bool DefaultStardustDragonEffect() + { + return (Card.Location == CardLocation.Grave) || Duel.LastChainPlayer == 1; + } + + /// + /// Summon when enemy have card which we must solve. + /// + protected bool DefaultCastelTheSkyblasterMusketeerSummon() + { + return Util.GetProblematicEnemyCard() != null; + } + + /// + /// Bounce the problematic enemy card. Ignore the 1st effect. + /// + protected bool DefaultCastelTheSkyblasterMusketeerEffect() + { + if (ActivateDescription == Util.GetStringId(_CardId.CastelTheSkyblasterMusketeer, 0)) + return false; + ClientCard target = Util.GetProblematicEnemyCard(); + if (target != null) + { + AI.SelectCard(0); + AI.SelectNextCard(target); + return true; + } + return false; + } + + /// + /// Summon when it should use effect, or when the attack of we is lower than enemy's, but not when enemy have monster higher than 3000. + /// + protected bool DefaultScarlightRedDragonArchfiendSummon() + { + int selfBestAttack = Util.GetBestAttack(Bot); + int oppoBestAttack = Util.GetBestPower(Enemy); + return (selfBestAttack <= oppoBestAttack && oppoBestAttack <= 3000) || DefaultScarlightRedDragonArchfiendEffect(); + } + + /// + /// Activate when we have less monsters than enemy, or when enemy have more than 3 monsters. + /// + protected bool DefaultScarlightRedDragonArchfiendEffect() + { + int selfCount = Bot.GetMonsters().Count(monster => !monster.Equals(Card) && monster.IsSpecialSummoned && monster.HasType(CardType.Effect) && monster.Attack <= Card.Attack); + int oppoCount = Enemy.GetMonsters().Count(monster => monster.IsSpecialSummoned && monster.HasType(CardType.Effect) && monster.Attack <= Card.Attack); + return selfCount <= oppoCount && oppoCount > 0 || oppoCount >= 3; + } + + /// + /// Clever enough. + /// + protected bool DefaultHonestEffect() + { + if (Card.Location == CardLocation.Hand) + { + return Bot.BattlingMonster.IsAttack() && + (((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Attack) || Bot.BattlingMonster.Attack >= Enemy.LifePoints) + || ((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Defense) && (Bot.BattlingMonster.Attack + Enemy.BattlingMonster.Attack > Enemy.BattlingMonster.Defense))); + } + + if (Util.IsTurn1OrMain2() && HonestEffectCount <= 5) + { + HonestEffectCount++; + return true; + } + + return false; + } + } +} diff --git a/Game/AI/Enums/DangerousMonster.cs b/ExecutorBase/Game/AI/Enums/DangerousMonster.cs similarity index 96% rename from Game/AI/Enums/DangerousMonster.cs rename to ExecutorBase/Game/AI/Enums/DangerousMonster.cs index 66276534..067d3935 100644 --- a/Game/AI/Enums/DangerousMonster.cs +++ b/ExecutorBase/Game/AI/Enums/DangerousMonster.cs @@ -1,28 +1,28 @@ -namespace WindBot.Game.AI.Enums -{ - /// - /// Cards that are dangerous to attack. - /// - public enum DangerousMonster - { - LionHeart = 54366836, - Yubel = 78371393, - YubelIncarnate = 4779091, - YubelNightmare = 31764700, - ZaphionTheTimelord = 28929131, - SadionTheTimelord = 65314286, - MetaionTheTimelord = 74530899, - KamionTheTimelord = 91712985, - LazionTheTimelord = 92435533, - MichionTheTimelord = 7733560, - HailonTheTimelord = 34137269, - RaphionTheTimelord = 60222213, - GabrionTheTimelord = 6616912, - SandaionTheTimelord = 33015627, - EaterOfMillions = 63845230, - ElShaddollConstruct = 20366274, - ZushintheSleepingGiant = 67547370, - Heart_eartHDragon = 97403510, - DaigustoSphreeze = 29552709, - } -} +namespace WindBot.Game.AI.Enums +{ + /// + /// Cards that are dangerous to attack. + /// + public enum DangerousMonster + { + LionHeart = 54366836, + Yubel = 78371393, + YubelIncarnate = 4779091, + YubelNightmare = 31764700, + ZaphionTheTimelord = 28929131, + SadionTheTimelord = 65314286, + MetaionTheTimelord = 74530899, + KamionTheTimelord = 91712985, + LazionTheTimelord = 92435533, + MichionTheTimelord = 7733560, + HailonTheTimelord = 34137269, + RaphionTheTimelord = 60222213, + GabrionTheTimelord = 6616912, + SandaionTheTimelord = 33015627, + EaterOfMillions = 63845230, + ElShaddollConstruct = 20366274, + ZushintheSleepingGiant = 67547370, + Heart_eartHDragon = 97403510, + DaigustoSphreeze = 29552709, + } +} diff --git a/Game/AI/Enums/Floodgate.cs b/ExecutorBase/Game/AI/Enums/Floodgate.cs similarity index 100% rename from Game/AI/Enums/Floodgate.cs rename to ExecutorBase/Game/AI/Enums/Floodgate.cs diff --git a/Game/AI/Enums/FusionSpell.cs b/ExecutorBase/Game/AI/Enums/FusionSpell.cs similarity index 100% rename from Game/AI/Enums/FusionSpell.cs rename to ExecutorBase/Game/AI/Enums/FusionSpell.cs diff --git a/Game/AI/Enums/InvincibleMonster.cs b/ExecutorBase/Game/AI/Enums/InvincibleMonster.cs similarity index 97% rename from Game/AI/Enums/InvincibleMonster.cs rename to ExecutorBase/Game/AI/Enums/InvincibleMonster.cs index bd9bc680..9399125e 100644 --- a/Game/AI/Enums/InvincibleMonster.cs +++ b/ExecutorBase/Game/AI/Enums/InvincibleMonster.cs @@ -1,71 +1,71 @@ -namespace WindBot.Game.AI.Enums -{ - /// - /// Cards that are invincible to battle. - /// - public enum InvincibleMonster - { - SpiritReaper = 23205979, - YubelTheUltimateNightmare = 31764700, - YubelTerrorIncarnate = 4779091, - SandaionTheTimelord = 33015627, - DarknessNeosphere = 60417395, - GabrionTheTimelord = 6616912, - MichionTheTimelord = 7733560, - ZaphionTheTimelord = 28929131, - HailonTheTimelord = 34137269, - RaphionTheTimelord = 60222213, - SadionTheTimelord = 65314286, - MetaionTheTimelord = 74530899, - Yubel = 78371393, - KamionTheTimelord = 91712985, - LazionTheTimelord = 92435533, - CloudianEyeofTheTyphoon = 57610714, - GimmickPuppetShadowFeeler = 34620088, - TheLegendaryFishermanIII = 44968687, - CastleGate = 36931229, - CloudianNimbusman = 20003527, - ExodiaNecross = 12600382, - Gellenduo = 11662742, - CloudianAltus = 79703905, - CloudianStormDragon = 13474291, - CloudianCirrostratus = 43318266, - CloudianTurbulence = 16197610, - CloudianAcidCloud = 17810268, - SuperheavySamuraiBlueBrawler = 41628550, - DinoSewing = 27143874, - Marshmallon = 31305911, - ShibaWarriorTaro = 27416701, - XSaberPashuul = 23093604, - SuperheavySamuraiBlowtorch = 7864030, - VijamTheCubicSeed = 15610297, - ArcanaForce0TheFool = 62892347, - ReptilianneNaga = 79491903, - AbyssStungray = 97232518, - ArmityleTheChaosPhantom = 43378048, - BlueEyesTwinBurstDragon = 2129638, - GladiatorBeastNerokius = 29357956, - MaskedHERODivineWind = 22093873, - ElementalHEROShiningPhoenixEnforcer = 88820235, - LunalightCatDancer = 51777272, - ElementalHEROPhoenixEnforcer = 41436536, - BloomDivaTheMelodiousChoir = 84988419, - ReaperonTheNightmare = 85684223, - BeelzeusofTheDiabolicDragons = 8763963, - DragocytosCorruptedNethersoulDragon = 21435914, - BeelzeofTheDiabolicDragons = 34408491, - BlackwingArmorMaster = 69031175, - DaigustoSphreeze = 29552709, - DarkDiviner = 31919988, - NumberC92HearteartHChaosDragon = 47017574, - Number92HearteartHDragon = 97403510, - Number51FinisherTheStrongArm = 56292140, - NumberC96DarkStorm = 77205367, - NumberF0UtopicFutureFutureSlash = 43490025, - NumberF0UtopicFuture = 65305468, - GoukiTheGiantOgre = 47946130, - BorrelswordDragon = 85289965, - NumberF0UtopicFutureDragon = 26973555, - BorrelendDragon = 98630720 - } -} +namespace WindBot.Game.AI.Enums +{ + /// + /// Cards that are invincible to battle. + /// + public enum InvincibleMonster + { + SpiritReaper = 23205979, + YubelTheUltimateNightmare = 31764700, + YubelTerrorIncarnate = 4779091, + SandaionTheTimelord = 33015627, + DarknessNeosphere = 60417395, + GabrionTheTimelord = 6616912, + MichionTheTimelord = 7733560, + ZaphionTheTimelord = 28929131, + HailonTheTimelord = 34137269, + RaphionTheTimelord = 60222213, + SadionTheTimelord = 65314286, + MetaionTheTimelord = 74530899, + Yubel = 78371393, + KamionTheTimelord = 91712985, + LazionTheTimelord = 92435533, + CloudianEyeofTheTyphoon = 57610714, + GimmickPuppetShadowFeeler = 34620088, + TheLegendaryFishermanIII = 44968687, + CastleGate = 36931229, + CloudianNimbusman = 20003527, + ExodiaNecross = 12600382, + Gellenduo = 11662742, + CloudianAltus = 79703905, + CloudianStormDragon = 13474291, + CloudianCirrostratus = 43318266, + CloudianTurbulence = 16197610, + CloudianAcidCloud = 17810268, + SuperheavySamuraiBlueBrawler = 41628550, + DinoSewing = 27143874, + Marshmallon = 31305911, + ShibaWarriorTaro = 27416701, + XSaberPashuul = 23093604, + SuperheavySamuraiBlowtorch = 7864030, + VijamTheCubicSeed = 15610297, + ArcanaForce0TheFool = 62892347, + ReptilianneNaga = 79491903, + AbyssStungray = 97232518, + ArmityleTheChaosPhantom = 43378048, + BlueEyesTwinBurstDragon = 2129638, + GladiatorBeastNerokius = 29357956, + MaskedHERODivineWind = 22093873, + ElementalHEROShiningPhoenixEnforcer = 88820235, + LunalightCatDancer = 51777272, + ElementalHEROPhoenixEnforcer = 41436536, + BloomDivaTheMelodiousChoir = 84988419, + ReaperonTheNightmare = 85684223, + BeelzeusofTheDiabolicDragons = 8763963, + DragocytosCorruptedNethersoulDragon = 21435914, + BeelzeofTheDiabolicDragons = 34408491, + BlackwingArmorMaster = 69031175, + DaigustoSphreeze = 29552709, + DarkDiviner = 31919988, + NumberC92HearteartHChaosDragon = 47017574, + Number92HearteartHDragon = 97403510, + Number51FinisherTheStrongArm = 56292140, + NumberC96DarkStorm = 77205367, + NumberF0UtopicFutureFutureSlash = 43490025, + NumberF0UtopicFuture = 65305468, + GoukiTheGiantOgre = 47946130, + BorrelswordDragon = 85289965, + NumberF0UtopicFutureDragon = 26973555, + BorrelendDragon = 98630720 + } +} diff --git a/Game/AI/Enums/OneForXyz.cs b/ExecutorBase/Game/AI/Enums/OneForXyz.cs similarity index 100% rename from Game/AI/Enums/OneForXyz.cs rename to ExecutorBase/Game/AI/Enums/OneForXyz.cs diff --git a/Game/AI/Enums/PreventActivationEffectInBattle.cs b/ExecutorBase/Game/AI/Enums/PreventActivationEffectInBattle.cs similarity index 100% rename from Game/AI/Enums/PreventActivationEffectInBattle.cs rename to ExecutorBase/Game/AI/Enums/PreventActivationEffectInBattle.cs diff --git a/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs b/ExecutorBase/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs similarity index 97% rename from Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs rename to ExecutorBase/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs index ff5df104..547e0c16 100644 --- a/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs +++ b/ExecutorBase/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs @@ -1,61 +1,61 @@ -namespace WindBot.Game.AI.Enums -{ - /// - /// Monsters that release or banish itself to use effect. So them should be disabled (with Breakthrough Skill) before it use effect. - /// - public enum ShouldBeDisabledBeforeItUseEffectMonster - { - MachinaMegaform = 51617185, - DarkSummoningBeast = 87917187, - GemKnightAlexandrite = 90019393, - RedEyesRetroDragon = 53485634, - DeepSweeper = 8649148, - BeastWarriorPuma = 16796157, - ZefrasaberSwordmasteroftheNekroz = 84388461, - CipherWing = 81974607, - MadolcheAnjelly = 34680482, - PlanetPathfinder = 97526666, - RescueCat = 14878871, - RescueHamster = 50485594, - RescueFerret = 56343672, - RescueRabbit = 85138716, - GalaxyWizard = 98555327, - Backlinker = 71172240, - Merlin = 3580032, - CrystalVanguard = 87475570, - TemperanceofProphecy = 87608852, - Kuribandit = 16404809, - PhotonLizard = 38973775, - SuperheavySamuraiFlutist = 27978707, - ConstellarRasalhague = 70624184, - CardcarD = 45812361, - UnifloraMysticalBeastoftheForest = 36318200, - BusterWhelpoftheDestructionSwordsman = 49823708, - GalaxyEyesCloudragon = 9260791, - SylvanPrincessprout = 20579538, - AltergeistPixiel = 57769391, - AbyssActorExtras = 88412339, - PerformapalTrumpWitch = 91584698, - RaidraptorLastStrix = 97219708, - MythicalBeastJackal = 91182675, - TimeMaiden = 27107590, - SuperQuantalFairyAlphan = 58753372, - TheBlackStoneofLegend = 66574418, - PaladinofDarkDragon = 71408082, - PaladinofPhotonDragon = 85346853, - TwinPhotonLizard = 29455728, - TimeThiefRegulator = 19891131, - MathmechNabla = 53577438, - NidhoggGeneraiderBossofIce = 49275969, - HoarrGeneraiderBossofRumbling = 68199168, - RedFamiliar = 8372133, - AccesscodeTalker = 86066372, - ChaosSummoningBeast = 27439792, - - CosmoBrain = 85679527, - ShiranuiSolitaire = 94801854, - Mixeroid = 71340250, - LonefireBlossom = 48686504, - BrotherhoodoftheFireFist_Leopard = 39699564 - } -} +namespace WindBot.Game.AI.Enums +{ + /// + /// Monsters that release or banish itself to use effect. So them should be disabled (with Breakthrough Skill) before it use effect. + /// + public enum ShouldBeDisabledBeforeItUseEffectMonster + { + MachinaMegaform = 51617185, + DarkSummoningBeast = 87917187, + GemKnightAlexandrite = 90019393, + RedEyesRetroDragon = 53485634, + DeepSweeper = 8649148, + BeastWarriorPuma = 16796157, + ZefrasaberSwordmasteroftheNekroz = 84388461, + CipherWing = 81974607, + MadolcheAnjelly = 34680482, + PlanetPathfinder = 97526666, + RescueCat = 14878871, + RescueHamster = 50485594, + RescueFerret = 56343672, + RescueRabbit = 85138716, + GalaxyWizard = 98555327, + Backlinker = 71172240, + Merlin = 3580032, + CrystalVanguard = 87475570, + TemperanceofProphecy = 87608852, + Kuribandit = 16404809, + PhotonLizard = 38973775, + SuperheavySamuraiFlutist = 27978707, + ConstellarRasalhague = 70624184, + CardcarD = 45812361, + UnifloraMysticalBeastoftheForest = 36318200, + BusterWhelpoftheDestructionSwordsman = 49823708, + GalaxyEyesCloudragon = 9260791, + SylvanPrincessprout = 20579538, + AltergeistPixiel = 57769391, + AbyssActorExtras = 88412339, + PerformapalTrumpWitch = 91584698, + RaidraptorLastStrix = 97219708, + MythicalBeastJackal = 91182675, + TimeMaiden = 27107590, + SuperQuantalFairyAlphan = 58753372, + TheBlackStoneofLegend = 66574418, + PaladinofDarkDragon = 71408082, + PaladinofPhotonDragon = 85346853, + TwinPhotonLizard = 29455728, + TimeThiefRegulator = 19891131, + MathmechNabla = 53577438, + NidhoggGeneraiderBossofIce = 49275969, + HoarrGeneraiderBossofRumbling = 68199168, + RedFamiliar = 8372133, + AccesscodeTalker = 86066372, + ChaosSummoningBeast = 27439792, + + CosmoBrain = 85679527, + ShiranuiSolitaire = 94801854, + Mixeroid = 71340250, + LonefireBlossom = 48686504, + BrotherhoodoftheFireFist_Leopard = 39699564 + } +} diff --git a/Game/AI/Enums/ShouldNotBeMonsterTarget.cs b/ExecutorBase/Game/AI/Enums/ShouldNotBeMonsterTarget.cs similarity index 97% rename from Game/AI/Enums/ShouldNotBeMonsterTarget.cs rename to ExecutorBase/Game/AI/Enums/ShouldNotBeMonsterTarget.cs index b5cb0f6f..855824e2 100644 --- a/Game/AI/Enums/ShouldNotBeMonsterTarget.cs +++ b/ExecutorBase/Game/AI/Enums/ShouldNotBeMonsterTarget.cs @@ -1,19 +1,19 @@ -namespace WindBot.Game.AI.Enums -{ - /// - /// Cards that are can't be selected as target of monster's effect, or immuned to monster's effect. - /// So them shouldn't be tried to be selected as target of monster at most times. - /// - public enum ShouldNotBeMonsterTarget - { - TheLegendaryFishermanII = 19801646, - GaiaDraketheUniversalForce = 58601383, - FirstoftheDragons = 10817524, - Tatsunoko = 55863245, - CXyzSimontheGreatMoralLeader = 41147577, - PaleozoicAnomalocaris = 61307542, - PaleozoicOpabinia = 37649320, - BorreloadDragon = 31833038, - BorrelendDragon = 98630720 - } -} +namespace WindBot.Game.AI.Enums +{ + /// + /// Cards that are can't be selected as target of monster's effect, or immuned to monster's effect. + /// So them shouldn't be tried to be selected as target of monster at most times. + /// + public enum ShouldNotBeMonsterTarget + { + TheLegendaryFishermanII = 19801646, + GaiaDraketheUniversalForce = 58601383, + FirstoftheDragons = 10817524, + Tatsunoko = 55863245, + CXyzSimontheGreatMoralLeader = 41147577, + PaleozoicAnomalocaris = 61307542, + PaleozoicOpabinia = 37649320, + BorreloadDragon = 31833038, + BorrelendDragon = 98630720 + } +} diff --git a/Game/AI/Enums/ShouldNotBeSpellTarget.cs b/ExecutorBase/Game/AI/Enums/ShouldNotBeSpellTarget.cs similarity index 97% rename from Game/AI/Enums/ShouldNotBeSpellTarget.cs rename to ExecutorBase/Game/AI/Enums/ShouldNotBeSpellTarget.cs index ba8e69c6..fa78eaf9 100644 --- a/Game/AI/Enums/ShouldNotBeSpellTarget.cs +++ b/ExecutorBase/Game/AI/Enums/ShouldNotBeSpellTarget.cs @@ -1,14 +1,14 @@ -namespace WindBot.Game.AI.Enums -{ - /// - /// Cards that are can't be selected as target of spell&trap's effect, or immuned to spell&trap's effect. - /// So them shouldn't be tried to be selected as target of spell&trap at most times. - /// - public enum ShouldNotBeSpellTrapTarget - { - ApoqliphortTowers = 27279764, - ApoqliphortSkybase = 40061558, - TheLegendaryFishermanIII = 44968687, - ChaosAncientGearGiant = 51788412 - } -} +namespace WindBot.Game.AI.Enums +{ + /// + /// Cards that are can't be selected as target of spell&trap's effect, or immuned to spell&trap's effect. + /// So them shouldn't be tried to be selected as target of spell&trap at most times. + /// + public enum ShouldNotBeSpellTrapTarget + { + ApoqliphortTowers = 27279764, + ApoqliphortSkybase = 40061558, + TheLegendaryFishermanIII = 44968687, + ChaosAncientGearGiant = 51788412 + } +} diff --git a/Game/AI/Enums/ShouldNotBeTarget.cs b/ExecutorBase/Game/AI/Enums/ShouldNotBeTarget.cs similarity index 97% rename from Game/AI/Enums/ShouldNotBeTarget.cs rename to ExecutorBase/Game/AI/Enums/ShouldNotBeTarget.cs index 7adcc587..c3e066d7 100644 --- a/Game/AI/Enums/ShouldNotBeTarget.cs +++ b/ExecutorBase/Game/AI/Enums/ShouldNotBeTarget.cs @@ -1,54 +1,54 @@ -namespace WindBot.Game.AI.Enums -{ - /// - /// Cards that are can't be selected as target, or immuned to most effect. - /// So them shouldn't be tried to be selected as target at most times. - /// - public enum ShouldNotBeTarget - { - DivineSerpentGeh = 82103466, - ObelisktheTormentor = 10000000, - TheWingedDragonofRaSphereMode = 10000080, - TheWingedDragonofRaImmortalPhoenix = 10000090, - KozmoDarkPlanet = 85991529, - ZushintheSleepingGiant = 67547370, - TheLegendaryExodiaIncarnate = 58604027, - KozmoDarkEclipser = 64063868, - KozmoDarkDestroyer = 55885348, - KozmoForerunner = 20849090, - MajespecterUnicornKirin = 31178212, - WorldLegacyWorldShield = 55787576, - KiwiMagicianGirl = 82627406, - MajespecterFoxKyubi = 94784213, - MajespecterToadOgama = 645794, - MajespecterCrowYata = 68395509, - MajespecterRaccoonBunbuku = 31991800, - MajespecterCatNekomata = 5506791, - HazyFlameHydra = 8696773, - HazyFlameMantikor = 96051150, - HazyFlameHyppogrif = 31303283, - HazyFlameCerbereus = 38525760, - HazyFlameSphynx = 1409474, - HazyFlamePeryton = 37803172, - HazyFlameGriffin = 74010769, - BlueEyesChaosMAXDragon = 55410871, - BlueEyesChaosDragon = 20654247, - SupremeKingZARC = 13331639, - CrimsonNovaTrinitytheDarkCubicLord = 72664875, - LunalightLeoDancer = 24550676, - TimaeustheKnightofDestiny = 53315891, - DantePilgrimoftheBurningAbyss = 18386170, - AncientGearHowitzer = 87182127, - InvokedCocytus = 85908279, - LyriluscIndependentNightingale = 76815942, - FlowerCardianLightshower = 42291297, - YaziEviloftheYangZing = 43202238, - RaidraptorUltimateFalcon = 86221741, - DisdainfulBirdofParadise = 27240101, - DarkestDiabolosLordOfTheLair = 50383626, - Blackwing_FullArmoredWing = 54082269, - DragunofRedEyes = 37818794, - RedEyesBDragon = 74677422, // sometimes the name of DragunofRedEyes will be changed to RedEyesBDragon - TheArrivalCyberseIgnister = 11738489 - } -} +namespace WindBot.Game.AI.Enums +{ + /// + /// Cards that are can't be selected as target, or immuned to most effect. + /// So them shouldn't be tried to be selected as target at most times. + /// + public enum ShouldNotBeTarget + { + DivineSerpentGeh = 82103466, + ObelisktheTormentor = 10000000, + TheWingedDragonofRaSphereMode = 10000080, + TheWingedDragonofRaImmortalPhoenix = 10000090, + KozmoDarkPlanet = 85991529, + ZushintheSleepingGiant = 67547370, + TheLegendaryExodiaIncarnate = 58604027, + KozmoDarkEclipser = 64063868, + KozmoDarkDestroyer = 55885348, + KozmoForerunner = 20849090, + MajespecterUnicornKirin = 31178212, + WorldLegacyWorldShield = 55787576, + KiwiMagicianGirl = 82627406, + MajespecterFoxKyubi = 94784213, + MajespecterToadOgama = 645794, + MajespecterCrowYata = 68395509, + MajespecterRaccoonBunbuku = 31991800, + MajespecterCatNekomata = 5506791, + HazyFlameHydra = 8696773, + HazyFlameMantikor = 96051150, + HazyFlameHyppogrif = 31303283, + HazyFlameCerbereus = 38525760, + HazyFlameSphynx = 1409474, + HazyFlamePeryton = 37803172, + HazyFlameGriffin = 74010769, + BlueEyesChaosMAXDragon = 55410871, + BlueEyesChaosDragon = 20654247, + SupremeKingZARC = 13331639, + CrimsonNovaTrinitytheDarkCubicLord = 72664875, + LunalightLeoDancer = 24550676, + TimaeustheKnightofDestiny = 53315891, + DantePilgrimoftheBurningAbyss = 18386170, + AncientGearHowitzer = 87182127, + InvokedCocytus = 85908279, + LyriluscIndependentNightingale = 76815942, + FlowerCardianLightshower = 42291297, + YaziEviloftheYangZing = 43202238, + RaidraptorUltimateFalcon = 86221741, + DisdainfulBirdofParadise = 27240101, + DarkestDiabolosLordOfTheLair = 50383626, + Blackwing_FullArmoredWing = 54082269, + DragunofRedEyes = 37818794, + RedEyesBDragon = 74677422, // sometimes the name of DragunofRedEyes will be changed to RedEyesBDragon + TheArrivalCyberseIgnister = 11738489 + } +} diff --git a/Game/AI/Executor.cs b/ExecutorBase/Game/AI/Executor.cs similarity index 95% rename from Game/AI/Executor.cs rename to ExecutorBase/Game/AI/Executor.cs index cc9f7774..7b8b3a6f 100644 --- a/Game/AI/Executor.cs +++ b/ExecutorBase/Game/AI/Executor.cs @@ -1,248 +1,251 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using YGOSharp.OCGWrapper.Enums; -using WindBot; -using WindBot.Game; -using WindBot.Game.AI; - -namespace WindBot.Game.AI -{ - public abstract class Executor - { - public string Deck { get; set; } - public Duel Duel { get; private set; } - public IList Executors { get; private set; } - public GameAI AI { get; private set; } - public AIUtil Util { get; private set; } - - protected MainPhase Main { get; private set; } - protected BattlePhase Battle { get; private set; } - - protected ExecutorType Type { get; private set; } - protected ClientCard Card { get; private set; } - protected int ActivateDescription { get; private set; } - - protected ClientField Bot { get; private set; } - protected ClientField Enemy { get; private set; } - - protected Executor(GameAI ai, Duel duel) - { - Duel = duel; - AI = ai; - Util = new AIUtil(duel); - Executors = new List(); - - Bot = Duel.Fields[0]; - Enemy = Duel.Fields[1]; - } - - public virtual int OnRockPaperScissors() - { - return Program.Rand.Next(1, 4); - } - - public virtual bool OnSelectHand() - { - return Program.Rand.Next(2) > 0; - } - - /// - /// Called when the AI has to decide if it should attack - /// - /// List of monsters that can attcack. - /// List of monsters of enemy. - /// A new BattlePhaseAction containing the action to do. - public virtual BattlePhaseAction OnBattle(IList attackers, IList defenders) - { - // For overriding - return null; - } - - /// - /// Called when the AI has to decide which card to attack first - /// - /// List of monsters that can attcack. - /// List of monsters of enemy. - /// The card to attack first. - public virtual ClientCard OnSelectAttacker(IList attackers, IList defenders) - { - // For overriding - return null; - } - - public virtual BattlePhaseAction OnSelectAttackTarget(ClientCard attacker, IList defenders) - { - // Overrided in DefalultExecutor - return null; - } - - public virtual bool OnPreBattleBetween(ClientCard attacker, ClientCard defender) - { - // Overrided in DefalultExecutor - return true; - } - - public virtual void OnChaining(int player, ClientCard card) - { - // For overriding - } - - public virtual void OnChainEnd() - { - // For overriding - } - public virtual void OnNewPhase() - { - // Some AI need do something on new phase - } - public virtual void OnNewTurn() - { - // Some AI need do something on new turn - } - - public virtual void OnDraw(int player) - { - // Some AI need do something on draw - } - - public virtual IList OnSelectCard(IList cards, int min, int max, int hint, bool cancelable) - { - // For overriding - return null; - } - - public virtual IList OnSelectSum(IList cards, int sum, int min, int max, int hint, bool mode) - { - // For overriding - return null; - } - - public virtual IList OnSelectFusionMaterial(IList cards, int min, int max) - { - // For overriding - return null; - } - - public virtual IList OnSelectSynchroMaterial(IList cards, int sum, int min, int max) - { - // For overriding - return null; - } - - public virtual IList OnSelectXyzMaterial(IList cards, int min, int max) - { - // For overriding - return null; - } - - public virtual IList OnSelectLinkMaterial(IList cards, int min, int max) - { - // For overriding - return null; - } - - public virtual IList OnSelectRitualTribute(IList cards, int sum, int min, int max) - { - // For overriding - return null; - } - - public virtual IList OnSelectPendulumSummon(IList cards, int max) - { - // For overriding - return null; - } - - public virtual IList OnCardSorting(IList cards) - { - // For overriding - return null; - } - - public virtual bool OnSelectYesNo(int desc) - { - return true; - } - - public virtual int OnSelectOption(IList options) - { - return -1; - } - - public virtual int OnSelectPlace(int cardId, int player, CardLocation location, int available) - { - // For overriding - return 0; - } - - public virtual CardPosition OnSelectPosition(int cardId, IList positions) - { - // Overrided in DefalultExecutor - return 0; - } - - public virtual bool OnSelectBattleReplay() - { - // Overrided in DefalultExecutor - return false; - } - - public void SetMain(MainPhase main) - { - Main = main; - } - - public void SetBattle(BattlePhase battle) - { - Battle = battle; - } - - /// - /// Set global variables Type, Card, ActivateDescription for Executor - /// - public void SetCard(ExecutorType type, ClientCard card, int description) - { - Type = type; - Card = card; - ActivateDescription = description; - } - - /// - /// Do the action for the card if func return true. - /// - public void AddExecutor(ExecutorType type, int cardId, Func func) - { - Executors.Add(new CardExecutor(type, cardId, func)); - } - - /// - /// Do the action for the card if available. - /// - public void AddExecutor(ExecutorType type, int cardId) - { - Executors.Add(new CardExecutor(type, cardId, null)); - } - - /// - /// Do the action for every card if func return true. - /// - public void AddExecutor(ExecutorType type, Func func) - { - Executors.Add(new CardExecutor(type, -1, func)); - } - - /// - /// Do the action for every card if no other Executor is added to it. - /// - public void AddExecutor(ExecutorType type) - { - Executors.Add(new CardExecutor(type, -1, DefaultNoExecutor)); - } - - private bool DefaultNoExecutor() - { - return Executors.All(exec => exec.Type != Type || exec.CardId != Card.Id); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using YGOSharp.OCGWrapper.Enums; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; + +namespace WindBot.Game.AI +{ + public abstract class Executor + { + public string Deck { get; set; } + public Duel Duel { get; private set; } + public IList Executors { get; private set; } + public GameAI AI { get; private set; } + public AIUtil Util { get; private set; } + + protected MainPhase Main { get; private set; } + protected BattlePhase Battle { get; private set; } + + protected ExecutorType Type { get; private set; } + protected ClientCard Card { get; private set; } + protected int ActivateDescription { get; private set; } + + protected ClientField Bot { get; private set; } + protected ClientField Enemy { get; private set; } + + public Random Rand; + + protected Executor(GameAI ai, Duel duel) + { + Rand = new Random(); + Duel = duel; + AI = ai; + Util = new AIUtil(duel); + Executors = new List(); + + Bot = Duel.Fields[0]; + Enemy = Duel.Fields[1]; + } + + public virtual int OnRockPaperScissors() + { + return Rand.Next(1, 4); + } + + public virtual bool OnSelectHand() + { + return Rand.Next(2) > 0; + } + + /// + /// Called when the AI has to decide if it should attack + /// + /// List of monsters that can attcack. + /// List of monsters of enemy. + /// A new BattlePhaseAction containing the action to do. + public virtual BattlePhaseAction OnBattle(IList attackers, IList defenders) + { + // For overriding + return null; + } + + /// + /// Called when the AI has to decide which card to attack first + /// + /// List of monsters that can attcack. + /// List of monsters of enemy. + /// The card to attack first. + public virtual ClientCard OnSelectAttacker(IList attackers, IList defenders) + { + // For overriding + return null; + } + + public virtual BattlePhaseAction OnSelectAttackTarget(ClientCard attacker, IList defenders) + { + // Overrided in DefalultExecutor + return null; + } + + public virtual bool OnPreBattleBetween(ClientCard attacker, ClientCard defender) + { + // Overrided in DefalultExecutor + return true; + } + + public virtual void OnChaining(int player, ClientCard card) + { + // For overriding + } + + public virtual void OnChainEnd() + { + // For overriding + } + public virtual void OnNewPhase() + { + // Some AI need do something on new phase + } + public virtual void OnNewTurn() + { + // Some AI need do something on new turn + } + + public virtual void OnDraw(int player) + { + // Some AI need do something on draw + } + + public virtual IList OnSelectCard(IList cards, int min, int max, int hint, bool cancelable) + { + // For overriding + return null; + } + + public virtual IList OnSelectSum(IList cards, int sum, int min, int max, int hint, bool mode) + { + // For overriding + return null; + } + + public virtual IList OnSelectFusionMaterial(IList cards, int min, int max) + { + // For overriding + return null; + } + + public virtual IList OnSelectSynchroMaterial(IList cards, int sum, int min, int max) + { + // For overriding + return null; + } + + public virtual IList OnSelectXyzMaterial(IList cards, int min, int max) + { + // For overriding + return null; + } + + public virtual IList OnSelectLinkMaterial(IList cards, int min, int max) + { + // For overriding + return null; + } + + public virtual IList OnSelectRitualTribute(IList cards, int sum, int min, int max) + { + // For overriding + return null; + } + + public virtual IList OnSelectPendulumSummon(IList cards, int max) + { + // For overriding + return null; + } + + public virtual IList OnCardSorting(IList cards) + { + // For overriding + return null; + } + + public virtual bool OnSelectYesNo(int desc) + { + return true; + } + + public virtual int OnSelectOption(IList options) + { + return -1; + } + + public virtual int OnSelectPlace(int cardId, int player, CardLocation location, int available) + { + // For overriding + return 0; + } + + public virtual CardPosition OnSelectPosition(int cardId, IList positions) + { + // Overrided in DefalultExecutor + return 0; + } + + public virtual bool OnSelectBattleReplay() + { + // Overrided in DefalultExecutor + return false; + } + + public void SetMain(MainPhase main) + { + Main = main; + } + + public void SetBattle(BattlePhase battle) + { + Battle = battle; + } + + /// + /// Set global variables Type, Card, ActivateDescription for Executor + /// + public void SetCard(ExecutorType type, ClientCard card, int description) + { + Type = type; + Card = card; + ActivateDescription = description; + } + + /// + /// Do the action for the card if func return true. + /// + public void AddExecutor(ExecutorType type, int cardId, Func func) + { + Executors.Add(new CardExecutor(type, cardId, func)); + } + + /// + /// Do the action for the card if available. + /// + public void AddExecutor(ExecutorType type, int cardId) + { + Executors.Add(new CardExecutor(type, cardId, null)); + } + + /// + /// Do the action for every card if func return true. + /// + public void AddExecutor(ExecutorType type, Func func) + { + Executors.Add(new CardExecutor(type, -1, func)); + } + + /// + /// Do the action for every card if no other Executor is added to it. + /// + public void AddExecutor(ExecutorType type) + { + Executors.Add(new CardExecutor(type, -1, DefaultNoExecutor)); + } + + private bool DefaultNoExecutor() + { + return Executors.All(exec => exec.Type != Type || exec.CardId != Card.Id); + } + } } \ No newline at end of file diff --git a/Game/AI/ExecutorType.cs b/ExecutorBase/Game/AI/ExecutorType.cs similarity index 94% rename from Game/AI/ExecutorType.cs rename to ExecutorBase/Game/AI/ExecutorType.cs index 47a557d3..dc4b8083 100644 --- a/Game/AI/ExecutorType.cs +++ b/ExecutorBase/Game/AI/ExecutorType.cs @@ -1,16 +1,16 @@ -namespace WindBot.Game.AI -{ - public enum ExecutorType - { - Summon, - SpSummon, - Repos, - MonsterSet, - SpellSet, - Activate, - SummonOrSet, - GoToBattlePhase, - GoToMainPhase2, - GoToEndPhase - } +namespace WindBot.Game.AI +{ + public enum ExecutorType + { + Summon, + SpSummon, + Repos, + MonsterSet, + SpellSet, + Activate, + SummonOrSet, + GoToBattlePhase, + GoToMainPhase2, + GoToEndPhase + } } \ No newline at end of file diff --git a/Game/AI/Zones.cs b/ExecutorBase/Game/AI/Zones.cs similarity index 95% rename from Game/AI/Zones.cs rename to ExecutorBase/Game/AI/Zones.cs index 05944de4..f4721c0b 100644 --- a/Game/AI/Zones.cs +++ b/ExecutorBase/Game/AI/Zones.cs @@ -1,24 +1,24 @@ -namespace WindBot.Game.AI -{ - public static class Zones - { - public const int z0 = 0x1, - z1 = 0x2, - z2 = 0x4, - z3 = 0x8, - z4 = 0x10, - z5 = 0x20, - z6 = 0x40, - - MonsterZones = 0x7f, - MainMonsterZones = 0x1f, - ExtraMonsterZones = 0x60, - - SpellZones = 0x1f, - - PendulumZones = 0x3, - - LinkedZones = 0x10000, - NotLinkedZones = 0x20000; - } +namespace WindBot.Game.AI +{ + public static class Zones + { + public const int z0 = 0x1, + z1 = 0x2, + z2 = 0x4, + z3 = 0x8, + z4 = 0x10, + z5 = 0x20, + z6 = 0x40, + + MonsterZones = 0x7f, + MainMonsterZones = 0x1f, + ExtraMonsterZones = 0x60, + + SpellZones = 0x1f, + + PendulumZones = 0x3, + + LinkedZones = 0x10000, + NotLinkedZones = 0x20000; + } } \ No newline at end of file diff --git a/Game/BattlePhase.cs b/ExecutorBase/Game/BattlePhase.cs similarity index 96% rename from Game/BattlePhase.cs rename to ExecutorBase/Game/BattlePhase.cs index e895a315..75139c6b 100644 --- a/Game/BattlePhase.cs +++ b/ExecutorBase/Game/BattlePhase.cs @@ -1,20 +1,20 @@ -using System.Collections.Generic; - -namespace WindBot.Game -{ - public class BattlePhase - { - public IList AttackableCards { get; private set; } - public IList ActivableCards { get; private set; } - public IList ActivableDescs { get; private set; } - public bool CanMainPhaseTwo { get; set; } - public bool CanEndPhase { get; set; } - - public BattlePhase() - { - AttackableCards = new List(); - ActivableCards = new List(); - ActivableDescs = new List(); - } - } +using System.Collections.Generic; + +namespace WindBot.Game +{ + public class BattlePhase + { + public IList AttackableCards { get; private set; } + public IList ActivableCards { get; private set; } + public IList ActivableDescs { get; private set; } + public bool CanMainPhaseTwo { get; set; } + public bool CanEndPhase { get; set; } + + public BattlePhase() + { + AttackableCards = new List(); + ActivableCards = new List(); + ActivableDescs = new List(); + } + } } \ No newline at end of file diff --git a/Game/BattlePhaseAction.cs b/ExecutorBase/Game/BattlePhaseAction.cs similarity index 95% rename from Game/BattlePhaseAction.cs rename to ExecutorBase/Game/BattlePhaseAction.cs index 3238a22b..f11d148b 100644 --- a/Game/BattlePhaseAction.cs +++ b/ExecutorBase/Game/BattlePhaseAction.cs @@ -1,33 +1,33 @@ -namespace WindBot.Game -{ - public class BattlePhaseAction - { - public enum BattleAction - { - Activate = 0, - Attack = 1, - ToMainPhaseTwo = 2, - ToEndPhase = 3 - } - - public BattleAction Action { get; private set; } - public int Index { get; private set; } - - public BattlePhaseAction(BattleAction action) - { - Action = action; - Index = 0; - } - - public BattlePhaseAction(BattleAction action, int[] indexes) - { - Action = action; - Index = indexes[(int)action]; - } - - public int ToValue() - { - return (Index << 16) + (int)Action; - } - } +namespace WindBot.Game +{ + public class BattlePhaseAction + { + public enum BattleAction + { + Activate = 0, + Attack = 1, + ToMainPhaseTwo = 2, + ToEndPhase = 3 + } + + public BattleAction Action { get; private set; } + public int Index { get; private set; } + + public BattlePhaseAction(BattleAction action) + { + Action = action; + Index = 0; + } + + public BattlePhaseAction(BattleAction action, int[] indexes) + { + Action = action; + Index = indexes[(int)action]; + } + + public int ToValue() + { + return (Index << 16) + (int)Action; + } + } } \ No newline at end of file diff --git a/Game/ClientCard.cs b/ExecutorBase/Game/ClientCard.cs similarity index 97% rename from Game/ClientCard.cs rename to ExecutorBase/Game/ClientCard.cs index 4a4ece3c..ba1e0e2e 100644 --- a/Game/ClientCard.cs +++ b/ExecutorBase/Game/ClientCard.cs @@ -1,374 +1,374 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using YGOSharp.OCGWrapper; -using YGOSharp.OCGWrapper.Enums; - -namespace WindBot.Game -{ - public class ClientCard - { - public int Id { get; private set; } - public NamedCard Data { get; private set; } - public string Name { get; private set; } - - public int Position { get; set; } - public int Sequence { get; set; } - public CardLocation Location { get; set; } - public int Alias { get; private set; } - public int Level { get; private set; } - public int Rank { get; private set; } - public int Type { get; private set; } - public int Attribute { get; private set; } - public int Race { get; private set; } - public int Attack { get; private set; } - public int Defense { get; private set; } - public int LScale { get; private set; } - public int RScale { get; private set; } - public int LinkCount { get; private set; } - public int LinkMarker { get; private set; } - public int BaseAttack { get; private set; } - public int BaseDefense { get; private set; } - public int RealPower { get; set; } - public List Overlays { get; private set; } - public int Owner { get; private set; } - public int Controller { get; private set; } - public int Disabled { get; private set; } - public int ProcCompleted { get; private set; } - public int SelectSeq { get; set; } - public int OpParam1 { get; set; } - public int OpParam2 { get; set; } - - public List EquipCards { get; set; } - public ClientCard EquipTarget; - public List OwnTargets { get; set; } - public List TargetCards { get; set; } - - public bool CanDirectAttack { get; set; } - public bool ShouldDirectAttack { get; set; } - public bool Attacked { get; set; } - public bool IsLastAttacker { get; set; } - public bool IsSpecialSummoned { get; set; } - - public int[] ActionIndex { get; set; } - public IDictionary ActionActivateIndex { get; private set; } - - public ClientCard(int id, CardLocation loc, int sequence) - : this(id, loc, -1 , 0) - { - } - - public ClientCard(int id, CardLocation loc, int sequence, int position) - { - SetId(id); - Sequence = sequence; - Position = position; - Overlays = new List(); - EquipCards = new List(); - OwnTargets = new List(); - TargetCards = new List(); - ActionIndex = new int[16]; - ActionActivateIndex = new Dictionary(); - Location = loc; - } - - public void SetId(int id) - { - if (Id == id) return; - Id = id; - Data = NamedCard.Get(Id); - if (Data != null) - { - Name = Data.Name; - if (Data.Alias != 0) - Alias = Data.Alias; - } - } - - public void Update(BinaryReader packet, Duel duel) - { - int flag = packet.ReadInt32(); - if ((flag & (int)Query.Code) != 0) - SetId(packet.ReadInt32()); - if ((flag & (int)Query.Position) != 0) - { - Controller = duel.GetLocalPlayer(packet.ReadByte()); - Location = (CardLocation)packet.ReadByte(); - Sequence = packet.ReadByte(); - Position = packet.ReadByte(); - } - if ((flag & (int)Query.Alias) != 0) - Alias = packet.ReadInt32(); - if ((flag & (int)Query.Type) != 0) - Type = packet.ReadInt32(); - if ((flag & (int)Query.Level) != 0) - Level = packet.ReadInt32(); - if ((flag & (int)Query.Rank) != 0) - Rank = packet.ReadInt32(); - if ((flag & (int)Query.Attribute) != 0) - Attribute = packet.ReadInt32(); - if ((flag & (int)Query.Race) != 0) - Race = packet.ReadInt32(); - if ((flag & (int)Query.Attack) != 0) - Attack = packet.ReadInt32(); - if ((flag & (int)Query.Defence) != 0) - Defense = packet.ReadInt32(); - if ((flag & (int)Query.BaseAttack) != 0) - BaseAttack = packet.ReadInt32(); - if ((flag & (int)Query.BaseDefence) != 0) - BaseDefense = packet.ReadInt32(); - if ((flag & (int)Query.Reason) != 0) - packet.ReadInt32(); - if ((flag & (int)Query.ReasonCard) != 0) - packet.ReadInt32(); // Int8 * 4 - if ((flag & (int)Query.EquipCard) != 0) - packet.ReadInt32(); // Int8 * 4 - if ((flag & (int)Query.TargetCard) != 0) - { - int count = packet.ReadInt32(); - for (int i = 0; i < count; ++i) - packet.ReadInt32(); // Int8 * 4 - } - if ((flag & (int)Query.OverlayCard) != 0) - { - Overlays.Clear(); - int count = packet.ReadInt32(); - for (int i = 0; i < count; ++i) - Overlays.Add(packet.ReadInt32()); - } - if ((flag & (int)Query.Counters) != 0) - { - int count = packet.ReadInt32(); - for (int i = 0; i < count; ++i) - packet.ReadInt32(); // Int16 * 2 - } - if ((flag & (int)Query.Owner) != 0) - Owner = duel.GetLocalPlayer(packet.ReadInt32()); - if ((flag & (int)Query.Status) != 0) { - int status = packet.ReadInt32(); - const int STATUS_DISABLED = 0x0001; - const int STATUS_PROC_COMPLETE = 0x0008; - Disabled = status & STATUS_DISABLED; - ProcCompleted = status & STATUS_PROC_COMPLETE; - } - if ((flag & (int)Query.LScale) != 0) - LScale = packet.ReadInt32(); - if ((flag & (int)Query.RScale) != 0) - RScale = packet.ReadInt32(); - if ((flag & (int)Query.Link) != 0) - { - LinkCount = packet.ReadInt32(); - LinkMarker = packet.ReadInt32(); - } - } - - public void ClearCardTargets() - { - foreach (ClientCard card in TargetCards) - { - card.OwnTargets.Remove(this); - } - foreach (ClientCard card in OwnTargets) - { - card.TargetCards.Remove(this); - } - OwnTargets.Clear(); - TargetCards.Clear(); - } - - public bool HasLinkMarker(int dir) - { - return (LinkMarker & dir) != 0; - } - - public bool HasLinkMarker(CardLinkMarker dir) - { - return (LinkMarker & (int)dir) != 0; - } - - public int GetLinkedZones() - { - if (!HasType(CardType.Link) || Location != CardLocation.MonsterZone) - return 0; - int zones = 0; - if (Sequence > 0 && Sequence <= 4 && HasLinkMarker(CardLinkMarker.Left)) - zones |= 1 << (Sequence - 1); - if (Sequence <= 3 && HasLinkMarker(CardLinkMarker.Right)) - zones |= 1 << (Sequence + 1); - if (Sequence == 0 && HasLinkMarker(CardLinkMarker.TopRight) - || Sequence == 1 && HasLinkMarker(CardLinkMarker.Top) - || Sequence == 2 && HasLinkMarker(CardLinkMarker.TopLeft)) - zones |= (1 << 5) | (1 << (16 + 6)); - if (Sequence == 2 && HasLinkMarker(CardLinkMarker.TopRight) - || Sequence == 3 && HasLinkMarker(CardLinkMarker.Top) - || Sequence == 4 && HasLinkMarker(CardLinkMarker.TopLeft)) - zones |= (1 << 6) | (1 << (16 + 5)); - if (Sequence == 5) - { - if (HasLinkMarker(CardLinkMarker.BottomLeft)) - zones |= 1 << 0; - if (HasLinkMarker(CardLinkMarker.Bottom)) - zones |= 1 << 1; - if (HasLinkMarker(CardLinkMarker.BottomRight)) - zones |= 1 << 2; - if (HasLinkMarker(CardLinkMarker.TopLeft)) - zones |= 1 << (16 + 4); - if (HasLinkMarker(CardLinkMarker.Top)) - zones |= 1 << (16 + 3); - if (HasLinkMarker(CardLinkMarker.TopRight)) - zones |= 1 << (16 + 2); - } - if (Sequence == 6) - { - if (HasLinkMarker(CardLinkMarker.BottomLeft)) - zones |= 1 << 2; - if (HasLinkMarker(CardLinkMarker.Bottom)) - zones |= 1 << 3; - if (HasLinkMarker(CardLinkMarker.BottomRight)) - zones |= 1 << 4; - if (HasLinkMarker(CardLinkMarker.TopLeft)) - zones |= 1 << (16 + 2); - if (HasLinkMarker(CardLinkMarker.Top)) - zones |= 1 << (16 + 1); - if (HasLinkMarker(CardLinkMarker.TopRight)) - zones |= 1 << (16 + 0); - } - return zones; - } - - public bool HasType(CardType type) - { - return (Type & (int)type) != 0; - } - - public bool HasPosition(CardPosition position) - { - return (Position & (int)position) != 0; - } - - public bool HasAttribute(CardAttribute attribute) - { - return (Attribute & (int)attribute) != 0; - } - - public bool HasRace(CardRace race) - { - return (Race & (int)race) != 0; - } - - public bool HasSetcode(int setcode) - { - if (Data == null) return false; - long setcodes = Data.Setcode; - int settype = setcode & 0xfff; - int setsubtype = setcode & 0xf000; - while (setcodes > 0) - { - long check_setcode = setcodes & 0xffff; - setcodes >>= 16; - if ((check_setcode & 0xfff) == settype && (check_setcode & 0xf000 & setsubtype) == setsubtype) return true; - } - return false; - } - - public bool IsMonster() - { - return HasType(CardType.Monster); - } - - public bool IsTuner() - { - return HasType(CardType.Tuner); - } - - public bool IsSpell() - { - return HasType(CardType.Spell); - } - - public bool IsTrap() - { - return HasType(CardType.Trap); - } - - public bool IsExtraCard() - { - return HasType(CardType.Fusion) || HasType(CardType.Synchro) || HasType(CardType.Xyz) || HasType(CardType.Link); - } - - public bool IsFaceup() - { - return HasPosition(CardPosition.FaceUp); - } - - public bool IsFacedown() - { - return HasPosition(CardPosition.FaceDown); - } - - public bool IsAttack() - { - return HasPosition(CardPosition.Attack); - } - - public bool IsDefense() - { - return HasPosition(CardPosition.Defence); - } - - public bool IsDisabled() - { - return Disabled != 0; - } - - public bool IsCanRevive() - { - return ProcCompleted != 0 || !(IsExtraCard() || HasType(CardType.Ritual) || HasType(CardType.SpSummon)); - } - - public bool IsCode(int id) - { - return Id == id || Alias != 0 && Alias == id; - } - - public bool IsCode(IList ids) - { - return ids.Contains(Id) || Alias != 0 && ids.Contains(Alias); - } - - public bool IsCode(params int[] ids) - { - return ids.Contains(Id) || Alias != 0 && ids.Contains(Alias); - } - - public bool IsOriginalCode(int id) - { - return Id == id || Alias - Id < 10 && Alias == id; - } - - public bool HasXyzMaterial() - { - return Overlays.Count > 0; - } - - public bool HasXyzMaterial(int count) - { - return Overlays.Count >= count; - } - - public bool HasXyzMaterial(int count, int cardid) - { - return Overlays.Count >= count && Overlays.Contains(cardid); - } - - public int GetDefensePower() - { - return IsAttack() ? Attack : Defense; - } - - public bool Equals(ClientCard card) - { - return ReferenceEquals(this, card); - } - } +using System.Collections.Generic; +using System.IO; +using System.Linq; +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game +{ + public class ClientCard + { + public int Id { get; private set; } + public NamedCard Data { get; private set; } + public string Name { get; private set; } + + public int Position { get; set; } + public int Sequence { get; set; } + public CardLocation Location { get; set; } + public int Alias { get; private set; } + public int Level { get; private set; } + public int Rank { get; private set; } + public int Type { get; private set; } + public int Attribute { get; private set; } + public int Race { get; private set; } + public int Attack { get; private set; } + public int Defense { get; private set; } + public int LScale { get; private set; } + public int RScale { get; private set; } + public int LinkCount { get; private set; } + public int LinkMarker { get; private set; } + public int BaseAttack { get; private set; } + public int BaseDefense { get; private set; } + public int RealPower { get; set; } + public List Overlays { get; private set; } + public int Owner { get; private set; } + public int Controller { get; private set; } + public int Disabled { get; private set; } + public int ProcCompleted { get; private set; } + public int SelectSeq { get; set; } + public int OpParam1 { get; set; } + public int OpParam2 { get; set; } + + public List EquipCards { get; set; } + public ClientCard EquipTarget; + public List OwnTargets { get; set; } + public List TargetCards { get; set; } + + public bool CanDirectAttack { get; set; } + public bool ShouldDirectAttack { get; set; } + public bool Attacked { get; set; } + public bool IsLastAttacker { get; set; } + public bool IsSpecialSummoned { get; set; } + + public int[] ActionIndex { get; set; } + public IDictionary ActionActivateIndex { get; private set; } + + public ClientCard(int id, CardLocation loc, int sequence) + : this(id, loc, -1 , 0) + { + } + + public ClientCard(int id, CardLocation loc, int sequence, int position) + { + SetId(id); + Sequence = sequence; + Position = position; + Overlays = new List(); + EquipCards = new List(); + OwnTargets = new List(); + TargetCards = new List(); + ActionIndex = new int[16]; + ActionActivateIndex = new Dictionary(); + Location = loc; + } + + public void SetId(int id) + { + if (Id == id) return; + Id = id; + Data = NamedCard.Get(Id); + if (Data != null) + { + Name = Data.Name; + if (Data.Alias != 0) + Alias = Data.Alias; + } + } + + public void Update(BinaryReader packet, Duel duel) + { + int flag = packet.ReadInt32(); + if ((flag & (int)Query.Code) != 0) + SetId(packet.ReadInt32()); + if ((flag & (int)Query.Position) != 0) + { + Controller = duel.GetLocalPlayer(packet.ReadByte()); + Location = (CardLocation)packet.ReadByte(); + Sequence = packet.ReadByte(); + Position = packet.ReadByte(); + } + if ((flag & (int)Query.Alias) != 0) + Alias = packet.ReadInt32(); + if ((flag & (int)Query.Type) != 0) + Type = packet.ReadInt32(); + if ((flag & (int)Query.Level) != 0) + Level = packet.ReadInt32(); + if ((flag & (int)Query.Rank) != 0) + Rank = packet.ReadInt32(); + if ((flag & (int)Query.Attribute) != 0) + Attribute = packet.ReadInt32(); + if ((flag & (int)Query.Race) != 0) + Race = packet.ReadInt32(); + if ((flag & (int)Query.Attack) != 0) + Attack = packet.ReadInt32(); + if ((flag & (int)Query.Defence) != 0) + Defense = packet.ReadInt32(); + if ((flag & (int)Query.BaseAttack) != 0) + BaseAttack = packet.ReadInt32(); + if ((flag & (int)Query.BaseDefence) != 0) + BaseDefense = packet.ReadInt32(); + if ((flag & (int)Query.Reason) != 0) + packet.ReadInt32(); + if ((flag & (int)Query.ReasonCard) != 0) + packet.ReadInt32(); // Int8 * 4 + if ((flag & (int)Query.EquipCard) != 0) + packet.ReadInt32(); // Int8 * 4 + if ((flag & (int)Query.TargetCard) != 0) + { + int count = packet.ReadInt32(); + for (int i = 0; i < count; ++i) + packet.ReadInt32(); // Int8 * 4 + } + if ((flag & (int)Query.OverlayCard) != 0) + { + Overlays.Clear(); + int count = packet.ReadInt32(); + for (int i = 0; i < count; ++i) + Overlays.Add(packet.ReadInt32()); + } + if ((flag & (int)Query.Counters) != 0) + { + int count = packet.ReadInt32(); + for (int i = 0; i < count; ++i) + packet.ReadInt32(); // Int16 * 2 + } + if ((flag & (int)Query.Owner) != 0) + Owner = duel.GetLocalPlayer(packet.ReadInt32()); + if ((flag & (int)Query.Status) != 0) { + int status = packet.ReadInt32(); + const int STATUS_DISABLED = 0x0001; + const int STATUS_PROC_COMPLETE = 0x0008; + Disabled = status & STATUS_DISABLED; + ProcCompleted = status & STATUS_PROC_COMPLETE; + } + if ((flag & (int)Query.LScale) != 0) + LScale = packet.ReadInt32(); + if ((flag & (int)Query.RScale) != 0) + RScale = packet.ReadInt32(); + if ((flag & (int)Query.Link) != 0) + { + LinkCount = packet.ReadInt32(); + LinkMarker = packet.ReadInt32(); + } + } + + public void ClearCardTargets() + { + foreach (ClientCard card in TargetCards) + { + card.OwnTargets.Remove(this); + } + foreach (ClientCard card in OwnTargets) + { + card.TargetCards.Remove(this); + } + OwnTargets.Clear(); + TargetCards.Clear(); + } + + public bool HasLinkMarker(int dir) + { + return (LinkMarker & dir) != 0; + } + + public bool HasLinkMarker(CardLinkMarker dir) + { + return (LinkMarker & (int)dir) != 0; + } + + public int GetLinkedZones() + { + if (!HasType(CardType.Link) || Location != CardLocation.MonsterZone) + return 0; + int zones = 0; + if (Sequence > 0 && Sequence <= 4 && HasLinkMarker(CardLinkMarker.Left)) + zones |= 1 << (Sequence - 1); + if (Sequence <= 3 && HasLinkMarker(CardLinkMarker.Right)) + zones |= 1 << (Sequence + 1); + if (Sequence == 0 && HasLinkMarker(CardLinkMarker.TopRight) + || Sequence == 1 && HasLinkMarker(CardLinkMarker.Top) + || Sequence == 2 && HasLinkMarker(CardLinkMarker.TopLeft)) + zones |= (1 << 5) | (1 << (16 + 6)); + if (Sequence == 2 && HasLinkMarker(CardLinkMarker.TopRight) + || Sequence == 3 && HasLinkMarker(CardLinkMarker.Top) + || Sequence == 4 && HasLinkMarker(CardLinkMarker.TopLeft)) + zones |= (1 << 6) | (1 << (16 + 5)); + if (Sequence == 5) + { + if (HasLinkMarker(CardLinkMarker.BottomLeft)) + zones |= 1 << 0; + if (HasLinkMarker(CardLinkMarker.Bottom)) + zones |= 1 << 1; + if (HasLinkMarker(CardLinkMarker.BottomRight)) + zones |= 1 << 2; + if (HasLinkMarker(CardLinkMarker.TopLeft)) + zones |= 1 << (16 + 4); + if (HasLinkMarker(CardLinkMarker.Top)) + zones |= 1 << (16 + 3); + if (HasLinkMarker(CardLinkMarker.TopRight)) + zones |= 1 << (16 + 2); + } + if (Sequence == 6) + { + if (HasLinkMarker(CardLinkMarker.BottomLeft)) + zones |= 1 << 2; + if (HasLinkMarker(CardLinkMarker.Bottom)) + zones |= 1 << 3; + if (HasLinkMarker(CardLinkMarker.BottomRight)) + zones |= 1 << 4; + if (HasLinkMarker(CardLinkMarker.TopLeft)) + zones |= 1 << (16 + 2); + if (HasLinkMarker(CardLinkMarker.Top)) + zones |= 1 << (16 + 1); + if (HasLinkMarker(CardLinkMarker.TopRight)) + zones |= 1 << (16 + 0); + } + return zones; + } + + public bool HasType(CardType type) + { + return (Type & (int)type) != 0; + } + + public bool HasPosition(CardPosition position) + { + return (Position & (int)position) != 0; + } + + public bool HasAttribute(CardAttribute attribute) + { + return (Attribute & (int)attribute) != 0; + } + + public bool HasRace(CardRace race) + { + return (Race & (int)race) != 0; + } + + public bool HasSetcode(int setcode) + { + if (Data == null) return false; + long setcodes = Data.Setcode; + int settype = setcode & 0xfff; + int setsubtype = setcode & 0xf000; + while (setcodes > 0) + { + long check_setcode = setcodes & 0xffff; + setcodes >>= 16; + if ((check_setcode & 0xfff) == settype && (check_setcode & 0xf000 & setsubtype) == setsubtype) return true; + } + return false; + } + + public bool IsMonster() + { + return HasType(CardType.Monster); + } + + public bool IsTuner() + { + return HasType(CardType.Tuner); + } + + public bool IsSpell() + { + return HasType(CardType.Spell); + } + + public bool IsTrap() + { + return HasType(CardType.Trap); + } + + public bool IsExtraCard() + { + return HasType(CardType.Fusion) || HasType(CardType.Synchro) || HasType(CardType.Xyz) || HasType(CardType.Link); + } + + public bool IsFaceup() + { + return HasPosition(CardPosition.FaceUp); + } + + public bool IsFacedown() + { + return HasPosition(CardPosition.FaceDown); + } + + public bool IsAttack() + { + return HasPosition(CardPosition.Attack); + } + + public bool IsDefense() + { + return HasPosition(CardPosition.Defence); + } + + public bool IsDisabled() + { + return Disabled != 0; + } + + public bool IsCanRevive() + { + return ProcCompleted != 0 || !(IsExtraCard() || HasType(CardType.Ritual) || HasType(CardType.SpSummon)); + } + + public bool IsCode(int id) + { + return Id == id || Alias != 0 && Alias == id; + } + + public bool IsCode(IList ids) + { + return ids.Contains(Id) || Alias != 0 && ids.Contains(Alias); + } + + public bool IsCode(params int[] ids) + { + return ids.Contains(Id) || Alias != 0 && ids.Contains(Alias); + } + + public bool IsOriginalCode(int id) + { + return Id == id || Alias - Id < 10 && Alias == id; + } + + public bool HasXyzMaterial() + { + return Overlays.Count > 0; + } + + public bool HasXyzMaterial(int count) + { + return Overlays.Count >= count; + } + + public bool HasXyzMaterial(int count, int cardid) + { + return Overlays.Count >= count && Overlays.Contains(cardid); + } + + public int GetDefensePower() + { + return IsAttack() ? Attack : Defense; + } + + public bool Equals(ClientCard card) + { + return ReferenceEquals(this, card); + } + } } \ No newline at end of file diff --git a/Game/ClientField.cs b/ExecutorBase/Game/ClientField.cs similarity index 96% rename from Game/ClientField.cs rename to ExecutorBase/Game/ClientField.cs index 6b4eff5e..82baeda7 100644 --- a/Game/ClientField.cs +++ b/ExecutorBase/Game/ClientField.cs @@ -1,352 +1,352 @@ -using System.Collections.Generic; -using System.Linq; -using WindBot.Game.AI; -using YGOSharp.OCGWrapper.Enums; - -namespace WindBot.Game -{ - public class ClientField - { - public IList Hand { get; private set; } - public ClientCard[] MonsterZone { get; private set; } - public ClientCard[] SpellZone { get; private set; } - public IList Graveyard { get; private set; } - public IList Banished { get; private set; } - public IList Deck { get; private set; } - public IList ExtraDeck { get; private set; } - - public int LifePoints; - public ClientCard BattlingMonster; - public bool UnderAttack; - - public ClientField() - { - } - - public void Init(int deck, int extra) - { - Hand = new List(); - MonsterZone = new ClientCard[7]; - SpellZone = new ClientCard[8]; - Graveyard = new List(); - Banished = new List(); - Deck = new List(); - ExtraDeck = new List(); - - for (int i = 0; i < deck; ++i) - Deck.Add(new ClientCard(0, CardLocation.Deck, -1)); - for (int i = 0; i < extra; ++i) - ExtraDeck.Add(new ClientCard(0, CardLocation.Extra, -1)); - } - - public int GetMonstersExtraZoneCount() - { - int count = 0; - if (MonsterZone[5] != null) - count++; - if (MonsterZone[6] != null) - count++; - return count; - } - public int GetMonsterCount() - { - return GetCount(MonsterZone); - } - - public int GetSpellCount() - { - return GetCount(SpellZone); - } - - public int GetHandCount() - { - return GetCount(Hand); - } - - public int GetSpellCountWithoutField() - { - int count = 0; - for (int i = 0; i < 5; ++i) - { - if (SpellZone[i] != null) - ++count; - } - return count; - } - - /// - /// Count Column - /// - /// range of zone 0-4 - public int GetColumnCount(int zone, bool IncludeExtraMonsterZone = true) - { - int count = 0; - if (SpellZone[zone] != null) - count++; - if (MonsterZone[zone] != null) - count++; - if(zone == 1 && IncludeExtraMonsterZone) - { - if (MonsterZone[5] != null) - count++; - } - if (zone == 3 && IncludeExtraMonsterZone) - { - if (MonsterZone[6] != null) - count++; - } - return count; - } - - public int GetFieldCount() - { - return GetSpellCount() + GetMonsterCount(); - } - - public int GetFieldHandCount() - { - return GetSpellCount() + GetMonsterCount() + GetHandCount(); - } - - public bool IsFieldEmpty() - { - return GetMonsters().Count == 0 && GetSpells().Count == 0; - } - - public int GetLinkedZones() - { - int zones = 0; - for (int i = 0; i < 7; i++) - { - zones |= MonsterZone[i]?.GetLinkedZones() ?? 0; - } - return zones; - } - - public List GetMonsters() - { - return GetCards(MonsterZone); - } - - public List GetGraveyardMonsters() - { - return GetCards(Graveyard, CardType.Monster); - } - - public List GetGraveyardSpells() - { - return GetCards(Graveyard, CardType.Spell); - } - - public List GetGraveyardTraps() - { - return GetCards(Graveyard, CardType.Trap); - } - - public List GetSpells() - { - return GetCards(SpellZone); - } - - public List GetMonstersInExtraZone() - { - return GetMonsters().Where(card => card.Sequence >= 5).ToList(); - } - - public List GetMonstersInMainZone() - { - return GetMonsters().Where(card => card.Sequence < 5).ToList(); - } - - public ClientCard GetFieldSpellCard() - { - return SpellZone[5]; - } - - public bool HasInHand(int cardId) - { - return HasInCards(Hand, cardId); - } - - public bool HasInHand(IList cardId) - { - return HasInCards(Hand, cardId); - } - - public bool HasInGraveyard(int cardId) - { - return HasInCards(Graveyard, cardId); - } - - public bool HasInGraveyard(IList cardId) - { - return HasInCards(Graveyard, cardId); - } - - public bool HasInBanished(int cardId) - { - return HasInCards(Banished, cardId); - } - - public bool HasInBanished(IList cardId) - { - return HasInCards(Banished, cardId); - } - - public bool HasInExtra(int cardId) - { - return HasInCards(ExtraDeck, cardId); - } - - public bool HasInExtra(IList cardId) - { - return HasInCards(ExtraDeck, cardId); - } - - public bool HasAttackingMonster() - { - return GetMonsters().Any(card => card.IsAttack()); - } - - public bool HasDefendingMonster() - { - return GetMonsters().Any(card => card.IsDefense()); - } - - public bool HasInMonstersZone(int cardId, bool notDisabled = false, bool hasXyzMaterial = false, bool faceUp = false) - { - return HasInCards(MonsterZone, cardId, notDisabled, hasXyzMaterial, faceUp); - } - - public bool HasInMonstersZone(IList cardId, bool notDisabled = false, bool hasXyzMaterial = false, bool faceUp = false) - { - return HasInCards(MonsterZone, cardId, notDisabled, hasXyzMaterial, faceUp); - } - - public bool HasInSpellZone(int cardId, bool notDisabled = false, bool faceUp = false) - { - return HasInCards(SpellZone, cardId, notDisabled, false, faceUp); - } - - public bool HasInSpellZone(IList cardId, bool notDisabled = false, bool faceUp = false) - { - return HasInCards(SpellZone, cardId, notDisabled, false, faceUp); - } - - public bool HasInHandOrInSpellZone(int cardId) - { - return HasInHand(cardId) || HasInSpellZone(cardId); - } - - public bool HasInHandOrHasInMonstersZone(int cardId) - { - return HasInHand(cardId) || HasInMonstersZone(cardId); - } - - public bool HasInHandOrInGraveyard(int cardId) - { - return HasInHand(cardId) || HasInGraveyard(cardId); - } - - public bool HasInMonstersZoneOrInGraveyard(int cardId) - { - return HasInMonstersZone(cardId) || HasInGraveyard(cardId); - } - - public bool HasInSpellZoneOrInGraveyard(int cardId) - { - return HasInSpellZone(cardId) || HasInGraveyard(cardId); - } - - public bool HasInHandOrInMonstersZoneOrInGraveyard(int cardId) - { - return HasInHand(cardId) || HasInMonstersZone(cardId) || HasInGraveyard(cardId); - } - - public bool HasInHandOrInSpellZoneOrInGraveyard(int cardId) - { - return HasInHand(cardId) || HasInSpellZone(cardId) || HasInGraveyard(cardId); - } - - public bool HasInHandOrInSpellZone(IList cardId) - { - return HasInHand(cardId) || HasInSpellZone(cardId); - } - - public bool HasInHandOrHasInMonstersZone(IList cardId) - { - return HasInHand(cardId) || HasInMonstersZone(cardId); - } - - public bool HasInHandOrInGraveyard(IList cardId) - { - return HasInHand(cardId) || HasInGraveyard(cardId); - } - - public bool HasInMonstersZoneOrInGraveyard(IList cardId) - { - return HasInMonstersZone(cardId) || HasInGraveyard(cardId); - } - - public bool HasInSpellZoneOrInGraveyard(IList cardId) - { - return HasInSpellZone(cardId) || HasInGraveyard(cardId); - } - - public bool HasInHandOrInMonstersZoneOrInGraveyard(IList cardId) - { - return HasInHand(cardId) || HasInMonstersZone(cardId) || HasInGraveyard(cardId); - } - - public bool HasInHandOrInSpellZoneOrInGraveyard(IList cardId) - { - return HasInHand(cardId) || HasInSpellZone(cardId) || HasInGraveyard(cardId); - } - - public int GetRemainingCount(int cardId, int initialCount) - { - int remaining = initialCount; - remaining = remaining - Hand.Count(card => card != null && card.IsOriginalCode(cardId)); - remaining = remaining - SpellZone.Count(card => card != null && card.IsOriginalCode(cardId)); - remaining = remaining - MonsterZone.Count(card => card != null && card.IsOriginalCode(cardId)); - remaining = remaining - Graveyard.Count(card => card != null && card.IsOriginalCode(cardId)); - remaining = remaining - Banished.Count(card => card != null && card.IsOriginalCode(cardId)); - return (remaining < 0) ? 0 : remaining; - } - - private static int GetCount(IEnumerable cards) - { - return cards.Count(card => card != null); - } - - public int GetCountCardInZone(IEnumerable cards, int cardId) - { - return cards.Count(card => card != null && card.IsCode(cardId)); - } - - public int GetCountCardInZone(IEnumerable cards, List cardId) - { - return cards.Count(card => card != null && card.IsCode(cardId)); - } - - private static List GetCards(IEnumerable cards, CardType type) - { - return cards.Where(card => card != null && card.HasType(type)).ToList(); - } - - private static List GetCards(IEnumerable cards) - { - return cards.Where(card => card != null).ToList(); - } - - private static bool HasInCards(IEnumerable cards, int cardId, bool notDisabled = false, bool hasXyzMaterial = false, bool faceUp = false) - { - return cards.Any(card => card != null && card.IsCode(cardId) && !(notDisabled && card.IsDisabled()) && !(hasXyzMaterial && !card.HasXyzMaterial()) && !(faceUp && card.IsFacedown())); - } - - private static bool HasInCards(IEnumerable cards, IList cardId, bool notDisabled = false, bool hasXyzMaterial = false, bool faceUp = false) - { - return cards.Any(card => card != null && card.IsCode(cardId) && !(notDisabled && card.IsDisabled()) && !(hasXyzMaterial && !card.HasXyzMaterial()) && !(faceUp && card.IsFacedown())); - } - } +using System.Collections.Generic; +using System.Linq; +using WindBot.Game.AI; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game +{ + public class ClientField + { + public IList Hand { get; private set; } + public ClientCard[] MonsterZone { get; private set; } + public ClientCard[] SpellZone { get; private set; } + public IList Graveyard { get; private set; } + public IList Banished { get; private set; } + public IList Deck { get; private set; } + public IList ExtraDeck { get; private set; } + + public int LifePoints; + public ClientCard BattlingMonster; + public bool UnderAttack; + + public ClientField() + { + } + + public void Init(int deck, int extra) + { + Hand = new List(); + MonsterZone = new ClientCard[7]; + SpellZone = new ClientCard[8]; + Graveyard = new List(); + Banished = new List(); + Deck = new List(); + ExtraDeck = new List(); + + for (int i = 0; i < deck; ++i) + Deck.Add(new ClientCard(0, CardLocation.Deck, -1)); + for (int i = 0; i < extra; ++i) + ExtraDeck.Add(new ClientCard(0, CardLocation.Extra, -1)); + } + + public int GetMonstersExtraZoneCount() + { + int count = 0; + if (MonsterZone[5] != null) + count++; + if (MonsterZone[6] != null) + count++; + return count; + } + public int GetMonsterCount() + { + return GetCount(MonsterZone); + } + + public int GetSpellCount() + { + return GetCount(SpellZone); + } + + public int GetHandCount() + { + return GetCount(Hand); + } + + public int GetSpellCountWithoutField() + { + int count = 0; + for (int i = 0; i < 5; ++i) + { + if (SpellZone[i] != null) + ++count; + } + return count; + } + + /// + /// Count Column + /// + /// range of zone 0-4 + public int GetColumnCount(int zone, bool IncludeExtraMonsterZone = true) + { + int count = 0; + if (SpellZone[zone] != null) + count++; + if (MonsterZone[zone] != null) + count++; + if(zone == 1 && IncludeExtraMonsterZone) + { + if (MonsterZone[5] != null) + count++; + } + if (zone == 3 && IncludeExtraMonsterZone) + { + if (MonsterZone[6] != null) + count++; + } + return count; + } + + public int GetFieldCount() + { + return GetSpellCount() + GetMonsterCount(); + } + + public int GetFieldHandCount() + { + return GetSpellCount() + GetMonsterCount() + GetHandCount(); + } + + public bool IsFieldEmpty() + { + return GetMonsters().Count == 0 && GetSpells().Count == 0; + } + + public int GetLinkedZones() + { + int zones = 0; + for (int i = 0; i < 7; i++) + { + zones |= MonsterZone[i]?.GetLinkedZones() ?? 0; + } + return zones; + } + + public List GetMonsters() + { + return GetCards(MonsterZone); + } + + public List GetGraveyardMonsters() + { + return GetCards(Graveyard, CardType.Monster); + } + + public List GetGraveyardSpells() + { + return GetCards(Graveyard, CardType.Spell); + } + + public List GetGraveyardTraps() + { + return GetCards(Graveyard, CardType.Trap); + } + + public List GetSpells() + { + return GetCards(SpellZone); + } + + public List GetMonstersInExtraZone() + { + return GetMonsters().Where(card => card.Sequence >= 5).ToList(); + } + + public List GetMonstersInMainZone() + { + return GetMonsters().Where(card => card.Sequence < 5).ToList(); + } + + public ClientCard GetFieldSpellCard() + { + return SpellZone[5]; + } + + public bool HasInHand(int cardId) + { + return HasInCards(Hand, cardId); + } + + public bool HasInHand(IList cardId) + { + return HasInCards(Hand, cardId); + } + + public bool HasInGraveyard(int cardId) + { + return HasInCards(Graveyard, cardId); + } + + public bool HasInGraveyard(IList cardId) + { + return HasInCards(Graveyard, cardId); + } + + public bool HasInBanished(int cardId) + { + return HasInCards(Banished, cardId); + } + + public bool HasInBanished(IList cardId) + { + return HasInCards(Banished, cardId); + } + + public bool HasInExtra(int cardId) + { + return HasInCards(ExtraDeck, cardId); + } + + public bool HasInExtra(IList cardId) + { + return HasInCards(ExtraDeck, cardId); + } + + public bool HasAttackingMonster() + { + return GetMonsters().Any(card => card.IsAttack()); + } + + public bool HasDefendingMonster() + { + return GetMonsters().Any(card => card.IsDefense()); + } + + public bool HasInMonstersZone(int cardId, bool notDisabled = false, bool hasXyzMaterial = false, bool faceUp = false) + { + return HasInCards(MonsterZone, cardId, notDisabled, hasXyzMaterial, faceUp); + } + + public bool HasInMonstersZone(IList cardId, bool notDisabled = false, bool hasXyzMaterial = false, bool faceUp = false) + { + return HasInCards(MonsterZone, cardId, notDisabled, hasXyzMaterial, faceUp); + } + + public bool HasInSpellZone(int cardId, bool notDisabled = false, bool faceUp = false) + { + return HasInCards(SpellZone, cardId, notDisabled, false, faceUp); + } + + public bool HasInSpellZone(IList cardId, bool notDisabled = false, bool faceUp = false) + { + return HasInCards(SpellZone, cardId, notDisabled, false, faceUp); + } + + public bool HasInHandOrInSpellZone(int cardId) + { + return HasInHand(cardId) || HasInSpellZone(cardId); + } + + public bool HasInHandOrHasInMonstersZone(int cardId) + { + return HasInHand(cardId) || HasInMonstersZone(cardId); + } + + public bool HasInHandOrInGraveyard(int cardId) + { + return HasInHand(cardId) || HasInGraveyard(cardId); + } + + public bool HasInMonstersZoneOrInGraveyard(int cardId) + { + return HasInMonstersZone(cardId) || HasInGraveyard(cardId); + } + + public bool HasInSpellZoneOrInGraveyard(int cardId) + { + return HasInSpellZone(cardId) || HasInGraveyard(cardId); + } + + public bool HasInHandOrInMonstersZoneOrInGraveyard(int cardId) + { + return HasInHand(cardId) || HasInMonstersZone(cardId) || HasInGraveyard(cardId); + } + + public bool HasInHandOrInSpellZoneOrInGraveyard(int cardId) + { + return HasInHand(cardId) || HasInSpellZone(cardId) || HasInGraveyard(cardId); + } + + public bool HasInHandOrInSpellZone(IList cardId) + { + return HasInHand(cardId) || HasInSpellZone(cardId); + } + + public bool HasInHandOrHasInMonstersZone(IList cardId) + { + return HasInHand(cardId) || HasInMonstersZone(cardId); + } + + public bool HasInHandOrInGraveyard(IList cardId) + { + return HasInHand(cardId) || HasInGraveyard(cardId); + } + + public bool HasInMonstersZoneOrInGraveyard(IList cardId) + { + return HasInMonstersZone(cardId) || HasInGraveyard(cardId); + } + + public bool HasInSpellZoneOrInGraveyard(IList cardId) + { + return HasInSpellZone(cardId) || HasInGraveyard(cardId); + } + + public bool HasInHandOrInMonstersZoneOrInGraveyard(IList cardId) + { + return HasInHand(cardId) || HasInMonstersZone(cardId) || HasInGraveyard(cardId); + } + + public bool HasInHandOrInSpellZoneOrInGraveyard(IList cardId) + { + return HasInHand(cardId) || HasInSpellZone(cardId) || HasInGraveyard(cardId); + } + + public int GetRemainingCount(int cardId, int initialCount) + { + int remaining = initialCount; + remaining = remaining - Hand.Count(card => card != null && card.IsOriginalCode(cardId)); + remaining = remaining - SpellZone.Count(card => card != null && card.IsOriginalCode(cardId)); + remaining = remaining - MonsterZone.Count(card => card != null && card.IsOriginalCode(cardId)); + remaining = remaining - Graveyard.Count(card => card != null && card.IsOriginalCode(cardId)); + remaining = remaining - Banished.Count(card => card != null && card.IsOriginalCode(cardId)); + return (remaining < 0) ? 0 : remaining; + } + + private static int GetCount(IEnumerable cards) + { + return cards.Count(card => card != null); + } + + public int GetCountCardInZone(IEnumerable cards, int cardId) + { + return cards.Count(card => card != null && card.IsCode(cardId)); + } + + public int GetCountCardInZone(IEnumerable cards, List cardId) + { + return cards.Count(card => card != null && card.IsCode(cardId)); + } + + private static List GetCards(IEnumerable cards, CardType type) + { + return cards.Where(card => card != null && card.HasType(type)).ToList(); + } + + private static List GetCards(IEnumerable cards) + { + return cards.Where(card => card != null).ToList(); + } + + private static bool HasInCards(IEnumerable cards, int cardId, bool notDisabled = false, bool hasXyzMaterial = false, bool faceUp = false) + { + return cards.Any(card => card != null && card.IsCode(cardId) && !(notDisabled && card.IsDisabled()) && !(hasXyzMaterial && !card.HasXyzMaterial()) && !(faceUp && card.IsFacedown())); + } + + private static bool HasInCards(IEnumerable cards, IList cardId, bool notDisabled = false, bool hasXyzMaterial = false, bool faceUp = false) + { + return cards.Any(card => card != null && card.IsCode(cardId) && !(notDisabled && card.IsDisabled()) && !(hasXyzMaterial && !card.HasXyzMaterial()) && !(faceUp && card.IsFacedown())); + } + } } \ No newline at end of file diff --git a/Game/AI/Dialogs.cs b/ExecutorBase/Game/Dialogs.cs similarity index 88% rename from Game/AI/Dialogs.cs rename to ExecutorBase/Game/Dialogs.cs index d0f7fa30..0d886479 100644 --- a/Game/AI/Dialogs.cs +++ b/ExecutorBase/Game/Dialogs.cs @@ -1,177 +1,177 @@ -using System; -using System.IO; -using System.Collections.Generic; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Json; - -namespace WindBot.Game.AI -{ - [DataContract] - public class DialogsData - { - [DataMember] - public string[] welcome { get; set; } - [DataMember] - public string[] deckerror { get; set; } - [DataMember] - public string[] duelstart { get; set; } - [DataMember] - public string[] newturn { get; set; } - [DataMember] - public string[] endturn { get; set; } - [DataMember] - public string[] directattack { get; set; } - [DataMember] - public string[] attack { get; set; } - [DataMember] - public string[] ondirectattack { get; set; } - [DataMember] - public string facedownmonstername { get; set; } - [DataMember] - public string[] activate { get; set; } - [DataMember] - public string[] summon { get; set; } - [DataMember] - public string[] setmonster { get; set; } - [DataMember] - public string[] chaining { get; set; } - } - public class Dialogs - { - private GameClient _game; - - private string[] _welcome; - private string[] _deckerror; - private string[] _duelstart; - private string[] _newturn; - private string[] _endturn; - private string[] _directattack; - private string[] _attack; - private string[] _ondirectattack; - private string _facedownmonstername; - private string[] _activate; - private string[] _summon; - private string[] _setmonster; - private string[] _chaining; - - public Dialogs(GameClient game) - { - _game = game; - DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DialogsData)); - string dialogfilename = game.Dialog; - using (FileStream fs = File.OpenRead("Dialogs/" + dialogfilename + ".json")) - { - DialogsData data = (DialogsData)serializer.ReadObject(fs); - _welcome = data.welcome; - _deckerror = data.deckerror; - _duelstart = data.duelstart; - _newturn = data.newturn; - _endturn = data.endturn; - _directattack = data.directattack; - _attack = data.attack; - _ondirectattack = data.ondirectattack; - _facedownmonstername = data.facedownmonstername; - _activate = data.activate; - _summon = data.summon; - _setmonster = data.setmonster; - _chaining = data.chaining; - } - } - - public void SendSorry() - { - InternalSendMessageForced(new[] { "Sorry, an error occurs." }); - } - - public void SendDeckSorry(string card) - { - if (card == "DECK") - InternalSendMessageForced(new[] { "Deck illegal. Please check the database of your YGOPro and WindBot." }); - else - InternalSendMessageForced(_deckerror, card); - } - - public void SendWelcome() - { - InternalSendMessage(_welcome); - } - - public void SendDuelStart() - { - InternalSendMessage(_duelstart); - } - - public void SendNewTurn() - { - InternalSendMessage(_newturn); - } - - public void SendEndTurn() - { - InternalSendMessage(_endturn); - } - - public void SendDirectAttack(string attacker) - { - InternalSendMessage(_directattack, attacker); - } - - public void SendAttack(string attacker, string defender) - { - if (defender=="monster") - { - defender = _facedownmonstername; - } - InternalSendMessage(_attack, attacker, defender); - } - - public void SendOnDirectAttack(string attacker) - { - if (string.IsNullOrEmpty(attacker)) - { - attacker = _facedownmonstername; - } - InternalSendMessage(_ondirectattack, attacker); - } - public void SendOnDirectAttack() - { - InternalSendMessage(_ondirectattack); - } - - public void SendActivate(string spell) - { - InternalSendMessage(_activate, spell); - } - - public void SendSummon(string monster) - { - InternalSendMessage(_summon, monster); - } - - public void SendSetMonster() - { - InternalSendMessage(_setmonster); - } - - public void SendChaining(string card) - { - InternalSendMessage(_chaining, card); - } - - private void InternalSendMessage(IList array, params object[] opts) - { - if (!_game._chat) - return; - string message = string.Format(array[Program.Rand.Next(array.Count)], opts); - if (message != "") - _game.Chat(message); - } - - private void InternalSendMessageForced(IList array, params object[] opts) - { - string message = string.Format(array[Program.Rand.Next(array.Count)], opts); - if (message != "") - _game.Chat(message); - } - } -} +using System; +using System.IO; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; + +namespace WindBot.Game.AI +{ + [DataContract] + public class DialogsData + { + [DataMember] + public string[] welcome { get; set; } + [DataMember] + public string[] deckerror { get; set; } + [DataMember] + public string[] duelstart { get; set; } + [DataMember] + public string[] newturn { get; set; } + [DataMember] + public string[] endturn { get; set; } + [DataMember] + public string[] directattack { get; set; } + [DataMember] + public string[] attack { get; set; } + [DataMember] + public string[] ondirectattack { get; set; } + [DataMember] + public string facedownmonstername { get; set; } + [DataMember] + public string[] activate { get; set; } + [DataMember] + public string[] summon { get; set; } + [DataMember] + public string[] setmonster { get; set; } + [DataMember] + public string[] chaining { get; set; } + } + public class Dialogs + { + + private string[] _welcome; + private string[] _deckerror; + private string[] _duelstart; + private string[] _newturn; + private string[] _endturn; + private string[] _directattack; + private string[] _attack; + private string[] _ondirectattack; + private string _facedownmonstername; + private string[] _activate; + private string[] _summon; + private string[] _setmonster; + private string[] _chaining; + + private Action Chat; + + private static Random Rand = new Random(); + + public Dialogs(string dialogfilename, Action chat) + { + Chat = chat; + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DialogsData)); + using (FileStream fs = File.OpenRead("Dialogs/" + dialogfilename + ".json")) + { + DialogsData data = (DialogsData)serializer.ReadObject(fs); + _welcome = data.welcome; + _deckerror = data.deckerror; + _duelstart = data.duelstart; + _newturn = data.newturn; + _endturn = data.endturn; + _directattack = data.directattack; + _attack = data.attack; + _ondirectattack = data.ondirectattack; + _facedownmonstername = data.facedownmonstername; + _activate = data.activate; + _summon = data.summon; + _setmonster = data.setmonster; + _chaining = data.chaining; + } + } + + public void SendSorry() + { + InternalSendMessageForced(new[] { "Sorry, an error occurs." }); + } + + public void SendDeckSorry(string card) + { + if (card == "DECK") + InternalSendMessageForced(new[] { "Deck illegal. Please check the database of your YGOPro and WindBot." }); + else + InternalSendMessageForced(_deckerror, card); + } + + public void SendWelcome() + { + InternalSendMessage(_welcome); + } + + public void SendDuelStart() + { + InternalSendMessage(_duelstart); + } + + public void SendNewTurn() + { + InternalSendMessage(_newturn); + } + + public void SendEndTurn() + { + InternalSendMessage(_endturn); + } + + public void SendDirectAttack(string attacker) + { + InternalSendMessage(_directattack, attacker); + } + + public void SendAttack(string attacker, string defender) + { + if (defender=="monster") + { + defender = _facedownmonstername; + } + InternalSendMessage(_attack, attacker, defender); + } + + public void SendOnDirectAttack(string attacker) + { + if (string.IsNullOrEmpty(attacker)) + { + attacker = _facedownmonstername; + } + InternalSendMessage(_ondirectattack, attacker); + } + public void SendOnDirectAttack() + { + InternalSendMessage(_ondirectattack); + } + + public void SendActivate(string spell) + { + InternalSendMessage(_activate, spell); + } + + public void SendSummon(string monster) + { + InternalSendMessage(_summon, monster); + } + + public void SendSetMonster() + { + InternalSendMessage(_setmonster); + } + + public void SendChaining(string card) + { + InternalSendMessage(_chaining, card); + } + + private void InternalSendMessage(IList array, params object[] opts) + { + string message = string.Format(array[Rand.Next(array.Count)], opts); + if (message != "") + Chat(message, false); + } + + private void InternalSendMessageForced(IList array, params object[] opts) + { + string message = string.Format(array[Rand.Next(array.Count)], opts); + if (message != "") + Chat(message, true); + } + } +} diff --git a/Game/Duel.cs b/ExecutorBase/Game/Duel.cs similarity index 97% rename from Game/Duel.cs rename to ExecutorBase/Game/Duel.cs index 115547bb..6c7d97fd 100644 --- a/Game/Duel.cs +++ b/ExecutorBase/Game/Duel.cs @@ -1,190 +1,190 @@ -using System.Collections.Generic; -using YGOSharp.OCGWrapper.Enums; - -namespace WindBot.Game -{ - public class Duel - { - public bool IsFirst { get; set; } - public bool IsNewRule { get; set; } - public bool IsNewRule2020 { get; set; } - - public ClientField[] Fields { get; private set; } - - public int Turn { get; set; } - public int Player { get; set; } - public DuelPhase Phase { get; set; } - public MainPhase MainPhase { get; set; } - public BattlePhase BattlePhase { get; set; } - - public int LastChainPlayer { get; set; } - public IList CurrentChain { get; set; } - public IList ChainTargets { get; set; } - public IList ChainTargetOnly { get; set; } - public int LastSummonPlayer { get; set; } - public IList SummoningCards { get; set; } - public IList LastSummonedCards { get; set; } - - public Duel() - { - Fields = new ClientField[2]; - Fields[0] = new ClientField(); - Fields[1] = new ClientField(); - LastChainPlayer = -1; - CurrentChain = new List(); - ChainTargets = new List(); - ChainTargetOnly = new List(); - LastSummonPlayer = -1; - SummoningCards = new List(); - LastSummonedCards = new List(); - } - - public ClientCard GetCard(int player, CardLocation loc, int seq) - { - return GetCard(player, (int)loc, seq, 0); - } - - public ClientCard GetCard(int player, int loc, int seq, int subSeq) - { - if (player < 0 || player > 1) - return null; - - bool isXyz = (loc & 0x80) != 0; - CardLocation location = (CardLocation)(loc & 0x7f); - - IList cards = null; - switch (location) - { - case CardLocation.Deck: - cards = Fields[player].Deck; - break; - case CardLocation.Hand: - cards = Fields[player].Hand; - break; - case CardLocation.MonsterZone: - cards = Fields[player].MonsterZone; - break; - case CardLocation.SpellZone: - cards = Fields[player].SpellZone; - break; - case CardLocation.Grave: - cards = Fields[player].Graveyard; - break; - case CardLocation.Removed: - cards = Fields[player].Banished; - break; - case CardLocation.Extra: - cards = Fields[player].ExtraDeck; - break; - } - if (cards == null) - return null; - - if (seq >= cards.Count) - return null; - - if (isXyz) - { - ClientCard card = cards[seq]; - if (card == null || subSeq >= card.Overlays.Count) - return null; - return null; // TODO card.Overlays[subSeq] - } - - return cards[seq]; - } - - public void AddCard(CardLocation loc, int cardId, int player, int seq, int pos) - { - switch (loc) - { - case CardLocation.Hand: - Fields[player].Hand.Add(new ClientCard(cardId, loc, -1, pos)); - break; - case CardLocation.Grave: - Fields[player].Graveyard.Add(new ClientCard(cardId, loc,-1, pos)); - break; - case CardLocation.Removed: - Fields[player].Banished.Add(new ClientCard(cardId, loc, -1, pos)); - break; - case CardLocation.MonsterZone: - Fields[player].MonsterZone[seq] = new ClientCard(cardId, loc, seq, pos); - break; - case CardLocation.SpellZone: - Fields[player].SpellZone[seq] = new ClientCard(cardId, loc, seq, pos); - break; - case CardLocation.Deck: - Fields[player].Deck.Add(new ClientCard(cardId, loc, -1, pos)); - break; - case CardLocation.Extra: - Fields[player].ExtraDeck.Add(new ClientCard(cardId, loc, -1, pos)); - break; - } - } - - public void AddCard(CardLocation loc, ClientCard card, int player, int seq, int pos, int id) - { - card.Location = loc; - card.Sequence = seq; - card.Position = pos; - card.SetId(id); - switch (loc) - { - case CardLocation.Hand: - Fields[player].Hand.Add(card); - break; - case CardLocation.Grave: - Fields[player].Graveyard.Add(card); - break; - case CardLocation.Removed: - Fields[player].Banished.Add(card); - break; - case CardLocation.MonsterZone: - Fields[player].MonsterZone[seq] = card; - break; - case CardLocation.SpellZone: - Fields[player].SpellZone[seq] = card; - break; - case CardLocation.Deck: - Fields[player].Deck.Add(card); - break; - case CardLocation.Extra: - Fields[player].ExtraDeck.Add(card); - break; - } - } - - public void RemoveCard(CardLocation loc, ClientCard card, int player, int seq) - { - switch (loc) - { - case CardLocation.Hand: - Fields[player].Hand.Remove(card); - break; - case CardLocation.Grave: - Fields[player].Graveyard.Remove(card); - break; - case CardLocation.Removed: - Fields[player].Banished.Remove(card); - break; - case CardLocation.MonsterZone: - Fields[player].MonsterZone[seq] = null; - break; - case CardLocation.SpellZone: - Fields[player].SpellZone[seq] = null; - break; - case CardLocation.Deck: - Fields[player].Deck.Remove(card); - break; - case CardLocation.Extra: - Fields[player].ExtraDeck.Remove(card); - break; - } - } - - public int GetLocalPlayer(int player) - { - return IsFirst ? player : 1 - player; - } - } +using System.Collections.Generic; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game +{ + public class Duel + { + public bool IsFirst { get; set; } + public bool IsNewRule { get; set; } + public bool IsNewRule2020 { get; set; } + + public ClientField[] Fields { get; private set; } + + public int Turn { get; set; } + public int Player { get; set; } + public DuelPhase Phase { get; set; } + public MainPhase MainPhase { get; set; } + public BattlePhase BattlePhase { get; set; } + + public int LastChainPlayer { get; set; } + public IList CurrentChain { get; set; } + public IList ChainTargets { get; set; } + public IList ChainTargetOnly { get; set; } + public int LastSummonPlayer { get; set; } + public IList SummoningCards { get; set; } + public IList LastSummonedCards { get; set; } + + public Duel() + { + Fields = new ClientField[2]; + Fields[0] = new ClientField(); + Fields[1] = new ClientField(); + LastChainPlayer = -1; + CurrentChain = new List(); + ChainTargets = new List(); + ChainTargetOnly = new List(); + LastSummonPlayer = -1; + SummoningCards = new List(); + LastSummonedCards = new List(); + } + + public ClientCard GetCard(int player, CardLocation loc, int seq) + { + return GetCard(player, (int)loc, seq, 0); + } + + public ClientCard GetCard(int player, int loc, int seq, int subSeq) + { + if (player < 0 || player > 1) + return null; + + bool isXyz = (loc & 0x80) != 0; + CardLocation location = (CardLocation)(loc & 0x7f); + + IList cards = null; + switch (location) + { + case CardLocation.Deck: + cards = Fields[player].Deck; + break; + case CardLocation.Hand: + cards = Fields[player].Hand; + break; + case CardLocation.MonsterZone: + cards = Fields[player].MonsterZone; + break; + case CardLocation.SpellZone: + cards = Fields[player].SpellZone; + break; + case CardLocation.Grave: + cards = Fields[player].Graveyard; + break; + case CardLocation.Removed: + cards = Fields[player].Banished; + break; + case CardLocation.Extra: + cards = Fields[player].ExtraDeck; + break; + } + if (cards == null) + return null; + + if (seq >= cards.Count) + return null; + + if (isXyz) + { + ClientCard card = cards[seq]; + if (card == null || subSeq >= card.Overlays.Count) + return null; + return null; // TODO card.Overlays[subSeq] + } + + return cards[seq]; + } + + public void AddCard(CardLocation loc, int cardId, int player, int seq, int pos) + { + switch (loc) + { + case CardLocation.Hand: + Fields[player].Hand.Add(new ClientCard(cardId, loc, -1, pos)); + break; + case CardLocation.Grave: + Fields[player].Graveyard.Add(new ClientCard(cardId, loc,-1, pos)); + break; + case CardLocation.Removed: + Fields[player].Banished.Add(new ClientCard(cardId, loc, -1, pos)); + break; + case CardLocation.MonsterZone: + Fields[player].MonsterZone[seq] = new ClientCard(cardId, loc, seq, pos); + break; + case CardLocation.SpellZone: + Fields[player].SpellZone[seq] = new ClientCard(cardId, loc, seq, pos); + break; + case CardLocation.Deck: + Fields[player].Deck.Add(new ClientCard(cardId, loc, -1, pos)); + break; + case CardLocation.Extra: + Fields[player].ExtraDeck.Add(new ClientCard(cardId, loc, -1, pos)); + break; + } + } + + public void AddCard(CardLocation loc, ClientCard card, int player, int seq, int pos, int id) + { + card.Location = loc; + card.Sequence = seq; + card.Position = pos; + card.SetId(id); + switch (loc) + { + case CardLocation.Hand: + Fields[player].Hand.Add(card); + break; + case CardLocation.Grave: + Fields[player].Graveyard.Add(card); + break; + case CardLocation.Removed: + Fields[player].Banished.Add(card); + break; + case CardLocation.MonsterZone: + Fields[player].MonsterZone[seq] = card; + break; + case CardLocation.SpellZone: + Fields[player].SpellZone[seq] = card; + break; + case CardLocation.Deck: + Fields[player].Deck.Add(card); + break; + case CardLocation.Extra: + Fields[player].ExtraDeck.Add(card); + break; + } + } + + public void RemoveCard(CardLocation loc, ClientCard card, int player, int seq) + { + switch (loc) + { + case CardLocation.Hand: + Fields[player].Hand.Remove(card); + break; + case CardLocation.Grave: + Fields[player].Graveyard.Remove(card); + break; + case CardLocation.Removed: + Fields[player].Banished.Remove(card); + break; + case CardLocation.MonsterZone: + Fields[player].MonsterZone[seq] = null; + break; + case CardLocation.SpellZone: + Fields[player].SpellZone[seq] = null; + break; + case CardLocation.Deck: + Fields[player].Deck.Remove(card); + break; + case CardLocation.Extra: + Fields[player].ExtraDeck.Remove(card); + break; + } + } + + public int GetLocalPlayer(int player) + { + return IsFirst ? player : 1 - player; + } + } } \ No newline at end of file diff --git a/Game/GameAI.cs b/ExecutorBase/Game/GameAI.cs similarity index 93% rename from Game/GameAI.cs rename to ExecutorBase/Game/GameAI.cs index ba544cc4..f72bc1ce 100644 --- a/Game/GameAI.cs +++ b/ExecutorBase/Game/GameAI.cs @@ -1,1105 +1,1115 @@ -using System.Linq; -using System.Collections.Generic; -using WindBot.Game.AI; -using YGOSharp.OCGWrapper.Enums; - -namespace WindBot.Game -{ - public class GameAI - { - public GameClient Game { get; private set; } - public Duel Duel { get; private set; } - public Executor Executor { get; set; } - - private Dialogs _dialogs; - - public GameAI(GameClient game, Duel duel) - { - Game = game; - Duel = duel; - - _dialogs = new Dialogs(game); - } - - /// - /// Called when the AI got the error message. - /// - public void OnRetry() - { - _dialogs.SendSorry(); - } - - public void OnDeckError(string card) - { - _dialogs.SendDeckSorry(card); - } - - /// - /// Called when the AI join the game. - /// - public void OnJoinGame() - { - _dialogs.SendWelcome(); - } - - /// - /// Called when the duel starts. - /// - public void OnStart() - { - _dialogs.SendDuelStart(); - } - - /// - /// Called when the AI do the rock-paper-scissors. - /// - /// 1 for Scissors, 2 for Rock, 3 for Paper. - public int OnRockPaperScissors() - { - return Executor.OnRockPaperScissors(); - } - - /// - /// Called when the AI won the rock-paper-scissors. - /// - /// True if the AI should begin first, false otherwise. - public bool OnSelectHand() - { - return Executor.OnSelectHand(); - } - - /// - /// Called when any player draw card. - /// - public void OnDraw(int player) - { - Executor.OnDraw(player); - } - - /// - /// Called when it's a new turn. - /// - public void OnNewTurn() - { - Executor.OnNewTurn(); - } - - /// - /// Called when it's a new phase. - /// - public void OnNewPhase() - { - m_selector.Clear(); - m_position.Clear(); - m_selector_pointer = -1; - m_materialSelector = null; - m_option = -1; - m_yesno = -1; - - m_place = 0; - if (Duel.Player == 0 && Duel.Phase == DuelPhase.Draw) - { - _dialogs.SendNewTurn(); - } - Executor.OnNewPhase(); - } - - /// - /// Called when the AI got attack directly. - /// - public void OnDirectAttack(ClientCard card) - { - _dialogs.SendOnDirectAttack(card.Name); - } - - /// - /// Called when a chain is executed. - /// - /// Card who is chained. - /// Player who is currently chaining. - public void OnChaining(ClientCard card, int player) - { - Executor.OnChaining(player,card); - } - - /// - /// Called when a chain has been solved. - /// - public void OnChainEnd() - { - m_selector.Clear(); - m_selector_pointer = -1; - Executor.OnChainEnd(); - } - - /// - /// Called when the AI has to do something during the battle phase. - /// - /// Informations about usable cards. - /// A new BattlePhaseAction containing the action to do. - public BattlePhaseAction OnSelectBattleCmd(BattlePhase battle) - { - Executor.SetBattle(battle); - foreach (CardExecutor exec in Executor.Executors) - { - if (exec.Type == ExecutorType.GoToMainPhase2 && battle.CanMainPhaseTwo && exec.Func()) // check if should enter main phase 2 directly - { - return ToMainPhase2(); - } - if (exec.Type == ExecutorType.GoToEndPhase && battle.CanEndPhase && exec.Func()) // check if should enter end phase directly - { - return ToEndPhase(); - } - for (int i = 0; i < battle.ActivableCards.Count; ++i) - { - ClientCard card = battle.ActivableCards[i]; - if (ShouldExecute(exec, card, ExecutorType.Activate, battle.ActivableDescs[i])) - { - _dialogs.SendChaining(card.Name); - return new BattlePhaseAction(BattlePhaseAction.BattleAction.Activate, card.ActionIndex); - } - } - } - - // Sort the attackers and defenders, make monster with higher attack go first. - List attackers = new List(battle.AttackableCards); - attackers.Sort(CardContainer.CompareCardAttack); - attackers.Reverse(); - - List defenders = new List(Duel.Fields[1].GetMonsters()); - defenders.Sort(CardContainer.CompareDefensePower); - defenders.Reverse(); - - // Let executor decide which card should attack first. - ClientCard selected = Executor.OnSelectAttacker(attackers, defenders); - if (selected != null && attackers.Contains(selected)) - { - attackers.Remove(selected); - attackers.Insert(0, selected); - } - - // Check for the executor. - BattlePhaseAction result = Executor.OnBattle(attackers, defenders); - if (result != null) - return result; - - if (attackers.Count == 0) - return ToMainPhase2(); - - if (defenders.Count == 0) - { - // Attack with the monster with the lowest attack first - for (int i = attackers.Count - 1; i >= 0; --i) - { - ClientCard attacker = attackers[i]; - if (attacker.Attack > 0) - return Attack(attacker, null); - } - } - else - { - for (int k = 0; k < attackers.Count; ++k) - { - ClientCard attacker = attackers[k]; - attacker.IsLastAttacker = (k == attackers.Count - 1); - result = Executor.OnSelectAttackTarget(attacker, defenders); - if (result != null) - return result; - } - } - - if (!battle.CanMainPhaseTwo) - return Attack(attackers[0], (defenders.Count == 0) ? null : defenders[0]); - - return ToMainPhase2(); - } - - /// - /// Called when the AI has to select one or more cards. - /// - /// List of available cards. - /// Minimal quantity. - /// Maximal quantity. - /// The hint message of the select. - /// True if you can return an empty list. - /// A new list containing the selected cards. - public IList OnSelectCard(IList cards, int min, int max, int hint, bool cancelable) - { - const int HINTMSG_FMATERIAL = 511; - const int HINTMSG_SMATERIAL = 512; - const int HINTMSG_XMATERIAL = 513; - const int HINTMSG_LMATERIAL = 533; - const int HINTMSG_SPSUMMON = 509; - - // Check for the executor. - IList result = Executor.OnSelectCard(cards, min, max, hint, cancelable); - if (result != null) - return result; - - if (hint == HINTMSG_SPSUMMON && min == 1 && max > min) // pendulum summon - { - result = Executor.OnSelectPendulumSummon(cards, max); - if (result != null) - return result; - } - - CardSelector selector = null; - if (hint == HINTMSG_FMATERIAL || hint == HINTMSG_SMATERIAL || hint == HINTMSG_XMATERIAL || hint == HINTMSG_LMATERIAL) - { - if (m_materialSelector != null) - { - //Logger.DebugWriteLine("m_materialSelector"); - selector = m_materialSelector; - } - else - { - if (hint == HINTMSG_FMATERIAL) - result = Executor.OnSelectFusionMaterial(cards, min, max); - if (hint == HINTMSG_SMATERIAL) - result = Executor.OnSelectSynchroMaterial(cards, 0, min, max); - if (hint == HINTMSG_XMATERIAL) - result = Executor.OnSelectXyzMaterial(cards, min, max); - if (hint == HINTMSG_LMATERIAL) - result = Executor.OnSelectLinkMaterial(cards, min, max); - - if (result != null) - return result; - - // Update the next selector. - selector = GetSelectedCards(); - } - } - else - { - // Update the next selector. - selector = GetSelectedCards(); - } - - // If we selected a card, use this card. - if (selector != null) - return selector.Select(cards, min, max); - - // Always select the first available cards and choose the minimum. - IList selected = new List(); - - if (cards.Count >= min) - { - for (int i = 0; i < min; ++i) - selected.Add(cards[i]); - } - return selected; - } - - /// - /// Called when the AI can chain (activate) a card. - /// - /// List of activable cards. - /// List of effect descriptions. - /// You can't return -1 if this param is true. - /// Index of the activated card or -1. - public int OnSelectChain(IList cards, IList descs, bool forced) - { - foreach (CardExecutor exec in Executor.Executors) - { - for (int i = 0; i < cards.Count; ++i) - { - ClientCard card = cards[i]; - if (ShouldExecute(exec, card, ExecutorType.Activate, descs[i])) - { - _dialogs.SendChaining(card.Name); - return i; - } - } - } - // If we're forced to chain, we chain the first card. However don't do anything. - return forced ? 0 : -1; - } - - /// - /// Called when the AI has to use one or more counters. - /// - /// Type of counter to use. - /// Quantity of counter to select. - /// List of available cards. - /// List of available counters. - /// List of used counters. - public IList OnSelectCounter(int type, int quantity, IList cards, IList counters) - { - // Always select the first available counters. - int[] used = new int[counters.Count]; - int i = 0; - while (quantity > 0) - { - if (counters[i] >= quantity) - { - used[i] = quantity; - quantity = 0; - } - else - { - used[i] = counters[i]; - quantity -= counters[i]; - } - i++; - } - return used; - } - - /// - /// Called when the AI has to sort cards. - /// - /// Cards to sort. - /// List of sorted cards. - public IList OnCardSorting(IList cards) - { - - IList result = Executor.OnCardSorting(cards); - if (result != null) - return result; - result = new List(); - // TODO: use selector - result = cards.ToList(); - return result; - } - - /// - /// Called when the AI has to choose to activate or not an effect. - /// - /// Card to activate. - /// True for yes, false for no. - public bool OnSelectEffectYn(ClientCard card, int desc) - { - foreach (CardExecutor exec in Executor.Executors) - { - if (ShouldExecute(exec, card, ExecutorType.Activate, desc)) - return true; - } - return false; - } - - /// - /// Called when the AI has to do something during the main phase. - /// - /// A lot of informations about the available actions. - /// A new MainPhaseAction containing the action to do. - public MainPhaseAction OnSelectIdleCmd(MainPhase main) - { - Executor.SetMain(main); - foreach (CardExecutor exec in Executor.Executors) - { - if (exec.Type == ExecutorType.GoToEndPhase && main.CanEndPhase && exec.Func()) // check if should enter end phase directly - { - _dialogs.SendEndTurn(); - return new MainPhaseAction(MainPhaseAction.MainAction.ToEndPhase); - } - if (exec.Type==ExecutorType.GoToBattlePhase && main.CanBattlePhase && exec.Func()) // check if should enter battle phase directly - { - return new MainPhaseAction(MainPhaseAction.MainAction.ToBattlePhase); - } - // NOTICE: GoToBattlePhase and GoToEndPhase has no "card" can be accessed to ShouldExecute(), so instead use exec.Func() to check ... - // enter end phase and enter battle pahse is in higher priority. - - for (int i = 0; i < main.ActivableCards.Count; ++i) - { - ClientCard card = main.ActivableCards[i]; - if (ShouldExecute(exec, card, ExecutorType.Activate, main.ActivableDescs[i])) - { - _dialogs.SendActivate(card.Name); - return new MainPhaseAction(MainPhaseAction.MainAction.Activate, card.ActionActivateIndex[main.ActivableDescs[i]]); - } - } - foreach (ClientCard card in main.MonsterSetableCards) - { - if (ShouldExecute(exec, card, ExecutorType.MonsterSet)) - { - _dialogs.SendSetMonster(); - return new MainPhaseAction(MainPhaseAction.MainAction.SetMonster, card.ActionIndex); - } - } - foreach (ClientCard card in main.ReposableCards) - { - if (ShouldExecute(exec, card, ExecutorType.Repos)) - return new MainPhaseAction(MainPhaseAction.MainAction.Repos, card.ActionIndex); - } - foreach (ClientCard card in main.SpecialSummonableCards) - { - if (ShouldExecute(exec, card, ExecutorType.SpSummon)) - { - _dialogs.SendSummon(card.Name); - return new MainPhaseAction(MainPhaseAction.MainAction.SpSummon, card.ActionIndex); - } - } - foreach (ClientCard card in main.SummonableCards) - { - if (ShouldExecute(exec, card, ExecutorType.Summon)) - { - _dialogs.SendSummon(card.Name); - return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex); - } - if (ShouldExecute(exec, card, ExecutorType.SummonOrSet)) - { - if (Executor.Util.IsAllEnemyBetter(true) && Executor.Util.IsAllEnemyBetterThanValue(card.Attack + 300, false) && - main.MonsterSetableCards.Contains(card)) - { - _dialogs.SendSetMonster(); - return new MainPhaseAction(MainPhaseAction.MainAction.SetMonster, card.ActionIndex); - } - _dialogs.SendSummon(card.Name); - return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex); - } - } - foreach (ClientCard card in main.SpellSetableCards) - { - if (ShouldExecute(exec, card, ExecutorType.SpellSet)) - return new MainPhaseAction(MainPhaseAction.MainAction.SetSpell, card.ActionIndex); - } - } - - if (main.CanBattlePhase && Duel.Fields[0].HasAttackingMonster()) - return new MainPhaseAction(MainPhaseAction.MainAction.ToBattlePhase); - - _dialogs.SendEndTurn(); - return new MainPhaseAction(MainPhaseAction.MainAction.ToEndPhase); - } - - /// - /// Called when the AI has to select an option. - /// - /// List of available options. - /// Index of the selected option. - public int OnSelectOption(IList options) - { - if (m_option != -1 && m_option < options.Count) - return m_option; - - int result = Executor.OnSelectOption(options); - if (result != -1) - return result; - - return 0; // Always select the first option. - } - - public int OnSelectPlace(int cardId, int player, CardLocation location, int available) - { - int selector_selected = m_place; - m_place = 0; - - int executor_selected = Executor.OnSelectPlace(cardId, player, location, available); - - if ((executor_selected & available) > 0) - return executor_selected & available; - if ((selector_selected & available) > 0) - return selector_selected & available; - - // TODO: LinkedZones - - return 0; - } - - /// - /// Called when the AI has to select a card position. - /// - /// Id of the card to position on the field. - /// List of available positions. - /// Selected position. - public CardPosition OnSelectPosition(int cardId, IList positions) - { - CardPosition selector_selected = GetSelectedPosition(); - - CardPosition executor_selected = Executor.OnSelectPosition(cardId, positions); - - // Selects the selected position if available, the first available otherwise. - if (positions.Contains(executor_selected)) - return executor_selected; - if (positions.Contains(selector_selected)) - return selector_selected; - - return positions[0]; - } - - /// - /// Called when the AI has to tribute for a synchro monster or ritual monster. - /// - /// Available cards. - /// Result of the operation. - /// Minimum cards. - /// Maximum cards. - /// True for exact equal. - /// - public IList OnSelectSum(IList cards, int sum, int min, int max, int hint, bool mode) - { - const int HINTMSG_RELEASE = 500; - const int HINTMSG_SMATERIAL = 512; - - IList selected = Executor.OnSelectSum(cards, sum, min, max, hint, mode); - if (selected != null) - { - return selected; - } - - if (hint == HINTMSG_RELEASE || hint == HINTMSG_SMATERIAL) - { - if (m_materialSelector != null) - { - selected = m_materialSelector.Select(cards, min, max); - } - else - { - switch (hint) - { - case HINTMSG_SMATERIAL: - selected = Executor.OnSelectSynchroMaterial(cards, sum, min, max); - break; - case HINTMSG_RELEASE: - selected = Executor.OnSelectRitualTribute(cards, sum, min, max); - break; - } - } - if (selected != null) - { - int s1 = 0, s2 = 0; - foreach (ClientCard card in selected) - { - s1 += card.OpParam1; - s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1; - } - if ((mode && (s1 == sum || s2 == sum)) || (!mode && (s1 >= sum || s2 >= sum))) - { - return selected; - } - } - } - - if (mode) - { - // equal - - if (sum == 0 && min == 0) - { - return new List(); - } - - if (min <= 1) - { - // try special level first - foreach (ClientCard card in cards) - { - if (card.OpParam2 == sum) - { - return new[] { card }; - } - } - // try level equal - foreach (ClientCard card in cards) - { - if (card.OpParam1 == sum) - { - return new[] { card }; - } - } - } - - // try all - int s1 = 0, s2 = 0; - foreach (ClientCard card in cards) - { - s1 += card.OpParam1; - s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1; - } - if (s1 == sum || s2 == sum) - { - return cards; - } - - // try all combinations - int i = (min <= 1) ? 2 : min; - while (i <= max && i <= cards.Count) - { - IEnumerable> combos = CardContainer.GetCombinations(cards, i); - - foreach (IEnumerable combo in combos) - { - Logger.DebugWriteLine("--"); - s1 = 0; - s2 = 0; - foreach (ClientCard card in combo) - { - s1 += card.OpParam1; - s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1; - } - if (s1 == sum || s2 == sum) - { - return combo.ToList(); - } - } - i++; - } - } - else - { - // larger - if (min <= 1) - { - // try special level first - foreach (ClientCard card in cards) - { - if (card.OpParam2 >= sum) - { - return new[] { card }; - } - } - // try level equal - foreach (ClientCard card in cards) - { - if (card.OpParam1 >= sum) - { - return new[] { card }; - } - } - } - - // try all combinations - int i = (min <= 1) ? 2 : min; - while (i <= max && i <= cards.Count) - { - IEnumerable> combos = CardContainer.GetCombinations(cards, i); - - foreach (IEnumerable combo in combos) - { - Logger.DebugWriteLine("----"); - int s1 = 0, s2 = 0; - foreach (ClientCard card in combo) - { - s1 += card.OpParam1; - s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1; - } - if (s1 >= sum || s2 >= sum) - { - return combo.ToList(); - } - } - i++; - } - } - - Logger.WriteErrorLine("Fail to select sum."); - return new List(); - } - - /// - /// Called when the AI has to tribute one or more cards. - /// - /// List of available cards. - /// Minimal quantity. - /// Maximal quantity. - /// The hint message of the select. - /// True if you can return an empty list. - /// A new list containing the tributed cards. - public IList OnSelectTribute(IList cards, int min, int max, int hint, bool cancelable) - { - // Always choose the minimum and lowest atk. - List sorted = new List(); - sorted.AddRange(cards); - sorted.Sort(CardContainer.CompareCardAttack); - - IList selected = new List(); - - for (int i = 0; i < min && i < sorted.Count; ++i) - selected.Add(sorted[i]); - - return selected; - } - - /// - /// Called when the AI has to select yes or no. - /// - /// Id of the question. - /// True for yes, false for no. - public bool OnSelectYesNo(int desc) - { - if (m_yesno != -1) - return m_yesno > 0; - return Executor.OnSelectYesNo(desc); - } - - /// - /// Called when the AI has to select if to continue attacking when replay. - /// - /// True for yes, false for no. - public bool OnSelectBattleReplay() - { - return Executor.OnSelectBattleReplay(); - } - - /// - /// Called when the AI has to declare a card. - /// - /// Id of the selected card. - public int OnAnnounceCard() - { - if (m_announce == 0) - return 89631139; // Blue-eyes white dragon - return m_announce; - } - - // _ Others functions _ - // Those functions are used by the AI behavior. - - - private CardSelector m_materialSelector; - private int m_place; - private int m_option; - private int m_number; - private int m_announce; - private int m_yesno; - private IList m_attributes = new List(); - private IList m_selector = new List(); - private IList m_position = new List(); - private int m_selector_pointer = -1; - private IList m_races = new List(); - - public void SelectCard(ClientCard card) - { - m_selector_pointer = m_selector.Count(); - m_selector.Add(new CardSelector(card)); - } - - public void SelectCard(IList cards) - { - m_selector_pointer = m_selector.Count(); - m_selector.Add(new CardSelector(cards)); - } - - public void SelectCard(int cardId) - { - m_selector_pointer = m_selector.Count(); - m_selector.Add(new CardSelector(cardId)); - } - - public void SelectCard(IList ids) - { - m_selector_pointer = m_selector.Count(); - m_selector.Add(new CardSelector(ids)); - } - - public void SelectCard(params int[] ids) - { - m_selector_pointer = m_selector.Count(); - m_selector.Add(new CardSelector(ids)); - } - - public void SelectCard(CardLocation loc) - { - m_selector_pointer = m_selector.Count(); - m_selector.Add(new CardSelector(loc)); - } - - public void SelectNextCard(ClientCard card) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectNextCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(card)); - } - - public void SelectNextCard(IList cards) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectNextCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(cards)); - } - - public void SelectNextCard(int cardId) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectNextCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(cardId)); - } - - public void SelectNextCard(IList ids) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectNextCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(ids)); - } - - public void SelectNextCard(params int[] ids) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectNextCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(ids)); - } - - public void SelectNextCard(CardLocation loc) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectNextCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(loc)); - } - - public void SelectThirdCard(ClientCard card) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectThirdCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(card)); - } - - public void SelectThirdCard(IList cards) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectThirdCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(cards)); - } - - public void SelectThirdCard(int cardId) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectThirdCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(cardId)); - } - - public void SelectThirdCard(IList ids) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectThirdCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(ids)); - } - - public void SelectThirdCard(params int[] ids) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectThirdCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(ids)); - } - - public void SelectThirdCard(CardLocation loc) - { - if (m_selector_pointer == -1) - { - Logger.WriteErrorLine("Error: Call SelectThirdCard() before SelectCard()"); - m_selector_pointer = 0; - } - m_selector.Insert(m_selector_pointer, new CardSelector(loc)); - } - - public void SelectMaterials(ClientCard card) - { - m_materialSelector = new CardSelector(card); - } - - public void SelectMaterials(IList cards) - { - m_materialSelector = new CardSelector(cards); - } - - public void SelectMaterials(int cardId) - { - m_materialSelector = new CardSelector(cardId); - } - - public void SelectMaterials(IList ids) - { - m_materialSelector = new CardSelector(ids); - } - - public void SelectMaterials(CardLocation loc) - { - m_materialSelector = new CardSelector(loc); - } - - public void CleanSelectMaterials() - { - m_materialSelector = null; - } - - public CardSelector GetSelectedCards() - { - CardSelector selected = null; - if (m_selector.Count > 0) - { - selected = m_selector[m_selector.Count - 1]; - m_selector.RemoveAt(m_selector.Count - 1); - } - return selected; - } - - public CardPosition GetSelectedPosition() - { - CardPosition selected = CardPosition.FaceUpAttack; - if (m_position.Count > 0) - { - selected = m_position[0]; - m_position.RemoveAt(0); - } - return selected; - } - - public void SelectPosition(CardPosition pos) - { - m_position.Add(pos); - } - - public void SelectPlace(int zones) - { - m_place = zones; - } - - public void SelectOption(int opt) - { - m_option = opt; - } - - public void SelectNumber(int number) - { - m_number = number; - } - - public void SelectAttribute(CardAttribute attribute) - { - m_attributes.Clear(); - m_attributes.Add(attribute); - } - - public void SelectAttributes(CardAttribute[] attributes) - { - m_attributes.Clear(); - foreach (CardAttribute attribute in attributes) - m_attributes.Add(attribute); - } - - public void SelectRace(CardRace race) - { - m_races.Clear(); - m_races.Add(race); - } - - public void SelectRaces(CardRace[] races) - { - m_races.Clear(); - foreach (CardRace race in races) - m_races.Add(race); - } - - public void SelectAnnounceID(int id) - { - m_announce = id; - } - - public void SelectYesNo(bool opt) - { - m_yesno = opt ? 1 : 0; - } - - /// - /// Called when the AI has to declare a number. - /// - /// List of available numbers. - /// Index of the selected number. - public int OnAnnounceNumber(IList numbers) - { - if (numbers.Contains(m_number)) - return numbers.IndexOf(m_number); - - return Program.Rand.Next(0, numbers.Count); // Returns a random number. - } - - /// - /// Called when the AI has to declare one or more attributes. - /// - /// Quantity of attributes to declare. - /// List of available attributes. - /// A list of the selected attributes. - public virtual IList OnAnnounceAttrib(int count, IList attributes) - { - IList foundAttributes = m_attributes.Where(attributes.Contains).ToList(); - if (foundAttributes.Count > 0) - return foundAttributes; - - return attributes; // Returns the first available Attribute. - } - - /// - /// Called when the AI has to declare one or more races. - /// - /// Quantity of races to declare. - /// List of available races. - /// A list of the selected races. - public virtual IList OnAnnounceRace(int count, IList races) - { - IList foundRaces = m_races.Where(races.Contains).ToList(); - if (foundRaces.Count > 0) - return foundRaces; - - return races; // Returns the first available Races. - } - - public BattlePhaseAction Attack(ClientCard attacker, ClientCard defender) - { - Executor.SetCard(0, attacker, -1); - if (defender != null) - { - string cardName = defender.Name ?? "monster"; - attacker.ShouldDirectAttack = false; - _dialogs.SendAttack(attacker.Name, cardName); - SelectCard(defender); - } - else - { - attacker.ShouldDirectAttack = true; - _dialogs.SendDirectAttack(attacker.Name); - } - return new BattlePhaseAction(BattlePhaseAction.BattleAction.Attack, attacker.ActionIndex); - } - - public BattlePhaseAction ToEndPhase() - { - _dialogs.SendEndTurn(); - return new BattlePhaseAction(BattlePhaseAction.BattleAction.ToEndPhase); - } - public BattlePhaseAction ToMainPhase2() - { - return new BattlePhaseAction(BattlePhaseAction.BattleAction.ToMainPhaseTwo); - } - - private bool ShouldExecute(CardExecutor exec, ClientCard card, ExecutorType type, int desc = -1) - { - Executor.SetCard(type, card, desc); - return card != null && - exec.Type == type && - (exec.CardId == -1 || exec.CardId == card.Id) && - (exec.Func == null || exec.Func()); - } - } -} +using System.Linq; +using System.Collections.Generic; +using WindBot.Game.AI; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game +{ + public enum LogLevel : int + { + Info, + Debug, + Error + } + public class GameAI + { + public Duel Duel { get; private set; } + public Executor Executor { get; set; } + + public Dialogs _dialogs; + private System.Action _log; + + public void Log(LogLevel level, string message) + { + _log(message, (int)level); + } + + public GameAI(Duel duel, string dialog, System.Action chat, System.Action log) + { + Duel = duel; + _log = log; + _dialogs = new Dialogs(dialog, chat); + } + + /// + /// Called when the AI got the error message. + /// + public void OnRetry() + { + _dialogs.SendSorry(); + } + + public void OnDeckError(string card) + { + _dialogs.SendDeckSorry(card); + } + + /// + /// Called when the AI join the game. + /// + public void OnJoinGame() + { + _dialogs.SendWelcome(); + } + + /// + /// Called when the duel starts. + /// + public void OnStart() + { + _dialogs.SendDuelStart(); + } + + /// + /// Called when the AI do the rock-paper-scissors. + /// + /// 1 for Scissors, 2 for Rock, 3 for Paper. + public int OnRockPaperScissors() + { + return Executor.OnRockPaperScissors(); + } + + /// + /// Called when the AI won the rock-paper-scissors. + /// + /// True if the AI should begin first, false otherwise. + public bool OnSelectHand() + { + return Executor.OnSelectHand(); + } + + /// + /// Called when any player draw card. + /// + public void OnDraw(int player) + { + Executor.OnDraw(player); + } + + /// + /// Called when it's a new turn. + /// + public void OnNewTurn() + { + Executor.OnNewTurn(); + } + + /// + /// Called when it's a new phase. + /// + public void OnNewPhase() + { + m_selector.Clear(); + m_position.Clear(); + m_selector_pointer = -1; + m_materialSelector = null; + m_option = -1; + m_yesno = -1; + + m_place = 0; + if (Duel.Player == 0 && Duel.Phase == DuelPhase.Draw) + { + _dialogs.SendNewTurn(); + } + Executor.OnNewPhase(); + } + + /// + /// Called when the AI got attack directly. + /// + public void OnDirectAttack(ClientCard card) + { + _dialogs.SendOnDirectAttack(card.Name); + } + + /// + /// Called when a chain is executed. + /// + /// Card who is chained. + /// Player who is currently chaining. + public void OnChaining(ClientCard card, int player) + { + Executor.OnChaining(player,card); + } + + /// + /// Called when a chain has been solved. + /// + public void OnChainEnd() + { + m_selector.Clear(); + m_selector_pointer = -1; + Executor.OnChainEnd(); + } + + /// + /// Called when the AI has to do something during the battle phase. + /// + /// Informations about usable cards. + /// A new BattlePhaseAction containing the action to do. + public BattlePhaseAction OnSelectBattleCmd(BattlePhase battle) + { + Executor.SetBattle(battle); + foreach (CardExecutor exec in Executor.Executors) + { + if (exec.Type == ExecutorType.GoToMainPhase2 && battle.CanMainPhaseTwo && exec.Func()) // check if should enter main phase 2 directly + { + return ToMainPhase2(); + } + if (exec.Type == ExecutorType.GoToEndPhase && battle.CanEndPhase && exec.Func()) // check if should enter end phase directly + { + return ToEndPhase(); + } + for (int i = 0; i < battle.ActivableCards.Count; ++i) + { + ClientCard card = battle.ActivableCards[i]; + if (ShouldExecute(exec, card, ExecutorType.Activate, battle.ActivableDescs[i])) + { + _dialogs.SendChaining(card.Name); + return new BattlePhaseAction(BattlePhaseAction.BattleAction.Activate, card.ActionIndex); + } + } + } + + // Sort the attackers and defenders, make monster with higher attack go first. + List attackers = new List(battle.AttackableCards); + attackers.Sort(CardContainer.CompareCardAttack); + attackers.Reverse(); + + List defenders = new List(Duel.Fields[1].GetMonsters()); + defenders.Sort(CardContainer.CompareDefensePower); + defenders.Reverse(); + + // Let executor decide which card should attack first. + ClientCard selected = Executor.OnSelectAttacker(attackers, defenders); + if (selected != null && attackers.Contains(selected)) + { + attackers.Remove(selected); + attackers.Insert(0, selected); + } + + // Check for the executor. + BattlePhaseAction result = Executor.OnBattle(attackers, defenders); + if (result != null) + return result; + + if (attackers.Count == 0) + return ToMainPhase2(); + + if (defenders.Count == 0) + { + // Attack with the monster with the lowest attack first + for (int i = attackers.Count - 1; i >= 0; --i) + { + ClientCard attacker = attackers[i]; + if (attacker.Attack > 0) + return Attack(attacker, null); + } + } + else + { + for (int k = 0; k < attackers.Count; ++k) + { + ClientCard attacker = attackers[k]; + attacker.IsLastAttacker = (k == attackers.Count - 1); + result = Executor.OnSelectAttackTarget(attacker, defenders); + if (result != null) + return result; + } + } + + if (!battle.CanMainPhaseTwo) + return Attack(attackers[0], (defenders.Count == 0) ? null : defenders[0]); + + return ToMainPhase2(); + } + + /// + /// Called when the AI has to select one or more cards. + /// + /// List of available cards. + /// Minimal quantity. + /// Maximal quantity. + /// The hint message of the select. + /// True if you can return an empty list. + /// A new list containing the selected cards. + public IList OnSelectCard(IList cards, int min, int max, int hint, bool cancelable) + { + const int HINTMSG_FMATERIAL = 511; + const int HINTMSG_SMATERIAL = 512; + const int HINTMSG_XMATERIAL = 513; + const int HINTMSG_LMATERIAL = 533; + const int HINTMSG_SPSUMMON = 509; + + // Check for the executor. + IList result = Executor.OnSelectCard(cards, min, max, hint, cancelable); + if (result != null) + return result; + + if (hint == HINTMSG_SPSUMMON && min == 1 && max > min) // pendulum summon + { + result = Executor.OnSelectPendulumSummon(cards, max); + if (result != null) + return result; + } + + CardSelector selector = null; + if (hint == HINTMSG_FMATERIAL || hint == HINTMSG_SMATERIAL || hint == HINTMSG_XMATERIAL || hint == HINTMSG_LMATERIAL) + { + if (m_materialSelector != null) + { + //Log(LogLevel.Debug, "m_materialSelector"); + selector = m_materialSelector; + } + else + { + if (hint == HINTMSG_FMATERIAL) + result = Executor.OnSelectFusionMaterial(cards, min, max); + if (hint == HINTMSG_SMATERIAL) + result = Executor.OnSelectSynchroMaterial(cards, 0, min, max); + if (hint == HINTMSG_XMATERIAL) + result = Executor.OnSelectXyzMaterial(cards, min, max); + if (hint == HINTMSG_LMATERIAL) + result = Executor.OnSelectLinkMaterial(cards, min, max); + + if (result != null) + return result; + + // Update the next selector. + selector = GetSelectedCards(); + } + } + else + { + // Update the next selector. + selector = GetSelectedCards(); + } + + // If we selected a card, use this card. + if (selector != null) + return selector.Select(cards, min, max); + + // Always select the first available cards and choose the minimum. + IList selected = new List(); + + if (cards.Count >= min) + { + for (int i = 0; i < min; ++i) + selected.Add(cards[i]); + } + return selected; + } + + /// + /// Called when the AI can chain (activate) a card. + /// + /// List of activable cards. + /// List of effect descriptions. + /// You can't return -1 if this param is true. + /// Index of the activated card or -1. + public int OnSelectChain(IList cards, IList descs, bool forced) + { + foreach (CardExecutor exec in Executor.Executors) + { + for (int i = 0; i < cards.Count; ++i) + { + ClientCard card = cards[i]; + if (ShouldExecute(exec, card, ExecutorType.Activate, descs[i])) + { + _dialogs.SendChaining(card.Name); + return i; + } + } + } + // If we're forced to chain, we chain the first card. However don't do anything. + return forced ? 0 : -1; + } + + /// + /// Called when the AI has to use one or more counters. + /// + /// Type of counter to use. + /// Quantity of counter to select. + /// List of available cards. + /// List of available counters. + /// List of used counters. + public IList OnSelectCounter(int type, int quantity, IList cards, IList counters) + { + // Always select the first available counters. + int[] used = new int[counters.Count]; + int i = 0; + while (quantity > 0) + { + if (counters[i] >= quantity) + { + used[i] = quantity; + quantity = 0; + } + else + { + used[i] = counters[i]; + quantity -= counters[i]; + } + i++; + } + return used; + } + + /// + /// Called when the AI has to sort cards. + /// + /// Cards to sort. + /// List of sorted cards. + public IList OnCardSorting(IList cards) + { + + IList result = Executor.OnCardSorting(cards); + if (result != null) + return result; + result = new List(); + // TODO: use selector + result = cards.ToList(); + return result; + } + + /// + /// Called when the AI has to choose to activate or not an effect. + /// + /// Card to activate. + /// True for yes, false for no. + public bool OnSelectEffectYn(ClientCard card, int desc) + { + foreach (CardExecutor exec in Executor.Executors) + { + if (ShouldExecute(exec, card, ExecutorType.Activate, desc)) + return true; + } + return false; + } + + /// + /// Called when the AI has to do something during the main phase. + /// + /// A lot of informations about the available actions. + /// A new MainPhaseAction containing the action to do. + public MainPhaseAction OnSelectIdleCmd(MainPhase main) + { + Executor.SetMain(main); + foreach (CardExecutor exec in Executor.Executors) + { + if (exec.Type == ExecutorType.GoToEndPhase && main.CanEndPhase && exec.Func()) // check if should enter end phase directly + { + _dialogs.SendEndTurn(); + return new MainPhaseAction(MainPhaseAction.MainAction.ToEndPhase); + } + if (exec.Type==ExecutorType.GoToBattlePhase && main.CanBattlePhase && exec.Func()) // check if should enter battle phase directly + { + return new MainPhaseAction(MainPhaseAction.MainAction.ToBattlePhase); + } + // NOTICE: GoToBattlePhase and GoToEndPhase has no "card" can be accessed to ShouldExecute(), so instead use exec.Func() to check ... + // enter end phase and enter battle pahse is in higher priority. + + for (int i = 0; i < main.ActivableCards.Count; ++i) + { + ClientCard card = main.ActivableCards[i]; + if (ShouldExecute(exec, card, ExecutorType.Activate, main.ActivableDescs[i])) + { + _dialogs.SendActivate(card.Name); + return new MainPhaseAction(MainPhaseAction.MainAction.Activate, card.ActionActivateIndex[main.ActivableDescs[i]]); + } + } + foreach (ClientCard card in main.MonsterSetableCards) + { + if (ShouldExecute(exec, card, ExecutorType.MonsterSet)) + { + _dialogs.SendSetMonster(); + return new MainPhaseAction(MainPhaseAction.MainAction.SetMonster, card.ActionIndex); + } + } + foreach (ClientCard card in main.ReposableCards) + { + if (ShouldExecute(exec, card, ExecutorType.Repos)) + return new MainPhaseAction(MainPhaseAction.MainAction.Repos, card.ActionIndex); + } + foreach (ClientCard card in main.SpecialSummonableCards) + { + if (ShouldExecute(exec, card, ExecutorType.SpSummon)) + { + _dialogs.SendSummon(card.Name); + return new MainPhaseAction(MainPhaseAction.MainAction.SpSummon, card.ActionIndex); + } + } + foreach (ClientCard card in main.SummonableCards) + { + if (ShouldExecute(exec, card, ExecutorType.Summon)) + { + _dialogs.SendSummon(card.Name); + return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex); + } + if (ShouldExecute(exec, card, ExecutorType.SummonOrSet)) + { + if (Executor.Util.IsAllEnemyBetter(true) && Executor.Util.IsAllEnemyBetterThanValue(card.Attack + 300, false) && + main.MonsterSetableCards.Contains(card)) + { + _dialogs.SendSetMonster(); + return new MainPhaseAction(MainPhaseAction.MainAction.SetMonster, card.ActionIndex); + } + _dialogs.SendSummon(card.Name); + return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex); + } + } + foreach (ClientCard card in main.SpellSetableCards) + { + if (ShouldExecute(exec, card, ExecutorType.SpellSet)) + return new MainPhaseAction(MainPhaseAction.MainAction.SetSpell, card.ActionIndex); + } + } + + if (main.CanBattlePhase && Duel.Fields[0].HasAttackingMonster()) + return new MainPhaseAction(MainPhaseAction.MainAction.ToBattlePhase); + + _dialogs.SendEndTurn(); + return new MainPhaseAction(MainPhaseAction.MainAction.ToEndPhase); + } + + /// + /// Called when the AI has to select an option. + /// + /// List of available options. + /// Index of the selected option. + public int OnSelectOption(IList options) + { + if (m_option != -1 && m_option < options.Count) + return m_option; + + int result = Executor.OnSelectOption(options); + if (result != -1) + return result; + + return 0; // Always select the first option. + } + + public int OnSelectPlace(int cardId, int player, CardLocation location, int available) + { + int selector_selected = m_place; + m_place = 0; + + int executor_selected = Executor.OnSelectPlace(cardId, player, location, available); + + if ((executor_selected & available) > 0) + return executor_selected & available; + if ((selector_selected & available) > 0) + return selector_selected & available; + + // TODO: LinkedZones + + return 0; + } + + /// + /// Called when the AI has to select a card position. + /// + /// Id of the card to position on the field. + /// List of available positions. + /// Selected position. + public CardPosition OnSelectPosition(int cardId, IList positions) + { + CardPosition selector_selected = GetSelectedPosition(); + + CardPosition executor_selected = Executor.OnSelectPosition(cardId, positions); + + // Selects the selected position if available, the first available otherwise. + if (positions.Contains(executor_selected)) + return executor_selected; + if (positions.Contains(selector_selected)) + return selector_selected; + + return positions[0]; + } + + /// + /// Called when the AI has to tribute for a synchro monster or ritual monster. + /// + /// Available cards. + /// Result of the operation. + /// Minimum cards. + /// Maximum cards. + /// True for exact equal. + /// + public IList OnSelectSum(IList cards, int sum, int min, int max, int hint, bool mode) + { + const int HINTMSG_RELEASE = 500; + const int HINTMSG_SMATERIAL = 512; + + IList selected = Executor.OnSelectSum(cards, sum, min, max, hint, mode); + if (selected != null) + { + return selected; + } + + if (hint == HINTMSG_RELEASE || hint == HINTMSG_SMATERIAL) + { + if (m_materialSelector != null) + { + selected = m_materialSelector.Select(cards, min, max); + } + else + { + switch (hint) + { + case HINTMSG_SMATERIAL: + selected = Executor.OnSelectSynchroMaterial(cards, sum, min, max); + break; + case HINTMSG_RELEASE: + selected = Executor.OnSelectRitualTribute(cards, sum, min, max); + break; + } + } + if (selected != null) + { + int s1 = 0, s2 = 0; + foreach (ClientCard card in selected) + { + s1 += card.OpParam1; + s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1; + } + if ((mode && (s1 == sum || s2 == sum)) || (!mode && (s1 >= sum || s2 >= sum))) + { + return selected; + } + } + } + + if (mode) + { + // equal + + if (sum == 0 && min == 0) + { + return new List(); + } + + if (min <= 1) + { + // try special level first + foreach (ClientCard card in cards) + { + if (card.OpParam2 == sum) + { + return new[] { card }; + } + } + // try level equal + foreach (ClientCard card in cards) + { + if (card.OpParam1 == sum) + { + return new[] { card }; + } + } + } + + // try all + int s1 = 0, s2 = 0; + foreach (ClientCard card in cards) + { + s1 += card.OpParam1; + s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1; + } + if (s1 == sum || s2 == sum) + { + return cards; + } + + // try all combinations + int i = (min <= 1) ? 2 : min; + while (i <= max && i <= cards.Count) + { + IEnumerable> combos = CardContainer.GetCombinations(cards, i); + + foreach (IEnumerable combo in combos) + { + Log(LogLevel.Debug, "--"); + s1 = 0; + s2 = 0; + foreach (ClientCard card in combo) + { + s1 += card.OpParam1; + s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1; + } + if (s1 == sum || s2 == sum) + { + return combo.ToList(); + } + } + i++; + } + } + else + { + // larger + if (min <= 1) + { + // try special level first + foreach (ClientCard card in cards) + { + if (card.OpParam2 >= sum) + { + return new[] { card }; + } + } + // try level equal + foreach (ClientCard card in cards) + { + if (card.OpParam1 >= sum) + { + return new[] { card }; + } + } + } + + // try all combinations + int i = (min <= 1) ? 2 : min; + while (i <= max && i <= cards.Count) + { + IEnumerable> combos = CardContainer.GetCombinations(cards, i); + + foreach (IEnumerable combo in combos) + { + Log(LogLevel.Debug, "----"); + int s1 = 0, s2 = 0; + foreach (ClientCard card in combo) + { + s1 += card.OpParam1; + s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1; + } + if (s1 >= sum || s2 >= sum) + { + return combo.ToList(); + } + } + i++; + } + } + + Log(LogLevel.Error, "Fail to select sum."); + return new List(); + } + + /// + /// Called when the AI has to tribute one or more cards. + /// + /// List of available cards. + /// Minimal quantity. + /// Maximal quantity. + /// The hint message of the select. + /// True if you can return an empty list. + /// A new list containing the tributed cards. + public IList OnSelectTribute(IList cards, int min, int max, int hint, bool cancelable) + { + // Always choose the minimum and lowest atk. + List sorted = new List(); + sorted.AddRange(cards); + sorted.Sort(CardContainer.CompareCardAttack); + + IList selected = new List(); + + for (int i = 0; i < min && i < sorted.Count; ++i) + selected.Add(sorted[i]); + + return selected; + } + + /// + /// Called when the AI has to select yes or no. + /// + /// Id of the question. + /// True for yes, false for no. + public bool OnSelectYesNo(int desc) + { + if (m_yesno != -1) + return m_yesno > 0; + return Executor.OnSelectYesNo(desc); + } + + /// + /// Called when the AI has to select if to continue attacking when replay. + /// + /// True for yes, false for no. + public bool OnSelectBattleReplay() + { + return Executor.OnSelectBattleReplay(); + } + + /// + /// Called when the AI has to declare a card. + /// + /// Id of the selected card. + public int OnAnnounceCard() + { + if (m_announce == 0) + return 89631139; // Blue-eyes white dragon + return m_announce; + } + + // _ Others functions _ + // Those functions are used by the AI behavior. + + + private CardSelector m_materialSelector; + private int m_place; + private int m_option; + private int m_number; + private int m_announce; + private int m_yesno; + private IList m_attributes = new List(); + private IList m_selector = new List(); + private IList m_position = new List(); + private int m_selector_pointer = -1; + private IList m_races = new List(); + + public void SelectCard(ClientCard card) + { + m_selector_pointer = m_selector.Count(); + m_selector.Add(new CardSelector(card)); + } + + public void SelectCard(IList cards) + { + m_selector_pointer = m_selector.Count(); + m_selector.Add(new CardSelector(cards)); + } + + public void SelectCard(int cardId) + { + m_selector_pointer = m_selector.Count(); + m_selector.Add(new CardSelector(cardId)); + } + + public void SelectCard(IList ids) + { + m_selector_pointer = m_selector.Count(); + m_selector.Add(new CardSelector(ids)); + } + + public void SelectCard(params int[] ids) + { + m_selector_pointer = m_selector.Count(); + m_selector.Add(new CardSelector(ids)); + } + + public void SelectCard(CardLocation loc) + { + m_selector_pointer = m_selector.Count(); + m_selector.Add(new CardSelector(loc)); + } + + public void SelectNextCard(ClientCard card) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectNextCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(card)); + } + + public void SelectNextCard(IList cards) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectNextCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(cards)); + } + + public void SelectNextCard(int cardId) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectNextCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(cardId)); + } + + public void SelectNextCard(IList ids) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectNextCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(ids)); + } + + public void SelectNextCard(params int[] ids) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectNextCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(ids)); + } + + public void SelectNextCard(CardLocation loc) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectNextCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(loc)); + } + + public void SelectThirdCard(ClientCard card) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectThirdCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(card)); + } + + public void SelectThirdCard(IList cards) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectThirdCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(cards)); + } + + public void SelectThirdCard(int cardId) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectThirdCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(cardId)); + } + + public void SelectThirdCard(IList ids) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectThirdCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(ids)); + } + + public void SelectThirdCard(params int[] ids) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectThirdCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(ids)); + } + + public void SelectThirdCard(CardLocation loc) + { + if (m_selector_pointer == -1) + { + Log(LogLevel.Error, "Error: Call SelectThirdCard() before SelectCard()"); + m_selector_pointer = 0; + } + m_selector.Insert(m_selector_pointer, new CardSelector(loc)); + } + + public void SelectMaterials(ClientCard card) + { + m_materialSelector = new CardSelector(card); + } + + public void SelectMaterials(IList cards) + { + m_materialSelector = new CardSelector(cards); + } + + public void SelectMaterials(int cardId) + { + m_materialSelector = new CardSelector(cardId); + } + + public void SelectMaterials(IList ids) + { + m_materialSelector = new CardSelector(ids); + } + + public void SelectMaterials(CardLocation loc) + { + m_materialSelector = new CardSelector(loc); + } + + public void CleanSelectMaterials() + { + m_materialSelector = null; + } + + public CardSelector GetSelectedCards() + { + CardSelector selected = null; + if (m_selector.Count > 0) + { + selected = m_selector[m_selector.Count - 1]; + m_selector.RemoveAt(m_selector.Count - 1); + } + return selected; + } + + public CardPosition GetSelectedPosition() + { + CardPosition selected = CardPosition.FaceUpAttack; + if (m_position.Count > 0) + { + selected = m_position[0]; + m_position.RemoveAt(0); + } + return selected; + } + + public void SelectPosition(CardPosition pos) + { + m_position.Add(pos); + } + + public void SelectPlace(int zones) + { + m_place = zones; + } + + public void SelectOption(int opt) + { + m_option = opt; + } + + public void SelectNumber(int number) + { + m_number = number; + } + + public void SelectAttribute(CardAttribute attribute) + { + m_attributes.Clear(); + m_attributes.Add(attribute); + } + + public void SelectAttributes(CardAttribute[] attributes) + { + m_attributes.Clear(); + foreach (CardAttribute attribute in attributes) + m_attributes.Add(attribute); + } + + public void SelectRace(CardRace race) + { + m_races.Clear(); + m_races.Add(race); + } + + public void SelectRaces(CardRace[] races) + { + m_races.Clear(); + foreach (CardRace race in races) + m_races.Add(race); + } + + public void SelectAnnounceID(int id) + { + m_announce = id; + } + + public void SelectYesNo(bool opt) + { + m_yesno = opt ? 1 : 0; + } + + /// + /// Called when the AI has to declare a number. + /// + /// List of available numbers. + /// Index of the selected number. + public int OnAnnounceNumber(IList numbers) + { + if (numbers.Contains(m_number)) + return numbers.IndexOf(m_number); + + return Executor.Rand.Next(0, numbers.Count); // Returns a random number. + } + + /// + /// Called when the AI has to declare one or more attributes. + /// + /// Quantity of attributes to declare. + /// List of available attributes. + /// A list of the selected attributes. + public virtual IList OnAnnounceAttrib(int count, IList attributes) + { + IList foundAttributes = m_attributes.Where(attributes.Contains).ToList(); + if (foundAttributes.Count > 0) + return foundAttributes; + + return attributes; // Returns the first available Attribute. + } + + /// + /// Called when the AI has to declare one or more races. + /// + /// Quantity of races to declare. + /// List of available races. + /// A list of the selected races. + public virtual IList OnAnnounceRace(int count, IList races) + { + IList foundRaces = m_races.Where(races.Contains).ToList(); + if (foundRaces.Count > 0) + return foundRaces; + + return races; // Returns the first available Races. + } + + public BattlePhaseAction Attack(ClientCard attacker, ClientCard defender) + { + Executor.SetCard(0, attacker, -1); + if (defender != null) + { + string cardName = defender.Name ?? "monster"; + attacker.ShouldDirectAttack = false; + _dialogs.SendAttack(attacker.Name, cardName); + SelectCard(defender); + } + else + { + attacker.ShouldDirectAttack = true; + _dialogs.SendDirectAttack(attacker.Name); + } + return new BattlePhaseAction(BattlePhaseAction.BattleAction.Attack, attacker.ActionIndex); + } + + public BattlePhaseAction ToEndPhase() + { + _dialogs.SendEndTurn(); + return new BattlePhaseAction(BattlePhaseAction.BattleAction.ToEndPhase); + } + public BattlePhaseAction ToMainPhase2() + { + return new BattlePhaseAction(BattlePhaseAction.BattleAction.ToMainPhaseTwo); + } + + private bool ShouldExecute(CardExecutor exec, ClientCard card, ExecutorType type, int desc = -1) + { + Executor.SetCard(type, card, desc); + return card != null && + exec.Type == type && + (exec.CardId == -1 || exec.CardId == card.Id) && + (exec.Func == null || exec.Func()); + } + } +} diff --git a/Game/MainPhase.cs b/ExecutorBase/Game/MainPhase.cs similarity index 97% rename from Game/MainPhase.cs rename to ExecutorBase/Game/MainPhase.cs index 4d69d278..394ffa78 100644 --- a/Game/MainPhase.cs +++ b/ExecutorBase/Game/MainPhase.cs @@ -1,28 +1,28 @@ -using System.Collections.Generic; - -namespace WindBot.Game -{ - public class MainPhase - { - public IList SummonableCards { get; private set; } - public IList SpecialSummonableCards { get; private set; } - public IList ReposableCards { get; private set; } - public IList MonsterSetableCards { get; private set; } - public IList SpellSetableCards { get; private set; } - public IList ActivableCards { get; private set; } - public IList ActivableDescs { get; private set; } - public bool CanBattlePhase { get; set; } - public bool CanEndPhase { get; set; } - - public MainPhase() - { - SummonableCards = new List(); - SpecialSummonableCards = new List(); - ReposableCards = new List(); - MonsterSetableCards = new List(); - SpellSetableCards = new List(); - ActivableCards = new List(); - ActivableDescs = new List(); - } - } +using System.Collections.Generic; + +namespace WindBot.Game +{ + public class MainPhase + { + public IList SummonableCards { get; private set; } + public IList SpecialSummonableCards { get; private set; } + public IList ReposableCards { get; private set; } + public IList MonsterSetableCards { get; private set; } + public IList SpellSetableCards { get; private set; } + public IList ActivableCards { get; private set; } + public IList ActivableDescs { get; private set; } + public bool CanBattlePhase { get; set; } + public bool CanEndPhase { get; set; } + + public MainPhase() + { + SummonableCards = new List(); + SpecialSummonableCards = new List(); + ReposableCards = new List(); + MonsterSetableCards = new List(); + SpellSetableCards = new List(); + ActivableCards = new List(); + ActivableDescs = new List(); + } + } } \ No newline at end of file diff --git a/Game/MainPhaseAction.cs b/ExecutorBase/Game/MainPhaseAction.cs similarity index 95% rename from Game/MainPhaseAction.cs rename to ExecutorBase/Game/MainPhaseAction.cs index 46fa2c8a..00746cd7 100644 --- a/Game/MainPhaseAction.cs +++ b/ExecutorBase/Game/MainPhaseAction.cs @@ -1,43 +1,43 @@ -namespace WindBot.Game -{ - public class MainPhaseAction - { - public enum MainAction - { - Summon = 0, - SpSummon = 1, - Repos = 2, - SetMonster = 3, - SetSpell = 4, - Activate = 5, - ToBattlePhase = 6, - ToEndPhase = 7 - } - - public MainAction Action { get; private set; } - public int Index { get; private set; } - - public MainPhaseAction(MainAction action) - { - Action = action; - Index = 0; - } - - public MainPhaseAction(MainAction action, int index) - { - Action = action; - Index = index; - } - - public MainPhaseAction(MainAction action, int[] indexes) - { - Action = action; - Index = indexes[(int)action]; - } - - public int ToValue() - { - return (Index << 16) + (int)Action; - } - } +namespace WindBot.Game +{ + public class MainPhaseAction + { + public enum MainAction + { + Summon = 0, + SpSummon = 1, + Repos = 2, + SetMonster = 3, + SetSpell = 4, + Activate = 5, + ToBattlePhase = 6, + ToEndPhase = 7 + } + + public MainAction Action { get; private set; } + public int Index { get; private set; } + + public MainPhaseAction(MainAction action) + { + Action = action; + Index = 0; + } + + public MainPhaseAction(MainAction action, int index) + { + Action = action; + Index = index; + } + + public MainPhaseAction(MainAction action, int[] indexes) + { + Action = action; + Index = indexes[(int)action]; + } + + public int ToValue() + { + return (Index << 16) + (int)Action; + } + } } \ No newline at end of file diff --git a/Game/Deck.cs b/Game/Deck.cs index 3ef51545..92274e86 100644 --- a/Game/Deck.cs +++ b/Game/Deck.cs @@ -1,91 +1,91 @@ -using System; -using System.Collections.Generic; -using System.IO; -using YGOSharp.OCGWrapper; - -namespace WindBot.Game -{ - public class Deck - { - public IList Cards { get; private set; } - public IList ExtraCards { get; private set; } - public IList SideCards { get; private set; } - - public Deck() - { - Cards = new List(); - ExtraCards = new List(); - SideCards = new List(); - } - - private void AddNewCard(int cardId, bool sideDeck) - { - NamedCard newCard = NamedCard.Get(cardId); - if (newCard == null) - return; - - if (!sideDeck) - AddCard(newCard); - else - SideCards.Add(newCard); - } - - private void AddCard(NamedCard card) - { - if (card.IsExtraCard()) - ExtraCards.Add(card); - else - Cards.Add(card); - } - - public static Deck Load(string name) - { - StreamReader reader = null; - try - { - reader = new StreamReader(new FileStream("Decks/" + name + ".ydk", FileMode.Open, FileAccess.Read)); - - Deck deck = new Deck(); - bool side = false; - - while (!reader.EndOfStream) - { - string line = reader.ReadLine(); - if (line == null) - continue; - - line = line.Trim(); - if (line.StartsWith("#")) - continue; - if (line.Equals("!side")) - { - side = true; - continue; - } - - int id; - if (!int.TryParse(line, out id)) - continue; - - deck.AddNewCard(id, side); - } - - reader.Close(); - - if (deck.Cards.Count > 60) - return null; - if (deck.ExtraCards.Count > 15) - return null; - if (deck.SideCards.Count > 15) - return null; - - return deck; - } - catch (Exception) - { - reader?.Close(); - return null; - } - } - } +using System; +using System.Collections.Generic; +using System.IO; +using YGOSharp.OCGWrapper; + +namespace WindBot.Game +{ + public class Deck + { + public IList Cards { get; private set; } + public IList ExtraCards { get; private set; } + public IList SideCards { get; private set; } + + public Deck() + { + Cards = new List(); + ExtraCards = new List(); + SideCards = new List(); + } + + private void AddNewCard(int cardId, bool sideDeck) + { + NamedCard newCard = NamedCard.Get(cardId); + if (newCard == null) + return; + + if (!sideDeck) + AddCard(newCard); + else + SideCards.Add(newCard); + } + + private void AddCard(NamedCard card) + { + if (card.IsExtraCard()) + ExtraCards.Add(card); + else + Cards.Add(card); + } + + public static Deck Load(string name) + { + StreamReader reader = null; + try + { + reader = new StreamReader(new FileStream("Decks/" + name + ".ydk", FileMode.Open, FileAccess.Read)); + + Deck deck = new Deck(); + bool side = false; + + while (!reader.EndOfStream) + { + string line = reader.ReadLine(); + if (line == null) + continue; + + line = line.Trim(); + if (line.StartsWith("#")) + continue; + if (line.Equals("!side")) + { + side = true; + continue; + } + + int id; + if (!int.TryParse(line, out id)) + continue; + + deck.AddNewCard(id, side); + } + + reader.Close(); + + if (deck.Cards.Count > 60) + return null; + if (deck.ExtraCards.Count > 15) + return null; + if (deck.SideCards.Count > 15) + return null; + + return deck; + } + catch (Exception) + { + reader?.Close(); + return null; + } + } + } } \ No newline at end of file diff --git a/Game/AI/DecksManager.cs b/Game/DecksManager.cs similarity index 58% rename from Game/AI/DecksManager.cs rename to Game/DecksManager.cs index 19852651..fa692b66 100644 --- a/Game/AI/DecksManager.cs +++ b/Game/DecksManager.cs @@ -1,77 +1,110 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace WindBot.Game.AI -{ - public static class DecksManager - { - private class DeckInstance - { - public string Deck { get; private set; } - public Type Type { get; private set; } - public string Level { get; private set; } - - public DeckInstance(string deck, Type type, string level) - { - Deck = deck; - Type = type; - Level = level; - } - } - - private static Dictionary _decks; - private static List _list; - private static Random _rand; - - public static void Init() - { - _decks = new Dictionary(); - _rand = new Random(); - - Assembly asm = Assembly.GetExecutingAssembly(); - Type[] types = asm.GetTypes(); - - foreach (Type type in types) - { - MemberInfo info = type; - object[] attributes = info.GetCustomAttributes(false); - foreach (object attribute in attributes) - { - if (attribute is DeckAttribute) - { - DeckAttribute deck = (DeckAttribute)attribute; - _decks.Add(deck.Name, new DeckInstance(deck.File, type, deck.Level)); - } - } - } - - _list = new List(); - _list.AddRange(_decks.Values); - - Logger.WriteLine("Decks initialized, " + _decks.Count + " found."); - } - - public static Executor Instantiate(GameAI ai, Duel duel) - { - DeckInstance infos; - - string deck = ai.Game.Deck; - - if (deck != null && _decks.ContainsKey(deck)) - infos = _decks[deck]; - else - { - do - { - infos = _list[_rand.Next(_list.Count)]; - } - while (infos.Level != "Normal"); - } - - Executor executor = (Executor)Activator.CreateInstance(infos.Type, ai, duel); - executor.Deck = infos.Deck; - return executor; - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using WindBot.Game.AI; + +namespace WindBot.Game +{ + public static class DecksManager + { + private class DeckInstance + { + public string Deck { get; private set; } + public Type Type { get; private set; } + public string Level { get; private set; } + + public DeckInstance(string deck, Type type, string level) + { + Deck = deck; + Type = type; + Level = level; + } + } + + private static Dictionary _decks; + private static List _list; + private static Random _rand; + + public static void Init() + { + _decks = new Dictionary(); + _rand = new Random(); + + Assembly asm = Assembly.GetExecutingAssembly(); + Type[] types = asm.GetTypes(); + + foreach (Type type in types) + { + MemberInfo info = type; + object[] attributes = info.GetCustomAttributes(false); + foreach (object attribute in attributes) + { + if (attribute is DeckAttribute) + { + DeckAttribute deck = (DeckAttribute)attribute; + _decks.Add(deck.Name, new DeckInstance(deck.File, type, deck.Level)); + } + } + } + try + { + string[] files = Directory.GetFiles("Executors", "*.dll", SearchOption.TopDirectoryOnly); + foreach (string file in files) + { + Assembly assembly = Assembly.LoadFrom(file); + Type[] types2 = assembly.GetTypes(); + foreach (Type type in types2) + { + try + { + MemberInfo info = type; + object[] attributes = info.GetCustomAttributes(false); + foreach (object attribute in attributes) + { + if (attribute is DeckAttribute) + { + DeckAttribute deck = (DeckAttribute)attribute; + _decks.Add(deck.Name, new DeckInstance(deck.File, type, deck.Level)); + } + } + } + catch (Exception ex) + { + Logger.WriteErrorLine("Executor loading (" + file + ") error: " + ex); + } + } + } + } + catch (Exception ex) + { + Logger.WriteErrorLine(ex.ToString()); + } + + _list = new List(); + _list.AddRange(_decks.Values); + + Logger.WriteLine("Decks initialized, " + _decks.Count + " found."); + } + + public static Executor Instantiate(GameAI ai, Duel duel, string deck) + { + DeckInstance infos; + + if (deck != null && _decks.ContainsKey(deck)) + infos = _decks[deck]; + else + { + do + { + infos = _list[_rand.Next(_list.Count)]; + } + while (infos.Level != "Normal"); + } + + Executor executor = (Executor)Activator.CreateInstance(infos.Type, ai, duel); + executor.Deck = infos.Deck; + return executor; + } + } +} diff --git a/Game/GameBehavior.cs b/Game/GameBehavior.cs index 30e0aff0..13413564 100644 --- a/Game/GameBehavior.cs +++ b/Game/GameBehavior.cs @@ -42,8 +42,8 @@ public GameBehavior(GameClient game) _room = new Room(); _duel = new Duel(); - _ai = new GameAI(Game, _duel); - _ai.Executor = DecksManager.Instantiate(_ai, _duel); + _ai = new GameAI(_duel, Game.Dialog, Game.Chat, Game.Log); + _ai.Executor = DecksManager.Instantiate(_ai, _duel, Game.Deck); Deck = Deck.Load(_ai.Executor.Deck); _select_hint = 0; diff --git a/Game/GameClient.cs b/Game/GameClient.cs index ee299203..9ad4da0e 100644 --- a/Game/GameClient.cs +++ b/Game/GameClient.cs @@ -68,14 +68,31 @@ public void Tick() Connection.Update(); } - public void Chat(string message) + public void Chat(string message, bool forced) { + if (!forced && !_chat) + return; byte[] content = Encoding.Unicode.GetBytes(message + "\0"); BinaryWriter chat = GamePacketFactory.Create(CtosMessage.Chat); chat.Write(content); Connection.Send(chat); } + public void Log(string message, int type) + { + if(type == 0) + { + Logger.WriteLine(message); + } else if (type == 1) + { + Logger.DebugWriteLine(message); + } + else if (type == 2) + { + Logger.WriteErrorLine(message); + } + } + private void OnPacketReceived(BinaryReader reader) { _behavior.OnPacket(reader); diff --git a/SampleExecutor.sln b/SampleExecutor.sln new file mode 100644 index 00000000..c7e5fa3a --- /dev/null +++ b/SampleExecutor.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.960 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleExecutor", "SampleExecutor\SampleExecutor.csproj", "{612BF3AB-571D-4ADF-AC20-47C20A7E991D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExecutorBase", "ExecutorBase\ExecutorBase.csproj", "{A1583FD7-7985-47DD-A835-8134DBF5811C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {612BF3AB-571D-4ADF-AC20-47C20A7E991D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {612BF3AB-571D-4ADF-AC20-47C20A7E991D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {612BF3AB-571D-4ADF-AC20-47C20A7E991D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {612BF3AB-571D-4ADF-AC20-47C20A7E991D}.Release|Any CPU.Build.0 = Release|Any CPU + {A1583FD7-7985-47DD-A835-8134DBF5811C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1583FD7-7985-47DD-A835-8134DBF5811C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1583FD7-7985-47DD-A835-8134DBF5811C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1583FD7-7985-47DD-A835-8134DBF5811C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {06D9979A-1D52-4DD2-A3A9-4644C7304AAE} + EndGlobalSection +EndGlobal diff --git a/SampleExecutor/SampleExecutor.cs b/SampleExecutor/SampleExecutor.cs new file mode 100644 index 00000000..a13dde9d --- /dev/null +++ b/SampleExecutor/SampleExecutor.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Sample", "AI_Sample")] + public class SampleExecutor : DefaultExecutor + { + public SampleExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + } +} +} \ No newline at end of file diff --git a/SampleExecutor/SampleExecutor.csproj b/SampleExecutor/SampleExecutor.csproj new file mode 100644 index 00000000..14c969b6 --- /dev/null +++ b/SampleExecutor/SampleExecutor.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {612BF3AB-571D-4ADF-AC20-47C20A7E991D} + Library + Properties + SampleExecutor + SampleExecutor + v4.0 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + ..\YGOSharp.OCGWrapper.dll + + + ..\YGOSharp.OCGWrapper.Enums.dll + + + + + + + + + + + {a1583fd7-7985-47dd-a835-8134dbf5811c} + ExecutorBase + + + + \ No newline at end of file diff --git a/WindBot.csproj b/WindBot.csproj index f99877ea..4132f10b 100644 --- a/WindBot.csproj +++ b/WindBot.csproj @@ -59,14 +59,9 @@ - - - - - - - + + @@ -107,33 +102,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -157,6 +128,12 @@ + + + {a1583fd7-7985-47dd-a835-8134dbf5811c} + ExecutorBase + +