diff --git a/Decks/AI_Benkei.ydk b/Decks/AI_Benkei.ydk new file mode 100644 index 00000000..ba3bc7f6 --- /dev/null +++ b/Decks/AI_Benkei.ydk @@ -0,0 +1,74 @@ +#created by M +#main +2563463 +84430950 +84430950 +84430950 +30680659 +30680659 +30680659 +22609617 +22609617 +22609617 +21015833 +14558127 +14558127 +14558127 +3285551 +3285551 +3285551 +14532163 +14532163 +14532163 +18144506 +32807846 +49238328 +49238328 +49238328 +39568067 +14745409 +38745520 +40619825 +40619825 +40619825 +82432018 +82432018 +82432018 +83746708 +83746708 +83746708 +88610708 +88610708 +88610708 +#extra +18386170 +56910167 +27552504 +64276752 +94798725 +75215744 +21293424 +28781003 +12014404 +62709239 +83531441 +26692769 +9205573 +36609518 +58699500 +!side +55063751 +55063751 +55063751 +97268402 +97268402 +97268402 +12580477 +12580477 +12580477 +8267140 +8267140 +8267140 +10045474 +10045474 +10045474 diff --git a/Game/AI/Decks/BenkeiExecutor.cs b/Game/AI/Decks/BenkeiExecutor.cs new file mode 100644 index 00000000..445c1824 --- /dev/null +++ b/Game/AI/Decks/BenkeiExecutor.cs @@ -0,0 +1,290 @@ +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using System.Linq; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Benkei", "AI_Benkei")] + public class BenkeiExecutor : DefaultExecutor + { + public class CardId + { + public const int WanderingGryphonRider = 2563463; + public const int Fateful = 39568067; + public const int Enchantress = 30680659; + public const int Aramesir = 3285551; + public const int AdventurerToken = 3285552; + public const int ReinforcementOfTheArmy = 32807846; + public const int Benkei = 84430950; + public const int PotofExtravagance = 49238328; + public const int Mataza = 22609617; + public const int Hayabusa = 21015833; + public const int LightningStorm = 14532163; + public const int Dracoback = 38745520; + public const int AxeDespair = 40619825; + public const int MaskBrutality = 82432018; + public const int MagePower = 83746708; + public const int BashingShield = 88610708; + public const int Gallatin = 14745409; + public const int Cowboy = 12014404; + } + public BenkeiExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + //Cowboy Burn + AddExecutor(ExecutorType.SpSummon, CardId.Cowboy, CowboySummon); + AddExecutor(ExecutorType.Activate, CardId.Cowboy); + //Counters + AddExecutor(ExecutorType.Activate, CardId.PotofExtravagance, PotActivate); + AddExecutor(ExecutorType.Activate, CardId.WanderingGryphonRider, GryphonEffect); + AddExecutor(ExecutorType.Activate, CardId.LightningStorm, LightningStormActivate); + AddExecutor(ExecutorType.Activate, _CardId.CosmicCyclone, DefaultCosmicCyclone); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, DefaultAshBlossomAndJoyousSpring); + AddExecutor(ExecutorType.Activate, _CardId.EffectVeiler, DefaultEffectVeiler); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, DefaultInfiniteImpermanence); + AddExecutor(ExecutorType.Activate, _CardId.HarpiesFeatherDuster, DefaultHarpiesFeatherDusterFirst); + AddExecutor(ExecutorType.Activate, _CardId.Raigeki, DefaultRaigeki); + AddExecutor(ExecutorType.SpSummon, _CardId.GamecieltheSeaTurtleKaiju, DefaultKaijuSpsummon); + AddExecutor(ExecutorType.Activate, _CardId.EvilswarmExcitonKnight, DefaultEvilswarmExcitonKnightEffect); + AddExecutor(ExecutorType.Activate, CardId.Dracoback, DracobackEffect); + AddExecutor(ExecutorType.Activate, CardId.Dracoback, DracobackEquip); + //OTK Setup + AddExecutor(ExecutorType.Activate, CardId.ReinforcementOfTheArmy, RotaSearch); + AddExecutor(ExecutorType.Summon, CardId.Benkei, WarriorSummon); + AddExecutor(ExecutorType.Summon, CardId.Mataza, WarriorSummon); + AddExecutor(ExecutorType.Summon, CardId.Hayabusa, WarriorSummon); + AddExecutor(ExecutorType.Activate, CardId.MagePower, GenericEquip); + AddExecutor(ExecutorType.Activate, CardId.AxeDespair, GenericEquip); + AddExecutor(ExecutorType.Activate, CardId.MaskBrutality, GenericEquip); + AddExecutor(ExecutorType.Activate, CardId.BashingShield, WarriorEquip); + AddExecutor(ExecutorType.Activate, CardId.Gallatin, WarriorEquip); + //Adventurer Setup + AddExecutor(ExecutorType.Activate, CardId.Aramesir, AdventurerSummon); + AddExecutor(ExecutorType.Activate, CardId.Enchantress, EnchantressEffect); + AddExecutor(ExecutorType.Activate, CardId.Fateful, FatefulEffect); + AddExecutor(ExecutorType.Activate, CardId.WanderingGryphonRider, GryphonSummon); + //End of Main + AddExecutor(ExecutorType.SpellSet, DefaultSpellSet); + AddExecutor(ExecutorType.Repos, DefaultMonsterRepos); + } + public bool EnchantressUsed = false; + + public override void OnNewTurn() + { + EnchantressUsed = false; + } + + public override int OnSelectOption(IList options) + { + return Program.Rand.Next(options.Count); + } + + 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.FaceUpAttack; + if (cardData.Attack <= 499) + return CardPosition.FaceUpDefence; + } + return 0; + } + private bool WarriorSummon() + { + if (Duel.Turn == 1) + return false; + else return true; + } + + private bool GryphonEffect() + { + if (Card.Location == CardLocation.Hand) + return false; + return Duel.LastChainPlayer == 1; + } + private bool GryphonSummon() + { + if (Card.Location != CardLocation.Hand) + return false; + if (Duel.Turn == 1) AI.SelectPosition(CardPosition.FaceUpDefence); + return Bot.HasInMonstersZone(CardId.AdventurerToken) || (Duel.Player == 0 && (Duel.LastChainPlayer == -1 || Bot.HasInSpellZone(CardId.Fateful))); + } + public bool AdventurerSummon() + { + if (Enemy.HasInGraveyard(43534808)) return false; //Token Collector + AI.SelectYesNo(true); + if (Duel.Turn == 1) AI.SelectPosition(CardPosition.FaceUpDefence); + return true; + } + private bool EnchantressEffect() + { + if (Card.Location == CardLocation.Grave) + { + AI.SelectCard(CardLocation.Deck); + EnchantressUsed = true; + return true; + } + if (ActivateDescription == Util.GetStringId(CardId.Enchantress, 0)) + { + // summon + return false; + } + else + { + // search + return !Bot.HasInHand(CardId.Aramesir) && !Bot.HasInMonstersZone(CardId.AdventurerToken); + } + } + private bool FatefulEffect() + { + if (Card.Location == CardLocation.Hand) + return false; + if (ActivateDescription == -1 || ActivateDescription == Util.GetStringId(CardId.Fateful, 1)) + { + // search equip to hand + AI.SelectOption(0); + return true; + } + else + { + // search rider or aquamancer + if (Bot.GetRemainingCount(CardId.WanderingGryphonRider, 1) == 0 || Bot.GetHandCount() == 0 || !Bot.HasInMonstersZone(CardId.AdventurerToken)) + { + AI.SelectCard(CardId.Enchantress); + if (Bot.HasInHandOrInMonstersZoneOrInGraveyard(CardId.Enchantress)) + AI.SelectNextCard(CardId.Enchantress); + } + return true; + } + } + public bool PotActivate() + { + if (SpellNegatable()) return false; + AI.SelectOption(1); + return true; + } + public bool SpellNegatable(bool isCounter = false, ClientCard target = null) + { + // target default set + if (target == null) target = Card; + // won't negate if not on field + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + // negate judge + if (Enemy.HasInMonstersZone(99916754, true) && !isCounter) return true; //Naturia Exterio + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(33198837, true)) return true; //Naturia Beast + if (Enemy.HasInSpellZone(61740673, true) || Enemy.HasInSpellZone(511002996, true)) return true; //Imperial Order + if (Enemy.HasInMonstersZone(37267041, true) || Bot.HasInMonstersZone(37267041, true)) return true; //Silent Swordsman + } + if (target.IsTrap()) + { + if (Enemy.HasInSpellZone(51452091, true) || Bot.HasInSpellZone(51452091, true)) return true; //Royal Decree + if (Enemy.HasInMonstersZone(73551138, true)) return true; //Gishki Emilia + if (Enemy.HasInSpellZone(37209439, true) || Bot.HasInSpellZone(37209439, true)) return true; //Dark Contract + } + // how to get here? + return false; + } + public bool RotaSearch() + { + AI.SelectCard(CardId.Benkei, CardId.Mataza, CardId.Hayabusa); + return true; + } + public bool LightningStormActivate() + { + int bestPower = 0; + foreach (ClientCard hand in Bot.Hand) + { + if (hand.IsMonster() && hand.Level <= 4 && hand.Attack > bestPower) bestPower = hand.Attack; + } + + int opt = -1; + // destroy monster + if (Enemy.MonsterZone.GetFirstMatchingCard(card => card.IsFloodgate() && card.IsAttack()) != null + || Enemy.MonsterZone.GetMatchingCardsCount(card => card.IsAttack() && card.Attack >= bestPower) >= 2) opt = 0; + // destroy spell/trap + else if (Enemy.GetSpellCount() >= 2 || Util.GetProblematicEnemySpell() != null) opt = 1; + + if (opt == -1) return false; + + // only one selection + if (Enemy.MonsterZone.GetFirstMatchingCard(card => card.IsAttack()) == null + || Enemy.GetSpellCount() == 0) + { + AI.SelectOption(0); + return true; + } + AI.SelectOption(opt); + return true; + } + private bool DracobackEffect() + { + if (Card.Location != CardLocation.SpellZone) + return false; + ClientCard target = Util.GetProblematicEnemyCard(); + AI.SelectCard(target); + return true; + } + + private bool DracobackEquip() + { + if (Card.Location == CardLocation.SpellZone) + return false; + if (Card.Location == CardLocation.Grave) + return true; + if (Bot.HasInMonstersZone(CardId.AdventurerToken, faceUp: true)) + { + AI.SelectCard(CardId.AdventurerToken); + return true; + } + return false; + } + private bool GenericEquip() + { + if (Card.Location == CardLocation.SpellZone) + return false; + if (Duel.Turn == 1) + return false; + if (Bot.GetMonsterCount() > 0) + { + AI.SelectCard(CardId.Benkei, CardId.Mataza, CardId.Hayabusa, CardId.AdventurerToken, CardId.Enchantress); + return true; + } + return false; + } + private bool WarriorEquip() + { + if (Card.Location == CardLocation.SpellZone) + return false; + if (Duel.Turn == 1) + return false; + if (Bot.GetMonsterCount() > 0) + { + AI.SelectCard(CardId.Benkei, CardId.Mataza, CardId.Hayabusa); + return true; + } + return false; + } + public override bool OnSelectHand() + { + // Going second deck. + return false; + } + private bool CowboySummon() + { + if (Enemy.LifePoints <= 800 || (Bot.GetMonsterCount() >= 4 && Enemy.LifePoints <= 1600)) + { + AI.SelectPosition(CardPosition.FaceUpDefence); + return true; + } + return false; + } + } +} diff --git a/README.md b/README.md index 2b801311..7ef28f66 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,15 @@ Written in C# targeting .NET Framework 4. Use Visual Studio 2015 or newer. ## Available decks and executors * ABC * Altergeist +* Ben Kei * Blue-Eyes * Blue-Eyes Ritual * Burn * Chain Burn * Cyberse * Dark Magician -* Dragma +* Dogmatika * Dragunity -* Dragun of Red-Eyes * Frog * Gren Maju Stun * Horus @@ -32,6 +32,7 @@ Written in C# targeting .NET Framework 4. Use Visual Studio 2015 or newer. * Qliphort * R5NK * Rainbow +* Red-Eyes Dark Dragoon * Rose Scrap Synchro * Salamangreat * Sky Striker diff --git a/WindBot.csproj b/WindBot.csproj index ebb904a1..42b17cf1 100644 --- a/WindBot.csproj +++ b/WindBot.csproj @@ -106,6 +106,7 @@ + @@ -170,4 +171,4 @@ --> - \ No newline at end of file + diff --git a/bots.json b/bots.json index ea8077a4..d160e4ee 100644 --- a/bots.json +++ b/bots.json @@ -11,6 +11,12 @@ "difficulty": 3, "masterRules": [ 4, 5 ] }, + { + "name": "Ben Kei", + "deck": "Benkei", + "difficulty": 2, + "masterRules": [ 3, 4, 5 ] + }, { "name": "Blue-Eyes", "deck": "Blue-Eyes", diff --git a/libWindbot.csproj b/libWindbot.csproj index e4585c36..18fe7fdc 100644 --- a/libWindbot.csproj +++ b/libWindbot.csproj @@ -93,6 +93,7 @@ + @@ -145,4 +146,4 @@ if exist %25E4K_OUTPUT%25 rmdir /S /Q %25E4K_OUTPUT%25 --> - \ No newline at end of file +