diff --git a/BotWrapper/bot.conf b/BotWrapper/bot.conf index 7c276289..3e2d153f 100644 --- a/BotWrapper/bot.conf +++ b/BotWrapper/bot.conf @@ -189,11 +189,16 @@ Name=我太帅了 Deck=Brave Dialog=smart.zh-CN 水机百头龙凤凰人勇者卡组。 AI_LV3 SUPPORT_MASTER_RULE_2020 -!玻璃女巫 +!玻璃女巫-魔女术 Name=玻璃女巫 Deck=Witchcraft Dialog=verre.zh-CN 魔女术卡组。 AI_LV3 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 +!玻璃女巫-救祓少女 +Name=玻璃女巫 Deck=Exosister Dialog=verre.zh-CN +救祓少女卡组。 +AI_LV3 SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 + !神数不神-刹帝利 Name=神数不神 Deck=Kashtira Dialog=Zefra.zh-CN 俱舍怒威族卡组。 diff --git a/Decks/AI_Exosister.ydk b/Decks/AI_Exosister.ydk new file mode 100644 index 00000000..1e5c0fac --- /dev/null +++ b/Decks/AI_Exosister.ydk @@ -0,0 +1,60 @@ +#created by ... +#main +37343995 +37343995 +37343995 +16889337 +16889337 +16889337 +16474916 +16474916 +16474916 +67972302 +67972302 +67972302 +79858629 +79858629 +43863925 +43863925 +43863925 +5352328 +5352328 +14558127 +14558127 +14558127 +23434538 +23434538 +23434538 +84211599 +84211599 +4408198 +24224830 +24224830 +77913594 +77913594 +77913594 +197042 +197042 +10045474 +10045474 +77891946 +77891946 +77891946 +#extra +90448279 +59242457 +59242457 +9272381 +42741437 +42741437 +42741437 +78135071 +78135071 +41524885 +41524885 +46772449 +5530780 +58858807 +8728498 +!side +74689476 diff --git a/ExecutorBase/Game/AI/DefaultExecutor.cs b/ExecutorBase/Game/AI/DefaultExecutor.cs index 01a545da..e13d417d 100644 --- a/ExecutorBase/Game/AI/DefaultExecutor.cs +++ b/ExecutorBase/Game/AI/DefaultExecutor.cs @@ -117,6 +117,98 @@ protected class _CardId public const int RoyalDecreel = 51452091; public const int NaturiaBeast = 33198837; public const int AntiSpellFragrance = 58921041; + + public const int VaylantzWorld_ShinraBansho = 49568943; + public const int VaylantzWorld_KonigWissen = 75952542; + public const int DivineArsenalAAZEUS_SkyThunder = 90448279; + public const int LightningStorm = 14532163; + + public const int BelialMarquisOfDarkness = 33655493; + public const int ChirubiméPrincessOfAutumnLeaves = 87294988; + public const int PerformapalBarokuriboh = 19050066; + public const int LabrynthArchfiend = 48745395; + public const int HarpiesPetDragonFearsomeFireBlast = 4991081; + public const int DynaHeroFurHire = 25123713; + public const int Hieracosphinx = 82260502; + public const int SpeedroidPassinglider = 26420373; + public const int TyrOfTheNordicChampions = 2333365; + public const int ValkyrianKnight = 99348756; + public const int Victoria = 75162696; + public const int MadolcheChouxvalier = 75363626; + public const int LadyOfD = 67511500; + public const int MermailAbysslung = 95466842; + public const int HarpiesPetBabyDragon = 6924874; + public const int HandHoldingGenie = 94535485; + public const int GolemDragon = 9666558; + public const int TwilightRoseKnight = 2986553; + public const int PerformapalThunderhino = 70458081; + public const int MiracleFlipper = 131182; + public const int Decoyroid = 25034083; + public const int AltergeistFifinellag = 12977245; + public const int BatterymanD = 55401221; + public const int Watthopper = 61380658; + public const int EgyptianGodSlime = 42166000; + public const int DinowrestlerChimeraTWrextle = 22900219; + public const int DinowrestlerGigaSpinosavate = 58672736; + public const int ScarredWarrior = 45298492; + public const int SharkFortress = 50449881; + public const int HeroicChampionClaivesolish = 97453744; + public const int GhostrickAlucard = 75367227; + public const int DinowrestlerKingTWrextle = 77967790; + + public const int PerformapalMissDirector = 92932860; + public const int AncientWarriorsMasterfulSunMou = 40140448; + public const int AncientWarriorsVirtuousLiuXuan = 40428851; + public const int CommandKnight = 10375182; + public const int HunterOwl = 51962254; + public const int RokketRecharger = 5969957; + public const int EmissaryOfTheOasis = 6103294; + public const int Zuttomozaurus = 24454387; + public const int Otoshidamashi = 14957440; + public const int NaturiaMosquito = 17285476; + public const int RescueACEHydrant = 37617348; + public const int MeizenTheBattleNinja = 11825276; + public const int VindikiteRGenex = 73483491; + public const int PrincessCologne = 75574498; + public const int Number48ShadowLich = 1426714; + public const int PhantomToken = 1426715; + public const int DuelLinkDragonTheDuelDragon = 60025883; + public const int DuelDragonToken = 60025884; + public const int SeleneQueenOfTheMasterMagicians = 45819647; + public const int TheWingedDragonofRaSphereMode = 10000080; + + public const int RockOfTheVanquisher = 28168628; + public const int SpiralDischarge = 29477860; + public const int GaiaTheDragonChampion = 66889139; + public const int CrusadiaVanguard = 55312487; + public const int GladiatorBeastDomitianus = 33652635; + public const int PatricianOfDarkness = 19153634; + public const int DictatorOfD = 66961194; + + public const int NovoxTheSilenforcerDisciple = 25801745; + public const int SilenforcingBarrier = 98477480; + } + + protected class _Setcode + { + public const int Watt = 0xe; + public const int Speedroid = 0x2016; + public const int EarthboundImmortal = 0x1021; + public const int Naturia = 0x2a; + public const int Nordic = 0x42; + public const int Harpie = 0x64; + public const int Madolche = 0x71; + public const int Ghostrick = 0x8d; + public const int OddEyes = 0x99; + public const int Performapal = 0x9f; + public const int BlueEyes = 0xdd; + public const int FurHire = 0x114; + public const int Altergeist = 0x103; + public const int Crusadia = 0x116; + public const int Endymion = 0x12a; + public const int AncientWarriors = 0x137; + public const int RescueACE = 0x18b; + public const int VanquishSoul = 0x195; } protected DefaultExecutor(GameAI ai, Duel duel) @@ -126,6 +218,79 @@ protected DefaultExecutor(GameAI ai, Duel duel) AddExecutor(ExecutorType.Activate, _CardId.SantaClaws); } + protected int lightningStormOption = -1; + + /// + /// Defined: + /// if monster with code as KEY, other monsters with rules as VALUE won't be targeted for attack. + /// + protected Dictionary> DefenderProtectRule = new Dictionary> { + {_CardId.BelialMarquisOfDarkness, defender => defender.IsFaceup()}, + {_CardId.ChirubiméPrincessOfAutumnLeaves, defender => defender.HasRace(CardRace.Plant)}, + {_CardId.PerformapalBarokuriboh, defender => true}, + {_CardId.LabrynthArchfiend, defender => defender.HasRace(CardRace.Fiend) && !defender.IsCode(_CardId.LabrynthArchfiend)}, + {_CardId.HarpiesPetDragonFearsomeFireBlast, defender => defender.Level <= 6 && defender.HasSetcode(_Setcode.Harpie)}, + {_CardId.DynaHeroFurHire, defender => defender.HasSetcode(_Setcode.FurHire)}, + {_CardId.Hieracosphinx, defender => defender.IsFacedown()}, + {_CardId.SpeedroidPassinglider, defender => defender.HasSetcode(_Setcode.Speedroid)}, + {_CardId.TyrOfTheNordicChampions, defender => defender.HasSetcode(_Setcode.Nordic)}, + {_CardId.ValkyrianKnight, defender => defender.HasRace(CardRace.Warrior) && !defender.IsCode(_CardId.ValkyrianKnight)}, + {_CardId.Victoria, defender => defender.HasRace(CardRace.Fairy)}, + {_CardId.MadolcheChouxvalier, defender => defender.HasSetcode(_Setcode.Madolche) && !defender.IsCode(_CardId.MadolcheChouxvalier)}, + {_CardId.LadyOfD, defender => defender.HasRace(CardRace.Dragon)}, + {_CardId.MermailAbysslung, defender => defender.HasAttribute(CardAttribute.Water)}, + {_CardId.HarpiesPetBabyDragon, defender => defender.HasSetcode(_Setcode.Harpie) && !defender.IsCode(_CardId.HarpiesPetBabyDragon)}, + {_CardId.HandHoldingGenie, defender => true}, + {_CardId.GolemDragon, defender => defender.HasRace(CardRace.Dragon)}, + {_CardId.MaraudingCaptain, defender => defender.HasRace(CardRace.Warrior)}, + {_CardId.TwilightRoseKnight, defender => defender.HasRace(CardRace.Plant)}, + {_CardId.PerformapalThunderhino, defender => defender.HasSetcode(_Setcode.Performapal)}, + {_CardId.MiracleFlipper, defender => defender.IsFaceup()}, + {_CardId.Decoyroid, defender => defender.IsFaceup()}, + {_CardId.DupeFrog, defender => true}, + {_CardId.AltergeistFifinellag, defender => defender.HasSetcode(_Setcode.Altergeist)}, + {_CardId.BatterymanD, defender => defender.HasRace(CardRace.Thunder) && !defender.IsCode(_CardId.BatterymanD)}, + {_CardId.Watthopper, defender => defender.HasSetcode(_Setcode.Watt) && defender.IsFaceup()}, + + {_CardId.EgyptianGodSlime, defender => true}, + {_CardId.DinowrestlerChimeraTWrextle, defender => true}, + {_CardId.DinowrestlerGigaSpinosavate, defender => true}, + {_CardId.ScarredWarrior, defender => defender.HasRace(CardRace.Warrior) && defender.IsFaceup()}, + {_CardId.SharkFortress, defender => true}, + {_CardId.HeroicChampionClaivesolish, defender => true}, + {_CardId.GhostrickAlucard, defender => defender.HasSetcode(_Setcode.Ghostrick) || defender.IsFacedown()}, + {_CardId.MekkKnightCrusadiaAstram, defender => true}, + {_CardId.DinowrestlerKingTWrextle, defender => true} + }; + + /// + /// Defined: + /// if monster with KEY on field, and meet VALUE(monster, all monster), it cannot be targeted for attack. + /// + protected Dictionary, bool>> DefenderInvisbleRule = new Dictionary, bool>> { + {_CardId.UltimayaTzolkin, (defender, list) => list.Any(monster => !monster.Equals(defender) && monster.HasType(CardType.Synchro))}, + {_CardId.PerformapalMissDirector, (defender, list) => list.Any(monster => monster.HasSetcode(_Setcode.OddEyes))}, + {_CardId.AncientWarriorsMasterfulSunMou, (defender, list) => list.Any(monster => !monster.Equals(defender) && monster.HasSetcode(_Setcode.AncientWarriors))}, + {_CardId.AncientWarriorsVirtuousLiuXuan, (defender, list) => list.Any(monster => !monster.Equals(defender) && monster.HasSetcode(_Setcode.AncientWarriors))}, + {_CardId.CommandKnight, (defender, list) => list.Any(monster => !monster.Equals(defender))}, + {_CardId.HunterOwl, (defender, list) => list.Any(monster => !monster.Equals(defender) && monster.HasAttribute(CardAttribute.Wind))}, + {_CardId.RokketRecharger, (defender, list) => list.Any(monster => monster.IsExtraCard() && monster.HasAttribute(CardAttribute.Dark))}, + {_CardId.EmissaryOfTheOasis, (defender, list) => list.Any(monster => monster.HasType(CardType.Normal) && monster.Level <= 3)}, + {_CardId.Zuttomozaurus, (defender, list) => list.Any(monster => !monster.Equals(defender) && monster.HasRace(CardRace.Dinosaur))}, + {_CardId.Otoshidamashi, (defender, list) => list.Any(monster => !monster.HasType(CardType.Tuner))}, + {_CardId.NaturiaMosquito, (defender, list) => list.Any(monster => !monster.Equals(defender) && monster.HasSetcode(_Setcode.Naturia))}, + {_CardId.RescueACEHydrant, (defender, list) => list.Any(monster => !monster.IsCode(_CardId.RescueACEHydrant) && monster.HasSetcode(_Setcode.RescueACE))}, + + {_CardId.MeizenTheBattleNinja, (defender, list) => list.Any(monster => monster.IsFacedown())}, + {_CardId.VindikiteRGenex, (defender, list) => true}, + {_CardId.PrincessCologne, (defender, list) => list.Any(monster => !monster.Equals(defender))}, + {_CardId.Number48ShadowLich, (defender, list) => list.Any(monster => monster.IsCode(_CardId.PhantomToken))}, + {_CardId.DuelLinkDragonTheDuelDragon, (defender, list) => list.Any(monster => monster.IsCode(_CardId.DuelDragonToken))}, + {_CardId.SeleneQueenOfTheMasterMagicians, (defender, list) => list.Any(monster => monster.HasSetcode(_Setcode.Endymion))}, + + {_CardId.TheWingedDragonofRaSphereMode, (defender, list) => true} + }; + /// /// Decide which card should the attacker attack. /// @@ -225,7 +390,22 @@ public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender } } - if (Enemy.HasInMonstersZone(_CardId.MekkKnightCrusadiaAstram, true) && !(defender).IsCode(_CardId.MekkKnightCrusadiaAstram)) + if (attacker.EquipCards.Any(equip => equip.IsCode(_CardId.MoonMirrorShield) && !equip.IsDisabled())) + attacker.RealPower = defender.RealPower + 100; + + if (!defender.IsDisabled()) + { + Func, bool> defenderRule = (card, monsterList) => false; + if (DefenderInvisbleRule.TryGetValue(defender.Id, out defenderRule)) + { + if (defenderRule(defender, Enemy.GetMonsters())) 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; if (Enemy.HasInMonstersZone(_CardId.DupeFrog, true) && !(defender).IsCode(_CardId.DupeFrog)) @@ -239,8 +419,11 @@ public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender if (Enemy.GetMonsters().Any(monster => !monster.Equals(defender) && monster.IsCode(_CardId.HamonLordofStrikingThunder) && !monster.IsDisabled() && monster.IsDefense())) return false; + + if (defender.IsCode(_CardId.RescueACEHydrant) && !defender.IsDisabled() && Enemy.GetMonsters().Any(monster => monster.HasSetcode(_Setcode.RescueACE) && !monster.IsCode(_CardId.RescueACEHydrant))) + return false; - if (defender.OwnTargets.Any(card => card.IsCode(_CardId.PhantomKnightsFogBlade) && !card.IsDisabled())) + if (Enemy.HasInSpellZone(_CardId.SilenforcingBarrier, true) && Enemy.HasInMonstersZone(_CardId.NovoxTheSilenforcerDisciple, faceUp: true) && !defender.HasType(CardType.Ritual)) return false; return true; @@ -788,16 +971,24 @@ protected bool DefaultOnBecomeTarget() _CardId.BlackRoseDragon, _CardId.JudgmentDragon, _CardId.TopologicTrisbaena, - _CardId.EvenlyMatched + _CardId.EvenlyMatched, + _CardId.DivineArsenalAAZEUS_SkyThunder }; int[] destroyAllOpponentList = { _CardId.HarpiesFeatherDuster, _CardId.DarkMagicAttack }; + int[] destroyAllOpponentSpellList = + { + _CardId.HarpiesFeatherDuster, + _CardId.DarkMagicAttack + }; if (Util.ChainContainsCard(destroyAllList)) return true; - if (Enemy.HasInSpellZone(destroyAllOpponentList, true)) return true; + if (Enemy.HasInSpellZone(destroyAllOpponentSpellList, true) && Card.Location == CardLocation.SpellZone) return true; + if (lightningStormOption == 0 && Card.Location == CardLocation.MonsterZone && Card.IsAttack()) return true; + if (lightningStormOption == 1 && Card.Location == CardLocation.SpellZone) return true; // TODO: ChainContainsCard(id, player) return false; } @@ -893,12 +1084,11 @@ protected bool DefaultDimensionalBarrier() 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)) + if (!monster.HasType(CardType.Xyz | CardType.Link)) { - nontuner = true; - levels[monster.Level] = levels[monster.Level] + 1; + if (monster.HasType(CardType.Tuner)) tuner = true; + else nontuner = true; + if (!monster.HasType(CardType.Token)) levels[monster.Level] = levels[monster.Level] + 1; } if (monster.IsOneForXyz()) @@ -929,7 +1119,8 @@ protected bool DefaultDimensionalBarrier() } } ClientCard lastchaincard = Util.GetLastChainCard(); - if (Duel.LastChainPlayer == 1 && lastchaincard != null && !lastchaincard.IsDisabled()) + if (Duel.LastChainPlayer == 1 && lastchaincard != null && !lastchaincard.IsDisabled() + && (lastchaincard.HasType(CardType.Spell | CardType.Trap) || lastchaincard.Location == CardLocation.MonsterZone)) { if (lastchaincard.HasType(CardType.Ritual)) { @@ -1177,5 +1368,71 @@ protected bool DefaultHonestEffect() return Util.IsTurn1OrMain2(); } + + /// + /// Always activate + /// + protected bool DefaultVaylantzWorld_ShinraBansho() + { + if (DefaultSpellWillBeNegated()) { + return false; + } + + return true; + } + + /// + /// Select enemy's best monster + /// + protected bool DefaultVaylantzWorld_KonigWissen() + { + if (DefaultSpellWillBeNegated()) { + return false; + } + + List monsters = Enemy.GetMonsters(); + if (monsters.Count == 0) { + return false; + } + + List targetList = new List(); + List floodgateCards = monsters + .Where(card => card?.Data != null && card.IsFloodgate() && card.IsFaceup() && !card.IsShouldNotBeTarget()) + .OrderByDescending(card => card.Attack).ToList(); + List dangerousCards = monsters + .Where(card => card?.Data != null && card.IsMonsterDangerous() && card.IsFaceup() && !card.IsShouldNotBeTarget()) + .OrderByDescending(card => card.Attack).ToList(); + List attackOrderedCards = monsters + .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && card.IsShouldNotBeTarget()) + .OrderByDescending(card => card.Attack).ToList(); + + targetList.AddRange(floodgateCards); + targetList.AddRange(dangerousCards); + targetList.AddRange(attackOrderedCards); + + if (targetList?.Count > 0) + { + AI.SelectCard(targetList); + return true; + } + + return false; + } + + public override void OnReceivingAnnouce(int player, long data) + { + if (player == 1 && data == Util.GetStringId(_CardId.LightningStorm, 0) || data == Util.GetStringId(_CardId.LightningStorm, 1)) + { + lightningStormOption = (int)(data - Util.GetStringId(_CardId.LightningStorm, 0)); + } + + base.OnReceivingAnnouce(player, data); + } + + public override void OnChainEnd() + { + lightningStormOption = -1; + base.OnChainEnd(); + } } } diff --git a/ExecutorBase/Game/AI/Enums/FusionSpell.cs b/ExecutorBase/Game/AI/Enums/FusionSpell.cs index b4859b55..e9def396 100644 --- a/ExecutorBase/Game/AI/Enums/FusionSpell.cs +++ b/ExecutorBase/Game/AI/Enums/FusionSpell.cs @@ -55,5 +55,36 @@ public enum FusionSpell FragmentFusion = 72029628, NecroFusion = 81223446, PredaplantVerteAnaconda = 70369116, + + DreamMirrorofChaos = 98570539, + PlunderPatrollShipshapeShipsShipping = 44227727, + FireFormationIngen = 29143457, + ParametalfoesFusion = 58549532, + ReadyFusion = 63854005, + BrandedinWhite = 34995106, + BrandedinRed = 82738008, + FaceCardFusion = 29062925, + MyutantFusion = 42577802, + MyutantCry = 31855260, + FallenOfAlbaz = 68468459, + GreaterPolymerization = 7614732, + UltimateFusion = 71143015, + BrandedFusion = 44362883, + GhostFusion = 35705817, + WitchcrafterConfusionConfession = 35098357, + BrandedBanishment = 6763530, + DinomorphiaDomain = 26631975, + DinomorphiaFrenzy = 78420796, + SouloftheSupremeKing = 92428405, + InstantContact = 16169772, + ScatterFusion = 40597694, + FavoriteContact = 75047173, + AmazonessSecretArts = 86758746, + DarkWorldAccession = 65956182, + BeetrooperLanding = 13234975, + FusionReproduction = 43331750, + ChimeraFusion = 63136489, + HarmonicSynchroFusion = 7473735, + SouloftheSupremeCelestialKing = 76840111 } } diff --git a/ExecutorBase/Game/AI/Executor.cs b/ExecutorBase/Game/AI/Executor.cs index 7b8b36f8..0e8bf6cd 100644 --- a/ExecutorBase/Game/AI/Executor.cs +++ b/ExecutorBase/Game/AI/Executor.cs @@ -22,6 +22,7 @@ public abstract class Executor protected ExecutorType Type { get; private set; } protected ClientCard Card { get; private set; } protected long ActivateDescription { get; private set; } + protected int CurrentTiming { get; private set; } protected ClientField Bot { get; private set; } protected ClientField Enemy { get; private set; } @@ -97,10 +98,21 @@ public virtual void OnChaining(int player, ClientCard card) // For overriding } + public virtual void OnChainSolved(int chainIndex) + { + // For overriding + } + public virtual void OnChainEnd() { // For overriding } + + public virtual void OnReceivingAnnouce(int player, long data) + { + // For overriding + } + public virtual void OnNewPhase() { // Some AI need do something on new phase @@ -115,6 +127,11 @@ public virtual void OnDraw(int player) // Some AI need do something on draw } + public virtual void OnMove(ClientCard card, int previousControler, int previousLocation, int currentControler, int currentLocation) + { + // Some AI need do something on card's moving + } + public virtual IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) { // For overriding @@ -174,6 +191,23 @@ public virtual void OnSelectChain(IList cards) return; } + 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 bool OnSelectYesNo(long desc) { return true; @@ -236,11 +270,12 @@ public void SetBattle(BattlePhase battle) /// /// Set global variables Type, Card, ActivateDescription for Executor /// - public void SetCard(ExecutorType type, ClientCard card, long description) + public void SetCard(ExecutorType type, ClientCard card, long description, int timing = -1) { Type = type; Card = card; ActivateDescription = description; + CurrentTiming = timing; } /// diff --git a/ExecutorBase/Game/AI/HintMsg.cs b/ExecutorBase/Game/AI/HintMsg.cs index 98e723a4..a781df42 100644 --- a/ExecutorBase/Game/AI/HintMsg.cs +++ b/ExecutorBase/Game/AI/HintMsg.cs @@ -56,6 +56,13 @@ public static class HintMsg DisableZone = 570, ToZone = 571, Counter = 572, - Negate = 575; + Disable = 573, + OperateCard = 574, + Negate = 575, + RITUAL = 1057, + FUSION = 1056, + SYNCHRO = 1063, + XYZ = 1073, + PENDULUM = 1074; } } diff --git a/ExecutorBase/Game/Duel.cs b/ExecutorBase/Game/Duel.cs index 8a4ccaeb..806ebc29 100644 --- a/ExecutorBase/Game/Duel.cs +++ b/ExecutorBase/Game/Duel.cs @@ -18,13 +18,17 @@ public class Duel public BattlePhase BattlePhase { get; set; } public int LastChainPlayer { get; set; } + public CardLocation LastChainLocation { get; set; } public IList CurrentChain { get; set; } public IList ChainTargets { get; set; } + public IList LastChainTargets { get; set; } public IList ChainTargetOnly { get; set; } public int LastSummonPlayer { get; set; } public IList SummoningCards { get; set; } public IList LastSummonedCards { get; set; } public bool MainPhaseEnd { get; set; } + public int SolvingChainIndex { get; set; } + public IList NegatedChainIndexList { get; set; } public Duel() { @@ -32,9 +36,11 @@ public Duel() Fields[0] = new ClientField(); Fields[1] = new ClientField(); LastChainPlayer = -1; + LastChainLocation = 0; MainPhaseEnd = false; CurrentChain = new List(); ChainTargets = new List(); + LastChainTargets = new List(); ChainTargetOnly = new List(); LastSummonPlayer = -1; SummoningCards = new List(); @@ -203,5 +209,16 @@ public int GetLocalPlayer(int player) { return IsFirst ? player : 1 - player; } + + public ClientCard GetCurrentSolvingChainCard() + { + if (SolvingChainIndex == 0 || SolvingChainIndex > CurrentChain.Count) return null; + return CurrentChain[SolvingChainIndex - 1]; + } + + public bool IsCurrentSolvingChainNegated() + { + return SolvingChainIndex > 0 && NegatedChainIndexList.Contains(SolvingChainIndex); + } } } diff --git a/ExecutorBase/Game/GameAI.cs b/ExecutorBase/Game/GameAI.cs index 267d8def..52d39a8d 100644 --- a/ExecutorBase/Game/GameAI.cs +++ b/ExecutorBase/Game/GameAI.cs @@ -120,6 +120,11 @@ public void OnNewPhase() Executor.OnNewPhase(); } + public void OnMove(ClientCard card, int previousControler, int previousLocation, int currentControler, int currentLocation) + { + Executor.OnMove(card, previousControler, previousLocation, currentControler, currentLocation); + } + /// /// Called when the AI got attack directly. /// @@ -148,6 +153,16 @@ public void OnChainEnd() Executor.OnChainEnd(); } + /// + /// Called when receiving annouce + /// + /// Player who announce. + /// Annouced info. + public void OnReceivingAnnouce(int player, long data) + { + Executor.OnReceivingAnnouce(player, data); + } + /// /// Called when the AI has to do something during the battle phase. /// diff --git a/Game/AI/Decks/ExosisterExecutor.cs b/Game/AI/Decks/ExosisterExecutor.cs new file mode 100644 index 00000000..587b8db5 --- /dev/null +++ b/Game/AI/Decks/ExosisterExecutor.cs @@ -0,0 +1,2992 @@ +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; +using System.Linq; +using System; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Exosister", "AI_Exosister")] + + class ExosisterExecutor : DefaultExecutor + { + public class CardId + { + public const int ExosisterElis = 16474916; + public const int ExosisterStella = 43863925; + public const int ExosisterIrene = 79858629; + public const int ExosisterSophia = 5352328; + public const int ExosisterMartha = 37343995; + public const int Aratama = 16889337; + public const int Sakitama = 67972302; + // _CardId.MaxxC = 23434538; + // _CardId.AshBlossom = 14558127; + + public const int ExosisterPax = 77913594; + public const int ExosisterArment = 4408198; + public const int PotofExtravagance = 84211599; + // _CardId.CalledByTheGrave = 24224830; + + public const int ExosisterVadis = 77891946; + public const int ExosisterReturnia = 197042; + // _CardId.InfiniteImpermanence = 10045474; + + public const int ExosisterMikailis = 42741437; + public const int ExosisterKaspitell = 78135071; + public const int ExosisterGibrine = 5530780; + public const int ExosisterAsophiel = 41524885; + public const int ExosistersMagnifica = 59242457; + public const int TellarknightConstellarCaduceus = 58858807; + public const int StellarknightConstellarDiamond = 9272381; + public const int DivineArsenalAAZEUS_SkyThunder = 90448279; + public const int DonnerDaggerFurHire = 8728498; + // _CardId.EvilswarmExcitonKnight = 46772449; + + public const int NaturalExterio = 99916754; + public const int NaturalBeast = 33198837; + public const int ImperialOrder = 61740673; + public const int SwordsmanLV7 = 37267041; + public const int RoyalDecree = 51452091; + public const int Number41BagooskatheTerriblyTiredTapir = 90590303; + public const int InspectorBoarder = 15397015; + public const int DimensionShifter = 91800273; + } + + public ExosisterExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + // trigger + AddExecutor(ExecutorType.Activate, CardId.ExosistersMagnifica, ExosistersMagnificaActivateTrigger); + + // quick effect + AddExecutor(ExecutorType.Activate, CardId.ExosisterMikailis, ExosisterMikailisActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosistersMagnifica, ExosistersMagnificaActivateBanish); + AddExecutor(ExecutorType.Activate, CardId.ExosisterReturnia, ExosisterReturniaActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterVadis, ExosisterVadisActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, CardId.StellarknightConstellarDiamond); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + AddExecutor(ExecutorType.Activate, _CardId.CalledByTheGrave, CalledbytheGraveActivate); + AddExecutor(ExecutorType.Activate, DefaultExosisterTransform); + AddExecutor(ExecutorType.Activate, CardId.ExosisterArment, ExosisterArmentActivate); + + // free chain + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + + // search + AddExecutor(ExecutorType.Activate, CardId.PotofExtravagance, PotofExtravaganceActivate); + + // field effect + AddExecutor(ExecutorType.Activate, CardId.Aratama); + AddExecutor(ExecutorType.Activate, CardId.DonnerDaggerFurHire, DonnerDaggerFurHireActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterKaspitell, ExosisterKaspitellActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterGibrine, ExosisterGibrineActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterAsophiel, ExosisterAsophielActivate); + + AddExecutor(ExecutorType.Activate, CardId.ExosisterSophia, ExosisterSophiaActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterIrene, ExosisterIreneActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterStella, ExosisterStellaActivate); + + // addition monster summmon + AddExecutor(ExecutorType.Activate, CardId.ExosisterElis, ExosisterElisActivate); + AddExecutor(ExecutorType.Activate, CardId.Sakitama, SakitamaActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterPax, ExosisterPaxActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterStella, ExosisterStellaSecondActivate); + + // xyz summon + AddExecutor(ExecutorType.SpSummon, CardId.StellarknightConstellarDiamond); + AddExecutor(ExecutorType.SpSummon, CardId.DonnerDaggerFurHire, DonnerDaggerFurHireSpSummonCheck); + AddExecutor(ExecutorType.SpSummon, CardId.ExosisterMikailis, ExosisterMikailisAdvancedSpSummonCheck); + AddExecutor(ExecutorType.SpSummon, CardId.ExosisterKaspitell, ExosisterKaspitellAdvancedSpSummonCheck); + + AddExecutor(ExecutorType.SpSummon, CardId.ExosisterKaspitell, ExosisterKaspitellSpSummonCheck); + AddExecutor(ExecutorType.SpSummon, CardId.ExosisterMikailis, ExosisterMikailisSpSummonCheck); + AddExecutor(ExecutorType.SpSummon, CardId.TellarknightConstellarCaduceus, TellarknightConstellarCaduceusSpSummonCheck); + + AddExecutor(ExecutorType.SpSummon, CardId.ExosistersMagnifica, ExosistersMagnificaSpSummonCheck); + + AddExecutor(ExecutorType.SpSummon, _CardId.EvilswarmExcitonKnight, DefaultEvilswarmExcitonKnightSummon); + AddExecutor(ExecutorType.Activate, _CardId.EvilswarmExcitonKnight, DefaultEvilswarmExcitonKnightEffect); + + // normal summon for xyz(avoiding MaxxC) + AddExecutor(ExecutorType.Summon, CardId.ExosisterStella, ExosisterAvoidMaxxCSummonCheck); + AddExecutor(ExecutorType.Summon, CardId.ExosisterSophia, ExosisterAvoidMaxxCSummonCheck); + AddExecutor(ExecutorType.Summon, CardId.ExosisterIrene, ExosisterAvoidMaxxCSummonCheck); + AddExecutor(ExecutorType.Summon, CardId.ExosisterElis, ExosisterAvoidMaxxCSummonCheck); + + // activate martha + AddExecutor(ExecutorType.Activate, CardId.ExosisterMartha, ExosisterMarthaActivate); + + // normal summon for xyz + AddExecutor(ExecutorType.Summon, CardId.ExosisterStella, ExosisterStellaSummonCheck); + AddExecutor(ExecutorType.Summon, CardId.Aratama, AratamaSummonCheck); + AddExecutor(ExecutorType.Summon, ExosisterForElisSummonCheck); + AddExecutor(ExecutorType.Summon, ForSakitamaSummonCheck); + AddExecutor(ExecutorType.Summon, CardId.ExosisterIrene, ExosisterIreneSummonCheck); + AddExecutor(ExecutorType.Summon, Level4SummonCheck); + AddExecutor(ExecutorType.Summon, ExosisterForArmentSummonCheck); + AddExecutor(ExecutorType.Summon, ForDonnerSummonCheck); + + AddExecutor(ExecutorType.Activate, CardId.ExosisterPax, ExosisterPaxActivateForEndSearch); + + // other + AddExecutor(ExecutorType.Repos, DefaultMonsterRepos); + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); + } + + const int SetcodeTimeLord = 0x4a; + const int SetcodeShadoll = 0x9d; + const int SetcodeInferoid = 0xbb; + const int SetcodeOrcust = 0x11b; + const int SetcodeExosister = 0x172; + const int SetcodeTearlaments = 0x181; + List SetcodeForDiamond = new List { SetcodeShadoll, SetcodeInferoid, SetcodeTearlaments }; + + List affectGraveCardIdList = new List{ + 71344451, 40975243, 87746184, 70534340, 45906428, 71490127, 3659803, 12071500, 6077601, 11827244, 95238394, 81223446, 40003819, + 72490637, 21011044, 59419719, 14735698, 45410988 + }; + + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.ExosisterElis, CardId.ExosisterStella, CardId.ExosisterMartha, CardId.Aratama, CardId.Sakitama, + _CardId.MaxxC, _CardId.AshBlossom, CardId.ExosisterPax, CardId.ExosisterVadis }}, + {2, new List { CardId.ExosisterIrene, CardId.ExosisterSophia, CardId.PotofExtravagance, _CardId.CalledByTheGrave, + CardId.ExosisterReturnia, _CardId.InfiniteImpermanence }}, + {1, new List { CardId.ExosisterArment }}, + }; + Dictionary ExosisterMentionTable = new Dictionary{ + {CardId.ExosisterElis, CardId.ExosisterStella}, {CardId.ExosisterStella, CardId.ExosisterElis}, + {CardId.ExosisterIrene, CardId.ExosisterSophia}, {CardId.ExosisterSophia, CardId.ExosisterIrene}, + {CardId.ExosisterMartha, CardId.ExosisterElis} + }; + List ExosisterSpellTrapList = new List { CardId.ExosisterPax, CardId.ExosisterArment, CardId.ExosisterVadis, CardId.ExosisterReturnia }; + + Dictionary calledbytheGraveCount = new Dictionary(); + bool enemyActivateMaxxC = false; + bool enemyActivateLockBird = false; + bool enemyMoveGrave = false; + bool paxCallToField = false; + List infiniteImpermanenceList = new List(); + + bool summoned = false; + bool elisEffect1Activated = false; + bool stellaEffect1Activated = false; + bool irenaEffect1Activated = false; + bool sophiaEffect1Activated = false; + bool marthaEffect1Activated = false; + bool mikailisEffect1Activated = false; + bool mikailisEffect3Activated = false; + bool kaspitellEffect1Activated = false; + bool kaspitellEffect3Activated = false; + bool gibrineEffect1Activated = false; + bool gibrineEffect3Activated = false; + bool asophielEffect1Activated = false; + bool asophielEffect3Activated = false; + bool sakitamaEffect1Activated = false; + List exosisterTransformEffectList = new List(); + List oncePerTurnEffectActivatedList = new List(); + List activatedMagnificaList = new List(); + List targetedMagnificaList = new List(); + List transformDestList = new List(); + List spSummonThisTurn = new List(); + bool potActivate = false; + List removeChosenList = new List(); + + /// + /// Shuffle List and return a random-order card list + /// + public List ShuffleCardList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(n + 1); + ClientCard temp = result[index]; + result[index] = result[n]; + result[n] = temp; + } + return result; + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false) + { + List floodagateList = Enemy.GetMonsters().Where(c => c?.Data != null && + c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (floodagateList.Count > 0) + { + floodagateList.Sort(CardContainer.CompareCardAttack); + floodagateList.Reverse(); + return floodagateList[0]; + } + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterDangerous() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (dangerList.Count > 0) + { + dangerList.Sort(CardContainer.CompareCardAttack); + dangerList.Reverse(); + return dangerList[0]; + } + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterInvincible() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (invincibleList.Count > 0) + { + invincibleList.Sort(CardContainer.CompareCardAttack); + invincibleList.Reverse(); + return invincibleList[0]; + } + + if (attack == 0) + attack = Util.GetBestAttack(Bot); + List betterList = Enemy.MonsterZone.GetMonsters() + .Where(card => card.GetDefensePower() >= attack && card.IsAttack() && (!canBeTarget || !card.IsShouldNotBeTarget())).ToList(); + if (betterList.Count > 0) + { + betterList.Sort(CardContainer.CompareCardAttack); + betterList.Reverse(); + return betterList[0]; + } + return null; + } + + public ClientCard GetProblematicEnemyCard(bool canBeTarget = false) + { + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null && !removeChosenList.Contains(c) && + c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (floodagateList.Count > 0) + { + floodagateList.Sort(CardContainer.CompareCardAttack); + floodagateList.Reverse(); + return floodagateList[0]; + } + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !removeChosenList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (problemEnemySpellList.Count > 0) + { + return ShuffleCardList(problemEnemySpellList)[0]; + } + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !removeChosenList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (dangerList.Count > 0 + && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) + { + dangerList.Sort(CardContainer.CompareCardAttack); + dangerList.Reverse(); + return dangerList[0]; + } + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !removeChosenList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (invincibleList.Count > 0) + { + invincibleList.Sort(CardContainer.CompareCardAttack); + invincibleList.Reverse(); + return invincibleList[0]; + } + + List enemyMonsters = Enemy.GetMonsters().Where(c => !removeChosenList.Contains(c)).ToList(); + if (enemyMonsters.Count > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + foreach (ClientCard target in enemyMonsters) + { + if (target.HasType(CardType.Fusion) || target.HasType(CardType.Ritual) || target.HasType(CardType.Synchro) || target.HasType(CardType.Xyz) || (target.HasType(CardType.Link) && target.LinkCount >= 2)) + { + if (!canBeTarget || !(target.IsShouldNotBeTarget() || target.IsShouldNotBeMonsterTarget())) return target; + } + } + } + + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() && !removeChosenList.Contains(c) + && (c.HasType(CardType.Equip) || c.HasType(CardType.Pendulum) || c.HasType(CardType.Field) || c.HasType(CardType.Continuous))) + .ToList(); + if (spells.Count > 0) + { + return ShuffleCardList(spells)[0]; + } + + 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 ShuffleCardList(monsters)[0]; + + return null; + } + + public ClientCard GetBestEnemySpell(bool onlyFaceup = false, bool canBeTarget = false) + { + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (problemEnemySpellList.Count > 0) + { + return ShuffleCardList(problemEnemySpellList)[0]; + } + + List spells = Enemy.GetSpells().Where(card => !(card.IsFaceup() && card.IsCode(_CardId.EvenlyMatched))).ToList(); + + List faceUpList = spells.Where(ecard => ecard.IsFaceup() && + (ecard.HasType(CardType.Continuous) || ecard.HasType(CardType.Field) || ecard.HasType(CardType.Pendulum))).ToList(); + if (faceUpList.Count > 0) + { + return ShuffleCardList(faceUpList)[0]; + } + + if (spells.Count > 0 && !onlyFaceup) + { + return ShuffleCardList(spells)[0]; + } + + return null; + } + + public ClientCard GetBestEnemyCard(bool onlyFaceup = false, bool canBeTarget = false, bool checkGrave = false) + { + ClientCard card = GetBestEnemyMonster(onlyFaceup, canBeTarget); + if (card != null) + { + return card; + } + + card = GetBestEnemySpell(onlyFaceup, canBeTarget); + if (card != null) + { + return card; + } + + if (checkGrave && Enemy.Graveyard.Count > 0) + { + List graveMonsterList = Enemy.Graveyard.GetMatchingCards(c => c.IsMonster()).ToList(); + if (graveMonsterList.Count > 0) + { + graveMonsterList.Sort(CardContainer.CompareCardAttack); + graveMonsterList.Reverse(); + return graveMonsterList[0]; + } + return ShuffleCardList(Enemy.Graveyard.ToList())[0]; + } + + return null; + } + + /// + /// Check remain cards in deck + /// + /// Card's ID + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) + { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + + /// + /// Check negated turn count of id + /// + public int CheckCalledbytheGrave(int id) + { + if (!calledbytheGraveCount.ContainsKey(id)) + { + return 0; + } + return calledbytheGraveCount[id]; + } + + public void CheckEnemyMoveGrave() + { + if (Duel.LastChainPlayer == 1) + { + ClientCard card = Util.GetLastChainCard(); + if (Duel.LastChainLocation == CardLocation.Grave && card.Location == CardLocation.Grave) + { + Logger.DebugWriteLine("===Exosister: enemy activate effect from GY."); + enemyMoveGrave = true; + } + else if (affectGraveCardIdList.Contains(card.Id)) + { + Logger.DebugWriteLine("===Exosister: enemy activate effect that affect GY."); + enemyMoveGrave = true; + } + else + { + foreach (ClientCard targetCard in Duel.LastChainTargets) + { + if (targetCard.Location == CardLocation.Grave) + { + Logger.DebugWriteLine("===Exosister: enemy target cards of GY."); + enemyMoveGrave = true; + break; + } + } + } + } + } + + /// + /// Check exosister's relative card. 0 for error. + /// + public int CheckExosisterMentionCard(int id) + { + if (!ExosisterMentionTable.ContainsKey(id)) + { + return 0; + } + return ExosisterMentionTable[id]; + } + + /// + /// Check whether last chain card should be disabled. + /// + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + if (lastcard.IsMonster() && lastcard.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + return true; + } + + /// + /// Check whether negate opposite's effect and clear flag + /// + public void CheckDeactiveFlag() + { + if (Util.GetLastChainCard() != null && Duel.LastChainPlayer == 1) + { + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + enemyActivateMaxxC = false; + } + if (Util.GetLastChainCard().IsCode(_CardId.LockBird)) + { + enemyActivateLockBird = false; + } + } + } + + /// + /// Check whether opposite use Maxx-C, and thus make less operation. + /// + public bool CheckLessOperation() + { + if (!enemyActivateMaxxC) + { + return false; + } + return CheckAtAdvantage(); + } + + /// + /// Check whether bot is at advantage. + /// + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && Bot.GetMonsters().Any(card => card.IsFaceup())) + { + return true; + } + return false; + } + + /// + /// Check whether bot is in danger and need to summon monster to defense. + /// + public bool CheckInDanger() + { + if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + int totalAtk = 0; + foreach (ClientCard m in Enemy.GetMonsters()) + { + if (m.IsAttack() && !m.Attacked) totalAtk += m.Attack; + } + if (totalAtk >= Bot.LifePoints) return true; + } + return false; + } + + /// + /// Check whether can be used for xyz summon. + /// + public bool CheckAbleForXyz(ClientCard card) + { + return card.IsFaceup() && !card.HasType(CardType.Xyz) && !card.HasType(CardType.Link) && !card.HasType(CardType.Token) && card.Level == 4; + } + + /// + /// Check whether bot can activate martha. + /// + public bool CheckMarthaActivatable() + { + return !marthaEffect1Activated && CheckCalledbytheGrave(CardId.ExosisterMartha) == 0 && CheckRemainInDeck(CardId.ExosisterElis) > 0 + && !Bot.GetMonsters().Any(card => card.IsFacedown() || !card.HasType(CardType.Xyz)); + } + + /// + /// check enemy's dangerous card in grave + /// + public List CheckDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && card.HasSetcode(SetcodeOrcust)).ToList(); + return result; + } + + /// + /// Whether spell or trap will be negate. If so, return true. + /// + /// is counter trap + /// check target + /// + 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(CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(CardId.NaturalBeast, true)) return true; + if (Enemy.HasInSpellZone(CardId.ImperialOrder, true) || Bot.HasInSpellZone(CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap()) + { + if (Enemy.HasInSpellZone(CardId.RoyalDecree, true) || Bot.HasInSpellZone(CardId.RoyalDecree, true)) return true; + } + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceList.Contains(selfSeq)) + { + return true; + } + } + // how to get here? + return false; + } + + /// + /// Check whether'll be negated + /// + /// check whether card itself is disabled. + public bool CheckWhetherNegated(bool disablecheck = true) + { + if (Card.IsSpell() || Card.IsTrap()) + { + if (SpellNegatable()) return true; + } + if (CheckCalledbytheGrave(Card.Id) > 0) + { + return true; + } + if (Card.IsMonster() && Card.Location == CardLocation.MonsterZone && Card.IsDefense()) + { + if (Enemy.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled()) != null + || Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled()) != null) + { + return true; + } + } + if (disablecheck) + { + return Card.IsDisabled(); + } + return false; + } + + /// + /// Select spell/trap's place randomly to avoid InfiniteImpermanence and so on. + /// + /// Card to set(default current card) + /// Whether need to avoid InfiniteImpermanence + /// Whether need to avoid set in this place + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + List list = new List { 0, 1, 2, 3, 4 }; + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(n + 1); + int temp = list[index]; + list[index] = list[n]; + list[n] = temp; + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceList.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + AI.SelectPlace(zone); + return; + }; + } + AI.SelectPlace(0); + } + + public void SelectXyzMaterial(int num = 2, bool needExosister = false) + { + List materialList = Bot.GetMonsters().Where(card => CheckAbleForXyz(card)).ToList(); + if (materialList?.Count() < num) + { + return; + } + if (needExosister && !materialList.Any(card => card.HasSetcode(SetcodeExosister))) + { + return; + } + List selectedList = new List(); + + // if needed, select exosister with less atk first + if (needExosister) + { + List exosisterList = materialList.Where(card => card.HasSetcode(SetcodeExosister)).ToList(); + exosisterList.Sort(CardContainer.CompareCardAttack); + ClientCard firstSelect = exosisterList[0]; + selectedList.Add(firstSelect); + materialList.Remove(firstSelect); + } + + // select non-exosister or effecte used's exosister first + // never use martha first + List sortMaterialList = materialList.Where(card => + (card?.Data != null && !card.HasSetcode(SetcodeExosister)) || (exosisterTransformEffectList.Contains(card.Id) && card.Id != CardId.ExosisterMartha)).ToList(); + sortMaterialList.Sort(CardContainer.CompareCardAttack); + foreach (ClientCard card in sortMaterialList) + { + selectedList.Add(card); + if (selectedList.Count() >= num) + { + AI.SelectMaterials(selectedList); + return; + } + } + + List valuableMaterialList = materialList.Where(card => card.Id == CardId.ExosisterMartha || !exosisterTransformEffectList.Contains(card.Id)).ToList(); + valuableMaterialList.Sort(CardContainer.CompareCardAttack); + foreach (ClientCard card in valuableMaterialList) + { + selectedList.Add(card); + if (selectedList.Count() >= num) + { + AI.SelectMaterials(selectedList); + return; + } + } + } + + public void SelectDetachMaterial(ClientCard activateCard) + { + // TODO + AI.SelectCard(0); + } + + /// + /// go first + /// + public override bool OnSelectHand() + { + return true; + } + + /// + /// check whether enemy activate important card + /// + public override void OnChaining(int player, ClientCard card) + { + if (card == null) return; + + if (player == 1) + { + if (card.IsCode(_CardId.MaxxC) && CheckCalledbytheGrave(_CardId.MaxxC) == 0) + { + enemyActivateMaxxC = true; + } + if (card.IsCode(_CardId.LockBird) && CheckCalledbytheGrave(_CardId.LockBird) == 0) + { + enemyActivateLockBird = true; + } + if (card.IsCode(_CardId.InfiniteImpermanence)) + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == card) + { + infiniteImpermanenceList.Add(4 - i); + break; + } + } + } + if (Duel.LastChainLocation == CardLocation.Grave && card.Location == CardLocation.Grave) + { + Logger.DebugWriteLine("===Exosister: enemy activate effect from GY."); + enemyMoveGrave = true; + } + } + base.OnChaining(player, card); + } + + public override void OnSelectChain(IList cards) + { + int player = Duel.LastChainPlayer; + ClientCard card = Util.GetLastChainCard(); + if (player == 1) + { + if (card != null && card.IsCode(_CardId.CalledByTheGrave)) + { + foreach (ClientCard targetCard in Duel.LastChainTargets) + { + Logger.DebugWriteLine("===Exosister: " + targetCard?.Name + " is targeted by called by the grave."); + calledbytheGraveCount[targetCard.Id] = 2; + } + } + foreach (ClientCard targetCard in Duel.LastChainTargets) + { + if (targetCard.Location == CardLocation.Grave) + { + Logger.DebugWriteLine("===Exosister: enemy target cards of GY."); + enemyMoveGrave = true; + break; + } + } + } + base.OnSelectChain(cards); + } + + /// + /// clear chain information + /// + public override void OnChainEnd() + { + enemyMoveGrave = false; + paxCallToField = false; + potActivate = false; + transformDestList.Clear(); + targetedMagnificaList.Clear(); + if (activatedMagnificaList.Count() > 0) + { + for (int idx = activatedMagnificaList.Count() - 1; idx >= 0; --idx) + { + ClientCard checkTarget = activatedMagnificaList[idx]; + if (checkTarget == null || checkTarget.IsFacedown() || checkTarget.Location != CardLocation.MonsterZone) + { + activatedMagnificaList.RemoveAt(idx); + } + } + } + if (spSummonThisTurn.Count() > 0) + { + for (int idx = spSummonThisTurn.Count() - 1; idx >= 0; --idx) + { + ClientCard checkTarget = spSummonThisTurn[idx]; + if (checkTarget == null || checkTarget.IsFacedown() || checkTarget.Location != CardLocation.MonsterZone) + { + spSummonThisTurn.RemoveAt(idx); + } + } + } + base.OnChainEnd(); + } + + public override void OnNewTurn() + { + enemyActivateMaxxC = false; + enemyActivateLockBird = false; + infiniteImpermanenceList.Clear(); + // CalledbytheGrave refresh + List key_list = calledbytheGraveCount.Keys.ToList(); + foreach (int dic in key_list) + { + if (calledbytheGraveCount[dic] > 1) + { + calledbytheGraveCount[dic] -= 1; + } + } + + summoned = false; + elisEffect1Activated = false; + stellaEffect1Activated = false; + irenaEffect1Activated = false; + sophiaEffect1Activated = false; + marthaEffect1Activated = false; + mikailisEffect1Activated = false; + mikailisEffect3Activated = false; + kaspitellEffect1Activated = false; + kaspitellEffect3Activated = false; + gibrineEffect1Activated = false; + gibrineEffect3Activated = false; + asophielEffect1Activated = false; + asophielEffect3Activated = false; + sakitamaEffect1Activated = false; + exosisterTransformEffectList.Clear(); + oncePerTurnEffectActivatedList.Clear(); + activatedMagnificaList.Clear(); + spSummonThisTurn.Clear(); + } + + /// + /// override for exosister's transform + /// + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + bool beginTransformCheck = false; + // transform for main monster + if (hint == HintMsg.SpSummon && min == 1 && max == 1 && transformDestList.Count() > 0) + { + // check whether for transform + if (cards.All(card => card.Location == CardLocation.Extra && card.Rank == 4 && card.HasSetcode(SetcodeExosister))) + { + beginTransformCheck = true; + } + } + // transform for magnifica + if (hint == HintMsg.ToDeck && min == 1 && max == 1 && transformDestList.Count() > 0) + { + if (cards.All(card => card.Location == CardLocation.Overlay)) + { + beginTransformCheck = true; + } + } + if (beginTransformCheck) + { + for (int idx = 0; idx < transformDestList.Count(); ++idx) + { + int targetId = transformDestList[idx]; + ClientCard targetCard = cards.FirstOrDefault(card => card.IsCode(targetId)); + if (targetCard != null) + { + List result = new List(); + result.Add(targetCard); + transformDestList.RemoveAt(idx); + spSummonThisTurn.AddRange(result); + return Util.CheckSelectCount(result, cards, min, max); + } + } + } + + if (Util.ChainContainsCard(_CardId.EvenlyMatched) && Util.ChainContainPlayer(1) && hint == HintMsg.Remove) + { + int botCount = Bot.GetMonsterCount() + Bot.GetSpellCount(); + int oppositeCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount(); + if (botCount - oppositeCount == min && min == max) + { + Logger.DebugWriteLine("===Exosister: Evenly Matched activated."); + List allBotCards = new List(); + allBotCards.AddRange(Bot.GetMonsters()); + allBotCards.AddRange(Bot.GetSpells()); + List importantList = new List(); + + List magnificaList = allBotCards.Where(card => card.IsCode(CardId.ExosistersMagnifica)).ToList(); + if (magnificaList.Count > 0) + { + allBotCards.RemoveAll(c => magnificaList.Contains(c)); + importantList.AddRange(magnificaList); + } + if (!mikailisEffect1Activated) + { + List mikailisList = allBotCards.Where(card => spSummonThisTurn.Contains(card) + && card.IsCode(CardId.ExosisterMikailis) && card.IsFaceup()).ToList(); + if (mikailisList.Count > 0) + { + allBotCards.RemoveAll(c => mikailisList.Contains(c)); + importantList.AddRange(mikailisList); + } + } + if (!gibrineEffect1Activated) + { + List gibrineList = allBotCards.Where(card => spSummonThisTurn.Contains(card) + && card.IsCode(CardId.ExosisterGibrine) && card.IsFaceup()).ToList(); + if (gibrineList.Count > 0) + { + allBotCards.RemoveAll(c => gibrineList.Contains(c)); + importantList.AddRange(gibrineList); + } + } + if (!oncePerTurnEffectActivatedList.Contains(CardId.ExosisterVadis)) + { + List vadisList = allBotCards.Where(card => card.IsCode(CardId.ExosisterVadis) && card.IsFacedown()).ToList(); + if (vadisList.Count > 0) + { + allBotCards.RemoveAll(c => vadisList.Contains(c)); + importantList.AddRange(vadisList); + } + } + List xyzList = allBotCards.Where(card => card.IsMonster() && card.HasType(CardType.Xyz)).ToList(); + if (xyzList.Count > 0) + { + xyzList.Sort(CardContainer.CompareCardAttack); + xyzList.Reverse(); + allBotCards.RemoveAll(c => xyzList.Contains(c)); + importantList.AddRange(xyzList); + } + List monsterList = allBotCards.Where(card => card.IsMonster()).ToList(); + if (monsterList.Count > 0) + { + monsterList.Sort(CardContainer.CompareCardAttack); + monsterList.Reverse(); + allBotCards.RemoveAll(c => monsterList.Contains(c)); + importantList.AddRange(monsterList); + } + List faceDownList = allBotCards.Where(card => card.IsFacedown()).ToList(); + if (faceDownList.Count > 0) + { + allBotCards.RemoveAll(c => faceDownList.Contains(c)); + importantList.AddRange(ShuffleCardList(faceDownList)); + } + + importantList.Reverse(); + return Util.CheckSelectCount(importantList, cards, min, max); + } + } + + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (Util.IsTurn1OrMain2()) + { + bool turnDefense = false; + if (cardId == CardId.DivineArsenalAAZEUS_SkyThunder || cardId == CardId.ExosistersMagnifica) + { + turnDefense = true; + } + if (!cardData.HasType(CardType.Xyz)) + { + turnDefense = true; + } + if (turnDefense) + { + return CardPosition.FaceUpDefence; + } + } + if (Duel.Player == 1) + { + if (!cardData.HasType(CardType.Xyz) || cardData.Defense >= cardData.Attack || Util.IsOneEnemyBetterThanValue(cardData.Attack, true)) + { + return CardPosition.FaceUpDefence; + } + } + int bestBotAttack = Math.Max(Util.GetBestAttack(Bot), cardData.Attack); + if (Util.IsAllEnemyBetterThanValue(bestBotAttack, true)) + { + return CardPosition.FaceUpDefence; + } + } + return base.OnSelectPosition(cardId, positions); + } + + /// + /// override for magnifica's spsummon + /// + public override bool OnSelectYesNo(int desc) + { + // magnifica spsummon + if (desc == Util.GetStringId(CardId.ExosistersMagnifica, 2)) + { + return true; + } + // pax spsummon + if (desc == Util.GetStringId(CardId.ExosisterPax, 1)) + { + return paxCallToField; + } + + return base.OnSelectYesNo(desc); + } + + /// + /// override for returnia's option + /// + public override int OnSelectOption(IList options) + { + // check retunia + int spSummonOption = -1; + int banishOption = -1; + int doNothingOption = -1; + for (int idx = 0; idx < options.Count(); ++idx) + { + int option = options[idx]; + if (option == Util.GetStringId(CardId.ExosisterReturnia, 0)) + { + spSummonOption = idx; + } + else if (option == Util.GetStringId(CardId.ExosisterReturnia, 1)) + { + banishOption = idx; + } + else if (option == Util.GetStringId(CardId.ExosisterReturnia, 2)) + { + doNothingOption = idx; + } + } + + if (spSummonOption >= 0 || banishOption >= 0 || doNothingOption >= 0) + { + if (spSummonOption < 0 && banishOption < 0) + { + return doNothingOption; + } + if (banishOption >= 0) + { + // banish problem card + ClientCard target = GetProblematicEnemyCard(true); + if (target != null) + { + AI.SelectCard(target); + return banishOption; + } + + // dump banish + target = GetBestEnemyCard(false, false); + if (target != null) + { + AI.SelectCard(target); + return banishOption; + } + } + if (spSummonOption >= 0) + { + // TODO + } + } + + // check pot + int potBanish6Option = -1; + int potBanish3Option = -1; + for (int idx = 0; idx < options.Count(); ++idx) + { + int option = options[idx]; + if (option == Util.GetStringId(CardId.PotofExtravagance, 0)) + { + potBanish3Option = idx; + } + else if (option == Util.GetStringId(CardId.PotofExtravagance, 1)) + { + potBanish6Option = idx; + } + } + if (potBanish3Option >= 0 || potBanish6Option >= 0) + { + if (Bot.ExtraDeck.Count() > 9 && potBanish6Option >= 0) + { + return potBanish6Option; + } + return potBanish3Option; + } + + return base.OnSelectOption(options); + } + + public bool AshBlossomActivate() + { + if (CheckWhetherNegated(true) || !CheckLastChainShouldNegated()) return false; + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + if (CheckAtAdvantage()) + { + return false; + } + } + CheckDeactiveFlag(); + return DefaultAshBlossomAndJoyousSpring(); + } + + public bool MaxxCActivate() + { + if (CheckWhetherNegated(true) || Duel.LastChainPlayer == 0) return false; + return DefaultMaxxC(); + } + + public bool InfiniteImpermanenceActivate() + { + if (CheckWhetherNegated()) return false; + // negate before effect used + foreach (ClientCard m in Enemy.GetMonsters()) + { + if (m.IsMonsterShouldBeDisabledBeforeItUseEffect() && !m.IsDisabled() && Duel.LastChainPlayer != 0) + { + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + AI.SelectCard(m); + return true; + } + } + + ClientCard LastChainCard = Util.GetLastChainCard(); + + // negate spells + if (Card.Location == CardLocation.SpellZone) + { + int this_seq = -1; + int that_seq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) this_seq = i; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) that_seq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) that_seq = i; + } + if ((this_seq * that_seq >= 0 && this_seq + that_seq == 4) + || (Util.IsChainTarget(Card)) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true); + List enemyMonsters = Enemy.GetMonsters(); + CheckDeactiveFlag(); + AI.SelectCard(target); + infiniteImpermanenceList.Add(this_seq); + return true; + } + } + if ((LastChainCard == null || LastChainCard.Controller != 1 || LastChainCard.Location != CardLocation.MonsterZone + || LastChainCard.IsDisabled() || LastChainCard.IsShouldNotBeTarget() || LastChainCard.IsShouldNotBeSpellTrapTarget())) + return false; + // negate monsters + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + if (LastChainCard != null) AI.SelectCard(LastChainCard); + else + { + List enemyMonsters = Enemy.GetMonsters(); + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + foreach (ClientCard card in enemyMonsters) + { + if (card.IsFaceup() && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeSpellTrapTarget()) + { + CheckDeactiveFlag(); + AI.SelectCard(card); + return true; + } + } + } + return true; + } + + public bool CalledbytheGraveActivate() + { + if (CheckWhetherNegated(true)) return false; + if (Duel.LastChainPlayer == 1) + { + // negate + if (Util.GetLastChainCard().IsMonster()) + { + int code = Util.GetLastChainCard().Id; + if (code == 0) return false; + if (CheckCalledbytheGrave(code) > 0) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC) && CheckAtAdvantage()) + { + return false; + } + if (code == CardId.DimensionShifter) + { + return false; + } + if (Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.IsOriginalCode(code)) != null) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(code); + calledbytheGraveCount[code] = 2; + CheckDeactiveFlag(); + return true; + } + } + + // banish target + foreach (ClientCard cards in Enemy.Graveyard) + { + if (Duel.ChainTargets.Contains(cards)) + { + int code = cards.Id; + AI.SelectCard(cards); + calledbytheGraveCount[code] = 2; + return true; + } + } + + // become targets + if (Duel.ChainTargets.Contains(Card)) + { + List enemyMonsters = Enemy.Graveyard.GetMatchingCards(card => card.IsMonster()).ToList(); + if (enemyMonsters.Count > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + int code = enemyMonsters[0].Id; + AI.SelectCard(code); + calledbytheGraveCount[code] = 2; + return true; + } + } + } + + // avoid danger monster in grave + if (Duel.LastChainPlayer == 1) return false; + List targets = CheckDangerousCardinEnemyGrave(true); + if (targets.Count() > 0) + { + int code = targets[0].Id; + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(code); + calledbytheGraveCount[code] = 2; + return true; + } + + return false; + } + + public List GetPotofExtravaganceBanish() + { + List banishList = new List(); + ClientCard aaZeus = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.DivineArsenalAAZEUS_SkyThunder)); + if (aaZeus != null) + { + banishList.Add(aaZeus); + } + + ClientCard diamond = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.StellarknightConstellarDiamond)); + if (diamond != null) + { + banishList.Add(diamond); + } + + ClientCard caduceus = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.TellarknightConstellarCaduceus)); + if (caduceus != null) + { + banishList.Add(caduceus); + } + + ClientCard evilswarm = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(_CardId.EvilswarmExcitonKnight)); + if (evilswarm != null) + { + banishList.Add(evilswarm); + } + + // second asophiel + if (Bot.ExtraDeck.Count(card => card.IsCode(CardId.ExosisterAsophiel)) > 1) + { + ClientCard asophiel2 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterAsophiel)); + banishList.Add(asophiel2); + } + + ClientCard gibrine = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterGibrine)); + if (gibrine != null) + { + banishList.Add(gibrine); + } + + // 6 done + + // third mikailis + if (Bot.ExtraDeck.Count(card => card.IsCode(CardId.ExosisterMikailis)) > 2) + { + ClientCard mikailis3 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterMikailis)); + banishList.Add(mikailis3); + } + + // first asophiel + ClientCard asophiel1 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterAsophiel) && !banishList.Contains(card)); + if (asophiel1 != null) + { + banishList.Add(asophiel1); + } + + ClientCard donner = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.DonnerDaggerFurHire)); + if (donner != null) + { + banishList.Add(donner); + } + + // 9 done + + // second kaspitell + if (Bot.ExtraDeck.Count(card => card.IsCode(CardId.ExosisterKaspitell)) > 1) + { + ClientCard kaspitell = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterKaspitell)); + banishList.Add(kaspitell); + } + + // second magnifica + if (Bot.ExtraDeck.Count(card => card.IsCode(CardId.ExosistersMagnifica)) > 1) + { + ClientCard magnifica2 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosistersMagnifica)); + banishList.Add(magnifica2); + } + + // second mikailis + if (Bot.ExtraDeck.Count(card => card.IsCode(CardId.ExosisterMikailis)) > 1) + { + ClientCard mikailis2 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterMikailis) && !banishList.Contains(card)); + banishList.Add(mikailis2); + } + + // first magnifica + ClientCard magnifica1 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosistersMagnifica) && !banishList.Contains(card)); + if (magnifica1 != null) + { + banishList.Add(magnifica1); + } + + // first kaspitell + ClientCard kaspitell1 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterKaspitell) && !banishList.Contains(card)); + if (kaspitell1 != null) + { + banishList.Add(kaspitell1); + } + + // first mikailis1 + ClientCard mikailis1 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterMikailis) && !banishList.Contains(card)); + if (mikailis1 != null) + { + banishList.Add(mikailis1); + } + + return banishList; + } + + public bool PotofExtravaganceActivate() + { + if (CheckWhetherNegated()) + { + return false; + } + List banishList = GetPotofExtravaganceBanish(); + + List addToHandOrderList = new List(); + + bool marthaActivatable = CheckMarthaActivatable(); + if (marthaActivatable) + { + if (!Bot.HasInHand(CardId.ExosisterMartha)) + { + addToHandOrderList.Add(CardId.ExosisterMartha); + } + if (Bot.HasInHand(CardId.ExosisterMartha) && !Bot.HasInHandOrInSpellZone(_CardId.CalledByTheGrave)) + { + addToHandOrderList.Add(_CardId.CalledByTheGrave); + } + } + int exosisterCount = Bot.Hand.Count(card => card?.Data != null && card.HasSetcode(SetcodeExosister)); + if (!stellaEffect1Activated && CheckCalledbytheGrave(CardId.ExosisterStella) == 0) + { + if (!Bot.HasInHand(CardId.ExosisterStella) && exosisterCount > 0) + { + addToHandOrderList.Add(CardId.ExosisterStella); + } + if (Bot.HasInHand(CardId.ExosisterStella) && exosisterCount == 0) + { + addToHandOrderList.AddRange(new List{ + CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterStella, CardId.ExosisterMartha, CardId.ExosisterElis}); + } + } + if (exosisterCount >= 0 && !Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia)) + { + addToHandOrderList.Add(CardId.ExosisterReturnia); + } + List remainOrderList = new List{ + CardId.Aratama, CardId.Sakitama, _CardId.MaxxC, _CardId.AshBlossom, _CardId.InfiniteImpermanence, + _CardId.CalledByTheGrave, CardId.ExosisterVadis, CardId.ExosisterReturnia, CardId.ExosisterPax + }; + addToHandOrderList.AddRange(remainOrderList); + + AI.SelectCard(banishList); + AI.SelectNextCard(addToHandOrderList); + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + + potActivate = true; + return true; + } + + public bool SakitamaActivate() + { + // summon + if (Card.Location == CardLocation.Hand) + { + // summon for xyz + if (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1) + { + AI.SelectCard(CardId.Aratama, CardId.Sakitama); + sakitamaEffect1Activated = true; + return true; + } + + // summon for summon donner + if (!CheckLessOperation() && Bot.HasInExtra(CardId.DonnerDaggerFurHire) && + !Bot.HasInHand(CardId.ExosisterMartha) || Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia)) + { + List illegalList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz) && card.Level != 4 + && (card.Data == null || !card.HasSetcode(SetcodeExosister))).ToList(); + if (illegalList.Count() > 0) + { + if (illegalList.Count() == 1) + { + List otherMaterialList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz)).ToList(); + otherMaterialList.Sort(CardContainer.CompareCardAttack); + illegalList.AddRange(otherMaterialList); + } + if (illegalList.Count() == 1) + { + Logger.DebugWriteLine("===Exosister: activate sakitama for donner"); + AI.SelectCard(CardId.Aratama, CardId.Sakitama); + sakitamaEffect1Activated = true; + return true; + } + } + } + return false; + } + // add to hand + if (Card.Location == CardLocation.Grave) + { + AI.SelectCard(CardId.Sakitama, CardId.Aratama); + return true; + } + return true; + } + + public bool DonnerDaggerFurHireActivate() + { + if (CheckAtAdvantage() && !Bot.HasInHand(CardId.ExosisterMartha) && !Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia)) + { + return false; + } + + ClientCard targetCard = Util.GetProblematicEnemyMonster(canBeTarget: true); + if (targetCard == null) + { + List enemyMonsters = Enemy.GetMonsters(); + if (enemyMonsters.Count() > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + targetCard = enemyMonsters[0]; + } + } + + if (targetCard != null) + { + AI.SelectCard(Card); + AI.SelectNextCard(targetCard); + return true; + } + + return false; + } + + public bool ExosisterElisActivate() + { + if (ActivateDescription != Util.GetStringId(CardId.ExosisterElis, 0)) + { + return false; + } + + if (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1) + { + elisEffect1Activated = true; + return true; + } + + return false; + } + + public bool ExosisterStellaActivate() + { + return ExosisterStellaActivateInner(true); + } + + public bool ExosisterStellaSecondActivate() + { + return ExosisterStellaActivateInner(false); + } + + public bool ExosisterStellaActivateInner(bool checkMartha = false) + { + if (ActivateDescription != Util.GetStringId(CardId.ExosisterStella, 0) || CheckWhetherNegated(true)) + { + return false; + } + + bool ableToXyz = Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) >= 2; + + if (CheckLessOperation() && ableToXyz) + { + return false; + } + if (checkMartha && Bot.HasInHand(CardId.ExosisterMartha) && ableToXyz + && Bot.Hand.Count(card => card.IsMonster() && card.HasSetcode(CardId.ExosisterMartha)) == 1) + { + return false; + } + + AI.SelectCard(CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterStella, CardId.ExosisterElis); + stellaEffect1Activated = true; + return true; + } + + public bool ExosisterIreneActivate() + { + if (ActivateDescription != Util.GetStringId(CardId.ExosisterIrene, 0) || CheckWhetherNegated(true)) + { + return false; + } + + List shuffleList = new List(); + foreach (int cardId in new List { CardId.ExosisterIrene, CardId.ExosisterSophia, CardId.ExosisterArment }) + { + if (Bot.HasInHand(cardId)) + { + shuffleList.Add(cardId); + } + } + if (elisEffect1Activated || Bot.Hand.Count(card => card.IsCode(CardId.ExosisterElis)) > 1) + { + shuffleList.Add(CardId.ExosisterElis); + } + foreach (int cardId in new List { CardId.ExosisterPax, CardId.ExosisterReturnia, CardId.ExosisterVadis }) + { + if ((oncePerTurnEffectActivatedList.Contains(cardId) && Bot.HasInHand(cardId)) || Bot.Hand.Count(card => card.IsCode(cardId)) > 1) + { + shuffleList.Add(cardId); + } + } + + if (shuffleList.Count() > 0) + { + Logger.DebugWriteLine("===Exosister: irene return " + shuffleList[0]); + AI.SelectCard(shuffleList); + return true; + } + return false; + } + + public bool ExosisterSophiaActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.ExosisterSophia, 0) && !CheckWhetherNegated(true)) + { + sophiaEffect1Activated = true; + return true; + } + return false; + } + + public bool ExosisterMarthaActivate() + { + if (ActivateDescription != Util.GetStringId(CardId.ExosisterMartha, 0)) + { + return false; + } + if (CheckLessOperation() && Bot.GetMonsterCount() > 0) + { + return false; + } + + marthaEffect1Activated = true; + return true; + } + + public bool DefaultExosisterTransform() + { + List canTransformList = new List + { + CardId.ExosisterElis, CardId.ExosisterStella, CardId.ExosisterIrene, CardId.ExosisterSophia, CardId.ExosisterMartha + }; + if (Card.IsDisabled() || !canTransformList.Contains(Card.Id)) + { + return false; + } + List checkTransformCode = new List{ + Util.GetStringId(CardId.ExosisterElis, 1), + Util.GetStringId(CardId.ExosisterStella, 1), + Util.GetStringId(CardId.ExosisterIrene, 1), + Util.GetStringId(CardId.ExosisterSophia, 1), + Util.GetStringId(CardId.ExosisterMartha, 1) + }; + if (!checkTransformCode.Contains(ActivateDescription) && ActivateDescription != -1) + { + return false; + } + + // mikailis + if (!Bot.HasInMonstersZone(CardId.ExosisterMikailis) && !mikailisEffect1Activated && (Duel.Player == 1 || !mikailisEffect3Activated) + && !transformDestList.Contains(CardId.ExosisterMikailis) && Bot.HasInExtra(CardId.ExosisterMikailis)) + { + exosisterTransformEffectList.Add(Card.Id); + transformDestList.Add(CardId.ExosisterMikailis); + return true; + } + + // kaspitell on bot's turn + if (!Bot.HasInMonstersZone(CardId.ExosisterKaspitell) && !kaspitellEffect3Activated && Duel.Player == 0 + && !transformDestList.Contains(CardId.ExosisterKaspitell) && Bot.HasInExtra(CardId.ExosisterKaspitell)) + { + exosisterTransformEffectList.Add(Card.Id); + transformDestList.Add(CardId.ExosisterKaspitell); + return true; + } + + // gibrine + if (!Bot.HasInMonstersZone(CardId.ExosisterGibrine) && !gibrineEffect1Activated + && !transformDestList.Contains(CardId.ExosisterGibrine) && Bot.HasInExtra(CardId.ExosisterGibrine)) + { + exosisterTransformEffectList.Add(Card.Id); + transformDestList.Add(CardId.ExosisterGibrine); + return true; + } + + // asophiel + if (!Bot.HasInMonstersZone(CardId.ExosisterAsophiel) && !asophielEffect1Activated + && !transformDestList.Contains(CardId.ExosisterAsophiel) && Bot.HasInExtra(CardId.ExosisterAsophiel)) + { + exosisterTransformEffectList.Add(Card.Id); + transformDestList.Add(CardId.ExosisterAsophiel); + return true; + } + + // kaspitell on bot's turn + if (!Bot.HasInMonstersZone(CardId.ExosisterKaspitell) && !kaspitellEffect1Activated + && !transformDestList.Contains(CardId.ExosisterKaspitell) && Bot.HasInExtra(CardId.ExosisterKaspitell)) + { + exosisterTransformEffectList.Add(Card.Id); + transformDestList.Add(CardId.ExosisterKaspitell); + return true; + } + + return false; + } + + public bool ExosisterMikailisActivate() + { + // banish + if (ActivateDescription == Util.GetStringId(CardId.ExosisterMikailis, 0)) + { + // activate after search + if (Duel.Player == 0 && !mikailisEffect3Activated && Duel.Phase < DuelPhase.End && !DefaultOnBecomeTarget()) + { + return false; + } + + // banish problem card + ClientCard target = GetProblematicEnemyCard(true); + if (target != null && Duel.LastChainPlayer != 0) + { + removeChosenList.Add(target); + mikailisEffect1Activated = true; + AI.SelectCard(target); + return true; + } + + // banish target + if (Duel.LastChainPlayer == 1) + { + List targetList = Duel.LastChainTargets.Where(card => card.Controller == 1 && + (card.Location == CardLocation.Grave || card.Location == CardLocation.MonsterZone || card.Location == CardLocation.SpellZone || card.Location == CardLocation.FieldZone)).ToList(); + if (targetList.Count() > 0) + { + mikailisEffect1Activated = true; + AI.SelectCard(ShuffleCardList(targetList)); + return true; + } + } + + // dump banish + target = GetBestEnemyCard(false, true, true); + if ((DefaultOnBecomeTarget() && !Util.ChainContainsCard(_CardId.EvenlyMatched)) || Bot.UnderAttack || (Duel.Phase == DuelPhase.End && Duel.LastChainPlayer != 0) + || (Duel.Player == 0 && Bot.GetMonsters().Count(card => card.HasType(CardType.Xyz) && card.Rank == 4 && card.HasSetcode(SetcodeExosister)) == 2 && Duel.LastChainPlayer != 0) + || (Duel.Player == 1 && Enemy.GetMonsterCount() >= 2)) + { + mikailisEffect1Activated = true; + AI.SelectCard(target); + return true; + } + return false; + } + + // search + if (CheckWhetherNegated(true)) + { + return false; + } + + List searchTarget = new List{ + CardId.ExosisterReturnia, + CardId.ExosisterVadis, + CardId.ExosisterPax, + CardId.ExosisterArment + }; + List firstSearchList = new List(); + List lastSearchList = new List(); + foreach (int cardId in searchTarget) + { + if (Bot.HasInHandOrInSpellZone(cardId) || CheckRemainInDeck(cardId) == 0) + { + lastSearchList.Add(cardId); + continue; + } + if (cardId == CardId.ExosisterReturnia && Bot.GetMonsters().Any(card => card.IsFacedown() || !card.HasSetcode(SetcodeExosister))) + { + lastSearchList.Add(cardId); + continue; + } + firstSearchList.Add(cardId); + } + firstSearchList.AddRange(lastSearchList); + + mikailisEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(firstSearchList); + return true; + } + + public bool ExosisterKaspitellActivate() + { + // block spsummon from GY + if (ActivateDescription == Util.GetStringId(CardId.ExosisterKaspitell, 0) || ActivateDescription == -1) + { + if (Enemy.HasInMonstersZone(CardId.InspectorBoarder, true)) + { + return false; + } + kaspitellEffect1Activated = true; + return true; + } + + // search + if (CheckWhetherNegated(true)) + { + return false; + } + + // search martha for activate + if (CheckMarthaActivatable() && CheckRemainInDeck(CardId.ExosisterMartha) > 0 && !Bot.HasInHand(CardId.ExosisterMartha)) + { + kaspitellEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(CardId.ExosisterMartha); + return true; + } + // search sophia for draw + if (!summoned && !sophiaEffect1Activated && CheckCalledbytheGrave(CardId.ExosisterSophia) == 0 && !Bot.HasInHand(CardId.ExosisterSophia) + && (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1 || (Bot.HasInHand(CardId.ExosisterElis) && !elisEffect1Activated))) + { + kaspitellEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(CardId.ExosisterSophia); + return true; + } + // search stella for next xyz + if (!summoned && !Bot.HasInHand(CardId.ExosisterStella) && !stellaEffect1Activated && CheckCalledbytheGrave(CardId.ExosisterStella) == 0 + && CheckRemainInDeck(CardId.ExosisterStella) > 0 && Bot.Hand.Any(card => card?.Data != null && card.IsMonster() && card.HasSetcode(SetcodeExosister))) + { + kaspitellEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(CardId.ExosisterStella); + return true; + } + kaspitellEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(CardId.ExosisterMartha, CardId.ExosisterStella, CardId.ExosisterElis, CardId.ExosisterSophia, CardId.ExosisterIrene); + return true; + } + + public bool ExosisterGibrineActivate() + { + // negate + if (ActivateDescription == Util.GetStringId(CardId.ExosisterGibrine, 0)) + { + if (Duel.Player == 1) + { + ClientCard target = Enemy.MonsterZone.GetShouldBeDisabledBeforeItUseEffectMonster(); + if (target != null) + { + gibrineEffect1Activated = true; + AI.SelectCard(target); + return true; + } + } + + ClientCard LastChainCard = Util.GetLastChainCard(); + if (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.MonsterZone && + !LastChainCard.IsDisabled() && !LastChainCard.IsShouldNotBeTarget() && !LastChainCard.IsShouldNotBeMonsterTarget()) + { + gibrineEffect1Activated = true; + AI.SelectCard(LastChainCard); + return true; + } + + return false; + } + + // gain atk + if (CheckWhetherNegated(true)) + { + return false; + } + gibrineEffect3Activated = true; + SelectDetachMaterial(Card); + return true; + } + + public bool ExosisterAsophielActivate() + { + // block activate from GY + if (ActivateDescription == Util.GetStringId(CardId.ExosisterAsophiel, 0) || ActivateDescription == -1) + { + if (Enemy.HasInMonstersZone(CardId.InspectorBoarder, true)) + { + return false; + } + asophielEffect1Activated = true; + return true; + } + + // return hand + if (CheckWhetherNegated(true)) + { + return false; + } + ClientCard targetCard = Util.GetProblematicEnemyMonster(0, true); + if (targetCard != null) + { + asophielEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(targetCard); + return true; + } + + return false; + } + + public bool ExosistersMagnificaActivateTrigger() + { + // sp summon + if (ActivateDescription == Util.GetStringId(CardId.ExosistersMagnifica, 1)) + { + // return after effect used + if (activatedMagnificaList.Contains(Card)) + { + // return to Mikailis for danger card + if (Card.Overlays.Contains(CardId.ExosisterMikailis) && !mikailisEffect1Activated) + { + ClientCard target = GetProblematicEnemyCard(true); + if (target != null && !Duel.CurrentChain.Any(card => card == Card)) + { + transformDestList.Add(CardId.ExosisterMikailis); + return true; + } + } + + // negate important card + if (Card.Overlays.Contains(CardId.ExosisterGibrine) && !gibrineEffect1Activated) + { + ClientCard target = Enemy.MonsterZone.GetShouldBeDisabledBeforeItUseEffectMonster(); + if (target != null) + { + transformDestList.Add(CardId.ExosisterGibrine); + return true; + } + } + } + + // become target + if ((DefaultOnBecomeTarget() && !Util.ChainContainsCard(_CardId.EvenlyMatched)) || (Duel.CurrentChain.Any(c => c == Card) && Duel.LastChainPlayer != 0)) + { + targetedMagnificaList.Add(Card); + transformDestList.AddRange(new List { CardId.ExosistersMagnifica, CardId.ExosisterMikailis, CardId.ExosisterGibrine, CardId.ExosisterKaspitell, CardId.ExosisterAsophiel }); + return true; + } + } + return false; + } + public bool ExosistersMagnificaActivateBanish() + { + // banish + if (ActivateDescription == Util.GetStringId(CardId.ExosistersMagnifica, 0)) + { + if (CheckWhetherNegated()) + { + return false; + } + // banish problem card + ClientCard target = GetProblematicEnemyCard(); + bool isProblemCard = false; + if (target != null) + { + isProblemCard = true; + Logger.DebugWriteLine("===Exosister: magnifica target 1: " + target?.Name); + } + + // banish target + if (Duel.LastChainPlayer == 1 && target == null) + { + List currentTargetList = Duel.LastChainTargets.Where(card => card.Controller == 1 && + (card.Location == CardLocation.MonsterZone || card.Location == CardLocation.SpellZone || card.Location == CardLocation.FieldZone)).ToList(); + if (currentTargetList.Count() > 0) + { + target = ShuffleCardList(currentTargetList)[0]; + Logger.DebugWriteLine("===Exosister: magnifica target 2: " + target?.Name); + } + } + + // dump banish + if (target == null) + { + target = GetBestEnemyCard(false, false); + bool check1 = !DefaultOnBecomeTarget() || Util.ChainContainsCard(_CardId.EvenlyMatched); + bool check2 = !targetedMagnificaList.Contains(Card); + bool check3 = !Bot.UnderAttack; + bool check4 = Duel.Phase != DuelPhase.End; + bool check5 = Duel.Player == 0 || Enemy.GetMonsterCount() < 2; + Logger.DebugWriteLine("===Exosister: magnifica check flag: " + check1 + " " + check2 + " " + check3 + " " + check4 + " " + check5); + if (check1 && check2 && check3 && check4 && check5) + { + target = null; + } + } + + if (target != null && (Duel.LastChainPlayer != 0 || Util.GetLastChainCard() == Card)) + { + if (isProblemCard) + { + removeChosenList.Add(target); + } + Logger.DebugWriteLine("===Exosister: magnifica target final: " + target?.Name); + activatedMagnificaList.Add(Card); + AI.SelectCard(CardId.ExosisterGibrine, CardId.ExosisterAsophiel, CardId.ExosisterKaspitell, CardId.ExosisterMikailis); + AI.SelectNextCard(target); + return true; + } + + return false; + } + return false; + } + + public bool ExosisterPaxActivate() + { + if (potActivate || Bot.LifePoints <= 800) + { + return false; + } + + List checkListForSpSummon = new List{ + CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterStella, CardId.ExosisterMartha, CardId.ExosisterElis + }; + List checkListForSearch = new List{ + CardId.ExosisterMartha, CardId.ExosisterStella, CardId.ExosisterVadis, CardId.ExosisterReturnia, CardId.ExosisterSophia, + CardId.ExosisterIrene, CardId.ExosisterArment, CardId.ExosisterElis + }; + if (Duel.Player == 0 && Duel.LastChainPlayer != 0) + { + // search returnia for banish + if (CheckAtAdvantage() && GetProblematicEnemyCard(true) != null && CheckRemainInDeck(CardId.ExosisterReturnia) > 0 && !Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia)) + { + if (Bot.GetMonsterCount() > 0 && Bot.GetMonsters().All(card => card.IsFaceup() && card.HasSetcode(SetcodeExosister))) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(CardId.ExosisterReturnia); + paxCallToField = false; + return true; + } + } + + // search martha for activate + if (CheckMarthaActivatable() && CheckRemainInDeck(CardId.ExosisterMartha) > 0 && !Bot.HasInHand(CardId.ExosisterMartha)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(CardId.ExosisterMartha); + paxCallToField = false; + return true; + } + + // stella relative + if (!stellaEffect1Activated && CheckCalledbytheGrave(CardId.ExosisterStella) == 0) + { + // try to search stella + if (Bot.Hand.Count(card => card.IsCode(CardId.ExosisterStella)) == 0 && CheckRemainInDeck(CardId.ExosisterStella) > 0) + { + bool shouldSpSummon = !CheckLessOperation() && summoned && Bot.HasInMonstersZoneOrInGraveyard(CardId.ExosisterElis); + if (Bot.Hand.Any(card => card?.Data != null && card.IsMonster() && card.HasSetcode(SetcodeExosister))) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(CardId.ExosisterStella); + paxCallToField = shouldSpSummon; + return true; + } + } + + // search monster for stella to summon + bool searchExosisterMonster = false; + if (Bot.HasInHand(CardId.ExosisterStella) && Bot.Hand.Count(card => card?.Data != null && card.IsMonster() && card.HasSetcode(SetcodeExosister)) == 1) + { + searchExosisterMonster = true; + } + if (Bot.HasInMonstersZone(CardId.ExosisterStella) && Bot.Hand.Count(card => card?.Data != null && card.IsMonster() && card.HasSetcode(SetcodeExosister)) == 0) + { + searchExosisterMonster = true; + } + if (searchExosisterMonster) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterMartha, CardId.ExosisterStella, CardId.ExosisterElis); + paxCallToField = false; + return true; + } + } + + // addition summon + if (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1 && summoned && !CheckLessOperation()) + { + if ((sakitamaEffect1Activated || !Bot.HasInHand(CardId.Sakitama)) + && (stellaEffect1Activated || !Bot.HasInMonstersZone(CardId.ExosisterStella)) + && (elisEffect1Activated || !Bot.HasInHand(CardId.ExosisterElis)) + ) + { + foreach (int checkId in checkListForSpSummon) + { + int checkTarget = CheckExosisterMentionCard(checkId); + if (checkTarget > 0 && Bot.HasInMonstersZoneOrInGraveyard(checkId) && CheckRemainInDeck(checkTarget) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(checkId); + paxCallToField = true; + return true; + } + } + } + } + } + + // in danger + bool inDanger = CheckInDanger(); + + // trigger transform + CheckEnemyMoveGrave(); + + bool forReturnia = false; + if (!oncePerTurnEffectActivatedList.Contains(CardId.ExosisterReturnia) && Bot.HasInSpellZone(CardId.ExosisterReturnia) && Bot.GetMonsters().Count() == 0) + { + forReturnia = true; + } + + // become target + if (enemyMoveGrave || DefaultOnBecomeTarget() || inDanger || forReturnia) + { + List checkList = checkListForSpSummon; + bool shouldSpSummon = enemyMoveGrave || inDanger || forReturnia; + if (!shouldSpSummon && !Bot.HasInMonstersZone(new List{ + CardId.ExosisterElis, CardId.ExosisterStella, CardId.ExosisterIrene, CardId.ExosisterSophia, CardId.ExosisterMartha})) + { + shouldSpSummon = true; + } + if (CheckAtAdvantage() && !enemyMoveGrave) + { + shouldSpSummon = false; + checkList = checkListForSearch; + } + foreach (int checkId in checkList) + { + bool checkSuccessFlag = false; + + if (shouldSpSummon) + { + int checkTarget = CheckExosisterMentionCard(checkId); + checkSuccessFlag = checkTarget > 0 && Bot.HasInMonstersZoneOrInGraveyard(checkTarget) && CheckRemainInDeck(checkId) > 0 + && !exosisterTransformEffectList.Contains(checkId) && !Bot.HasInMonstersZone(checkId); + } + else + { + checkSuccessFlag = !Bot.HasInHandOrHasInMonstersZone(checkId) && !Bot.HasInSpellZone(checkId) && CheckRemainInDeck(checkId) > 0; + } + + if (checkSuccessFlag) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(checkId); + paxCallToField = shouldSpSummon; + return true; + } + } + } + + return false; + } + + public bool ExosisterPaxActivateForEndSearch() + { + if (potActivate || Bot.LifePoints <= 800) + { + return false; + } + + if (Duel.Player == 0 || Duel.Phase >= DuelPhase.End) + { + // search spell/trap + List checkSpellTrapListForSearch = new List{ + CardId.ExosisterVadis, CardId.ExosisterMartha, CardId.ExosisterReturnia, CardId.ExosisterStella, CardId.ExosisterSophia, + CardId.ExosisterIrene, CardId.ExosisterArment, CardId.ExosisterElis + }; + foreach (int checkId in checkSpellTrapListForSearch) + { + if (!Bot.HasInHandOrHasInMonstersZone(checkId) && !Bot.HasInSpellZone(checkId) && CheckRemainInDeck(checkId) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterMartha, CardId.ExosisterStella, CardId.ExosisterElis); + paxCallToField = false; + return true; + } + } + } + return false; + } + + public bool ExosisterArmentActivate() + { + if (Bot.LifePoints <= 800) + { + return false; + } + ClientCard activateTarget = null; + + if (Duel.Player == 0) + { + bool decided = false; + + // addition summon + if (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1 && summoned && !CheckLessOperation()) + { + if ((sakitamaEffect1Activated || !Bot.HasInHand(CardId.Sakitama)) + && (stellaEffect1Activated || !Bot.HasInMonstersZone(CardId.ExosisterStella)) + && (elisEffect1Activated || !Bot.HasInHand(CardId.ExosisterElis)) + ) + { + decided = true; + } + } + + if (Duel.LastChainPlayer == 1) + { + foreach (ClientCard target in Duel.LastChainTargets) + { + if (target.Controller == 0 && target.Location == CardLocation.MonsterZone && target.IsFaceup() && target.HasSetcode(SetcodeExosister)) + { + activateTarget = target; + decided = true; + break; + } + } + } + + if (!decided) + { + return false; + } + } + + if (activateTarget == null && Duel.LastChainPlayer == 1) + { + { + foreach (ClientCard target in Duel.LastChainTargets) + { + if (target.Controller == 0 && target.Location == CardLocation.MonsterZone && target.IsFaceup() && target.HasSetcode(SetcodeExosister)) + { + activateTarget = target; + break; + } + } + } + } + + if (activateTarget == null) + { + List targetList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasSetcode(SetcodeExosister) && !card.HasType(CardType.Xyz)).ToList(); + if (targetList.Count() > 0) + { + targetList.Sort(CardContainer.CompareCardAttack); + activateTarget = targetList[0]; + } + } + + if (activateTarget == null) + { + return false; + } + + // mikailis + if (!Bot.HasInMonstersZone(CardId.ExosisterMikailis) && !mikailisEffect1Activated && (Duel.Player == 1 || !mikailisEffect3Activated) + && !transformDestList.Contains(CardId.ExosisterMikailis) && Bot.HasInExtra(CardId.ExosisterMikailis)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(activateTarget); + transformDestList.Add(CardId.ExosisterMikailis); + return true; + } + + // kaspitell on bot's turn + if (!Bot.HasInMonstersZone(CardId.ExosisterKaspitell) && !kaspitellEffect3Activated && Duel.Player == 0 + && !transformDestList.Contains(CardId.ExosisterKaspitell) && Bot.HasInExtra(CardId.ExosisterKaspitell)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(activateTarget); + transformDestList.Add(CardId.ExosisterKaspitell); + return true; + } + + // gibrine + if (!Bot.HasInMonstersZone(CardId.ExosisterGibrine) && !gibrineEffect1Activated + && !transformDestList.Contains(CardId.ExosisterGibrine) && Bot.HasInExtra(CardId.ExosisterGibrine)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(activateTarget); + transformDestList.Add(CardId.ExosisterGibrine); + return true; + } + + // asophiel + if (!Bot.HasInMonstersZone(CardId.ExosisterAsophiel) && !asophielEffect1Activated + && !transformDestList.Contains(CardId.ExosisterAsophiel) && Bot.HasInExtra(CardId.ExosisterAsophiel)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(activateTarget); + transformDestList.Add(CardId.ExosisterAsophiel); + return true; + } + + // kaspitell on bot's turn + if (!Bot.HasInMonstersZone(CardId.ExosisterKaspitell) && !kaspitellEffect1Activated + && !transformDestList.Contains(CardId.ExosisterKaspitell) && Bot.HasInExtra(CardId.ExosisterKaspitell)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(activateTarget); + transformDestList.Add(CardId.ExosisterKaspitell); + return true; + } + + return false; + } + + public bool ExosisterVadisActivate() + { + if (Bot.LifePoints <= 800) + { + return false; + } + + List checkListForSpSummon = new List{ + CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterStella, CardId.ExosisterMartha, CardId.ExosisterElis + }; + + bool decideToActivate = false; + bool checkTransform = false; + + // special summon for xyz + if (Duel.Player == 0 && Duel.Phase > DuelPhase.Draw && !CheckLessOperation()) + { + decideToActivate = true; + } + + // move grave + CheckEnemyMoveGrave(); + if (enemyMoveGrave) + { + decideToActivate = true; + checkTransform = true; + } + + // for returia + if (!oncePerTurnEffectActivatedList.Contains(CardId.ExosisterReturnia) && Bot.HasInSpellZone(CardId.ExosisterReturnia) && Bot.GetMonsters().Count() == 0) + { + decideToActivate = true; + } + + if (CheckInDanger() || (DefaultOnBecomeTarget() && !Util.ChainContainsCard(_CardId.EvenlyMatched))) + { + decideToActivate = true; + } + + if (decideToActivate) + { + foreach (int checkId in checkListForSpSummon) + { + int checkTarget = CheckExosisterMentionCard(checkId); + if (checkTarget > 0 && CheckRemainInDeck(checkId) > 0 && CheckRemainInDeck(checkTarget) > 0) + { + if (checkTransform) + { + int canTransformCount = 0; + foreach (int transformCheckId in new List { checkId, checkTarget }) + { + if (!Bot.HasInMonstersZone(checkId) && !exosisterTransformEffectList.Contains(checkId)) + { + canTransformCount++; + } + } + + if (canTransformCount == 0) + { + continue; + } + } + oncePerTurnEffectActivatedList.Add(Card.Id); + Logger.DebugWriteLine("Exosiseter Vadis decide: " + checkId); + AI.SelectCard(checkId); + AI.SelectNextCard(checkTarget); + return true; + } + } + } + + return false; + } + + public bool ExosisterReturniaActivate() + { + if (Bot.LifePoints <= 800) + { + return false; + } + + // banish problem card + ClientCard target = GetProblematicEnemyCard(true); + if (target != null && Duel.LastChainPlayer != 0) + { + Logger.DebugWriteLine("===Exosister: returnia target 1: " + target?.Name); + removeChosenList.Add(target); + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(target); + return true; + } + + // banish target + if (Duel.LastChainPlayer == 1) + { + List targetList = Duel.LastChainTargets.Where(card => card.Controller == 1 && + (card.Location == CardLocation.Grave || card.Location == CardLocation.MonsterZone || card.Location == CardLocation.SpellZone || card.Location == CardLocation.FieldZone)).ToList(); + if (targetList.Count() > 0) + { + oncePerTurnEffectActivatedList.Add(Card.Id); + List shuffleTargetList = ShuffleCardList(targetList); + Logger.DebugWriteLine("===Exosister: returnia target 2: " + shuffleTargetList[0]?.Name); + AI.SelectCard(shuffleTargetList); + return true; + } + } + + // dump banish + target = GetBestEnemyCard(false, true, true); + bool check1 = DefaultOnBecomeTarget() && target != null && (target.Location != CardLocation.Onfield || target.Id != _CardId.EvenlyMatched); + bool check2 = Bot.UnderAttack; + bool check3 = (Duel.Player == 1 && Duel.Phase == DuelPhase.End && Duel.LastChainPlayer != 0 && target != null && target.Location != CardLocation.Grave); + bool check4 = (Duel.Player == 1 && Enemy.GetMonsterCount() >= 2 && Duel.LastChainPlayer != 0); + Logger.DebugWriteLine("===Exosister: returnia check flag: " + check1 + " " + check2 + " " + check3 + " " + check4); + if (check1 || check2 || check3 || check4) + { + oncePerTurnEffectActivatedList.Add(Card.Id); + Logger.DebugWriteLine("===Exosister: returnia target 3: " + target?.Name); + AI.SelectCard(target); + return true; + } + + return false; + } + + /// + /// Check hand like exosister + elis + martha + /// + public bool ExosisterAvoidMaxxCSummonCheck() + { + if (!Bot.HasInHand(CardId.ExosisterMartha) || !Bot.HasInHand(CardId.ExosisterElis) || elisEffect1Activated || marthaEffect1Activated) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + // normal summon non-elis exosister + if (Card.Id != CardId.ExosisterElis && Card.Id != CardId.ExosisterMartha) + { + summoned = true; + return true; + } + // normal summon elis + if (Card.IsCode(CardId.ExosisterElis)) + { + int otherExosisterCount = Bot.Hand.Count(card => card?.Data != null && !card.IsCode(CardId.ExosisterElis) && !card.IsCode(CardId.ExosisterMartha) + && card.IsMonster() && card.HasSetcode(SetcodeExosister)); + if (otherExosisterCount > 0) + { + return false; + } + if (Bot.Hand.Count(card => card?.Data != null && card.IsCode(CardId.ExosisterElis)) > 1) + { + summoned = true; + return true; + } + } + + return false; + } + + /// + /// Check hand like exosister + stella + /// + public bool ExosisterStellaSummonCheck() + { + if (stellaEffect1Activated || Bot.HasInMonstersZone(CardId.ExosisterStella, true) || CheckWhetherNegated(true) || CheckLessOperation()) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + + int summonableCount = Bot.Hand.Count(card => card != Card && card?.Data != null && card.IsMonster() + && card.HasSetcode(SetcodeExosister)); + + if (summonableCount > 0) + { + summoned = true; + return true; + } + return false; + } + + /// + /// Check whether need Irene's redraw effect to search elis for xyz + /// + public bool ExosisterIreneSummonCheck() + { + if (irenaEffect1Activated || CheckLessOperation() + || CheckWhetherNegated(true) || CheckCalledbytheGrave(CardId.ExosisterElis) > 0 || CheckCalledbytheGrave(CardId.ExosisterIrene) > 0) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + + if (CheckRemainInDeck(CardId.ExosisterElis) > 0) + { + summoned = true; + return true; + } + return false; + } + + /// + /// Check hand like exosister + elis + /// + public bool ExosisterForElisSummonCheck() + { + if (elisEffect1Activated || CheckCalledbytheGrave(CardId.ExosisterElis) > 0 || CheckLessOperation()) + { + return false; + } + if (Card?.Data == null) + { + return false; + } + if (!Card.HasSetcode(SetcodeExosister) || (Card.IsCode(CardId.ExosisterMartha) && CheckRemainInDeck(CardId.ExosisterElis) > 0)) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + + if (Bot.Hand.Count(card => card != Card && card?.Data != null && card.IsCode(CardId.ExosisterElis)) > 0) + { + summoned = true; + return true; + } + + return false; + } + + public bool AratamaSummonCheck() + { + if (sakitamaEffect1Activated || CheckCalledbytheGrave(CardId.Aratama) > 0 || CheckCalledbytheGrave(CardId.Sakitama) > 0) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + if (CheckRemainInDeck(CardId.Sakitama) > 0) + { + summoned = true; + return true; + } + return false; + } + + public bool ForSakitamaSummonCheck() + { + if (sakitamaEffect1Activated || CheckCalledbytheGrave(CardId.Sakitama) > 0 || CheckLessOperation()) + { + return false; + } + if (Bot.Hand.Count(card => card?.Data != null && Card != card && card.IsCode(CardId.Sakitama)) == 0) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + if (Card?.Data != null && !Card.IsCode(CardId.ExosisterMartha) && Card.Level == 4) + { + summoned = true; + return true; + } + + return false; + } + + public bool Level4SummonCheck() + { + if (Card.Id == CardId.ExosisterMartha) + { + return false; + } + if (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1) + { + summoned = true; + return true; + } + return false; + } + + public bool ForDonnerSummonCheck() + { + if (!Bot.HasInExtra(CardId.DonnerDaggerFurHire) || (!Bot.HasInHand(CardId.ExosisterMartha) && !Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia))) + { + return false; + } + if (CheckLessOperation()) + { + return false; + } + + List illegalList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz) && card.Level != 4 + && (card.Data == null || !card.HasSetcode(SetcodeExosister))).ToList(); + if (illegalList.Count() == 0) + { + return false; + } + + if (illegalList.Count() == 1) + { + List otherMaterialList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz)).ToList(); + otherMaterialList.Sort(CardContainer.CompareCardAttack); + illegalList.AddRange(otherMaterialList); + } + if (illegalList.Count() == 1) + { + List hands = Bot.Hand.Where(card => card?.Data != null && card.IsMonster()).ToList(); + if (hands.Count() > 0) + { + hands.Sort(CardContainer.CompareCardAttack); + if (Card != hands[0]) + { + return false; + } + } + Logger.DebugWriteLine("===Exosister: summon for donner"); + summoned = true; + return true; + } + + return false; + } + + public bool ExosisterForArmentSummonCheck() + { + if (!Bot.HasInHandOrInSpellZone(CardId.ExosisterArment)) + { + return false; + } + if (Card?.Data == null) + { + return false; + } + if (!Card.HasSetcode(SetcodeExosister)) + { + return false; + } + + if (!Bot.GetMonsters().Any(card => card?.Data != null && card.IsFaceup() && card.HasSetcode(SetcodeExosister))) + { + summoned = true; + return true; + } + + return false; + } + + public bool ExosisterMikailisSpSummonCheck() + { + return ExosisterMikailisSpSummonCheckInner(true); + } + + public bool ExosisterMikailisAdvancedSpSummonCheck() + { + if (!CheckLessOperation() || enemyActivateLockBird) + { + return false; + } + + return ExosisterMikailisSpSummonCheckInner(false); + } + + public bool ExosisterMikailisSpSummonCheckInner(bool shouldCheckLessOperation = true) + { + if (Bot.HasInMonstersZone(CardId.ExosisterMikailis) || mikailisEffect3Activated || (CheckLessOperation() && shouldCheckLessOperation)) + { + return false; + } + + // check searched spell/trap + if (!enemyActivateLockBird) + { + foreach (int cardId in ExosisterSpellTrapList) + { + if (!Bot.HasInHandOrInSpellZone(cardId)) + { + SelectXyzMaterial(2); + return true; + } + } + } + + // clear enemy card + if (!mikailisEffect1Activated && !Bot.HasInMonstersZone(CardId.ExosisterMikailis)) + { + ClientCard target = GetProblematicEnemyCard(true); + if (target != null) + { + List exosisterMaterialList = Bot.GetMonsters().Where(card => CheckAbleForXyz(card) && card.HasSetcode(SetcodeExosister)).ToList(); + if (exosisterMaterialList?.Count() > 0) + { + SelectXyzMaterial(2, true); + return true; + } + } + } + + return false; + } + + public bool ExosisterKaspitellSpSummonCheck() + { + return ExosisterKaspitellSpSummonCheckInner(true); + } + + public bool ExosisterKaspitellAdvancedSpSummonCheck() + { + if (!CheckLessOperation() || enemyActivateLockBird) + { + return false; + } + return ExosisterKaspitellSpSummonCheckInner(false); + } + + public bool ExosisterKaspitellSpSummonCheckInner(bool shouldCheckLessOperation = true) + { + if (Bot.HasInMonstersZone(CardId.ExosisterKaspitell) || kaspitellEffect3Activated || (shouldCheckLessOperation && CheckLessOperation())) + { + return false; + } + + bool searchMartha = true; + bool searchStella = true; + bool forMagnifica = false; + if (marthaEffect1Activated || CheckCalledbytheGrave(CardId.ExosisterMartha) > 0 + || CheckRemainInDeck(CardId.ExosisterMartha) == 0 || CheckRemainInDeck(CardId.ExosisterElis) == 0) + { + searchMartha = false; + } + if (Bot.GetMonsters().Any(card => card.HasType(CardType.Link) || card.HasType(CardType.Token))) + { + searchMartha = false; + } + if (stellaEffect1Activated || CheckCalledbytheGrave(CardId.ExosisterStella) > 0 || CheckRemainInDeck(CardId.ExosisterStella) == 0 + || !Bot.Hand.Any(card => card?.Data != null && card.IsMonster() && card.HasSetcode(SetcodeExosister))) + { + searchStella = false; + } + if (Bot.GetMonsters().Count(card => card?.Data != null + && card.HasType(CardType.Xyz) && card.HasType(CardType.Xyz) && !card.IsCode(CardId.ExosistersMagnifica)) == 1) + { + forMagnifica = true; + } + if (enemyActivateLockBird) + { + searchMartha = false; + searchStella = false; + } + + if (!searchMartha && !searchStella && !forMagnifica) + { + return false; + } + + List materialCheckList = Bot.GetMonsters().Where(card => + !card.HasType(CardType.Xyz) && !card.HasType(CardType.Token) && !card.HasType(CardType.Link)).ToList(); + if (materialCheckList.Count() == 2 && materialCheckList.All(card => card.Level == 4)) + { + SelectXyzMaterial(2); + return true; + } + + return false; + } + + public bool ExosistersMagnificaSpSummonCheck() + { + if (CheckLessOperation()) + { + return false; + } + + List materialList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasType(CardType.Xyz) + && card.Rank == 4 && card.HasSetcode(SetcodeExosister)).ToList(); + materialList.Sort(CardContainer.CompareCardAttack); + + AI.SelectMaterials(materialList); + return true; + } + + public bool CheckCaduceusInner(ClientCard card) + { + if (card?.Data == null) + { + return false; + } + foreach (int setcode in SetcodeForDiamond) + { + if (card.HasSetcode(setcode)) + { + return true; + } + } + return false; + } + + public bool TellarknightConstellarCaduceusSpSummonCheck() + { + if (Duel.Turn == 1 || !Bot.HasInExtra(CardId.StellarknightConstellarDiamond)) + { + return false; + } + + // check whether need to call Diamond + if (Enemy.Graveyard.Any(card => CheckCaduceusInner(card))) + { + SelectXyzMaterial(2); + return true; + } + + return false; + } + + public bool DonnerDaggerFurHireSpSummonCheck() + { + if (!Bot.HasInHand(CardId.ExosisterMartha) && !Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia)) + { + return false; + } + + if (CheckLessOperation()) + { + return false; + } + + List illegalList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz) && card.Level != 4 + && (card.Data == null || !card.HasSetcode(SetcodeExosister))).ToList(); + + if (illegalList.Count() == 1) + { + + List otherMaterialList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz)).ToList(); + otherMaterialList.Sort(CardContainer.CompareCardAttack); + illegalList.AddRange(otherMaterialList); + } + if (illegalList.Count() > 1) + { + AI.SelectMaterials(illegalList); + return true; + } + + return false; + } + + public bool SpellSetCheck() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + List onlyOneSetList = new List{ + CardId.ExosisterPax, CardId.ExosisterArment, CardId.ExosisterVadis, CardId.ExosisterReturnia + }; + if (onlyOneSetList.Contains(Card.Id) && Bot.HasInSpellZone(Card.Id)) + { + return false; + } + + // select place + if ((Card.IsTrap() || Card.HasType(CardType.QuickPlay))) + { + List avoid_list = new List(); + int setFornfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoid_list.Add(4 - i); + setFornfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setFornfiniteImpermanence); + return true; + } + else + { + SelectSTPlace(Card, false, avoid_list); + return true; + } + } + else + { + SelectSTPlace(); + } + return true; + } + + return false; + } + } +} diff --git a/Game/AI/Decks/LabrynthExecutor.cs b/Game/AI/Decks/LabrynthExecutor.cs new file mode 100644 index 00000000..548eb159 --- /dev/null +++ b/Game/AI/Decks/LabrynthExecutor.cs @@ -0,0 +1,4368 @@ +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using System.Linq; +using System; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Labrynth", "AI_Labrynth")] + public class LabrynthExecutor : DefaultExecutor + { + public class CardId + { + public const int LadyLabrynthOfTheSilverCastle = 81497285; + public const int LovelyLabrynthOfTheSilverCastle = 2347656; + public const int UnchainedSoulOfSharvara = 41165831; + public const int AriasTheLabrynthButler = 73602965; + public const int ArianeTheLabrynthServant = 75730490; + public const int AriannaTheLabrynthServant = 1225009; + public const int LabrynthChandraglier = 37629703; + // _CardId.AshBlossom = 14558127; + // _CardId.MaxxC = 23434538; + public const int LabrynthStovieTorbie = 74018812; + public const int LabrynthCooclock = 2511; + + public const int PotOfExtravagance = 49238328; + + public const int WelcomeLabrynth = 5380979; + public const int TransactionRollback = 6351147; + // _CardId.InfiniteImpermanence = 10045474; + public const int DestructiveDarumaKarmaCannon = 30748475; + public const int EscapeOfTheUnchained = 53417695; + // _CardId.DimensionalBarrier = 83326048; + public const int BigWelcomeLabrynth = 92714517; + + public const int ChaosAngel = 22850702; + public const int SuperStarslayerTYPHON = 93039339; + public const int UnchainedAbomination = 29479265; + public const int UnchainedSoulOfAnguish = 93084621; + public const int UnchainedSoulLordOfYama = 24269961; + public const int UnchainedSoulOfRage = 67680512; + public const int SPLittleKnight = 29301450; + public const int MuckrakerFromTheUnderworld = 71607202; + public const int RelinquishedAnima = 94259633; + + public const int NaturalExterio = 99916754; + public const int NaturalBeast = 33198837; + public const int ImperialOrder = 61740673; + public const int SwordsmanLV7 = 37267041; + public const int RoyalDecree = 51452091; + public const int Number41BagooskatheTerriblyTiredTapir = 90590303; + public const int InspectorBoarder = 15397015; + public const int SkillDrain = 82732705; + + public const int DimensionShifter = 91800273; + public const int MacroCosmos = 30241314; + public const int DimensionalFissure = 81674782; + public const int BanisheroftheRadiance = 94853057; + public const int BanisheroftheLight = 61528025; + public const int KashtiraAriseHeart = 48626373; + public const int AccesscodeTalker = 86066372; + public const int GhostMournerMoonlitChill = 52038441; + } + + public LabrynthExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + // startup effect/triggered chain + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + AddExecutor(ExecutorType.Activate, CardId.PotOfExtravagance, PotOfExtravaganceActivate); + AddExecutor(ExecutorType.Repos, CardId.LovelyLabrynthOfTheSilverCastle, ReposForLabrynth); + AddExecutor(ExecutorType.Activate, CardId.ChaosAngel, ChaosAngelActivate); + AddExecutor(ExecutorType.Activate, CardId.LovelyLabrynthOfTheSilverCastle, LovelyLabrynthOfTheSilverCastleActivate); + AddExecutor(ExecutorType.Activate, CardId.RelinquishedAnima, RelinquishedAnimaActivate); + AddExecutor(ExecutorType.Activate, CardId.AriannaTheLabrynthServant, AriannaTheLabrynthServantActivate); + AddExecutor(ExecutorType.Activate, CardId.ArianeTheLabrynthServant, ArianeTheLabrynthServantActivate); + AddExecutor(ExecutorType.Activate, CardId.LabrynthChandraglier, RecycleActivate); + AddExecutor(ExecutorType.Activate, CardId.LabrynthStovieTorbie, RecycleActivate); + AddExecutor(ExecutorType.Activate, CardId.LabrynthCooclock, RecycleActivate); + AddExecutor(ExecutorType.Activate, CardId.UnchainedSoulLordOfYama, UnchainedSoulLordOfYamaActivate); + AddExecutor(ExecutorType.Activate, CardId.WelcomeLabrynth, RecycleActivate); + AddExecutor(ExecutorType.Activate, CardId.SuperStarslayerTYPHON, SuperStarslayerTYPHONActivate); + AddExecutor(ExecutorType.Activate, CardId.UnchainedAbomination, UnchainedAbominationActivate); + + // repos + AddExecutor(ExecutorType.Repos, CardId.ArianeTheLabrynthServant, ReposForLabrynth); + AddExecutor(ExecutorType.Repos, CardId.AriannaTheLabrynthServant, ReposForLabrynth); + + // negate/chain + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + AddExecutor(ExecutorType.Activate, CardId.LadyLabrynthOfTheSilverCastle, LadyLabrynthOfTheSilverCastleFieldActivate); + AddExecutor(ExecutorType.Activate, CardId.AriasTheLabrynthButler, RecycleActivate); + AddExecutor(ExecutorType.Activate, CardId.SPLittleKnight, SPLittleKnightActivate); + AddExecutor(ExecutorType.Activate, _CardId.DimensionalBarrier, DimensionalBarrierActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + + AddExecutor(ExecutorType.Activate, CardId.MuckrakerFromTheUnderworld, MuckrakerFromTheUnderworldActivate); + AddExecutor(ExecutorType.Activate, CardId.UnchainedSoulOfRage, UnchainedSoulOfRageActivate); + AddExecutor(ExecutorType.Activate, CardId.TransactionRollback, TransactionRollbackActivate); + AddExecutor(ExecutorType.Activate, CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonActivate); + AddExecutor(ExecutorType.Activate, CardId.EscapeOfTheUnchained, EscapeOfTheUnchainedActivate); + + // sp summon + AddExecutor(ExecutorType.Activate, CardId.LadyLabrynthOfTheSilverCastle, LadyLabrynthOfTheSilverCastleHandActivate); + AddExecutor(ExecutorType.Activate, CardId.BigWelcomeLabrynth, BigWelcomeLabrynthBecomeTargetActivate); + AddExecutor(ExecutorType.Activate, CardId.WelcomeLabrynth, WelcomeLabrynthActivate); + AddExecutor(ExecutorType.Activate, CardId.BigWelcomeLabrynth, BigWelcomeLabrynthActivate); + + // clock + AddExecutor(ExecutorType.Activate, CardId.AriasTheLabrynthButler, AriasTheLabrynthButlerActivate); + AddExecutor(ExecutorType.Activate, CardId.LabrynthCooclock, LabrynthCooclockActivate); + AddExecutor(ExecutorType.Activate, CardId.BigWelcomeLabrynth, BigWelcomeLabrynthGraveActivate); + AddExecutor(ExecutorType.Activate, CardId.UnchainedSoulOfAnguish, UnchainedSoulOfAnguishActivate); + + // summon step + AddExecutor(ExecutorType.SpellSet, SpellSetForCooClockCheck); + AddExecutor(ExecutorType.Summon, CardId.ArianeTheLabrynthServant, ArianeTheLabrynthServantForRollbackSummon); + AddExecutor(ExecutorType.Summon, CardId.AriannaTheLabrynthServant, AriannaTheLabrynthServantSummon); + AddExecutor(ExecutorType.Summon, CardId.ArianeTheLabrynthServant, ArianeTheLabrynthServantSummon); + AddExecutor(ExecutorType.Summon, LabrynthForCooClockSummon); + AddExecutor(ExecutorType.Summon, ForLinkSummon); + AddExecutor(ExecutorType.Summon, ForSynchroSummon); + AddExecutor(ExecutorType.Summon, CardId.LabrynthCooclock, ForAnimaSummon); + + // furniture set + AddExecutor(ExecutorType.Activate, CardId.LabrynthChandraglier, FurnitureSetWelcomeActivate); + AddExecutor(ExecutorType.Activate, CardId.LabrynthStovieTorbie, FurnitureSetWelcomeActivate); + + // sp summon from extra + AddExecutor(ExecutorType.SpSummon, CardId.ChaosAngel, ChaosAngelSpSummonWith2Monster); + AddExecutor(ExecutorType.SpSummon, CardId.RelinquishedAnima, RelinquishedAnimaSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.UnchainedSoulLordOfYama, UnchainedSoulLordOfYamaSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.UnchainedSoulOfAnguish, UnchainedSoulOfAnguishSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.UnchainedSoulOfRage, UnchainedSoulOfRageSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.UnchainedAbomination, UnchainedAbominationSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.SPLittleKnight, SPLittleKnightSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.ChaosAngel, ChaosAngelSpSummonWith3Monster); + AddExecutor(ExecutorType.SpSummon, CardId.MuckrakerFromTheUnderworld, MuckrakerFromTheUnderworldSpSummon); + + // hand eff + AddExecutor(ExecutorType.Activate, CardId.UnchainedSoulOfSharvara, UnchainedSoulOfSharvaraActivate); + + AddExecutor(ExecutorType.SpSummon, CardId.SuperStarslayerTYPHON, SuperStarslayerTYPHONSpSummon); + AddExecutor(ExecutorType.Repos, MonsterRepos); + AddExecutor(ExecutorType.Summon, SummonForTYPHONCheck); + AddExecutor(ExecutorType.SummonOrSet, ForBigWelcomeSummon); + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); + } + + const int SetcodeTimeLord = 0x4a; + const int SetcodePhantom = 0xdb; + const int SetcodeOrcust = 0x11b; + const int SetcodeUnchained = 0x130; + const int SetcodeLabrynth = 0x17e; + const int SetcodeHorus = 0x19d; + const int hintTimingMainEnd = 0x4; + const int hintBattleStart = 0x8; + + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.AriannaTheLabrynthServant, CardId.LabrynthChandraglier, _CardId.AshBlossom, _CardId.MaxxC, + CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, _CardId.InfiniteImpermanence, CardId.BigWelcomeLabrynth }}, + {2, new List { CardId.LadyLabrynthOfTheSilverCastle, CardId.AriasTheLabrynthButler, CardId.PotOfExtravagance, CardId.WelcomeLabrynth, + CardId.TransactionRollback }}, + {1, new List { CardId.LovelyLabrynthOfTheSilverCastle, CardId.UnchainedSoulOfSharvara, CardId.ArianeTheLabrynthServant, + CardId.DestructiveDarumaKarmaCannon, CardId.EscapeOfTheUnchained, _CardId.DimensionalBarrier }} + }; + List notToNegateIdList = new List{ + 58699500, 20343502 + }; + List notToBeTrapTargetList = new List{ + 72144675, 86188410, 41589166, 11443677, 72566043, 1688285, 59071624, 6511113, 48183890, 952523, 22423493, 73639099 + }; + List targetNegateIdList = new List { + _CardId.EffectVeiler, _CardId.InfiniteImpermanence, CardId.GhostMournerMoonlitChill, _CardId.BreakthroughSkill, 74003290, 67037924, + 9753964, 66192538, 23204029, 73445448, 35103106, 30286474, 45002991, 5795980, 38511382, 53742162, 30430448 + }; + List notToDestroySpellTrap = new List { 50005218, 6767771 }; + + Dictionary calledbytheGraveCount = new Dictionary(); + bool enemyActivateMaxxC = false; + List infiniteImpermanenceList = new List(); + bool summoned = false; + List activatedCardIdList = new List(); + List currentNegateMonsterList = new List(); + List currentDestroyCardList = new List(); + List setTrapThisTurn = new List(); + List summonThisTurn = new List(); + List enemySetThisTurn = new List(); + List escapeTargetList = new List(); + List summonInChainList = new List(); + bool cooclockAffected = false; + bool cooclockActivating = false; + bool furnitureActivating = false; + bool dimensionBarrierAnnouncing = false; + int banSpSummonExceptFiendCount = 0; + int dimensionShifterCount = 0; + int enemySpSummonFromExLastTurn = 0; + int enemySpSummonFromExThisTurn = 0; + bool enemyActivateInfiniteImpermanenceFromHand = false; + int rollbackCopyCardId = 0; + List dimensionalBarrierAnnouced = new List(); + List chainSummoningIdList = new List(3); + ClientCard bigwelcomeEscaseTarget = null; + + /// + /// Shuffle List and return a random-order card list + /// + public List ShuffleList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(result.Count); + int nextIndex = (index + Program.Rand.Next(result.Count - 1)) % result.Count; + T tempCard = result[index]; + result[index] = result[nextIndex]; + result[nextIndex] = tempCard; + } + return result; + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + List floodagateList = Enemy.GetMonsters().Where(c => c?.Data != null && + c.IsFloodgate() && c.IsFaceup() + && CheckCanBeTargeted(c, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).OrderByDescending(card => card.Attack).ToList(); + if (floodagateList.Count() > 0) return floodagateList[0]; + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).OrderByDescending(card => card.Attack).ToList(); + if (dangerList.Count() > 0) return dangerList[0]; + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).OrderByDescending(card => card.Attack).ToList(); + if (invincibleList.Count() > 0) return invincibleList[0]; + + List equippedList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.EquipCards.Count() > 0 && CheckCanBeTargeted(c, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).OrderByDescending(card => card.Attack).ToList(); + if (equippedList.Count() > 0) return equippedList[0]; + + List enemyMonsters = Enemy.GetMonsters().OrderByDescending(card => card.Attack).ToList(); + if (enemyMonsters.Count() > 0) + { + foreach (ClientCard target in enemyMonsters) + { + if ((target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2)) + && CheckCanBeTargeted(target, canBeTarget, selfType) && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(target)) + ) return target; + } + } + + if (attack >= 0) + { + if (attack == 0) + attack = Util.GetBestAttack(Bot); + List betterList = Enemy.MonsterZone.GetMonsters() + .Where(card => card.GetDefensePower() >= attack && card.GetDefensePower() > 0 && card.IsAttack() && CheckCanBeTargeted(card, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).OrderByDescending(card => card.Attack).ToList(); + if (betterList.Count() > 0) return betterList[0]; + } + return null; + } + + public List GetProblematicEnemyCardList(bool canBeTarget = false, bool ignoreSpells = false, CardType selfType = 0) + { + List resultList = new List(); + + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (floodagateList.Count() > 0) resultList.AddRange(floodagateList); + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).ToList(); + if (problemEnemySpellList.Count() > 0) resultList.AddRange(ShuffleList(problemEnemySpellList)); + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (dangerList.Count() > 0 + && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) resultList.AddRange(dangerList); + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (invincibleList.Count() > 0) resultList.AddRange(invincibleList); + + List enemyMonsters = Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c)).OrderByDescending(card => card.Attack).ToList(); + if (enemyMonsters.Count() > 0) + { + foreach(ClientCard target in enemyMonsters) + { + if ( (target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2)) + && !resultList.Contains(target) && CheckCanBeTargeted(target, canBeTarget, selfType) + ) + { + resultList.Add(target); + } + } + } + + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() && !currentDestroyCardList.Contains(c) + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous) && CheckCanBeTargeted(c, canBeTarget, selfType) + && !notToDestroySpellTrap.Contains(c.Id)).ToList(); + if (spells.Count() > 0 && !ignoreSpells) resultList.AddRange(ShuffleList(spells)); + + return resultList; + } + + public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + ClientCard card = GetProblematicEnemyMonster(0, canBeTarget, ignoreCurrentDestroy, selfType); + if (card != null) + return card; + + card = Enemy.MonsterZone.Where(c => c?.Data != null && c.HasType(CardType.Monster) && c.IsFaceup() + && CheckCanBeTargeted(c, canBeTarget, selfType) && (!ignoreCurrentDestroy || currentDestroyCardList.Contains(c))) + .OrderByDescending(c => c.Attack).FirstOrDefault(); + if (card != null) + return card; + + List monsters = Enemy.GetMonsters().Where(c => !ignoreCurrentDestroy || currentDestroyCardList.Contains(c)).ToList(); + + // after GetHighestAttackMonster, the left monsters must be face-down. + if (monsters.Count() > 0 && !onlyFaceup) + return ShuffleList(monsters)[0]; + + return null; + } + + /// + /// check enemy's dangerous card in grave + /// + public List GetDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && (card.HasSetcode(SetcodeOrcust) || card.HasSetcode(SetcodePhantom) || card.HasSetcode(SetcodeHorus))).ToList(); + List dangerMonsterIdList = new List{ + 99937011, 63542003, 9411399, 28954097, 30680659 + }; + result.AddRange(Enemy.Graveyard.GetMatchingCards(card => dangerMonsterIdList.Contains(card.Id))); + return result; + } + + public int GetEmptyMainMonsterZoneCount() + { + int remainCount = 0; + for (int idx = 0; idx < 5; ++idx) + { + if (Bot.MonsterZone[idx] == null) remainCount++; + } + return remainCount; + } + + public List GetNormalEnemyTargetList(bool canBeTarget = true, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + List targetList = GetProblematicEnemyCardList(canBeTarget, selfType: selfType); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => + (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && enemySetThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => + (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && !enemySetThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetMonsters().Where(card => card.IsFacedown() && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList())); + + return targetList; + } + + public List GetMonsterListForTargetNegate(bool canBeTarget = false, CardType selfType = 0) + { + List resultList = new List(); + if (CheckWhetherNegated()) + { + return resultList; + } + + // negate before used + ClientCard target = Enemy.MonsterZone.FirstOrDefault(card => card?.Data != null + && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && !card.IsShouldNotBeTarget() + && CheckCanBeTargeted(card, canBeTarget, selfType) + && !currentNegateMonsterList.Contains(card)); + if (target != null) + { + resultList.Add(target); + } + + // negate monster effect on the field + foreach (ClientCard chainingCard in Duel.CurrentChain) + { + if (chainingCard.Location == CardLocation.MonsterZone && chainingCard.Controller == 1 && !chainingCard.IsDisabled() + && CheckCanBeTargeted(chainingCard, canBeTarget, selfType) && !currentNegateMonsterList.Contains(chainingCard)) + { + resultList.Add(chainingCard); + } + } + + return resultList; + } + + public int GetMaterialAttack(List materials) + { + if (Util.IsTurn1OrMain2()) return 0; + int result = 0; + foreach (ClientCard material in materials) + { + if (material.IsAttack() || !summonThisTurn.Contains(material)) result += material.Attack; + } + return result; + } + + public int GetBotCurrentTotalAttack(List exceptList = null) + { + if (Util.IsTurn1OrMain2()) return 0; + int result = 0; + foreach (ClientCard monster in Bot.GetMonsters()) + { + if (exceptList != null && exceptList.Contains(monster)) continue; + if (monster.IsAttack() || !summonThisTurn.Contains(monster)) result += monster.Attack; + } + return result; + } + + public List GetCanBeUsedForLinkMaterial(bool useAdvancedMonster = false, Func exceptRule = null) + { + List materialList = Bot.GetMonsters().Where(card => { + if (card.IsFacedown() || (exceptRule != null && exceptRule(card))) return false; + if (card.IsCode(CardId.MuckrakerFromTheUnderworld) && summonThisTurn.Contains(card)) return false; + if (card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle) && !card.IsDisabled() && Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth)) return false; + if ((card.IsCode(CardId.ChaosAngel) || card.IsCode(CardId.LadyLabrynthOfTheSilverCastle)) + && !useAdvancedMonster && (card.IsAttack() || !summonThisTurn.Contains(card))) return false; + + return true; + }).ToList(); + materialList.Sort(CompareUsableAttack); + return materialList; + } + + public bool CheckCanDirectAttack() + { + return Enemy.GetMonsterCount() == 0 && !activatedCardIdList.Contains(CardId.SPLittleKnight) && Duel.Turn > 1 && Duel.Player == 0 && Duel.Phase < DuelPhase.Main2; + } + + /// + /// Check negated turn count of id + /// + public int CheckCalledbytheGrave(int id) + { + if (!calledbytheGraveCount.ContainsKey(id)) + { + return 0; + } + return calledbytheGraveCount[id]; + } + + public bool CheckCanBeTargeted(ClientCard card, bool canBeTarget, CardType selfType) + { + if (card == null) return true; + if (canBeTarget) + { + if (card.IsShouldNotBeTarget()) return false; + if (((int)selfType & (int)CardType.Monster) > 0 && card.IsShouldNotBeMonsterTarget()) return false; + if (((int)selfType & (int)CardType.Spell) > 0 && card.IsShouldNotBeSpellTrapTarget()) return false; + if (((int)selfType & (int)CardType.Trap) > 0 + && (card.IsShouldNotBeSpellTrapTarget() || (!card.IsDisabled() && notToBeTrapTargetList.Contains(card.Id)))) return false; + } + return true; + } + + /// + /// Check remain cards in deck + /// + /// Card's ID + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + public int CheckRemainInDeck(params int[] ids) + { + int sumResult = 0; + foreach (int id in ids) + { + sumResult += CheckRemainInDeck(id); + } + + return sumResult; + } + + /// + /// Whether spell or trap will be negate. If so, return true. + /// + /// is counter trap + /// check target + /// + public bool CheckSpellWillBeNegate(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(CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(CardId.NaturalBeast, true)) return true; + if (Enemy.HasInSpellZone(CardId.ImperialOrder, true) || Bot.HasInSpellZone(CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap() && (Enemy.HasInSpellZone(CardId.RoyalDecree, true) || Bot.HasInSpellZone(CardId.RoyalDecree, true))) return true; + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceList.Contains(selfSeq)) return true; + } + // how to get here? + return false; + } + + /// + /// Check whether'll be negated + /// + /// check whether card itself is disabled. + public bool CheckWhetherNegated(bool disablecheck = true, bool toFieldCheck = false, CardType type = 0) + { + if ((Card.IsSpell() || Card.IsTrap() || (((int)type & (int)CardType.Spell) == 0) || (((int)type & (int)CardType.Trap) == 0)) && CheckSpellWillBeNegate()) + return true; + if (CheckCalledbytheGrave(Card.Id) > 0) return true; + if ((Card.IsMonster() || (((int)type & (int)CardType.Monster) == 0)) && (toFieldCheck || Card.Location == CardLocation.MonsterZone)) + { + if ((toFieldCheck && (((int)type & (int)CardType.Link) == 0)) || Card.IsDefense()) + { + if (Enemy.MonsterZone.Any(card => CheckNumber41(card)) || Bot.MonsterZone.Any(card => CheckNumber41(card))) return true; + } + if (Enemy.HasInSpellZone(CardId.SkillDrain, true, true)) return true; + } + if (disablecheck) return Card.IsDisabled(); + return false; + } + + public bool CheckNumber41(ClientCard card) + { + return card != null && card.IsFaceup() && card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled(); + } + + /// + /// Check whether cards will be removed. If so, do not send cards to grave. + /// + public bool CheckWhetherWillbeRemoved() + { + if (dimensionShifterCount > 0) return true; + List checkIdList = new List { CardId.BanisheroftheRadiance, CardId.BanisheroftheLight, CardId.MacroCosmos, CardId.DimensionalFissure, + CardId.KashtiraAriseHeart, 58481572 }; + foreach (int cardid in checkIdList) + { + List fields = new List { Bot, Enemy }; + foreach (ClientField cf in fields) + { + if (cf.HasInMonstersZone(cardid, true, false, true) || cf.HasInSpellZone(cardid, true, true)) + { + return true; + } + } + } + return false; + } + + /// + /// Check whether bot is at advantage. + /// + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && (Duel.Player == 0 || Bot.GetMonsterCount() > 0)) return true; + return false; + } + + public bool CheckShouldNoMoreSpSummon(bool isLabrynth = true) + { + if (CheckAtAdvantage() && enemyActivateMaxxC && (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2)) + { + if (!isLabrynth) return true; + if (cooclockAffected) + { + if (Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth))) return true; + if (Duel.Player == 0 && !summoned) return true; + if (setTrapThisTurn.Count() == 0) return true; + return false; + } + return true; + } + return false; + } + + /// + /// Check whether last chain card should be disabled. + /// + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + if (lastcard.IsMonster() && lastcard.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (notToNegateIdList.Contains(lastcard.Id)) return false; + + return true; + } + + public bool CheckChainContainEnemyMaxxC() + { + foreach (ClientCard card in Duel.CurrentChain) + { + if (card.Controller == 1 && card.IsCode(_CardId.MaxxC)) return true; + } + + return false; + } + + public bool CheckBigWelcomeCanSpSummon(int cardId) + { + return Bot.HasInHandOrInGraveyard(cardId) || CheckRemainInDeck(cardId) > 0; + } + + public int CompareUsableAttack(ClientCard cardA, ClientCard cardB) + { + if (cardA == null && cardB == null) + return 0; + if (cardA == null) + return -1; + if (cardB == null) + return 1; + int powerA = (cardA.IsDefense() && summonThisTurn.Contains(cardA)) ? 0 : cardA.Attack; + int powerB = (cardB.IsDefense() && summonThisTurn.Contains(cardB)) ? 0 : cardB.Attack; + if (powerA < powerB) + return -1; + if (powerA == powerB) + return CardContainer.CompareCardLevel(cardA, cardB); + return 1; + } + + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + ClientCard currentSolvingChain = Duel.GetCurrentSolvingChainCard(); + if (currentSolvingChain != null) + { + if (currentSolvingChain.Controller == 1 && currentSolvingChain.IsCode(_CardId.EvenlyMatched)) + { + Logger.DebugWriteLine("=== Evenly Matched activated."); + List banishList = new List(); + List botMonsters = Bot.GetMonsters().Where(card => !card.HasType(CardType.Token)).ToList(); + + // monster + List faceDownMonsters = botMonsters.Where(card => card.IsFacedown()).ToList(); + banishList.AddRange(faceDownMonsters); + List notImportantMonster = botMonsters.Where(card => !banishList.Contains(card) + && ((card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && Bot.HasInExtra(card.Id)) + || CheckRemainInDeck(card.Id) > 0)).ToList(); + notImportantMonster.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(notImportantMonster); + + // spells + List faceUpSpells = Bot.GetSpells().Where(c => c.IsFaceup()).ToList(); + banishList.AddRange(ShuffleList(faceUpSpells)); + List faceDownSpells = Bot.GetSpells().Where(c => c.IsFacedown()).ToList(); + banishList.AddRange(ShuffleList(faceDownSpells)); + + List importantMonster = botMonsters.Where(card => !banishList.Contains(card) && !card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle) + && ((card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && !Bot.HasInExtra(card.Id)) + || CheckRemainInDeck(card.Id) == 0)).ToList(); + importantMonster.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(importantMonster); + + // lovely + List lovelyList = botMonsters.Where(card => !banishList.Contains(card) && card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)).ToList(); + lovelyList.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(lovelyList); + + return Util.CheckSelectCount(banishList, cards, min, max); + } + + if (currentSolvingChain.IsCode(CardId.LadyLabrynthOfTheSilverCastle) && min == 1 && max == 1 && hint == HintMsg.Set) + { + SortedDictionary> trapCheckDict = new SortedDictionary>{ + {_CardId.DimensionalBarrier, DimensionalBarrierActivate}, + {CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonSetCheck}, + {_CardId.InfiniteImpermanence, InfiniteImpermanenceSetCheck} + }; + foreach (KeyValuePair> pair in trapCheckDict) + { + ClientCard target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + SelectSTPlace(null, true); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + + ClientCard rollback = cards.FirstOrDefault(card => card.IsCode(CardId.TransactionRollback)); + if (rollback != null) + { + bool haveUnchainSoul = false; + if (!activatedCardIdList.Contains(CardId.UnchainedSoulOfSharvara)) + { + haveUnchainSoul |= Bot.HasInHand(CardId.UnchainedSoulOfSharvara); + haveUnchainSoul |= Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2 + && Bot.HasInExtra(CardId.UnchainedSoulLordOfYama) && !activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama) + && (CheckRemainInDeck(CardId.UnchainedSoulOfSharvara) > 0 || Bot.HasInGraveyard(CardId.UnchainedSoulOfSharvara)) + && Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasRace(CardRace.Fiend) && card.Level <= 4).Count() >= 2; + } + bool haveAriane = false; + if (!activatedCardIdList.Contains(CardId.ArianeTheLabrynthServant) && Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2) + { + haveAriane |= Bot.HasInMonstersZone(CardId.ArianeTheLabrynthServant); + haveAriane |= Bot.HasInHand(CardId.ArianeTheLabrynthServant) && !summoned; + haveAriane |= Bot.GetSpells().Any(card => card.IsFacedown() && + ( + (card.IsCode(CardId.WelcomeLabrynth) && !activatedCardIdList.Contains(CardId.WelcomeLabrynth) && (cooclockAffected || !setTrapThisTurn.Contains(card))) + || + (card.IsCode(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && (cooclockAffected || !setTrapThisTurn.Contains(card))) + ) + ); + } + + if (haveUnchainSoul || haveAriane) + { + return Util.CheckSelectCount(new List { rollback }, cards, min, max); + } + } + + // welcome check + SortedDictionary welcomeCheck = new SortedDictionary { + {CardId.BigWelcomeLabrynth, cards.FirstOrDefault(card => card.IsCode(CardId.BigWelcomeLabrynth))}, + {CardId.WelcomeLabrynth, cards.FirstOrDefault(card => card.IsCode(CardId.WelcomeLabrynth))} + }; + List welcomeCheckIdList = new List { CardId.BigWelcomeLabrynth, CardId.WelcomeLabrynth }; + foreach (KeyValuePair checkPair in welcomeCheck) + { + if (checkPair.Value != null && !Bot.HasInHand(checkPair.Key) && !Bot.HasInGraveyard(checkPair.Key) + && !Bot.GetSpells().Any(card => card.IsCode(checkPair.Key) && card.IsFacedown())) + { + SelectSTPlace(null, true); + return Util.CheckSelectCount(new List { checkPair.Value }, cards, min, max); + } + } + if (welcomeCheck[CardId.BigWelcomeLabrynth] != null && + !Bot.HasInHand(CardId.BigWelcomeLabrynth) && !Bot.GetSpells().Any(card => card.IsCode(CardId.BigWelcomeLabrynth) && card.IsFacedown())) + { + SelectSTPlace(null, true); + return Util.CheckSelectCount(new List { welcomeCheck[CardId.BigWelcomeLabrynth] }, cards, min, max); + } + + // normal set + List checkIdList = new List{_CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, CardId.DestructiveDarumaKarmaCannon, + CardId.BigWelcomeLabrynth, CardId.TransactionRollback, CardId.WelcomeLabrynth}; + foreach (int checkId in checkIdList) + { + ClientCard checkCard = cards.FirstOrDefault(card => card.IsCode(checkId)); + if (checkCard != null) + { + SelectSTPlace(null, true); + return Util.CheckSelectCount(new List { checkCard }, cards, min, max); + } + } + } + + if (currentSolvingChain.IsCode(CardId.WelcomeLabrynth)) + { + banSpSummonExceptFiendCount = 2; + } + + if (currentSolvingChain.IsCode(CardId.WelcomeLabrynth) || (currentSolvingChain.IsCode(CardId.TransactionRollback) && rollbackCopyCardId == CardId.WelcomeLabrynth)) + { + Logger.DebugWriteLine("rewrite welcome's select."); + List selection = new List(); + + ClientCard ariane = GetWelcomeOrBigWelcomeTarget(cards, CardId.ArianeTheLabrynthServant); + if (ariane != null && !summonInChainList.Any(card => card.IsCode(CardId.ArianeTheLabrynthServant))) + { + if ((Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2 || Duel.Player == 1 && Duel.Phase >= DuelPhase.Main2) + && Bot.HasInHandOrInSpellZone(CardId.TransactionRollback)) + { + selection.Add(ariane); + } + } + + ClientCard arianna = GetWelcomeOrBigWelcomeTarget(cards, CardId.AriannaTheLabrynthServant); + if (arianna != null && !summonInChainList.Any(card => card.IsCode(CardId.AriannaTheLabrynthServant))) + { + bool canActivateCheck = !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !CheckWhetherNegated(true, true, CardType.Monster); + if (canActivateCheck) + { + bool checkFlag = !(!activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && + (Bot.HasInGraveyard(CardId.BigWelcomeLabrynth) || Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth)))); + checkFlag |= !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) + && (CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle) || Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true)) + && Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth) + && (!setTrapThisTurn.Contains(card) || cooclockAffected)); + checkFlag |= !(Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) + || CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle)); + if (checkFlag) + { + selection.Add(arianna); + } + } + } + + ClientCard arias = GetWelcomeOrBigWelcomeTarget(cards, CardId.AriasTheLabrynthButler); + if (arias != null && !summonInChainList.Any(card => card.IsCode(CardId.AriasTheLabrynthButler)) && !Bot.HasInHandOrHasInMonstersZone(CardId.AriasTheLabrynthButler)) + { + bool canActivateCheck = !activatedCardIdList.Contains(CardId.AriasTheLabrynthButler) && !CheckWhetherNegated(true, true, CardType.Monster); + if (canActivateCheck && Bot.HasInHand(CardId.LovelyLabrynthOfTheSilverCastle)) + { + selection.Add(arias); + } + } + + ClientCard lovely = GetWelcomeOrBigWelcomeTarget(cards, CardId.LovelyLabrynthOfTheSilverCastle); + if (lovely != null && !summonInChainList.Any(card => card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle))) + { + if (Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth)) + { + selection.Add(lovely); + } + } + + ClientCard lady = GetWelcomeOrBigWelcomeTarget(cards, CardId.LadyLabrynthOfTheSilverCastle); + if (lady != null) + { + if (Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) + && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasRace(CardRace.Fiend) && card.Level >= 8 && !card.HasType(CardType.Xyz | CardType.Link))) + { + selection.Add(lady); + } + } + + bool attackFlag = CheckCanDirectAttack(); + bool defenseFlag = Bot.UnderAttack && Bot.GetMonsterCount() == 0; + if (attackFlag || defenseFlag) + { + ClientCard bestPowerMonster = null; + int bestPower = -1; + foreach (ClientCard target in cards) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(target.Id); + if (cardData != null) + { + int power = attackFlag ? cardData.Attack : Math.Max(cardData.Attack, cardData.Defense); + if (bestPowerMonster == null || power > bestPower) + { + bestPowerMonster = target; + bestPower = power; + } + } + } + if (defenseFlag || GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack() + bestPower >= Enemy.LifePoints) + { + ClientCard realTarget = GetWelcomeOrBigWelcomeTarget(cards, bestPowerMonster.Id); + if (realTarget != null) selection.Add(realTarget); + } + } + + List checkIdList = new List{CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock, CardId.AriasTheLabrynthButler}; + foreach (int checkId in checkIdList) + { + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId)) + { + ClientCard target = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (target != null) selection.Add(target); + } + } + + List fullCheckIdList = new List{ + CardId.LadyLabrynthOfTheSilverCastle, CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock, + CardId.AriasTheLabrynthButler, CardId.ArianeTheLabrynthServant, CardId.AriannaTheLabrynthServant + }; + foreach (int checkId in fullCheckIdList) + { + ClientCard target = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (target != null && !selection.Contains(target)) selection.Add(target); + } + + if (selection.Count() > 0) return Util.CheckSelectCount(selection, cards, min, max); + } + + bool searchFlag = currentSolvingChain.IsCode(CardId.AriannaTheLabrynthServant) && hint == HintMsg.AddToHand; + bool bigwelcomeSoving = currentSolvingChain.IsCode(CardId.BigWelcomeLabrynth) || (currentSolvingChain.IsCode(CardId.TransactionRollback) && rollbackCopyCardId == CardId.BigWelcomeLabrynth); + searchFlag |= bigwelcomeSoving && hint == HintMsg.SpSummon && Bot.GetMonsterCount() == 0; + if (searchFlag) + { + Logger.DebugWriteLine("rewrite search."); + List selection = new List(); + + List furnitureCheckIdList = new List { CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, CardId.LabrynthChandraglier }; + ClientCard bigWelcome = GetWelcomeOrBigWelcomeTarget(cards, CardId.BigWelcomeLabrynth); + ClientCard welcome = GetWelcomeOrBigWelcomeTarget(cards, CardId.WelcomeLabrynth); + ClientCard arianna = GetWelcomeOrBigWelcomeTarget(cards, CardId.AriannaTheLabrynthServant); + + // search big welcome to activate this turn + if (Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2) + { + if (!summoned && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !CheckWhetherNegated(true, true, CardType.Monster) + && CheckCalledbytheGrave(CardId.AriannaTheLabrynthServant) == 0 && arianna != null && !Bot.HasInHand(CardId.AriannaTheLabrynthServant)) + { + return Util.CheckSelectCount(new List { arianna }, cards, min, max); + } + if (!CheckShouldNoMoreSpSummon()) + { + if (bigWelcome != null && !activatedCardIdList.Contains(CardId.AriasTheLabrynthButler) + && Bot.HasInHandOrHasInMonstersZone(CardId.AriasTheLabrynthButler)) + { + return Util.CheckSelectCount(new List { bigWelcome }, cards, min, max); + } + + bool canActivateSetTrap = (cooclockAffected || Bot.HasInHand(CardId.LabrynthCooclock)) + && Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)); + if (canActivateSetTrap && !Bot.HasInHandOrInSpellZone(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth)) + { + return Util.CheckSelectCount(new List { AriannaSearchWelcomeTrap(cards, CardId.BigWelcomeLabrynth) }, cards, min, max); + } + } + } + + // search cooclock/bulter to activate trap + ClientCard arias = null; + ClientCard cooclock = null; + if (!activatedCardIdList.Contains(CardId.AriasTheLabrynthButler) && CheckRemainInDeck(CardId.AriasTheLabrynthButler) > 0 + && !Bot.HasInHand(CardId.AriasTheLabrynthButler)) + { + arias = GetWelcomeOrBigWelcomeTarget(cards, CardId.AriasTheLabrynthButler); + } + if (!activatedCardIdList.Contains(CardId.LabrynthCooclock) && CheckRemainInDeck(CardId.LabrynthCooclock) > 0 + && !Bot.HasInHand(CardId.LabrynthCooclock)) + { + cooclock = GetWelcomeOrBigWelcomeTarget(cards, CardId.LabrynthCooclock); + } + if (arias != null || cooclock != null) + { + SortedDictionary> trapCheckDict = new SortedDictionary> { + {CardId.BigWelcomeLabrynth, BigWelcomeLabrynthSetCheck}, + {_CardId.DimensionalBarrier, DimensionalBarrierActivate}, + {CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonSetCheck}, + {CardId.WelcomeLabrynth, WelcomeLabrynthSetCheck} + }; + foreach (KeyValuePair> checkPair in trapCheckDict) + { + if (Bot.GetSpells().Any(card => card.IsFacedown() && !setTrapThisTurn.Contains(card) && card.IsCode(checkPair.Key))) continue; + if (!activatedCardIdList.Contains(checkPair.Key)) + { + if (Bot.GetSpells().Any(card => card.IsFacedown() && setTrapThisTurn.Contains(card) && card.IsCode(checkPair.Key)) + && cooclock != null) + { + return Util.CheckSelectCount(new List { cooclock }, cards, min, max); + } + else if (Bot.HasInHand(checkPair.Key)) + { + if (checkPair.Value()) + { + if (arias != null) + { + return Util.CheckSelectCount(new List { arias }, cards, min, max); + } + if (Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2 && cooclock != null) + { + return Util.CheckSelectCount(new List { cooclock }, cards, min, max); + } + } + } + } + } + } + + bool lackUnimportantCost = !Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth)); + if (lackUnimportantCost) + { + List handCost = Bot.Hand.Where(card => card != Card).ToList(); + lackUnimportantCost &= handCost.Count() <= 2 && !handCost.Any(card => !card.IsCode(_CardId.MaxxC, _CardId.AshBlossom, CardId.LabrynthCooclock)); + } + if (!lackUnimportantCost && cooclock != null && bigWelcome != null) + { + foreach (int furnitureId in furnitureCheckIdList) + { + if (furnitureId == CardId.LabrynthCooclock) continue; + if (CheckCalledbytheGrave(furnitureId) == 0 && !activatedCardIdList.Contains(furnitureId) && Bot.HasInHand(furnitureId)) + { + return Util.CheckSelectCount(new List { cooclock }, cards, min, max); + } + } + } + + if (Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2) + { + // search not exist furniture + if (!lackUnimportantCost) + { + foreach (int checkId in furnitureCheckIdList) + { + ClientCard furniture = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId) && furniture != null) + { + if (checkId == CardId.LabrynthCooclock) + { + if (Enemy.GetMonsterCount() > 0 && !Bot.HasInHandOrInMonstersZoneOrInGraveyard(CardId.LabrynthChandraglier)) continue; + } + return Util.CheckSelectCount(new List { furniture }, cards, min, max); + } + } + } + + // search big welcome + if (bigWelcome != null) + { + bool needSpSummonLovely = !Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) + && !Bot.HasInHandOrInSpellZone(CardId.BigWelcomeLabrynth) && cards.Any(c => c.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)); + needSpSummonLovely |= Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) && !Bot.HasInHandOrInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth); + if (needSpSummonLovely) + { + return Util.CheckSelectCount(new List { AriannaSearchWelcomeTrap(cards, CardId.BigWelcomeLabrynth) }, cards, min, max); + } + } + // search welcome + if (welcome != null && Bot.HasInHandOrInSpellZone(CardId.BigWelcomeLabrynth) && !Bot.HasInHandOrInSpellZone(CardId.WelcomeLabrynth)) + { + return Util.CheckSelectCount(new List { AriannaSearchWelcomeTrap(cards, CardId.WelcomeLabrynth) }, cards, min, max); + } + } + + // search big welcome/arias + if (Duel.Player == 1 && (Duel.Phase <= DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2) + && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.AriasTheLabrynthButler) + && !Bot.HasInSpellZone(CardId.BigWelcomeLabrynth)) + { + if (Bot.HasInHand(CardId.BigWelcomeLabrynth) && !Bot.HasInHandOrHasInMonstersZone(CardId.AriasTheLabrynthButler) && arias != null) + { + return Util.CheckSelectCount(new List { arias }, cards, min, max); + } + if (Bot.HasInHand(CardId.AriasTheLabrynthButler) && !Bot.HasInHandOrHasInMonstersZone(CardId.BigWelcomeLabrynth) && bigWelcome != null) + { + return Util.CheckSelectCount(new List { bigWelcome }, cards, min, max); + } + } + + // search lady + ClientCard lady = GetWelcomeOrBigWelcomeTarget(cards, CardId.LadyLabrynthOfTheSilverCastle); + bool haveTrap = Duel.Player == 0 && Bot.Hand.Any(card => card.Type == (int)CardType.Trap) && Duel.Phase <= DuelPhase.Main2; + haveTrap |= Bot.GetSpells().Any(card => card.IsFacedown() && card.Type == (int)CardType.Trap); + if (!Bot.HasInHandOrHasInMonstersZone(CardId.LadyLabrynthOfTheSilverCastle) && !activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle) + && haveTrap && lady != null) + { + return Util.CheckSelectCount(new List { lady }, cards, min, max); + } + + if (!activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !CheckWhetherNegated(true, true, CardType.Monster) + && CheckCalledbytheGrave(CardId.AriannaTheLabrynthServant) == 0 && arianna != null && !Bot.HasInHand(CardId.AriannaTheLabrynthServant)) + { + return Util.CheckSelectCount(new List { arianna }, cards, min, max); + } + + // search not exist furniture + if (!lackUnimportantCost) + { + foreach (int checkId in furnitureCheckIdList) + { + ClientCard furniture = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId) && furniture != null) + { + if (checkId == CardId.LabrynthCooclock) + { + if (Enemy.GetMonsterCount() > 0 && !Bot.HasInHandOrInMonstersZoneOrInGraveyard(CardId.LabrynthChandraglier)) continue; + } + return Util.CheckSelectCount(new List { furniture }, cards, min, max); + } + } + } + + // search not exist card + List uniqueCheckIdList = new List{ + CardId.BigWelcomeLabrynth, CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, CardId.LabrynthChandraglier, + CardId.LadyLabrynthOfTheSilverCastle, CardId.AriasTheLabrynthButler, CardId.ArianeTheLabrynthServant, CardId.WelcomeLabrynth}; + foreach (int checkId in uniqueCheckIdList) + { + ClientCard targetCard = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (!Bot.HasInMonstersZone(checkId) && !Bot.HasInHandOrInSpellZone(checkId) && targetCard != null) + { + if (checkId == CardId.BigWelcomeLabrynth || checkId == CardId.WelcomeLabrynth) + { + return Util.CheckSelectCount(new List { AriannaSearchWelcomeTrap(cards, checkId) }, cards, min, max); + } + else + { + return Util.CheckSelectCount(new List { targetCard }, cards, min, max); + } + } + } + + foreach (int checkId in uniqueCheckIdList) + { + ClientCard targetCard = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (CheckRemainInDeck(checkId) > 0) + { + if (checkId == CardId.BigWelcomeLabrynth || checkId == CardId.WelcomeLabrynth) + { + return Util.CheckSelectCount(new List { AriannaSearchWelcomeTrap(cards, checkId) }, cards, min, max); + } + else + { + return Util.CheckSelectCount(new List { targetCard }, cards, min, max); + } + } + } + } + + // solved when have more than 1 monster + if (bigwelcomeSoving && hint == HintMsg.SpSummon) + { + bool activateTimingFlag = Duel.Phase > DuelPhase.Main2 || (Card.IsCode(CardId.AriasTheLabrynthButler) && (CurrentTiming & hintTimingMainEnd) > 0); + + bool needDestroyFlag = GetProblematicEnemyCardList(false).Count() > 0; + needDestroyFlag |= activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && activateTimingFlag; + needDestroyFlag |= Bot.UnderAttack && (Bot.BattlingMonster?.GetDefensePower() ?? 0) <= (Enemy.BattlingMonster?.GetDefensePower() ?? 0) && Duel.LastChainPlayer != 0; + needDestroyFlag |= Duel.Turn == 1 && Duel.Player == 0 && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1); + needDestroyFlag |= Duel.Turn == 1 && Enemy.GetMonsterCount() == 0 && Enemy.GetSpellCount() == 0 && Enemy.Hand.Count > 0 + && (CurrentTiming & hintTimingMainEnd) > 0; + + if (needDestroyFlag && cards.Any(c => c.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) + && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1)) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, CardId.LovelyLabrynthOfTheSilverCastle) }, cards, min, max); + } + if (cards.Any(c => c.IsCode(CardId.AriannaTheLabrynthServant)) + && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !Bot.HasInMonstersZone(CardId.AriannaTheLabrynthServant)) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, CardId.AriannaTheLabrynthServant) }, cards, min, max); + } + if (cards.Any(c => c.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) + && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1)) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, CardId.LovelyLabrynthOfTheSilverCastle) }, cards, min, max); + } + if (cards.Any(c => c.IsCode(CardId.AriannaTheLabrynthServant)) + && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, CardId.AriannaTheLabrynthServant) }, cards, min, max); + } + if (cards.Any(c => c.IsCode(CardId.LadyLabrynthOfTheSilverCastle)) + && Duel.Turn > 1 && Duel.Phase < DuelPhase.Main2 && Duel.Player == 0 && Enemy.GetMonsterCount() == 0) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, CardId.LadyLabrynthOfTheSilverCastle) }, cards, min, max); + } + List furnitureCheckIdList = new List { CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, CardId.LabrynthChandraglier, CardId.AriasTheLabrynthButler }; + foreach (int furniture in furnitureCheckIdList) + { + if (cards.Any(c => c.IsCode(furniture)) && !Bot.HasInHandOrInMonstersZoneOrInGraveyard(furniture)) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, furniture) }, cards, min, max); + } + } + List checkIdList = new List{CardId.ArianeTheLabrynthServant, CardId.LadyLabrynthOfTheSilverCastle, CardId.AriannaTheLabrynthServant, + CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, CardId.LabrynthChandraglier, CardId.AriasTheLabrynthButler}; + foreach (int checkId in checkIdList) + { + if (cards.Any(c => c.IsCode(checkId))) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, checkId) }, cards, min, max); + } + } + // should not get here + Logger.DebugWriteLine("[warning] call BigWelcomeSpSummon with no select."); + } + + if (bigwelcomeSoving && hint == HintMsg.ReturnToHand) + { + if (bigwelcomeEscaseTarget != null && cards.Contains(bigwelcomeEscaseTarget)) + { + return Util.CheckSelectCount(new List { bigwelcomeEscaseTarget }, cards, min, max); + } + + ClientCard cooclock = cards.FirstOrDefault(c => c.IsCode(CardId.LabrynthCooclock)); + bool canSearchWelcome = CheckRemainInDeck(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth) > 0 + && (Bot.HasInHandOrHasInMonstersZone(new List { CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie }) + || summonInChainList.Any(c => c.IsCode(CardId.AriannaTheLabrynthServant)) + || (Duel.Player == 0 && !summoned && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && Bot.HasInHand(CardId.AriannaTheLabrynthServant))); + if (cooclock != null) + { + if (setTrapThisTurn.Count() > 0 + || (Duel.Turn == 1 && ( + (!activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle) && Bot.HasInHandOrHasInMonstersZone(CardId.LadyLabrynthOfTheSilverCastle)) + || canSearchWelcome + ) + ) + || Duel.Turn == 0 && canSearchWelcome + ) + return Util.CheckSelectCount(new List { cooclock }, cards, min, max); + } + + ClientCard defenseLady = cards.FirstOrDefault(c => c.IsDefense() && c.IsCode(CardId.LadyLabrynthOfTheSilverCastle)); + ClientCard attackLady = cards.FirstOrDefault(c => c.IsAttack() && c.IsCode(CardId.LadyLabrynthOfTheSilverCastle)); + if (Bot.GetMonsters().Any(card => (Duel.Player == 1 || card.IsDefense()) && card.IsCode(CardId.LadyLabrynthOfTheSilverCastle)) + && (!activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle) || activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle + 1))) + { + if (defenseLady != null) return Util.CheckSelectCount(new List { defenseLady }, cards, min, max); + if (attackLady != null) return Util.CheckSelectCount(new List { attackLady }, cards, min, max); + } + if (summonInChainList.Any(c => c.IsCode(CardId.LovelyLabrynthOfTheSilverCastle))) + { + List returnCheckIdList = new List{ + _CardId.MaxxC, CardId.AriannaTheLabrynthServant, _CardId.AshBlossom, CardId.LabrynthCooclock, CardId.LadyLabrynthOfTheSilverCastle, + CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie, CardId.AriasTheLabrynthButler, CardId.UnchainedSoulOfSharvara, CardId.ArianeTheLabrynthServant + }; + foreach (int checkId in returnCheckIdList) + { + ClientCard returnTarget = cards.FirstOrDefault(c => c.IsCode(checkId)); + if (returnTarget != null) return Util.CheckSelectCount(new List { returnTarget }, cards, min, max); + } + return Util.CheckSelectCount(cards.OrderBy(card => card.Attack).ToList(), cards, min, max); + } + if (cards.Count() == 1) return Util.CheckSelectCount(cards.OrderBy(card => card.Attack).ToList(), cards, min, max); + ClientCard ariannaNotSummon = cards.FirstOrDefault(c => c.IsCode(CardId.AriannaTheLabrynthServant) && !summonInChainList.Contains(c)); + if (ariannaNotSummon != null) return Util.CheckSelectCount(new List { ariannaNotSummon }, cards, min, max); + else + { + // compare which have lower attack + ClientCard fieldTarget = Bot.GetMonsters().Where(card => !card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) + .OrderBy(card => card.Attack).FirstOrDefault(); + if (fieldTarget != null) return Util.CheckSelectCount(new List { fieldTarget }, cards, min, max); + } + } + } + + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + public ClientCard GetWelcomeOrBigWelcomeTarget(IList cards, int cardId) + { + ClientCard graveTarget = cards.FirstOrDefault(card => card.IsCode(cardId) && card.Location == CardLocation.Grave); + if (graveTarget != null) return graveTarget; + ClientCard deckTarget = cards.FirstOrDefault(card => card.IsCode(cardId) && card.Location == CardLocation.Deck); + if (deckTarget != null) return deckTarget; + ClientCard handTarget = cards.FirstOrDefault(card => card.IsCode(cardId) && card.Location == CardLocation.Hand); + if (handTarget != null) return handTarget; + return null; + } + + public ClientCard AriannaSearchWelcomeTrap(IList cards, int welcomeId) + { + bool haveCostToSolve = Bot.HasInHand(new List { CardId.LovelyLabrynthOfTheSilverCastle, CardId.TransactionRollback, CardId.AriasTheLabrynthButler }); + if (haveCostToSolve) + { + List checkIdList = new List { CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier }; + foreach (int checkId in checkIdList) + { + ClientCard targetCard = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (targetCard != null && !Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId) && CheckCalledbytheGrave(checkId) == 0 && !activatedCardIdList.Contains(checkId)) + { + return targetCard; + } + } + foreach (int checkId in checkIdList) + { + ClientCard targetCard = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (targetCard != null && CheckCalledbytheGrave(checkId) == 0 && !activatedCardIdList.Contains(checkId)) + { + return targetCard; + } + } + } + + return GetWelcomeOrBigWelcomeTarget(cards, welcomeId); + } + + /// + /// go first + /// + public override bool OnSelectHand() + { + return true; + } + + public override bool OnSelectMonsterSummonOrSet(ClientCard card) + { + if (card.Attack > 0 && CheckCanDirectAttack()) return false; + if (card.Attack <= 1000) return true; + + return base.OnSelectMonsterSummonOrSet(card); + } + + public override int OnSelectPlace(int cardId, int player, CardLocation location, int available) + { + if (player == 0 && location == CardLocation.MonsterZone) + { + // selected in summon process + if (cardId == CardId.RelinquishedAnima) + { + return base.OnSelectPlace(cardId, player, location, available); + } + if (cardId == CardId.UnchainedSoulLordOfYama || cardId == CardId.UnchainedSoulOfAnguish) + { + if (Bot.MonsterZone[0] != null && Bot.MonsterZone[2] != null && (Zones.z6 & available) != 0) return Zones.z6; + if (Bot.MonsterZone[2] != null && Bot.MonsterZone[4] != null && (Zones.z5 & available) != 0) return Zones.z5; + } + if (cardId == CardId.MuckrakerFromTheUnderworld || cardId == CardId.UnchainedSoulOfRage) + { + if (Bot.MonsterZone[1] != null && (Zones.z6 & available) != 0) return Zones.z6; + if (Bot.MonsterZone[3] != null && (Zones.z5 & available) != 0) return Zones.z5; + } + + List zoneIdList = ShuffleList(new List { 0, 2, 4 }); + zoneIdList.AddRange(ShuffleList(new List { 1, 3 })); + zoneIdList.AddRange(ShuffleList(new List { 5, 6 })); + foreach (int zoneId in zoneIdList) + { + int zone = (int)System.Math.Pow(2, zoneId); + if ((available & zone) != 0 && Bot.MonsterZone[zoneId] == null) + { + return zone; + } + } + } + return base.OnSelectPlace(cardId, player, location, available); + } + + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2) + { + bool turnDefense = false; + if (cardData.Attack <= cardData.Defense) + { + turnDefense = true; + } + if (turnDefense) + { + return CardPosition.FaceUpDefence; + } + } + if (Duel.Player == 1) + { + if (cardData.Defense >= cardData.Attack || Util.IsOneEnemyBetterThanValue(cardData.Attack, true)) + { + return CardPosition.FaceUpDefence; + } + } + int cardAttack = cardData.Attack; + int bestBotAttack = Math.Max(Util.GetBestAttack(Bot), cardAttack); + if (Util.IsAllEnemyBetterThanValue(bestBotAttack, true)) + { + return CardPosition.FaceUpDefence; + } + } + return base.OnSelectPosition(cardId, positions); + } + + public override int OnSelectOption(IList options) + { + // override for cooclock + if (options.Count() == 2 && options.Contains(1190) && options.Contains(1152)) + { + // 1190=Add to Hand, 1152=Special Summon + // return to hand to activate trap set this turn + bool canLink = Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2; + if (!canLink && !Bot.HasInHand(CardId.LabrynthCooclock) && Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)) + && !activatedCardIdList.Contains(CardId.LabrynthCooclock) && !CheckWhetherWillbeRemoved() + && (activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) || Bot.GetSpells().All(card => setTrapThisTurn.Contains(card) || !card.IsCode(CardId.BigWelcomeLabrynth))) + && setTrapThisTurn.Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth, _CardId.DimensionalBarrier, _CardId.InfiniteImpermanence, CardId.DestructiveDarumaKarmaCannon))) + { + return options.IndexOf(1190); + } + if (!enemyActivateMaxxC) return options.IndexOf(1152); + if (activatedCardIdList.Contains(CardId.LabrynthCooclock)) + { + if (!CheckShouldNoMoreSpSummon()) return options.IndexOf(1152); + } + return options.IndexOf(1190); + } + + // overrride for lovely + if (options.Contains(Util.GetStringId(CardId.LovelyLabrynthOfTheSilverCastle, 3)) && options.Contains(Util.GetStringId(CardId.LovelyLabrynthOfTheSilverCastle, 4))) + { + int botWorstAttack = 0; + ClientCard botWorstMonster = Util.GetWorstBotMonster(true); + if (botWorstMonster != null) + { + botWorstAttack = botWorstMonster.Attack; + } + List targetList = GetProblematicEnemyCardList(false); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && card.GetDefensePower() >= botWorstAttack && !currentDestroyCardList.Contains(card)).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => !currentDestroyCardList.Contains(card) && card.IsFacedown()).ToList())); + if (targetList.Count() > 0) + { + currentDestroyCardList.Add(targetList[0]); + AI.SelectCard(targetList); + return options.IndexOf(Util.GetStringId(CardId.LovelyLabrynthOfTheSilverCastle, 4)); + } + else + { + return options.IndexOf(Util.GetStringId(CardId.LovelyLabrynthOfTheSilverCastle, 3)); + } + } + + // override for dimensional barrier + if (options.IndexOf(HintMsg.RITUAL) >= 0 || options.IndexOf(HintMsg.FUSION) >= 0 || options.IndexOf(HintMsg.SYNCHRO) >= 0 + || options.IndexOf(HintMsg.XYZ) >= 0 || options.IndexOf(HintMsg.PENDULUM) >= 0) + { + Dictionary> barrierCheckDict = new Dictionary> + { + {HintMsg.RITUAL, DimensionalBarrierForRitual}, + {HintMsg.FUSION, DimensionalBarrierForFusion}, + {HintMsg.SYNCHRO, DimensionalBarrierForSynchro}, + {HintMsg.XYZ, DimensionalBarrierForXyz}, + {HintMsg.PENDULUM, DimensionalBarrierForPendulum}, + }; + dimensionBarrierAnnouncing = true; + foreach (KeyValuePair> checkPair in barrierCheckDict) + { + if (options.Contains(checkPair.Key) && checkPair.Value()) + { + dimensionBarrierAnnouncing = false; + dimensionalBarrierAnnouced.Add(checkPair.Key); + return options.IndexOf(checkPair.Key); + } + } + dimensionBarrierAnnouncing = false; + List enemyMonsterList = new List(Enemy.GetMonsters()); + enemyMonsterList.AddRange(Enemy.GetGraveyardMonsters()); + Dictionary barrierCheckSecondark = new Dictionary + { + {HintMsg.RITUAL, enemyMonsterList.Any(card => card.HasType(CardType.Ritual))}, + {HintMsg.FUSION, enemyMonsterList.Any(card => card.HasType(CardType.Fusion))}, + {HintMsg.SYNCHRO, enemyMonsterList.Any(card => card.HasType(CardType.Synchro))}, + {HintMsg.XYZ, enemyMonsterList.Any(card => card.HasType(CardType.Xyz))}, + {HintMsg.PENDULUM, enemyMonsterList.Any(card => card.HasType(CardType.Pendulum))}, + }; + foreach (KeyValuePair checkPair in barrierCheckSecondark) + { + if (options.Contains(checkPair.Key) && checkPair.Value) + { + dimensionBarrierAnnouncing = false; + dimensionalBarrierAnnouced.Add(checkPair.Key); + return options.IndexOf(checkPair.Key); + } + } + List annouceList = new List { HintMsg.XYZ, HintMsg.SYNCHRO, HintMsg.FUSION, HintMsg.PENDULUM, HintMsg.RITUAL }; + foreach (int annouce in annouceList) + { + if (options.Contains(annouce)) + { + return options.IndexOf(annouce); + } + } + } + + // override for servant + // sp summon + if (options.Contains(Util.GetStringId(CardId.AriannaTheLabrynthServant, 2)) || options.Contains(Util.GetStringId(CardId.ArianeTheLabrynthServant, 2))) + { + if (GetEmptyMainMonsterZoneCount() > chainSummoningIdList.Count()) + { + bool checkFlag = false; + if (!activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && Bot.HasInHand(CardId.AriannaTheLabrynthServant) + && !CheckWhetherNegated(true, true, CardType.Monster) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + checkFlag = true; + AI.SelectCard(CardId.AriannaTheLabrynthServant); + } + if (!checkFlag) + { + List checkIdList = new List { CardId.LovelyLabrynthOfTheSilverCastle, CardId.LadyLabrynthOfTheSilverCastle }; + foreach (int checkId in checkIdList) + { + if (Bot.HasInHand(checkId)) + { + checkFlag = true; + AI.SelectCard(checkId); + break; + } + } + } + if (!checkFlag && Duel.Player == 0 && Duel.Phase < DuelPhase.End) + { + List linkMaterialList = GetCanBeUsedForLinkMaterial(true, card => !card.HasRace(CardRace.Fiend)); + if (linkMaterialList.Count() + chainSummoningIdList.Count() == 2) + { + // summon less atk for link + ClientCard selected = null; + int attack = 0; + foreach (ClientCard hand in Bot.Hand) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(hand.Id); + if (cardData == null || cardData.Race != (int)CardRace.Fiend) continue; + if (selected == null || attack > hand.Attack) + { + selected = hand; + attack = hand.Attack; + } + } + if (selected != null) + { + checkFlag = true; + AI.SelectCard(selected); + } + } + } + if (!checkFlag && CheckCanDirectAttack()) + { + // summon best attack for attack + ClientCard selected = null; + int attack = 0; + foreach (ClientCard hand in Bot.Hand) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(hand.Id); + if (cardData == null || cardData.Race != (int)CardRace.Fiend) continue; + if (selected == null || attack < hand.Attack) + { + selected = hand; + attack = hand.Attack; + } + } + if (selected != null) + { + checkFlag = true; + AI.SelectCard(selected); + } + } + + if (checkFlag) + { + if (options.Contains(Util.GetStringId(CardId.AriannaTheLabrynthServant, 2))) return options.IndexOf(Util.GetStringId(CardId.AriannaTheLabrynthServant, 2)); + if (options.Contains(Util.GetStringId(CardId.ArianeTheLabrynthServant, 2))) return options.IndexOf(Util.GetStringId(CardId.ArianeTheLabrynthServant, 2)); + } + } + } + + // set + if (options.Contains(Util.GetStringId(CardId.AriannaTheLabrynthServant, 3)) || options.Contains(Util.GetStringId(CardId.ArianeTheLabrynthServant, 3))) + { + if (!Util.ChainContainsCard(CardId.WelcomeLabrynth) || Bot.GetSpellCountWithoutField() < 4) + { + List checkIdList = new List { CardId.BigWelcomeLabrynth, CardId.WelcomeLabrynth, _CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, CardId.DestructiveDarumaKarmaCannon }; + foreach (int checkId in checkIdList) + { + if (Bot.HasInHand(checkId) && (checkId == _CardId.InfiniteImpermanence || !Bot.HasInSpellZone(checkId, faceUp:false))) + { + AI.SelectCard(checkId); + if (options.Contains(Util.GetStringId(CardId.AriannaTheLabrynthServant, 3))) return options.IndexOf(Util.GetStringId(CardId.AriannaTheLabrynthServant, 3)); + if (options.Contains(Util.GetStringId(CardId.ArianeTheLabrynthServant, 3))) return options.IndexOf(Util.GetStringId(CardId.ArianeTheLabrynthServant, 3)); + } + } + } + } + + // do nothing + if (options.Contains(Util.GetStringId(CardId.AriannaTheLabrynthServant, 4))) return options.IndexOf(Util.GetStringId(CardId.AriannaTheLabrynthServant, 4)); + if (options.Contains(Util.GetStringId(CardId.ArianeTheLabrynthServant, 4))) return options.IndexOf(Util.GetStringId(CardId.ArianeTheLabrynthServant, 4)); + + return base.OnSelectOption(options); + } + + public override bool OnSelectYesNo(int desc) + { + if (desc == 96) + { + Logger.DebugWriteLine("*** muckraker replace."); + AI.SelectCard(Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasRace(CardRace.Fiend)).OrderBy(card => card.Attack).ToList()); + return true; + } + return base.OnSelectYesNo(desc); + } + + public override void OnNewTurn() + { + if (Duel.Turn <= 1) + { + calledbytheGraveCount.Clear(); + dimensionShifterCount = 0; + + enemySpSummonFromExLastTurn = 0; + enemySpSummonFromExThisTurn = 0; + banSpSummonExceptFiendCount = 0; + } + enemyActivateMaxxC = false; + enemySpSummonFromExLastTurn = enemySpSummonFromExThisTurn; + enemySpSummonFromExThisTurn = 0; + rollbackCopyCardId = 0; + + List keyList = calledbytheGraveCount.Keys.ToList(); + foreach (int dic in keyList) + { + if (calledbytheGraveCount[dic] > 1) calledbytheGraveCount[dic] -= 1; + } + if (dimensionShifterCount > 0) dimensionShifterCount--; + if (banSpSummonExceptFiendCount > 0) banSpSummonExceptFiendCount--; + infiniteImpermanenceList.Clear(); + + summoned = false; + cooclockAffected = false; + activatedCardIdList.Clear(); + setTrapThisTurn.Clear(); + summonThisTurn.Clear(); + enemySetThisTurn.Clear(); + dimensionalBarrierAnnouced.Clear(); + summonInChainList.Clear(); + } + + public override void OnChaining(int player, ClientCard card) + { + if (card == null) return; + if (chainSummoningIdList.Count() > 0) + Logger.DebugWriteLine("[Welcome] Summoning: " + string.Join(",", chainSummoningIdList) + "\n"); + + if (player == 1) + { + if (card.IsCode(_CardId.InfiniteImpermanence)) + { + if (enemyActivateInfiniteImpermanenceFromHand) + { + enemyActivateInfiniteImpermanenceFromHand = false; + } + else { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == card) + { + infiniteImpermanenceList.Add(4-i); + break; + } + } + } + } + } + base.OnChaining(player, card); + } + + public override void OnChainSolved(int chainIndex) + { + ClientCard currentCard = Duel.GetCurrentSolvingChainCard(); + if (currentCard != null && !Duel.IsCurrentSolvingChainNegated()) + { + if (currentCard.Controller == 1) + { + if (currentCard.IsCode(_CardId.MaxxC)) + enemyActivateMaxxC = true; + if (currentCard.IsCode(CardId.DimensionShifter)) + dimensionShifterCount = 2; + } + if (currentCard.Controller == 0) + { + if (currentCard.IsCode(CardId.LabrynthCooclock)) + cooclockAffected = true; + } + } + + base.OnChainSolved(chainIndex); + } + + public override void OnChainEnd() + { + rollbackCopyCardId = 0; + currentNegateMonsterList.Clear(); + currentDestroyCardList.Clear(); + escapeTargetList.Clear(); + chainSummoningIdList.Clear(); + summonInChainList.Clear(); + enemyActivateInfiniteImpermanenceFromHand = false; + for (int idx = enemySetThisTurn.Count() - 1; idx >= 0; idx --) + { + ClientCard checkTarget = enemySetThisTurn[idx]; + if (checkTarget == null || checkTarget.Location != CardLocation.SpellZone || checkTarget.HasPosition(CardPosition.FaceUp)) + { + enemySetThisTurn.RemoveAt(idx); + } + } + if (cooclockActivating) + cooclockActivating = false; + furnitureActivating = false; + dimensionBarrierAnnouncing = false; + bigwelcomeEscaseTarget = null; + base.OnChainEnd(); + } + + public override void OnMove(ClientCard card, int previousControler, int previousLocation, int currentControler, int currentLocation) + { + if (previousControler == 1) + { + if (previousLocation == (int)CardLocation.Extra && currentLocation == (int)CardLocation.MonsterZone) enemySpSummonFromExThisTurn ++; + if (card != null) + { + if (card.IsCode(_CardId.InfiniteImpermanence) && previousLocation == (int)CardLocation.Hand && currentLocation == (int)CardLocation.SpellZone) + enemyActivateInfiniteImpermanenceFromHand = true; + if (card.Location == CardLocation.SpellZone && card.HasPosition(CardPosition.FaceDown)) + enemySetThisTurn.Add(card); + } + } + if (previousControler == 0) + { + if (previousLocation == (int)CardLocation.MonsterZone && currentLocation != (int)CardLocation.MonsterZone) + { + if (summonThisTurn.Contains(card)) summonThisTurn.Remove(card); + if (summonInChainList.Contains(card)) summonInChainList.Remove(card); + } + if (previousLocation == (int)CardLocation.SpellZone && currentLocation != (int)CardLocation.SpellZone) + { + if (setTrapThisTurn.Contains(card)) setTrapThisTurn.Remove(card); + } + } + if (currentControler == 0) + { + ClientCard currentSolvingChain = Duel.GetCurrentSolvingChainCard(); + if (currentLocation == (int)CardLocation.SpellZone && (currentSolvingChain == null || !currentSolvingChain.IsCode(CardId.AriasTheLabrynthButler)) + && card != null && (card.HasType(CardType.Trap) || card.IsCode(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth)) + ) + { + Logger.DebugWriteLine("[setTrapThisTurn]set " + card.Name ?? "UnknowCard"); + setTrapThisTurn.Add(card); + } + if (currentLocation == (int)CardLocation.MonsterZone) + { + summonThisTurn.Add(card); + if (currentSolvingChain != null) summonInChainList.Add(card); + } + if (previousLocation == (int)CardLocation.Grave && currentLocation == (int)CardLocation.Removed) + { + if (currentSolvingChain != null && currentSolvingChain.Controller == 1 && currentSolvingChain.IsCode(_CardId.CalledByTheGrave)) + { + Logger.DebugWriteLine("*** " + (card.Name ?? "UnknowCard") + " is banished by CallByTheGrave"); + calledbytheGraveCount[card.Id] = 2; + } + } + } + + base.OnMove(card, previousControler, previousLocation, currentControler, currentLocation); + } + + public override BattlePhaseAction OnBattle(IList attackers, IList defenders) + { + if (attackers.Count() > 0 && defenders.Count() > 0) + { + List sortedAttacker = attackers.OrderBy(card => card.Attack).ToList(); + for (int k = 0; k < sortedAttacker.Count; ++k) + { + ClientCard attacker = sortedAttacker[k]; + attacker.IsLastAttacker = k == sortedAttacker.Count - 1; + BattlePhaseAction result = OnSelectAttackTarget(attacker, defenders); + if (result != null) + return result; + } + } + + return base.OnBattle(attackers, defenders); + } + + /// + /// Select spell/trap's place randomly to avoid InfiniteImpermanence and so on. + /// + /// Card to set(default current card) + /// Whether need to avoid InfiniteImpermanence + /// Whether need to avoid set in this place + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceList.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + list.Add(seq); + } + } + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoidImpermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + AI.SelectPlace(0); + } + + public void ResetCooclockEffect(bool onlyCheck) + { + if (!onlyCheck && cooclockAffected && setTrapThisTurn.Contains(Card)) + { + cooclockAffected = false; + setTrapThisTurn.Remove(Card); + } + } + + + + + + public bool LadyLabrynthOfTheSilverCastleFieldActivate() + { + if (Card.Location == CardLocation.MonsterZone && (Util.GetLastChainCard() == null || !Util.GetLastChainCard().IsCode(_CardId.EvenlyMatched)) + && (!CheckWhetherNegated() || Enemy.HasInMonstersZone(CardId.LadyLabrynthOfTheSilverCastle))) + { + activatedCardIdList.Add(Card.Id + 1); + return true; + } + return false; + } + + public bool LadyLabrynthOfTheSilverCastleHandActivate() + { + if (Card.Location == CardLocation.Hand) + { + // sp summon from hand + if (CheckShouldNoMoreSpSummon(true) || Util.ChainContainsCard(_CardId.EvenlyMatched)) return false; + bool activateFlag = false; + activateFlag |= CheckChainContainEnemyMaxxC(); + if (!activateFlag && GetEmptyMainMonsterZoneCount() + chainSummoningIdList.Count() <= 0) + { + return false; + } + activateFlag |= cooclockAffected && setTrapThisTurn.Count() > 0 && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)); + activateFlag |= Bot.GetSpells().Any(card => card.IsFacedown() && card.HasType(CardType.Trap) && !setTrapThisTurn.Contains(card)) + && !Bot.HasInMonstersZone(Card.Id, true, faceUp: true); + activateFlag |= Duel.Player == 1 && Duel.Phase >= DuelPhase.End; + activateFlag |= setTrapThisTurn.Count() > 0 && Duel.Phase >= DuelPhase.End; + activateFlag |= Bot.UnderAttack && Bot.GetMonsterCount() == 0 && !Util.ChainContainsCard(CardId.DestructiveDarumaKarmaCannon); + // for link + if (Bot.HasInExtra(CardId.UnchainedSoulLordOfYama) && Duel.Player == 0 && Duel.Phase < DuelPhase.End) + { + // check whether need summon for material count + List materialList = GetCanBeUsedForLinkMaterial(true, card => !card.HasRace(CardRace.Fiend)); + int materialCount = materialList.Count(); + if (!activatedCardIdList.Contains(CardId.UnchainedSoulOfSharvara) && Bot.GetSpells().Any(card => card.IsFacedown()) + && (!activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama) || Bot.HasInHand(CardId.UnchainedSoulOfSharvara))) materialCount++; + if ( (materialCount == 2 || materialCount == 1 && materialList.Any(card => card.IsCode(CardId.UnchainedSoulLordOfYama))) + && Bot.HasInExtra(CardId.UnchainedSoulLordOfYama) || materialList.Any(card => card.HasSetcode(SetcodeUnchained))) + { + activateFlag |= Enemy.GetMonsterCount() > 0 && Bot.HasInExtra(CardId.UnchainedSoulOfAnguish); + activateFlag |= Bot.HasInExtra(CardId.UnchainedSoulOfRage); + } + } + + if (activateFlag) + { + activatedCardIdList.Add(Card.Id); + chainSummoningIdList.Add(Card.Id); + return true; + } + } + return false; + } + + public bool LovelyLabrynthOfTheSilverCastleActivate() + { + if (CheckWhetherNegated()) return false; + if (ActivateDescription == Util.GetStringId(CardId.LovelyLabrynthOfTheSilverCastle, 0)) + { + // set from GY + List checkIdList = new List{CardId.BigWelcomeLabrynth, _CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, + CardId.DestructiveDarumaKarmaCannon, CardId.WelcomeLabrynth}; + foreach (int checkId in checkIdList) + { + if (Bot.HasInGraveyard(checkId) && !Bot.HasInHandOrInSpellZone(checkId)) + { + AI.SelectCard(checkId); + activatedCardIdList.Add(Card.Id); + return true; + } + } + foreach (int checkId in checkIdList) + { + if (Bot.HasInGraveyard(checkId)) + { + AI.SelectCard(checkId); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + if (GetCanBeUsedForLinkMaterial(true, card => !card.HasRace(CardRace.Fiend)).Count() == 2 + && !activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama) + && (Bot.HasInGraveyard(CardId.UnchainedSoulOfSharvara) || CheckRemainInDeck(CardId.UnchainedSoulOfSharvara) > 0)) + { + activatedCardIdList.Add(Card.Id); + return true; + } + } + else + { + // destroy + if (Enemy.GetHandCount() == 0) + { + int botWorstAttack = 0; + ClientCard botWorstMonster = Util.GetWorstBotMonster(true); + if (botWorstMonster != null) + { + botWorstAttack = botWorstMonster.Attack; + } + List targetList = GetProblematicEnemyCardList(false); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && card.GetDefensePower() >= botWorstAttack && !currentDestroyCardList.Contains(card)).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => !currentDestroyCardList.Contains(card)).ToList())); + + if (targetList.Count() > 0) + { + currentDestroyCardList.Add(targetList[0]); + AI.SelectCard(targetList); + AI.SelectOption(1); + } + else + { + AI.SelectOption(0); + } + } + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool UnchainedSoulOfSharvaraActivate() + { + // search + if (Card.Location == CardLocation.Grave) + { + activatedCardIdList.Add(Card.Id + 1); + SelectSTPlace(null, false); + return true; + } + + // sp summon + if (Bot.HasInSpellZone(CardId.TransactionRollback) && GetEmptyMainMonsterZoneCount() > chainSummoningIdList.Count() + && !CheckWhetherWillbeRemoved() && !CheckShouldNoMoreSpSummon(false)) + { + AI.SelectCard(CardId.TransactionRollback); + activatedCardIdList.Add(Card.Id); + return true; + } + + // escape target + if (Duel.LastChainPlayer == 1) + { + ClientCard chainCard = Util.GetLastChainCard(); + if (chainCard != null && chainCard.IsCode(targetNegateIdList)) + { + if (Duel.LastChainTargets.Any(card => card.Controller == 0 && card.IsFaceup() && card.HasRace(CardRace.Fiend) + && Duel.CurrentChain.Any(chain => chain == card) && !card.IsCode(CardId.UnchainedSoulOfRage, CardId.UnchainedSoulOfAnguish))) + { + escapeTargetList.AddRange(Duel.LastChainTargets); + AI.SelectCard(Duel.LastChainTargets); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + + // for link + bool destroySpells = Duel.Player == 0 && GetEmptyMainMonsterZoneCount() > chainSummoningIdList.Count() && Bot.GetMonsterCount() > 0 && CurrentTiming <= 0; + if (destroySpells) + { + List materialList = GetCanBeUsedForLinkMaterial(true, card => !card.HasRace(CardRace.Fiend)); + destroySpells = CheckAtAdvantage() && !Bot.HasInMonstersZone(CardId.UnchainedSoulOfRage) + && Bot.HasInExtra(CardId.UnchainedSoulOfRage) && materialList.Count() == 1; + if (Bot.HasInExtra(CardId.UnchainedSoulOfAnguish) && !Bot.HasInMonstersZone(CardId.UnchainedSoulOfAnguish) + && !activatedCardIdList.Contains(CardId.UnchainedSoulOfAnguish) && Enemy.GetMonsters().Where(card => card.IsFaceup()).Count() > 0) + { + destroySpells |= materialList.Count() == 2; + destroySpells |= materialList.Count() == 1 && materialList.Any(card => card.HasType(CardType.Link) && card.LinkCount == 2); + } + } + // for attack + destroySpells |= CheckCanDirectAttack() && GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack() + 2000 >= Enemy.LifePoints + && GetEmptyMainMonsterZoneCount() > chainSummoningIdList.Count(); + // for avoid lose + destroySpells |= Duel.Player == 1 && Duel.Phase == DuelPhase.Main1 && Bot.GetMonsterCount() == 0 && (CurrentTiming & hintTimingMainEnd) != 0 + && Util.GetTotalAttackingMonsterAttack(1) >= Bot.LifePoints; + if (destroySpells) + { + List destroyIdList = new List{_CardId.InfiniteImpermanence, CardId.TransactionRollback, CardId.WelcomeLabrynth, + _CardId.DimensionalBarrier, CardId.DestructiveDarumaKarmaCannon, CardId.BigWelcomeLabrynth}; + foreach (int checkId in destroyIdList) + { + ClientCard target = Bot.GetSpells().FirstOrDefault(card => card.IsFacedown() && card.IsCode(checkId)); + if (target != null) + { + AI.SelectCard(target); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + + return false; + } + + public bool AriasTheLabrynthButlerActivate() + { + if (Card.Location != CardLocation.Grave) + { + if (Util.ChainContainsCard(new int[] { _CardId.DivineArsenalAAZEUS_SkyThunder, _CardId.EvenlyMatched, _CardId.EvilswarmExcitonKnight })) return false; + if (Duel.CurrentChain.Any(card => card.Controller == 0 && card.IsCode(CardId.AriannaTheLabrynthServant))) return false; + + // set trap to activate + SortedList> checkList = new SortedList> { + {CardId.BigWelcomeLabrynth, BigWelcomeLabrynthSetCheck}, + {CardId.WelcomeLabrynth, WelcomeLabrynthSetCheck}, + {CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonSetCheck}, + {_CardId.DimensionalBarrier, DimensionalBarrierActivate} + }; + foreach (KeyValuePair> pair in checkList) + { + ClientCard setTarget = Bot.Hand.FirstOrDefault(card => card.IsCode(pair.Key)); + if (setTarget != null && !activatedCardIdList.Contains(pair.Key) && pair.Value()) + { + AI.SelectOption(1); + AI.SelectCard(pair.Key); + activatedCardIdList.Add(Card.Id); + SelectSTPlace(setTarget, true); + return true; + } + } + + // special summon monster + if (Bot.HasInHand(CardId.LovelyLabrynthOfTheSilverCastle)) + { + // before main end + if (Duel.Player == 0 || (CurrentTiming & hintTimingMainEnd) != 0) + { + AI.SelectOption(0); + AI.SelectCard(CardId.LovelyLabrynthOfTheSilverCastle); + chainSummoningIdList.Add(CardId.LovelyLabrynthOfTheSilverCastle); + activatedCardIdList.Add(Card.Id); + return true; + } + } + if (Bot.HasInHand(CardId.AriannaTheLabrynthServant) && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) + && !CheckWhetherNegated(true, true) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + bool searchFlag = false; + if (Duel.Player == 1) + { + searchFlag |= (CurrentTiming & hintTimingMainEnd) != 0; + searchFlag |= GetProblematicEnemyCardList(false).Count() > 0 + && (Bot.HasInMonstersZoneOrInGraveyard(CardId.LovelyLabrynthOfTheSilverCastle) || CheckRemainInDeck(CardId.LovelyLabrynthOfTheSilverCastle) > 0) + && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1); + } + if (Duel.Player == 0) searchFlag |= summoned && !CheckShouldNoMoreSpSummon(); + if (searchFlag) + { + AI.SelectOption(0); + AI.SelectCard(CardId.AriannaTheLabrynthServant); + chainSummoningIdList.Add(CardId.AriannaTheLabrynthServant); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + return false; + } + + public bool ArianeTheLabrynthServantSummon() + { + // for attack + if (Duel.Turn > 1 && Enemy.GetMonsterCount() == 0) + { + summoned = true; + return true; + } + // for activate effect + if (!activatedCardIdList.Contains(Card.Id) && !CheckWhetherNegated(true, true) && !CheckWhetherWillbeRemoved()) + { + bool haveCost = Bot.Hand.Any(card => card.Type == (int)CardType.Trap) || Bot.GetSpells().Any(card => card.IsFacedown() && card.Type == (int)CardType.Trap); + if (haveCost && !CheckShouldNoMoreSpSummon(true)) + { + summoned = true; + return true; + } + } + + return false; + } + public bool ArianeTheLabrynthServantForRollbackSummon() + { + if (activatedCardIdList.Contains(Card.Id)) return false; + if (Bot.HasInHandOrInSpellZone(CardId.TransactionRollback) && !CheckWhetherWillbeRemoved()) + { + summoned = true; + return true; + } + + return false; + } + public bool ArianeTheLabrynthServantActivate() + { + // special summon + if (ActivateDescription == Util.GetStringId(Card.Id, 0)) + { + bool haveRollback = Bot.HasInHandOrInSpellZone(CardId.TransactionRollback); + if (CheckWhetherNegated() && !haveRollback) return false; + if (CheckShouldNoMoreSpSummon() && !(haveRollback && Bot.Graveyard.Any(card => card.IsCode(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth)))) return false; + int specialSummonId = 0; + // arianna + if (!activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && CheckRemainInDeck(CardId.AriannaTheLabrynthServant) > 0) + { + specialSummonId = CardId.AriannaTheLabrynthServant; + } + // sp summon not used furniture + if (specialSummonId == 0) + { + List checkIdList = new List{CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock}; + foreach (int checkId in checkIdList) + { + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId) && CheckRemainInDeck(checkId) > 0) + { + specialSummonId = checkId; + break; + } + } + } + // for attack/link + if (specialSummonId == 0) + { + List checkIdList = new List(); + if (Enemy.GetMonsterCount() == 0) checkIdList.AddRange(new List{ + CardId.AriannaTheLabrynthServant, CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock + }); + else checkIdList.AddRange(new List{ + CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, CardId.AriannaTheLabrynthServant + }); + foreach (int checkId in checkIdList) + { + if (CheckRemainInDeck(checkId) > 0) + { + specialSummonId = checkId; + break; + } + } + } + + if (specialSummonId > 0) + { + bool costSelected = false; + if (haveRollback) + { + AI.SelectCard(CardId.TransactionRollback); + costSelected = true; + } + if (!costSelected) { + ClientCard welcome = Bot.GetSpells().FirstOrDefault(card => card.IsCode(CardId.WelcomeLabrynth)); + if (welcome != null) + { + AI.SelectCard(welcome); + costSelected = true; + } + } + List costCheckList = Bot.Hand.Where(card => card.IsFacedown() && card.Type == (int)CardType.Trap).ToList(); + costCheckList.AddRange(Bot.GetSpells().Where(card => card.IsFacedown() && card.Type == (int)CardType.Trap).ToList()); + if (!costSelected) + { + List checkIdList = new List{_CardId.InfiniteImpermanence, CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth}; + foreach (int checkId in checkIdList) + { + ClientCard dumpCard = costCheckList.FirstOrDefault(card => card.IsCode(checkId)); + if (costCheckList.Count(card => card.IsCode(checkId)) > 1 && dumpCard != null) + { + AI.SelectCard(dumpCard); + costSelected = true; + break; + } + } + } + if (!costSelected) + { + List checkIdList = new List{_CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, CardId.DestructiveDarumaKarmaCannon, + CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth}; + foreach (int checkId in checkIdList) + { + ClientCard checkCard = costCheckList.FirstOrDefault(card => card.IsCode(checkId)); + if (checkCard != null) + { + AI.SelectCard(checkCard); + costSelected = true; + break; + } + } + } + } + } else { + // draw + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool AriannaTheLabrynthServantSummon() + { + // summon for search + if (!CheckWhetherNegated(true, true) && !activatedCardIdList.Contains(Card.Id)) + { + summoned = true; + return true; + } + + // summon for attack + if (Duel.Turn > 1 && Duel.Player == 0 && Duel.Phase < DuelPhase.Main2 && Enemy.GetMonsterCount() == 0 && !Bot.HasInHand(CardId.ArianeTheLabrynthServant)) + { + summoned = true; + return true; + } + + return false; + } + public bool AriannaTheLabrynthServantActivate() + { + if (CheckWhetherNegated()) return false; + // search or draw + // search target is overrided in OnSelectCard() + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool AshBlossomActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC)) return false; + if (DefaultAshBlossomAndJoyousSpring()) + { + if (Util.GetLastChainCard().Location == CardLocation.MonsterZone) currentNegateMonsterList.Add(Util.GetLastChainCard()); + return true; + } + return false; + } + + public bool MaxxCActivate() + { + if (CheckWhetherNegated(true) || Duel.LastChainPlayer == 0) return false; + return DefaultMaxxC(); + } + + public bool FurnitureSetWelcomeActivate() + { + if (furnitureActivating && (Card.Location == CardLocation.Hand || !DefaultOnBecomeTarget())) return false; + if (Util.ChainContainsCard(new int[]{ _CardId.DivineArsenalAAZEUS_SkyThunder, _CardId.EvenlyMatched, _CardId.EvilswarmExcitonKnight })) return false; + + if (CheckWhetherNegated()) return false; + if (Card.Location != CardLocation.Grave) + { + bool becomeTarget = Card.Location == CardLocation.MonsterZone && DefaultOnBecomeTarget() && !escapeTargetList.Contains(Card); + bool lackUnimportantCost = Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth)); + if (lackUnimportantCost) + { + List handCost = Bot.Hand.Where(card => card != Card).ToList(); + lackUnimportantCost &= handCost.Count() <= 2 && handCost.All(card => card.IsCode(_CardId.MaxxC, _CardId.AshBlossom)); + } + bool activateFlag = becomeTarget; + // set big welcome for lovely + bool canActivateSetBigWelcomeThisTurn = CheckRemainInDeck(CardId.BigWelcomeLabrynth) > 0 && cooclockAffected && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) + && (Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) || CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle)) + && (!Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth) && !setTrapThisTurn.Contains(card))) + && ( + Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)) + || (Bot.HasInGraveyard(CardId.LabrynthCooclock) && !activatedCardIdList.Contains(CardId.LabrynthCooclock + 1)) + || (Bot.HasInHand(CardId.LadyLabrynthOfTheSilverCastle) && !activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle)) + ); + if (canActivateSetBigWelcomeThisTurn && ShouldSetBigWelcome()) + { + bool force = becomeTarget | GetProblematicEnemyCardList(false).Count() > 0; + ClientCard cost = FurnitureGetCost(force); + if (cost != null) + { + AI.SelectCard(cost); + AI.SelectNextCard(CardId.BigWelcomeLabrynth); + activatedCardIdList.Add(Card.Id); + furnitureActivating = true; + SelectSTPlace(null, true); + return true; + } + } + bool keepOnField = (cooclockActivating || cooclockAffected) && activatedCardIdList.Contains(CardId.LabrynthCooclock + 1) + && Card.Location == CardLocation.MonsterZone && !Bot.GetMonsters().Any(card => card.IsFaceup() && card != Card && card.HasSetcode(SetcodeLabrynth)) + && setTrapThisTurn.Count() > 0; + // normal set + activateFlag |= Duel.Phase > DuelPhase.Main2 && !lackUnimportantCost && !keepOnField; + activateFlag |= Bot.HasInGraveyard(new List { CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth }) + && Bot.HasInHand(CardId.TransactionRollback) && !activatedCardIdList.Contains(CardId.TransactionRollback); + if (Duel.CurrentChain.Any(card => card != null && card.Controller == 0 && card.IsCode(CardId.BigWelcomeLabrynth) && card.Location == CardLocation.SpellZone) + && !(Bot.GetMonsterCount() == 1 && Card.Location == CardLocation.MonsterZone)) + { + activateFlag |= !lackUnimportantCost && Bot.GetMonsters().Any(card => card != Card) && !Bot.HasInGraveyard(Card.Id) && !activatedCardIdList.Contains(Card.Id + 1); + } + // trigger cooclock to defense + activateFlag |= !Util.ChainContainPlayer(0) && Duel.Player == 1 && Bot.UnderAttack && Bot.GetMonsterCount() == 0 + && Bot.HasInGraveyard(CardId.LabrynthCooclock) && !activatedCardIdList.Contains(CardId.LabrynthCooclock + 1) + && !(Bot.HasInHand(CardId.LadyLabrynthOfTheSilverCastle) && !activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle)); + + if (activateFlag) + { + ClientCard cost = FurnitureGetCost(becomeTarget); + if (cost != null) + { + AI.SelectCard(cost); + activatedCardIdList.Add(Card.Id); + furnitureActivating = true; + bool setWelcome = Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth)); + setWelcome |= Bot.GetMonsterCount() == 0 && !Bot.HasInHandOrInSpellZone(CardId.WelcomeLabrynth) + && (!Bot.HasInGraveyard(CardId.LabrynthCooclock) || activatedCardIdList.Contains(CardId.LabrynthCooclock + 1)) + && ((Duel.Player == 0 && Duel.Phase > DuelPhase.Main2) || !Bot.Hand.Any(card => card != Card && card.Level <= 4)); + if (setWelcome) + { + AI.SelectNextCard(CardId.WelcomeLabrynth); + } else { + AI.SelectNextCard(CardId.BigWelcomeLabrynth); + } + SelectSTPlace(null, true); + return true; + } + } + } + + return false; + } + + public ClientCard FurnitureGetCost(bool force = false, List ignoreList = null) + { + if (ignoreList == null) ignoreList = new List(); + // advance cost + List advancedCostIdList = new List{ + CardId.TransactionRollback, CardId.LovelyLabrynthOfTheSilverCastle, CardId.AriasTheLabrynthButler, CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie, + CardId.WelcomeLabrynth + }; + foreach (int checkId in advancedCostIdList) + { + ClientCard cost = Bot.Hand.FirstOrDefault(card => !ignoreList.Contains(card) && card.IsCode(checkId) && card != Card); + if (cost != null) return cost; + } + // dump cost + List canCostHand = Bot.Hand.Where(card => !ignoreList.Contains(card)).ToList(); + List appearedCode = new List(canCostHand.Count()); + foreach (ClientCard hand in canCostHand) + { + if (Duel.CurrentChain.Contains(hand)) continue; + if (appearedCode.Contains(hand.Id)) return hand; + appearedCode.Add(hand.Id); + } + List costIdList = new List{ + _CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, CardId.UnchainedSoulOfSharvara, CardId.EscapeOfTheUnchained, CardId.DestructiveDarumaKarmaCannon, + CardId.LabrynthCooclock, CardId.ArianeTheLabrynthServant, CardId.WelcomeLabrynth, CardId.PotOfExtravagance, CardId.LadyLabrynthOfTheSilverCastle + }; + if (force) costIdList.AddRange(new List{CardId.AriannaTheLabrynthServant, _CardId.AshBlossom, CardId.BigWelcomeLabrynth, _CardId.MaxxC}); + foreach (int checkId in costIdList) + { + ClientCard target = canCostHand.FirstOrDefault(card => !Duel.CurrentChain.Contains(card) && card.IsCode(checkId) && !Duel.CurrentChain.Contains(card)); + if (target != null) return target; + } + foreach (int checkId in costIdList) + { + ClientCard target = canCostHand.FirstOrDefault(card => card.IsCode(checkId) && !Duel.CurrentChain.Contains(card)); + if (target != null) return target; + } + + return null; + } + + public bool ShouldSetBigWelcome(bool checkArianna = true) + { + if (CheckWhetherWillbeRemoved()) return false; + bool shouldTriggerBigWelcomeFlag = GetProblematicEnemyCardList(false).Count() > 0; + shouldTriggerBigWelcomeFlag |= Duel.Player == 1 && Duel.Phase > DuelPhase.Main2; + shouldTriggerBigWelcomeFlag |= Duel.Player == 1 && GetProblematicEnemyCardList(false).Count() == 0 && GetProblematicEnemyMonster(selfType: CardType.Monster) == null + && Enemy.Hand.Count() == 1; + if (checkArianna) shouldTriggerBigWelcomeFlag |= Duel.Player == 0 && !summoned && Bot.HasInHandOrHasInMonstersZone(CardId.AriannaTheLabrynthServant) + && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant); + shouldTriggerBigWelcomeFlag |= Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2; + return shouldTriggerBigWelcomeFlag; + } + + public bool LabrynthCooclockActivate() + { + if (Card.Location == CardLocation.Hand) + { + bool shouldTriggerBigWelcomeFlag = false; + if (Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) || CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle)) { + shouldTriggerBigWelcomeFlag |= ShouldSetBigWelcome(); + } + shouldTriggerBigWelcomeFlag &= !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth); + if (shouldTriggerBigWelcomeFlag && !Bot.GetSpells().Any(card => card.IsFacedown() && !setTrapThisTurn.Contains(card) && card.IsCode(CardId.BigWelcomeLabrynth))) + { + // whether have labrynth to trigger cooclock + bool haveBigWelcome = Duel.Player == 0 && Bot.HasInHand(CardId.BigWelcomeLabrynth) + && (Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)) || (!summoned && Bot.Hand.Any(card => card != Card && card.HasType(CardType.Monster) && card.Level <= 4 && card.HasSetcode(SetcodeLabrynth)))); + if (CheckRemainInDeck(CardId.BigWelcomeLabrynth) > 0) + { + foreach (int checkId in new List { CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie }) + { + if (activatedCardIdList.Contains(checkId) || CheckCalledbytheGrave(checkId) > 0) continue; + if (Bot.HasInHand(checkId) && Bot.Hand.Count > 2 || Bot.GetMonsters().Any(card => card.IsFaceup() && !card.IsDisabled() && card.IsCode(checkId)) && Bot.Hand.Count > 1) + { + haveBigWelcome = true; + break; + } + } + } + if (haveBigWelcome) + { + activatedCardIdList.Add(Card.Id); + cooclockActivating = true; + return true; + } + } + + bool haveLabrynth = Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)); + bool triggerFlag = Duel.Player == 1 && Duel.Phase <= DuelPhase.Main2 && setTrapThisTurn.Any(card => !activatedCardIdList.Contains(card.Id)) && haveLabrynth; + triggerFlag |= Duel.Player == 1 && activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle + 1) + && (Bot.GetSpells().Any(card => card.IsFacedown() && card.Type == (int)CardType.Trap) || Util.ChainContainsCard(CardId.LadyLabrynthOfTheSilverCastle)); + triggerFlag |= setTrapThisTurn.Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth)) && haveLabrynth; + triggerFlag |= setTrapThisTurn.Any(card => card.IsFacedown() && card.IsCode(CardId.WelcomeLabrynth) && !activatedCardIdList.Contains(CardId.WelcomeLabrynth)) && haveLabrynth; + + if (triggerFlag) + { + activatedCardIdList.Add(Card.Id); + cooclockActivating = true; + return true; + } + } + + return false; + } + + public bool RecycleActivate() + { + if (Card.Location == CardLocation.Grave) + { + if (Card.IsCode(CardId.LabrynthStovieTorbie, CardId.AriasTheLabrynthButler)) + { + if (CheckShouldNoMoreSpSummon() || GetEmptyMainMonsterZoneCount() + chainSummoningIdList.Count() <= 0) return false; + chainSummoningIdList.Add(Card.Id); + } + if (Card.IsCode(CardId.WelcomeLabrynth)) SelectSTPlace(Card, false); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool ForLinkSummon() + { + if (Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained))) return false; + if (Card.Level > 4) return false; + if (CheckShouldNoMoreSpSummon()) return false; + if (!Bot.HasInExtra(CardId.UnchainedSoulLordOfYama)) return false; + + // check whether need summon for material count + List materialList = GetCanBeUsedForLinkMaterial(true, card => !card.HasRace(CardRace.Fiend)); + int materialCount = materialList.Count(); + if (!activatedCardIdList.Contains(CardId.UnchainedSoulOfSharvara) && Bot.GetSpells().Any(card => card.IsFacedown()) + && (!activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama) || Bot.HasInHand(CardId.UnchainedSoulOfSharvara))) materialCount++; + if (materialCount != 2) + { + if (materialCount != 1 || !materialList.Any(card => card.IsCode(CardId.UnchainedSoulLordOfYama))) return false; + } + + if (!Bot.HasInExtra(CardId.UnchainedSoulLordOfYama) && !materialList.Any(card => card.HasSetcode(SetcodeUnchained))) return false; + bool needSummon = false; + needSummon |= Enemy.GetMonsterCount() > 0 && Bot.HasInExtra(CardId.UnchainedSoulOfAnguish); + needSummon |= Bot.HasInExtra(CardId.UnchainedSoulOfRage); + if (needSummon) + { + // use monster with least attack + YGOSharp.OCGWrapper.NamedCard thisCardData = YGOSharp.OCGWrapper.NamedCard.Get(Card.Id); + if (thisCardData == null) return false; + if (thisCardData.Race != (int)CardRace.Fiend) return false; + foreach (ClientCard hand in Bot.Hand) + { + YGOSharp.OCGWrapper.NamedCard compareCardData = YGOSharp.OCGWrapper.NamedCard.Get(hand.Id); + if (compareCardData == null) continue; + if (!compareCardData.HasType(CardType.Monster) || compareCardData.Level > 4) continue; + if (compareCardData.Attack < thisCardData.Attack) return false; + } + summoned = true; + return true; + } + + return false; + } + public bool ForSynchroSummon() + { + if (Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained))) return false; + if (!Card.IsCode(new List { CardId.LabrynthStovieTorbie, CardId.ArianeTheLabrynthServant, CardId.AriannaTheLabrynthServant })) return false; + if (CheckShouldNoMoreSpSummon()) return false; + if (!Bot.HasInExtra(CardId.ChaosAngel) || dimensionalBarrierAnnouced.Contains(HintMsg.SYNCHRO)) return false; + + bool checkFlag = GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0 && !CheckWhetherNegated(true, true, CardType.Monster); + if (Card.IsCode(CardId.LabrynthStovieTorbie)) + { + if (!Bot.GetMonsters().Any(card => card.IsFaceup() && !card.HasType(CardType.Xyz | CardType.Link) + && card.Level == 8 && card.HasAttribute(CardAttribute.Light | CardAttribute.Dark))) return false; + summoned = true; + return true; + } + else + { + if (!Bot.GetMonsters().Any(card => card.IsFaceup() && !card.HasType(CardType.Xyz | CardType.Link) + && card.Level == 6 && card.HasAttribute(CardAttribute.Light | CardAttribute.Dark))) return false; + summoned = true; + return true; + } + } + public bool ForAnimaSummon() + { + if (banSpSummonExceptFiendCount > 0 || !Bot.HasInExtra(CardId.RelinquishedAnima)) return false; + if (CheckWhetherNegated() || Duel.Turn == 1) return false; + + bool checkFlag = Bot.MonsterZone[1] == null && Enemy.MonsterZone[6] != null && Enemy.MonsterZone[6].HasType(CardType.Link) && Enemy.MonsterZone[6].HasLinkMarker(CardLinkMarker.Top); + checkFlag |= Bot.MonsterZone[3] == null && Enemy.MonsterZone[5] != null && Enemy.MonsterZone[5].HasType(CardType.Link) && Enemy.MonsterZone[5].HasLinkMarker(CardLinkMarker.Top); + if (Bot.GetMonstersExtraZoneCount() == 0) checkFlag |= Enemy.MonsterZone[1] != null || Enemy.MonsterZone[3] != null; + + return checkFlag; + } + + public bool LabrynthForCooClockSummon() + { + if (!cooclockAffected) return false; + if (Card.Level > 4 || !Card.HasSetcode(SetcodeLabrynth)) return false; + bool welcomeFlag = Bot.Hand.Any(card => (card.IsCode(CardId.WelcomeLabrynth) && !activatedCardIdList.Contains(CardId.WelcomeLabrynth)) + || (card.IsCode(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth))); + welcomeFlag |= Bot.GetSpells().Any(card => card.IsFacedown() && setTrapThisTurn.Contains(card) && ( + (card.IsCode(CardId.WelcomeLabrynth) && !activatedCardIdList.Contains(CardId.WelcomeLabrynth)) + || (card.IsCode(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth)))); + if (welcomeFlag && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth))) + { + // summon highest attack + int currentAttack = 0; + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(Card.Id); + if (cardData != null) currentAttack = cardData.Attack; + List summonList = Bot.Hand.Where(card => card.IsMonster() && card.Level <= 4 && card.HasSetcode(SetcodeLabrynth)).ToList(); + foreach (ClientCard checkCard in summonList) + { + cardData = YGOSharp.OCGWrapper.NamedCard.Get(checkCard.Id); + if (cardData != null && cardData.Attack < currentAttack) return false; + } + + return true; + } + return false; + } + + public bool ForBigWelcomeSummon() + { + if (Bot.HasInSpellZone(CardId.BigWelcomeLabrynth) && Bot.GetMonsterCount() == 0 && Card.Level <= 4) + { + summoned = true; + return true; + } + return false; + } + + public bool PotOfExtravaganceActivate() + { + if (CheckWhetherNegated()) return false; + SelectSTPlace(Card, true); + activatedCardIdList.Add(Card.Id); + AI.SelectOption(1); + return true; + } + + public bool WelcomeLabrynthActivate() + { + return WelcomeLabrynthActivateCheck(false); + } + public bool WelcomeLabrynthActivateCopy() + { + return WelcomeLabrynthActivateCheck(true); + } + public bool WelcomeLabrynthSetCheck() + { + return !CheckShouldNoMoreSpSummon() && WelcomeLabrynthActivateCheck(true, true); + } + public bool WelcomeLabrynthActivateCheck(bool onlyCheck = false, bool noSelect = false) + { + if (Card.Location == CardLocation.SpellZone || onlyCheck) + { + if (GetEmptyMainMonsterZoneCount() == 0) return false; + if (CheckShouldNoMoreSpSummon()) return false; + bool activateTimingFlag = Duel.Phase > DuelPhase.Main2 || (Card.IsCode(CardId.AriasTheLabrynthButler) && (CurrentTiming & hintTimingMainEnd) > 0); + + bool becomeTarget = Card.Location == CardLocation.SpellZone && DefaultOnBecomeTarget(); + if ((Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2 || Duel.Player == 1 && activateTimingFlag) + && CheckRemainInDeck(CardId.ArianeTheLabrynthServant) > 0 && Bot.HasInHandOrInSpellZone(CardId.TransactionRollback) + && !chainSummoningIdList.Contains(CardId.ArianeTheLabrynthServant)) + { + if (!noSelect) + { + chainSummoningIdList.Add(CardId.ArianeTheLabrynthServant); + activatedCardIdList.Add(Card.Id); + } + return true; + } + bool ariannaCheck = !Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth) + || !(Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) || CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle)); + ariannaCheck |= Duel.Player == 1 && activateTimingFlag; + ariannaCheck |= Duel.Player == 0; + if (ariannaCheck) + { + if (CheckRemainInDeck(CardId.AriannaTheLabrynthServant) > 0 && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) + && !CheckWhetherNegated(true, true, CardType.Monster) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + if (!noSelect) + { + chainSummoningIdList.Add(CardId.AriannaTheLabrynthServant); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + if (Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth)) + { + if (Bot.HasInHand(CardId.LovelyLabrynthOfTheSilverCastle) && CheckRemainInDeck(CardId.AriasTheLabrynthButler) > 0 + && !chainSummoningIdList.Contains(CardId.AriasTheLabrynthButler) && !Bot.HasInMonstersZone(CardId.AriasTheLabrynthButler, true, false, true)) + { + if (!noSelect) + { + chainSummoningIdList.Add(CardId.AriasTheLabrynthButler); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + + bool activateFlag = becomeTarget; + activateFlag |= Bot.UnderAttack && Bot.GetMonsterCount() == 0; + activateFlag |= ShouldSetBigWelcome(false); + if (activateFlag) + { + if (!noSelect) + { + if (Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) + && CheckRemainInDeck(CardId.LovelyLabrynthOfTheSilverCastle) > 0 && !chainSummoningIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle)) + { + chainSummoningIdList.Add(CardId.LovelyLabrynthOfTheSilverCastle); + } + else if (!activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && CheckRemainInDeck(CardId.AriannaTheLabrynthServant) > 0 + && !CheckWhetherNegated(true, true, CardType.Monster) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + chainSummoningIdList.Add(CardId.AriannaTheLabrynthServant); + } + else if (Bot.HasInGraveyard(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) + && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasRace(CardRace.Fiend) && card.Level >= 8) + && CheckRemainInDeck(CardId.LadyLabrynthOfTheSilverCastle) > 0 && !chainSummoningIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle)) + { + chainSummoningIdList.Add(CardId.LadyLabrynthOfTheSilverCastle); + } + else { + int selectId = 0; + List checkIdList = new List{CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock}; + foreach (int checkId in checkIdList) + { + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId) && CheckRemainInDeck(checkId) > 0 + && !chainSummoningIdList.Contains(checkId)) + { + selectId = checkId; + break; + } + } + List fullCheckIdList = new List{ + CardId.LadyLabrynthOfTheSilverCastle, CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock, + CardId.AriasTheLabrynthButler, CardId.ArianeTheLabrynthServant, CardId.AriannaTheLabrynthServant + }; + if (selectId == 0) + { + foreach (int checkId in fullCheckIdList) + { + if (CheckRemainInDeck(checkId) > 0 && !chainSummoningIdList.Contains(checkId)) + { + selectId = checkId; + break; + } + } + } + if (selectId > 0) { + chainSummoningIdList.Add(selectId); + } + } + ResetCooclockEffect(onlyCheck); + + activatedCardIdList.Add(Card.Id); + } + return true; + } + + } + + return false; + } + + public bool TransactionRollbackActivate() + { + if (Card.Location == CardLocation.Grave) + { + SortedList> checkList = new SortedList> { + {CardId.BigWelcomeLabrynth, BigWelcomeLabrynthActivateCopy}, + {_CardId.DimensionalBarrier, DimensionalBarrierActivate}, + {CardId.EscapeOfTheUnchained, EscapeOfTheUnchainedActivateCopy}, + {_CardId.InfiniteImpermanence, InfiniteImpermanenceActivateCopy}, + {CardId.WelcomeLabrynth, WelcomeLabrynthActivateCopy}, + {CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonActivate} + }; + foreach (KeyValuePair> pair in checkList) + { + if (Bot.HasInGraveyard(pair.Key) && pair.Value()) + { + rollbackCopyCardId = pair.Key; + AI.SelectCard(pair.Key); + return true; + } + } + } + if (Card.Location == CardLocation.SpellZone) + { + if (CheckWhetherNegated()) return false; + SortedList> checkList = new SortedList> { + {CardId.WelcomeLabrynth, WelcomeLabrynthActivateCopy}, + {_CardId.CompulsoryEvacuationDevice, DefaultCompulsoryEvacuationDevice }, + {CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonActivate}, + {_CardId.DimensionalBarrier, DimensionalBarrierActivate}, + {CardId.EscapeOfTheUnchained, EscapeOfTheUnchainedActivateCopy}, + {_CardId.InfiniteImpermanence, InfiniteImpermanenceActivateCopy}, + {_CardId.BreakthroughSkill, DefaultBreakthroughSkill}, + {CardId.BigWelcomeLabrynth, BigWelcomeLabrynthActivateCopy} + }; + foreach (KeyValuePair> pair in checkList) + { + if (Enemy.HasInGraveyard(pair.Key) && pair.Value()) + { + rollbackCopyCardId = pair.Key; + AI.SelectCard(pair.Key); + ResetCooclockEffect(false); + return true; + } + } + } + return false; + } + + public bool InfiniteImpermanenceActivate() + { + return InfiniteImpermanenceActivateCheck(false); + } + public bool InfiniteImpermanenceActivateCopy() + { + return InfiniteImpermanenceActivateCheck(true); + } + public bool InfiniteImpermanenceSetCheck() + { + return InfiniteImpermanenceActivateCheck(true, true); + } + public bool InfiniteImpermanenceActivateCheck(bool onlyCheck = false, bool noSelect = false) + { + if (CheckWhetherNegated()) return false; + + ClientCard LastChainCard = Util.GetLastChainCard(); + + // negate spells + if (Card.Location == CardLocation.SpellZone) + { + int thisSeq = -1; + int thatSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) thisSeq = i; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) thatSeq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) thatSeq = i; + } + if ( (thisSeq * thatSeq >= 0 && thisSeq + thatSeq == 4) + || Util.IsChainTarget(Card) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true, selfType: CardType.Trap); + if (!noSelect) + { + if (target != null) + { + AI.SelectCard(target); + } else { + AI.SelectCard(Enemy.GetMonsters()); + } + } + if (!onlyCheck) + { + infiniteImpermanenceList.Add(thatSeq); + if (cooclockAffected && setTrapThisTurn.Contains(Card)) + { + cooclockAffected = false; + setTrapThisTurn.Remove(Card); + } + } + return true; + } + } + + // negate monster + List shouldNegateList = GetMonsterListForTargetNegate(true, CardType.Trap); + if (shouldNegateList.Count() > 0) + { + ClientCard negateTarget = shouldNegateList[0]; + currentNegateMonsterList.Add(negateTarget); + + if (Card.Location == CardLocation.SpellZone && !onlyCheck) + { + for (int i = 0; i < 5; ++ i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + if (!noSelect) AI.SelectCard(negateTarget); + currentDestroyCardList.Add(negateTarget); + ResetCooclockEffect(onlyCheck); + return true; + } + + return false; + } + + public bool DestructiveDarumaKarmaCannonActivate() + { + return DestructiveDarumaKarmaCannonActivateCheck(false); + } + public bool DestructiveDarumaKarmaCannonSetCheck() + { + return DestructiveDarumaKarmaCannonActivateCheck(true); + } + public bool DestructiveDarumaKarmaCannonActivateCheck(bool noSelect = false) + { + bool becomeTarget = Card.Location == CardLocation.SpellZone && DefaultOnBecomeTarget(); + bool activateFlag = becomeTarget && Util.IsOneEnemyBetter(true); + bool canTriggerLovely = + (!activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth) && (!cooclockAffected || !setTrapThisTurn.Contains(card))) + || Util.ChainContainsCard(CardId.BigWelcomeLabrynth)) + && (Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) || (CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle) && Bot.GetMonsterCount() > 0)) + && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1); + + activateFlag |= Bot.UnderAttack && (Bot.BattlingMonster?.GetDefensePower() ?? 0) <= (Enemy.BattlingMonster?.GetDefensePower() ?? 0) && !Util.ChainContainPlayer(0) && !canTriggerLovely; + activateFlag |= Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2 && Bot.GetMonsterCount() == 0 && Enemy.GetMonsterCount() > 0; + activateFlag |= Enemy.HasInMonstersZone(CardId.AccesscodeTalker, true) && !Util.ChainContainPlayer(0); + int linkCount = 0; + foreach (ClientCard monster in Enemy.GetMonsters()) + { + if (monster.IsFacedown()) continue; + if (!monster.HasType(CardType.Link)) linkCount++; + else linkCount += monster.LinkCount; + } + activateFlag |= linkCount >= 6 && Util.IsOneEnemyBetter(true); + if (activateFlag) + { + if (!noSelect) + { + currentDestroyCardList.AddRange(Enemy.GetMonsters()); + escapeTargetList.AddRange(Bot.GetMonsters()); + } + return true; + } + + return false; + } + + public bool EscapeOfTheUnchainedActivate() + { + return EscapeOfTheUnchainedActivateCheck(false); + } + public bool EscapeOfTheUnchainedActivateCopy() + { + return EscapeOfTheUnchainedActivateCheck(true); + } + public bool EscapeOfTheUnchainedActivateCheck(bool onlyCheck = false, bool noSelect = false) + { + if (Card.Location == CardLocation.SpellZone || onlyCheck) + { + // select targeted unchained + ClientCard selfTarget = Bot.GetMonsters().FirstOrDefault(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained) + && Duel.ChainTargets.Contains(card) && !escapeTargetList.Contains(card)); + if (selfTarget == null) + { + selfTarget = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained)) + .OrderBy(card => card.Attack).FirstOrDefault(); + } + if (selfTarget == null) return false; + // destroy danger card + List dangerList = GetProblematicEnemyCardList(true, selfType: CardType.Trap); + if (dangerList.Count() > 0 && Duel.LastChainPlayer != 0) + { + if (!noSelect) + { + AI.SelectCard(selfTarget); + AI.SelectNextCard(dangerList); + escapeTargetList.Add(selfTarget); + currentDestroyCardList.Add(dangerList[0]); + activatedCardIdList.Add(Card.Id); + } + return true; + } + + // best monster + int botBestPower = Util.GetBestPower(Bot); + if (Duel.Player == 1 && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + List dangerMonsters = Enemy.GetMonsters().Where(card => card.IsFaceup() && card.Attack >= botBestPower + && !currentDestroyCardList.Contains(card) && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeSpellTrapTarget()) + .OrderByDescending(card => card.Attack).ToList(); + if (dangerMonsters.Count() > 0) + { + if (!noSelect) + { + AI.SelectCard(selfTarget); + AI.SelectNextCard(dangerMonsters); + escapeTargetList.Add(selfTarget); + currentDestroyCardList.Add(dangerMonsters[0]); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + + // end phase + bool activateFlag = Duel.Player == 1 && Duel.Phase > DuelPhase.Main2 + && ((Bot.HasInGraveyard(CardId.UnchainedSoulLordOfYama) && !activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama + 1)) + || (Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1))); + activateFlag |= DefaultOnBecomeTarget() && Card.Location == CardLocation.SpellZone && !Util.ChainContainsCard(_CardId.EvenlyMatched); + if (activateFlag) + { + List destroyTarget = GetNormalEnemyTargetList(true, true, CardType.Trap); + if (destroyTarget.Count() > 0) + { + if (!noSelect) + { + AI.SelectCard(selfTarget); + AI.SelectNextCard(destroyTarget); + escapeTargetList.Add(selfTarget); + currentDestroyCardList.Add(destroyTarget[0]); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + + } else { + if (!noSelect) + { + AI.SelectCard(CardId.UnchainedSoulOfSharvara); + activatedCardIdList.Add(Card.Id + 1); + } + return true; + } + + return false; + } + + public bool DimensionalBarrierActivate() + { + if (Duel.Player == 0 && Duel.Turn == 1) return false; + if (CheckWhetherNegated()) return false; + Dictionary> checkDict = new Dictionary> + { + {HintMsg.RITUAL, DimensionalBarrierForRitual}, + {HintMsg.FUSION, DimensionalBarrierForFusion}, + {HintMsg.SYNCHRO, DimensionalBarrierForSynchro}, + {HintMsg.XYZ, DimensionalBarrierForXyz}, + {HintMsg.PENDULUM, DimensionalBarrierForPendulum}, + }; + foreach (KeyValuePair> checkType in checkDict) + { + if (dimensionalBarrierAnnouced.Contains(checkType.Key)) continue; + if (checkType.Value()) { + ResetCooclockEffect(false); + return true; + } + } + + return DefaultOnBecomeTarget(); + } + public bool DimensionalBarrierForRitual() + { + foreach (ClientCard chainCard in Duel.CurrentChain) + { + if (chainCard != null && chainCard.Controller == 1 && !chainCard.IsDisabled() && chainCard.HasType(CardType.Ritual) + && (chainCard.HasType(CardType.Spell) || (chainCard.Location == CardLocation.MonsterZone && !currentNegateMonsterList.Contains(chainCard)))) + { + if (dimensionBarrierAnnouncing) currentNegateMonsterList.Add(chainCard); + return true; + } + } + + return false; + } + public bool DimensionalBarrierForFusion() + { + foreach (ClientCard chainCard in Duel.CurrentChain) + { + if (chainCard != null && chainCard.Controller == 1 && !chainCard.IsDisabled() && (chainCard.IsFusionSpell() + || (chainCard.HasType(CardType.Fusion) && chainCard.Location == CardLocation.MonsterZone && !currentNegateMonsterList.Contains(chainCard)))) + { + if (dimensionBarrierAnnouncing) currentNegateMonsterList.Add(chainCard); + return true; + } + } + + return false; + } + public bool DimensionalBarrierForSynchro() + { + foreach (ClientCard chainCard in Duel.CurrentChain) + { + if (chainCard != null && chainCard.Controller == 1 && !chainCard.IsDisabled() + && chainCard.HasType(CardType.Synchro) && chainCard.Location == CardLocation.MonsterZone && !currentNegateMonsterList.Contains(chainCard)) + { + if (dimensionBarrierAnnouncing) currentNegateMonsterList.Add(chainCard); + return true; + } + } + if (Duel.Player == 1 && !Util.ChainContainsCard(CardId.DestructiveDarumaKarmaCannon) && Enemy.ExtraDeck.Count() > 0) + { + bool tunerCheck = false; + bool nontunerCheck = false; + foreach (ClientCard monster in Enemy.GetMonsters()) + { + if (monster.IsFacedown() || monster.HasType(CardType.Xyz | CardType.Link)) continue; + if (monster.HasType(CardType.Tuner)) tunerCheck = true; + else nontunerCheck = true; + } + if (tunerCheck && nontunerCheck) return true; + } + + return false; + } + public bool DimensionalBarrierForXyz() + { + foreach (ClientCard chainCard in Duel.CurrentChain) + { + if (chainCard != null && chainCard.Controller == 1 && !chainCard.IsDisabled() + && chainCard.HasType(CardType.Xyz) && chainCard.Location == CardLocation.MonsterZone && !currentNegateMonsterList.Contains(chainCard)) + { + if (dimensionBarrierAnnouncing) currentNegateMonsterList.Add(chainCard); + return true; + } + } + if (Duel.Player == 1 && !Util.ChainContainsCard(CardId.DestructiveDarumaKarmaCannon) && Enemy.ExtraDeck.Count() > 0) + { + List existsLevel = new List(6); + foreach (ClientCard monster in Enemy.GetMonsters()) + { + if (monster.IsFacedown()) continue; + if (monster.IsOneForXyz()) return true; + if (monster.HasType(CardType.Xyz | CardType.Token)) continue; + int level = monster.Level; + if (level != 2 && monster.HasType(CardType.Link)) continue; + if (existsLevel.Contains(level)) return true; + existsLevel.Add(level); + } + } + + return false; + } + public bool DimensionalBarrierForPendulum() + { + foreach (ClientCard chainCard in Duel.CurrentChain) + { + if (chainCard != null && chainCard.Controller == 1 && !chainCard.IsDisabled() + && chainCard.HasType(CardType.Pendulum) && chainCard.Location == CardLocation.MonsterZone && !currentNegateMonsterList.Contains(chainCard)) + { + if (dimensionBarrierAnnouncing) currentNegateMonsterList.Add(chainCard); + return true; + } + } + + ClientCard l = Enemy.SpellZone[6]; + ClientCard r = Enemy.SpellZone[7]; + if (l != null && r != null && l.LScale != r.RScale) return true; + + return false; + } + + public bool BigWelcomeLabrynthActivate() + { + return BigWelcomeLabrynthActivateCheck(false); + } + public bool BigWelcomeLabrynthBecomeTargetActivate() + { + if (DefaultOnBecomeTarget()) return BigWelcomeLabrynthActivateCheck(false); + return false; + } + public bool BigWelcomeLabrynthActivateCopy() + { + return BigWelcomeLabrynthActivateCheck(true); + } + public bool BigWelcomeLabrynthSetCheck() + { + return !CheckShouldNoMoreSpSummon() && BigWelcomeLabrynthActivateCheck(true, true); + } + public bool BigWelcomeLabrynthActivateCheck(bool onlyCheck = false, bool noSelect = false) + { + if (CheckWhetherNegated()) return false; + if (Card.Location != CardLocation.SpellZone && !onlyCheck) return false; + if (GetEmptyMainMonsterZoneCount() == 0) return false; + bool activateTimingFlag = Duel.Phase > DuelPhase.Main2 || (Card.IsCode(CardId.AriasTheLabrynthButler) && (CurrentTiming & hintTimingMainEnd) > 0); + + bool needDestroyFlag = GetProblematicEnemyCardList(false).Count() > 0; + needDestroyFlag |= activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && activateTimingFlag; + needDestroyFlag |= Bot.UnderAttack && (Bot.BattlingMonster?.GetDefensePower() ?? 0) <= (Enemy.BattlingMonster?.GetDefensePower() ?? 0) && Duel.LastChainPlayer != 0; + needDestroyFlag |= Duel.Turn == 1 && Duel.Player == 0 && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1); + needDestroyFlag |= Duel.Turn == 1 && Enemy.GetMonsterCount() == 0 && Enemy.GetSpellCount() == 0 && Enemy.Hand.Count > 0 + && (CurrentTiming & hintTimingMainEnd) > 0; + + // do not activate when welcome is activating + bool haveEnemyChain = false; + bool haveWelcome = false; + foreach (ClientCard chain in Duel.CurrentChain) + { + if (chain != null) + { + if (chain.Controller == 1) + { + haveEnemyChain = true; + break; + } + if (chain.IsCode(CardId.WelcomeLabrynth, CardId.TransactionRollback, CardId.LadyLabrynthOfTheSilverCastle)) haveWelcome = true; + } + } + if (haveWelcome && !haveEnemyChain) return false; + + // escape target + List targetList = Bot.GetMonsters(); + foreach (ClientCard target in targetList) + { + if (Duel.ChainTargets.Contains(target) && !escapeTargetList.Contains(target) + && !(target.IsCode(CardId.UnchainedSoulOfRage, CardId.UnchainedSoulOfAnguish) && Duel.CurrentChain.Contains(target))) + { + Logger.DebugWriteLine("[BigWelcome]escape target"); + if (!noSelect) + { + bigwelcomeEscaseTarget = target; + escapeTargetList.Add(target); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + + if (Bot.GetMonsterCount() > 0) + { + bool flag1 = needDestroyFlag && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1) && (Util.ChainContainPlayer(1) || Duel.LastChainPlayer != 0); + bool flag2 = DefaultOnBecomeTarget(); + bool flag3 = Duel.Player == 1 && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && activateTimingFlag; + bool flag4 = Duel.Player == 0 && Duel.LastChainPlayer != 0 && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth); + Logger.DebugWriteLine("[BigWelcome count>0]flag: "+ flag1 + " " + flag2 + " " + flag3 + " " + flag4); + needDestroyFlag |= flag3; + if (flag1 || flag2 || flag3 || flag4) + { + bool spSummonLovely = CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle) && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1); + bool haveLovely = Bot.GetMonsters().Any(card => card.IsFaceup() && card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)); + if (!noSelect) + { + activatedCardIdList.Add(Card.Id); + } + ResetCooclockEffect(onlyCheck); + return true; + } + } + else { + bool activateFlag = DefaultOnBecomeTarget(); + activateFlag |= Duel.Player == 1 && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && activateTimingFlag; + activateFlag |= Duel.Player == 0 && !summoned && !Bot.HasInHand(CardId.AriannaTheLabrynthServant) && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) + && !(Duel.Phase < DuelPhase.Main1 && Bot.HasInHand(CardId.PotOfExtravagance) && Bot.ExtraDeck.Count() >= 3) + && !(Duel.CurrentChain.Any(card => card.IsCode(CardId.PotOfExtravagance) && card.Controller == 0)); + if (activateFlag && !noSelect) + { + activatedCardIdList.Add(Card.Id); + ResetCooclockEffect(onlyCheck); + return true; + } + } + + return false; + } + + public bool BigWelcomeLabrynthGraveActivate() + { + if (Card.Location == CardLocation.Grave) + { + // bounce enemy + if (Bot.GetMonsters().Any(card => card.Level >= 8 && card.IsFaceup() && card.HasRace(CardRace.Fiend) && !card.HasType(CardType.Xyz | CardType.Link))) + { + // danger monster + ClientCard problemCard = GetProblematicEnemyMonster(-1, true, true, CardType.Trap); + if (problemCard != null) + { + AI.SelectCard(problemCard); + currentDestroyCardList.Add(problemCard); + activatedCardIdList.Add(Card.Id); + return true; + } + // problem spell + if (!Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) || + activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle) && activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1)) + { + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && c.IsFaceup() + && c.IsFloodgate() + && !c.IsShouldNotBeTarget() && (c.HasType(CardType.Trap) || Duel.Player == 0)).ToList(); + + problemEnemySpellList.AddRange(Enemy.SpellZone.Where(c => c?.Data != null && c.IsFaceup() && !problemEnemySpellList.Contains(c) + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous) + && !c.IsShouldNotBeTarget() && (c.HasType(CardType.Trap) || Duel.Player == 0)).ToList()); + + if (problemEnemySpellList.Count() > 0) + { + AI.SelectCard(problemEnemySpellList); + currentDestroyCardList.Add(problemEnemySpellList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + // best monster + int botBestPower = Util.GetBestPower(Bot); + if (Duel.Player == 1 && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + List dangerMonsters = Enemy.GetMonsters().Where(card => card.IsFaceup() && card.Attack >= botBestPower + && !currentDestroyCardList.Contains(card) && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeSpellTrapTarget()) + .OrderByDescending(card => card.Attack).ToList(); + if (dangerMonsters.Count() > 0) + { + AI.SelectCard(dangerMonsters); + currentDestroyCardList.Add(dangerMonsters[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + // end phase + if (Duel.Phase > DuelPhase.Main2) + { + List returnList = GetNormalEnemyTargetList(true, true, CardType.Trap); + if (returnList.Count() > 0) + { + AI.SelectCard(returnList); + currentDestroyCardList.Add(returnList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + + // escape target + List targetList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasRace(CardRace.Fiend)).ToList(); + foreach (ClientCard target in targetList) + { + if (Duel.ChainTargets.Contains(target) && !escapeTargetList.Contains(target) + && !(target.IsCode(CardId.UnchainedSoulOfRage, CardId.UnchainedSoulOfAnguish) && Duel.CurrentChain.Contains(target))) + { + AI.SelectCard(target); + escapeTargetList.Add(target); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + // bounce arianna + if (Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2 && !summoned && !Bot.HasInHand(CardId.AriannaTheLabrynthServant) + && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + ClientCard target = targetList.FirstOrDefault(card => card.IsCode(CardId.AriannaTheLabrynthServant)); + if (target != null) + { + AI.SelectCard(target); + escapeTargetList.Add(target); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + // trigger furniture/welcome + List checkFurnitureList = new List(Bot.Hand); + checkFurnitureList.AddRange(Bot.GetMonsters()); + if ((CheckRemainInDeck(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth) == 0 + || !checkFurnitureList.Any(card => card.IsCode(CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie))) + && Duel.LastChainPlayer < 0 && Duel.Player == 0 && !Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) + && !(cooclockAffected && Bot.HasInHandOrInSpellZone(CardId.BigWelcomeLabrynth))) + { + int checkCount = 0; + List checkIdList = new List { CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie, CardId.WelcomeLabrynth }; + foreach (int checkId in checkIdList) + { + if (Bot.HasInGraveyard(checkId) && !activatedCardIdList.Contains(checkId + 1)) checkCount++; + } + if (checkCount > 0) + { + ClientCard target = targetList.FirstOrDefault(card => card.IsFaceup() && card.HasRace(CardRace.Fiend) && + ((card.Level <= 4 && !card.HasType(CardType.Link | CardType.Xyz | CardType.Synchro)) || card.IsCode(CardId.LadyLabrynthOfTheSilverCastle))); + if (target != null) + { + AI.SelectCard(target); + escapeTargetList.Add(target); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + } + + return false; + } + + public bool ChaosAngelSpSummonWith2Monster() + { + if (CheckShouldNoMoreSpSummon(false)) return false; + + List level2MonsterList = new List(); + List level4MonsterList = new List(); + List level6MonsterList = new List(); + List level8MonsterList = new List(); + foreach (ClientCard monster in Bot.GetMonsters()) + { + if (monster.IsFaceup() && !monster.HasType(CardType.Xyz | CardType.Link) && monster.HasAttribute(CardAttribute.Light | CardAttribute.Dark)) + { + if (monster.Level == 2) level2MonsterList.Add(monster); + if (monster.Level == 4) level4MonsterList.Add(monster); + if (monster.Level == 6) level6MonsterList.Add(monster); + if (monster.Level == 8) level8MonsterList.Add(monster); + } + } + level2MonsterList.Sort(CompareUsableAttack); + level4MonsterList.Sort(CompareUsableAttack); + level6MonsterList.Sort(CompareUsableAttack); + level8MonsterList.Sort(CompareUsableAttack); + bool checkFlag = GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0 && !CheckWhetherNegated(true, true, CardType.Monster); + if (Util.GetBestPower(Bot, true) <= Util.GetBestPower(Enemy)) + { + checkFlag |= Util.GetBestPower(Enemy) <= 3500; + checkFlag |= !Util.GetBestEnemyMonster().IsShouldNotBeTarget() && !Util.GetBestEnemyMonster().IsShouldNotBeMonsterTarget(); + } + // 4+6 + if (level4MonsterList.Count() > 0 && level6MonsterList.Count() > 0) + { + List materials = new List{level4MonsterList[0], level6MonsterList[0]}; + bool summonFlag = checkFlag; + if (Enemy.GetMonsterCount() == 0 && Duel.Phase < DuelPhase.Main2) + summonFlag |= GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(materials) + 3500 >= Enemy.LifePoints; + if (summonFlag) + { + AI.SelectMaterials(materials); + return true; + } + } + // 2+8 + if (level2MonsterList.Count() > 0 && level8MonsterList.Count() > 0) + { + foreach (ClientCard level2 in level2MonsterList) + { + foreach (ClientCard level8 in level8MonsterList) + { + List materials = new List{level2, level8}; + if (checkFlag && !(level8.IsCode(CardId.LovelyLabrynthOfTheSilverCastle) && !level8.IsDisabled() && Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth))) + { + AI.SelectMaterials(materials); + return true; + } + if (Enemy.GetMonsterCount() == 0 && GetMaterialAttack(materials) < 3500 && Duel.Phase < DuelPhase.Main2) + { + if (GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(materials) + 3500 >= Enemy.LifePoints) + { + AI.SelectMaterials(materials); + return true; + } + } + } + } + } + return false; + } + public bool ChaosAngelSpSummonWith3Monster() + { + if (CheckShouldNoMoreSpSummon(false)) return false; + + List level2MonsterList = new List(); + List level4MonsterList = new List(); + foreach (ClientCard monster in Bot.GetMonsters()) + { + if (monster.IsFaceup() && !monster.HasType(CardType.Xyz | CardType.Link) && monster.HasAttribute(CardAttribute.Light | CardAttribute.Dark)) + { + if (monster.Level == 2) level2MonsterList.Add(monster); + if (monster.Level == 4) level4MonsterList.Add(monster); + } + } + level2MonsterList.Sort(CompareUsableAttack); + level4MonsterList.Sort(CompareUsableAttack); + bool checkFlag = GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0 && !CheckWhetherNegated(true, true, CardType.Monster); + if (Util.GetBestPower(Bot, true) <= Util.GetBestPower(Enemy)) + { + checkFlag |= Util.GetBestPower(Enemy) <= 3500; + checkFlag |= !Util.GetBestEnemyMonster().IsShouldNotBeTarget() && !Util.GetBestEnemyMonster().IsShouldNotBeMonsterTarget(); + } + // 2+4+4 + if (level2MonsterList.Count() >= 1 && level4MonsterList.Count() >= 2) + { + foreach (ClientCard level2 in level2MonsterList) + { + for (int level4Index1 = 0; level4Index1 < level4MonsterList.Count() - 1; ++level4Index1) + { + ClientCard level41 = level4MonsterList[level4Index1]; + for (int level4Index2 = level4Index1 + 1; level4Index2 < level4MonsterList.Count(); ++level4Index2) + { + ClientCard level42 = level4MonsterList[level4Index2]; + List materials = new List { level2, level41, level42 }; + bool summonFlag = checkFlag; + if (Enemy.GetMonsterCount() == 0 && Duel.Phase < DuelPhase.Main2) + summonFlag |= GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(materials) + 3500 >= Enemy.LifePoints; + if (summonFlag) + { + AI.SelectMaterials(materials); + return true; + } + } + } + } + } + return false; + } + public bool ChaosAngelActivate() + { + List targetList = GetNormalEnemyTargetList(true, true, CardType.Monster); + if (targetList.Count() > 0) + { + AI.SelectCard(targetList); + currentDestroyCardList.Add(targetList[0]); + return true; + } + + return false; + } + + public bool SummonForTYPHONCheck() + { + if (!Bot.HasInExtra(CardId.SuperStarslayerTYPHON) || Bot.GetMonsters().Any(card => card.IsFaceup())) return false; + if (enemySpSummonFromExLastTurn < 2 && enemySpSummonFromExThisTurn < 2) return false; + if (Card.Level > 4) return false; + + int currentAttack = 0; + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(Card.Id); + if (cardData != null) currentAttack = cardData.Attack; + List summonList = Bot.Hand.Where(card => card.IsMonster() && card.Level <= 4).ToList(); + foreach (ClientCard checkCard in summonList) + { + cardData = YGOSharp.OCGWrapper.NamedCard.Get(checkCard.Id); + if (cardData != null && cardData.Attack < currentAttack) return false; + } + + return true; + } + public bool SuperStarslayerTYPHONSpSummon() + { + ClientCard material = Bot.GetMonsters().Where(card => card.IsFaceup()).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (material == null || (material.Attack >= 2900 && material.Owner == 0)) return false; + + bool checkFlag = GetProblematicEnemyMonster(material.Attack) != null; + checkFlag |= material.Level <= 4; + checkFlag &= !(material.HasType(CardType.Link) && Duel.Phase >= DuelPhase.Main2); + if (checkFlag) + { + Logger.DebugWriteLine("*** TYPHON select: " + material.Name ?? "UnkonwCard"); + AI.SelectMaterials(material); + return true; + } + + return false; + } + public bool SuperStarslayerTYPHONActivate() + { + if (CheckWhetherNegated()) return false; + List targetList = new List(); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.IsFloodgate() && c.IsFaceup()).OrderByDescending(card => card.Attack)); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.IsMonsterDangerous() && c.IsFaceup()).OrderByDescending(card => card.Attack)); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.IsMonsterInvincible() && c.IsFaceup()).OrderByDescending(card => card.Attack)); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.GetDefensePower() >= Util.GetBestAttack(Bot) && c.IsAttack()).OrderByDescending(card => card.Attack)); + if (Duel.Phase >= DuelPhase.Main2) + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link | CardType.SpSummon)).OrderByDescending(card => card.Attack)); + + if (targetList.Count() > 0) + { + targetList.AddRange(Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card)).OrderByDescending(card => card.Attack)); + targetList.AddRange(ShuffleList(Enemy.GetMonsters().Where(card => card.IsFacedown() && !targetList.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Bot.GetMonsters().Where(card => card.IsFacedown() && !targetList.Contains(card)).ToList())); + targetList.AddRange(Bot.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card)).OrderBy(card => card.Attack)); + AI.SelectCard(Card.Overlays); + Logger.DebugWriteLine("TYPHON first target: " + targetList[0]?.Name ?? "UNKNOWN"); + AI.SelectNextCard(targetList); + return true; + } + + return false; + } + + public bool UnchainedAbominationSpSummon() + { + if (CheckShouldNoMoreSpSummon(false)) return false; + if (Enemy.GetMonsterCount() > 0 && Bot.HasInMonstersZone(CardId.UnchainedSoulOfAnguish) && !activatedCardIdList.Contains(CardId.UnchainedSoulOfAnguish)) return false; + List> usableMaterialMultiList = new List>(); + // anguish + 1 + ClientCard anguish = Bot.GetMonsters().FirstOrDefault(card => card.IsCode(CardId.UnchainedSoulOfAnguish)); + if (anguish != null) + { + List materials = GetCanBeUsedForLinkMaterial(true, card => card == anguish); + if (materials.Count() > 0) + { + usableMaterialMultiList.Add(new List { anguish, materials[0] }); + } + } + // link2 + 1 + 1 or link2 + link2 + List link2List = Bot.GetMonsters().Where(card => card.HasType(CardType.Link) && card.LinkCount == 2 + && !(card.IsCode(CardId.MuckrakerFromTheUnderworld) && summonThisTurn.Contains(card))).OrderBy(card => card.Attack).ToList(); + if (link2List.Count() > 0) + { + ClientCard link2Material = null; + ClientCard littleKnight = link2List.FirstOrDefault(card => card.Sequence >= 5 && card.IsCode(CardId.SPLittleKnight)); + if (littleKnight != null) link2Material = littleKnight; + else link2Material = link2List[0]; + if (link2List.Count() >= 2) + { + usableMaterialMultiList.Add(new List { link2Material, link2List.FirstOrDefault(card => card != link2Material) }); + } + List remainList = GetCanBeUsedForLinkMaterial(false, card => card != link2Material && !(card.HasType(CardType.Link) && card.LinkMarker > 2)); + if (remainList.Count() >= 2) + { + usableMaterialMultiList.Add(new List { link2Material, remainList[0], remainList[1] }); + } + } + + // check material list + foreach (List currMaterials in usableMaterialMultiList) + { + bool summonFlag = CheckCanDirectAttack() && GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(currMaterials) + 3000 >= Enemy.LifePoints; + summonFlag |= GetProblematicEnemyMonster(0) != null && GetProblematicEnemyMonster(3000) == null; + + if (summonFlag) + { + AI.SelectMaterials(currMaterials); + return true; + } + } + + return false; + } + public bool UnchainedAbominationActivate() + { + if (CheckWhetherNegated()) return false; + List targetList = GetNormalEnemyTargetList(true, true, CardType.Monster); + if (targetList.Count() == 0) return false; + long logDesc = ActivateDescription; + if (logDesc >= Util.GetStringId(CardId.UnchainedAbomination, 0)) + { + logDesc = (int)(Util.GetStringId(CardId.UnchainedAbomination, 0) - 10); + } + Logger.DebugWriteLine("[UnchainedAbomination]desc: " + logDesc + ", timing = " + CurrentTiming); + if (ActivateDescription == Util.GetStringId(CardId.UnchainedAbomination, 0)) activatedCardIdList.Add(Card.Id); + if (ActivateDescription == Util.GetStringId(CardId.UnchainedAbomination, 1) || ActivateDescription == -1) activatedCardIdList.Add(Card.Id + 1); + if (ActivateDescription == Util.GetStringId(CardId.UnchainedAbomination, 2)) activatedCardIdList.Add(Card.Id + 2); + AI.SelectCard(targetList); + + return true; + } + + public bool UnchainedSoulOfAnguishSpSummon() + { + if (CheckShouldNoMoreSpSummon(false)) return false; + + ClientCard unchainedNonLink = Bot.GetMonsters().FirstOrDefault(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained) && !card.HasType(CardType.Link)); + ClientCard unchainedLink2 = Bot.GetMonsters().FirstOrDefault(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained) && card.HasType(CardType.Link) && card.LinkCount == 2); + Logger.DebugWriteLine("[Anguish summon] unchainedNonLink = " + unchainedNonLink?.Name + ", unchainedLink2 = " + unchainedLink2?.Name); + if (unchainedNonLink == null && unchainedLink2 == null) return false; + int needMonsterCount = 2; + if (unchainedLink2 != null) needMonsterCount = 1; + if (needMonsterCount == 2 && Bot.HasInExtra(CardId.UnchainedSoulLordOfYama)) return false; + bool needAnguish = !Bot.HasInMonstersZone(CardId.UnchainedSoulOfAnguish) && !activatedCardIdList.Contains(CardId.UnchainedSoulOfAnguish) + && Enemy.GetMonsters().Any(card => card.IsFaceup()); + if (needAnguish) + { + needAnguish = Bot.HasInExtra(CardId.UnchainedSoulOfRage); + needAnguish |= Bot.HasInExtra(CardId.UnchainedAbomination); + needAnguish |= Bot.HasInExtra(CardId.SPLittleKnight) && banSpSummonExceptFiendCount == 0; + } + Logger.DebugWriteLine("[Anguish summon] needAnguish = " + needAnguish.ToString()); + + // check material + if (needMonsterCount == 1) + { + List materialList = GetCanBeUsedForLinkMaterial(needAnguish, card => card == unchainedLink2); + Logger.DebugWriteLine("[Anguish summon 1] material count = " + materialList.Count().ToString()); + if (materialList.Count() == 0) return false; + List selectMaterials = new List{ unchainedLink2, materialList[0]}; + bool summonFlag = needAnguish; + summonFlag |= CheckCanDirectAttack() && GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(selectMaterials) + 2400 >= Enemy.LifePoints; + Logger.DebugWriteLine("[Anguish summon 1] summon flag " + summonFlag.ToString()); + if (summonFlag) + { + AI.SelectMaterials(selectMaterials); + return true; + } + } + if (needMonsterCount == 2) + { + List materialList = GetCanBeUsedForLinkMaterial(needAnguish, card => card == unchainedNonLink); + Logger.DebugWriteLine("[Anguish summon 2] material count = " + materialList.Count().ToString()); + if (materialList.Count() >= 2) + { + List selectMaterials = new List { unchainedNonLink, materialList[0], materialList[1] }; + if (needAnguish || GetMaterialAttack(selectMaterials) < 2400) + { + AI.SelectMaterials(selectMaterials); + return true; + } + } + } + + return false; + } + public bool UnchainedSoulOfAnguishActivate() + { + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated()) return false; + List targetList = Enemy.GetMonsters().Where(card => card.IsFaceup() && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeMonsterTarget()).OrderByDescending(card => card.Attack).ToList(); + if (targetList.Count() > 0) + { + currentDestroyCardList.Add(targetList[0]); + int summonId = 0; + if (Bot.HasInExtra(CardId.UnchainedAbomination) && GetProblematicEnemyMonster(3000, ignoreCurrentDestroy:true) == null) + summonId = CardId.UnchainedAbomination; + else if (banSpSummonExceptFiendCount == 0 && Bot.HasInExtra(CardId.SPLittleKnight) && GetProblematicEnemyCardList(true, false, CardType.Monster).Count() > 0) + summonId = CardId.SPLittleKnight; + else if (Bot.HasInExtra(CardId.UnchainedSoulOfRage)) summonId = CardId.UnchainedSoulOfRage; + if (summonId > 0) + { + List materialList = new List(targetList){Card}; + Logger.DebugWriteLine("*** Anguish select: " + summonId.ToString()); + + AI.SelectCard(targetList); + AI.SelectNextCard(summonId); + AI.SelectMaterials(materialList); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + if (Card.Location == CardLocation.Grave) + { + return UnchainRecycleActivate(); + } + + return false; + } + + public bool UnchainedSoulLordOfYamaSpSummon() + { + if (CheckShouldNoMoreSpSummon(false)) return false; + if (Bot.HasInMonstersZone(CardId.UnchainedSoulLordOfYama) || activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama)) return false; + + bool need3Monster = Bot.HasInExtra(CardId.UnchainedSoulOfAnguish) && !Bot.HasInMonstersZone(CardId.UnchainedSoulOfAnguish) + && !activatedCardIdList.Contains(CardId.UnchainedSoulOfAnguish) && GetProblematicEnemyMonster(canBeTarget:true, selfType: CardType.Monster) != null; + need3Monster |= CheckAtAdvantage() && Duel.Phase == DuelPhase.Main2 + && Bot.HasInExtra(CardId.UnchainedSoulOfRage) && !Bot.HasInMonstersZone(CardId.UnchainedSoulOfRage); + bool haveUnchainSoul = Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained)); + if (need3Monster) + { + need3Monster = Bot.HasInExtra(CardId.UnchainedSoulOfRage); + need3Monster |= Bot.HasInExtra(CardId.UnchainedAbomination); + need3Monster |= Bot.HasInExtra(CardId.SPLittleKnight) && banSpSummonExceptFiendCount == 0; + } + // check material + List materialList = GetCanBeUsedForLinkMaterial(need3Monster, + card => !card.HasRace(CardRace.Fiend) || (card.HasType(CardType.Link) && card.HasSetcode(SetcodeUnchained))); + Logger.DebugWriteLine("[Yama Summon]need3Monster = " + need3Monster.ToString() + ", material count = " + materialList.Count()); + for (int index1 = 0; index1 < materialList.Count() - 1; ++ index1) + { + ClientCard material1 = materialList[index1]; + for (int index2 = index1 + 1; index2 < materialList.Count(); ++ index2) + { + ClientCard material2 = materialList[index2]; + List selectMaterials = new List{material1, material2}; + if (need3Monster && materialList.Count() == 2 && (activatedCardIdList.Contains(CardId.UnchainedSoulOfSharvara) || Bot.GetSpells().Count() == 0)) + { + // only for attack + if (GetProblematicEnemyMonster() != null || !CheckCanDirectAttack() || GetMaterialAttack(selectMaterials) >= 2000) return false; + } + bool summonFlag = need3Monster; + summonFlag |= Enemy.GetMonsterCount() == 0 && GetMaterialAttack(selectMaterials) < 2000; + summonFlag |= CheckAtAdvantage() && !haveUnchainSoul; + if (summonFlag) + { + AI.SelectMaterials(selectMaterials); + return true; + } + } + } + + return false; + } + public bool UnchainedSoulLordOfYamaActivate() + { + if (Card.Location == CardLocation.MonsterZone && (ActivateDescription == Util.GetStringId(CardId.UnchainedSoulLordOfYama, 0) || ActivateDescription == -1)) + { + // search + if (CheckWhetherNegated()) return false; + AI.SelectCard(CardId.UnchainedSoulOfSharvara, CardId.UnchainedAbomination, CardId.UnchainedSoulOfAnguish, CardId.UnchainedSoulOfRage); + activatedCardIdList.Add(Card.Id); + return true; + } + else if (Card.Location == CardLocation.Grave) + { + // spsummon & destroy + ClientCard chaosAngel = null; + ClientCard abomination = null; + ClientCard lady = null; + ClientCard lovely = null; + ClientCard arianna = null; + ClientCard bestAttack = null; + ClientCard rage = null; + foreach (ClientCard grave in Bot.Graveyard) + { + if (grave.IsCode(CardId.ChaosAngel) && grave.ProcCompleted != 0 && !dimensionalBarrierAnnouced.Contains(HintMsg.SYNCHRO)) chaosAngel = grave; + if (grave.IsCode(CardId.UnchainedSoulOfRage) && grave.ProcCompleted != 0) rage = grave; + if (grave.IsCode(CardId.UnchainedAbomination) && grave.ProcCompleted != 0) abomination = grave; + if (grave.IsCode(CardId.LadyLabrynthOfTheSilverCastle)) lady = grave; + if (grave.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) lovely = grave; + if (grave.IsCode(CardId.AriannaTheLabrynthServant)) arianna = grave; + if (Card != grave && grave.IsMonster() && grave.HasRace(CardRace.Fiend)) + { + if (!grave.IsCanRevive()) continue; + if (bestAttack == null || grave.Attack > bestAttack.Attack) bestAttack = grave; + } + } + + ClientCard select = null; + bool destroyWelcome = false; + if (chaosAngel != null && (GetProblematicEnemyCardList(selfType: CardType.Monster).Count() > 0 || + (Bot.GetMonsterCount() == 0 && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) + { + select = chaosAngel; + } + if (select == null && abomination != null && (GetProblematicEnemyCardList(selfType: CardType.Monster).Count() > 0 || + (Bot.GetMonsterCount() == 0 && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) + { + select = abomination; + Logger.DebugWriteLine("[Yama] timing: " + CurrentTiming.ToString()); + if (Bot.HasInSpellZone(CardId.WelcomeLabrynth) && !(Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + && !activatedCardIdList.Contains(CardId.UnchainedAbomination)) + { + destroyWelcome = true; + } + } + if (select == null && rage != null && (Duel.Player == 0 || (!activatedCardIdList.Contains(CardId.UnchainedSoulOfRage) && (Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2))) + && Bot.HasInExtra(new List { CardId.UnchainedSoulOfAnguish, CardId.SPLittleKnight })) select = rage; + if (select == null && arianna != null && Duel.Player == 0 && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant)) select = arianna; + if (select == null && lovely != null && Duel.Player == 1 && Util.GetBestAttack(Enemy) < 2900) select = lovely; + if (select == null && lady != null && Duel.Player == 1 && Util.GetBestAttack(Enemy) < 3000) select = lady; + if (select == null && arianna != null && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant)) select = arianna; + if (select == null && bestAttack != null) select = bestAttack; + + if (select != null) + { + activatedCardIdList.Add(Card.Id + 1); + AI.SelectCard(select); + if (destroyWelcome) + { + AI.SelectYesNo(true); + AI.SelectNextCard(CardId.WelcomeLabrynth); + } else { + AI.SelectYesNo(false); + } + return true; + } + } + + return false; + } + + public bool UnchainedSoulOfRageSpSummon() + { + if (CheckShouldNoMoreSpSummon(false) || CheckWhetherNegated(true, true, CardType.Monster | CardType.Link)) return false; + if (Bot.HasInMonstersZone(CardId.UnchainedSoulOfRage)) return false; + + ClientCard unchained = Bot.GetMonsters().FirstOrDefault(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained) + && !card.IsCode(CardId.UnchainedSoulOfAnguish, CardId.UnchainedAbomination)); + if (unchained == null) return false; + + bool summonFlag = CheckAtAdvantage() && Util.IsTurn1OrMain2(); + summonFlag |= !(Bot.HasInExtra(CardId.UnchainedSoulOfAnguish) && !activatedCardIdList.Contains(CardId.UnchainedSoulOfAnguish)) && Util.IsTurn1OrMain2(); + if (summonFlag) + { + summonFlag = Bot.HasInExtra(CardId.UnchainedSoulOfAnguish); + summonFlag |= Bot.HasInExtra(CardId.SPLittleKnight) && banSpSummonExceptFiendCount == 0; + } + + List materialList = GetCanBeUsedForLinkMaterial(Util.IsTurn1OrMain2(), + card => !card.HasRace(CardRace.Fiend) || card == unchained); + if (materialList.Count() > 0) + { + List selectMaterials = new List{unchained, materialList[0]}; + summonFlag |= Enemy.GetMonsterCount() == 0 && GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(selectMaterials) + 1800 >= Enemy.LifePoints; + if (summonFlag) + { + AI.SelectMaterials(selectMaterials); + return true; + } + } + + return false; + } + public bool UnchainedSoulOfRageActivate() + { + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated()) return false; + bool activateFlag = DefaultOnBecomeTarget() && !Util.ChainContainsCard(CardId.EscapeOfTheUnchained); + ClientCard problemMonster = GetProblematicEnemyMonster(-1, true, true, CardType.Monster); + List targetList = Enemy.GetMonsters().Where(card => card.IsFaceup() && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeTarget()).OrderBy(card => card.Attack).ToList(); + if (problemMonster != null) targetList.Insert(0, problemMonster); + + activateFlag |= (CurrentTiming & hintTimingMainEnd) > 0 && Util.IsOneEnemyBetterThanValue(Card.Attack, true); + activateFlag |= problemMonster != null; + + if (activateFlag && targetList.Count() > 0) + { + ClientCard target = targetList[0]; + int summonId = 0; + if (Bot.HasInExtra(CardId.UnchainedAbomination) && GetProblematicEnemyMonster(3000) == null + && target.HasType(CardType.Link) && target.LinkCount == 2) summonId = CardId.UnchainedSoulOfAnguish; + else if (banSpSummonExceptFiendCount == 0 && Bot.HasInExtra(CardId.SPLittleKnight)) summonId = CardId.SPLittleKnight; + else if (Bot.HasInExtra(CardId.UnchainedSoulOfAnguish) && GetProblematicEnemyMonster(2400) == null) summonId = CardId.UnchainedSoulOfAnguish; + List materialList = new List(targetList){Card}; + + AI.SelectCard(targetList); + AI.SelectNextCard(summonId); + AI.SelectMaterials(materialList); + activatedCardIdList.Add(Card.Id); + escapeTargetList.Add(Card); + currentDestroyCardList.Add(target); + return true; + } + } + if (Card.Location == CardLocation.Grave) + { + return UnchainRecycleActivate(); + } + + return false; + } + + public bool UnchainRecycleActivate() + { + AI.SelectCard(CardId.UnchainedSoulOfSharvara, CardId.LovelyLabrynthOfTheSilverCastle, CardId.AriannaTheLabrynthServant, + CardId.UnchainedAbomination, CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock, + CardId.AriasTheLabrynthButler, CardId.ArianeTheLabrynthServant); + activatedCardIdList.Add(Card.Id + 1); + + return true; + } + + public bool SPLittleKnightSpSummon() + { + if (CheckCanDirectAttack()) + { + // for attack + List materialList = SPLittleKnightSelectMaterial(); + if (materialList.Count() >= 2 && GetMaterialAttack(materialList) < 1600) + { + AI.SelectMaterials(materialList); + return true; + } + } else if (!CheckWhetherNegated(true, true, CardType.Monster | CardType.Link) && GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0) + { + // for remove + List materialList = SPLittleKnightSelectMaterial(true); + if (materialList.Count() >= 2 && materialList.Any(card => card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link))) + { + AI.SelectMaterials(materialList); + return true; + } + } + return false; + } + public List SPLittleKnightSelectMaterial(bool needToUseEffect = false) + { + List usedMaterialList = new List(); + if (Bot.GetMonstersExtraZoneCount() > 0) + { + ClientCard botMonsterExtraZome = Bot.GetMonstersInExtraZone()[0]; + if (botMonsterExtraZome.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Pendulum) || botMonsterExtraZome.IsCode(CardId.RelinquishedAnima)) + { + usedMaterialList.Add(botMonsterExtraZome); + if (botMonsterExtraZome.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)) needToUseEffect = false; + } + List materialList = GetCanBeUsedForLinkMaterial(true, card => card == botMonsterExtraZome); + if (materialList.Count() > 0) + { + foreach (ClientCard card in materialList) + { + if (!needToUseEffect || card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz) || (card.HasType(CardType.Link) && card.LinkCount <= 2)) + { + usedMaterialList.Add(card); + if (card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)) needToUseEffect = false; + } + if (usedMaterialList.Count() >= 2) break; + } + } + if (usedMaterialList.Count() < 2) usedMaterialList.Clear(); + } else { + List materialList = GetCanBeUsedForLinkMaterial(true, card => !needToUseEffect + || card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz) || (card.HasType(CardType.Link) && card.LinkCount <= 2)); + if (materialList.Count() >= 2) + { + for (int idx1 = 0; idx1 < materialList.Count() - 1; ++ idx1) + { + ClientCard material1 = materialList[idx1]; + if (material1.HasType(CardType.Link) && material1.LinkCount >= 3) continue; + bool flag1 = !needToUseEffect || material1.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link); + for (int idx2 = 0; idx2 < materialList.Count(); ++ idx2) + { + ClientCard material2 = materialList[idx2]; + if (material2.HasType(CardType.Link) && material2.LinkCount >= 3) continue; + bool flag2 = !needToUseEffect || material2.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link); + if (flag1 || flag2) + { + return new List{material1, material2}; + } + } + } + } + } + + return usedMaterialList; + } + + public bool SPLittleKnightActivate() + { + if (ActivateDescription == -1 || ActivateDescription == Util.GetStringId(CardId.SPLittleKnight, 0)) + { + // banish card + List problemCardList = GetProblematicEnemyCardList(true, selfType: CardType.Monster); + problemCardList.AddRange(GetDangerousCardinEnemyGrave(false)); + problemCardList.AddRange(GetNormalEnemyTargetList(true, true, CardType.Monster)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => card.HasType(CardType.Monster)).OrderByDescending(card => card.Attack)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => !card.HasType(CardType.Monster))); + if (problemCardList.Count() > 0) + { + AI.SelectCard(problemCardList); + activatedCardIdList.Add(Card.Id); + return true; + } + } else if (ActivateDescription == Util.GetStringId(CardId.SPLittleKnight, 1)) + { + ClientCard selfMonster = null; + foreach (ClientCard target in Bot.GetMonsters()) + { + if (Duel.ChainTargets.Contains(target) && !escapeTargetList.Contains(target)) + { + selfMonster = target; + break; + } + } + if (selfMonster == null) + { + if (Duel.Player == 1) + { + selfMonster = Bot.GetMonsters().Where(card => card.IsAttack()).OrderBy(card => card.Attack).FirstOrDefault(); + if (!Util.IsOneEnemyBetterThanValue(selfMonster.Attack, true)) selfMonster = null; + } + } + if (selfMonster != null) + { + ClientCard nextMonster = null; + List selfTargetList = Bot.GetMonsters().Where(card => card != selfMonster).ToList(); + if (Enemy.GetMonsterCount() == 0 && selfTargetList.Count() > 0) + { + selfTargetList.Sort(CompareUsableAttack); + nextMonster = selfTargetList[0]; + escapeTargetList.Add(nextMonster); + } + if (Enemy.GetMonsterCount() > 0) + { + nextMonster = GetBestEnemyMonster(false, true, true); + currentDestroyCardList.Add(nextMonster); + } + if (nextMonster != null) + { + AI.SelectCard(selfMonster); + AI.SelectNextCard(nextMonster); + escapeTargetList.Add(selfMonster); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + } + + return false; + } + + public bool MuckrakerFromTheUnderworldSpSummon() + { + List materialList = GetCanBeUsedForLinkMaterial(true, card => card.HasType(CardType.Link)); + if (materialList.Count() < 2) return false; + bool willBeNegated = CheckWhetherNegated(true, true, CardType.Monster | CardType.Link) && Bot.Hand.Count() > 0; + bool canRebornAngel = Bot.Graveyard.Any(card => card.IsCanRevive() && card.IsCode(CardId.ChaosAngel)) && !willBeNegated; + bool canRebornLovely = Bot.Graveyard.Any(card => card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) && !willBeNegated; + int bestAttackGrave = 0; + bool chaosAngelFlag = GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0 && !CheckWhetherNegated(true, true, CardType.Monster); + foreach (ClientCard grave in Bot.Graveyard) + { + if (grave.IsMonster() && grave.HasRace(CardRace.Fiend)) + { + if (!grave.IsCanRevive()) continue; + if (grave.Attack > bestAttackGrave) bestAttackGrave = grave.Attack; + } + } + for (int idx1 = 0; idx1 < materialList.Count() - 1; ++ idx1) + { + ClientCard material1 = materialList[idx1]; + for (int idx2 = idx1 + 1; idx2 < materialList.Count(); ++ idx2) + { + ClientCard material2 = materialList[idx1]; + List currentList = new List { material1, material2 }; + bool summonFlag = chaosAngelFlag && (canRebornAngel || (currentList.Any(card => card.IsCode(CardId.ChaosAngel)) && !willBeNegated)); + summonFlag |= Enemy.GetMonsterCount() == 0 && canRebornLovely; + summonFlag |= !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle) && Bot.Graveyard.Any(card => card.Type == (int)CardType.Trap) + && currentList.Any(card => card.IsDisabled() && card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)); + if (CheckCanDirectAttack()) + { + summonFlag |= GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(currentList) + bestAttackGrave >= Enemy.LifePoints; + summonFlag |= GetMaterialAttack(currentList) < 1000; + } + if (summonFlag) + { + AI.SelectMaterials(currentList); + return true; + } + } + } + return false; + } + public bool MuckrakerFromTheUnderworldActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.MuckrakerFromTheUnderworld, 0)) + { + if (CheckWhetherNegated()) return false; + ClientCard chaosAngel = null; + ClientCard lovely = null; + ClientCard arianna = null; + ClientCard bestAttack = null; + foreach (ClientCard grave in Bot.Graveyard) + { + if (grave.IsCode(CardId.ChaosAngel) && grave.ProcCompleted != 0 && !dimensionalBarrierAnnouced.Contains(HintMsg.SYNCHRO)) chaosAngel = grave; + if (grave.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) lovely = grave; + if (grave.IsCode(CardId.AriannaTheLabrynthServant)) arianna = grave; + if (Card != grave && grave.IsMonster() && grave.HasRace(CardRace.Fiend)) + { + if (!grave.IsCanRevive()) continue; + if (bestAttack == null || grave.Attack > bestAttack.Attack) bestAttack = grave; + } + } + + ClientCard rebornTarget = null; + if (chaosAngel != null && (GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0)) rebornTarget = chaosAngel; + if (rebornTarget == null && lovely != null && Util.GetBestAttack(Enemy) < 2900 && + (!activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle) || Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth))) rebornTarget = lovely; + if (rebornTarget == null && bestAttack != null && CheckCanDirectAttack() + && GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack() + bestAttack.Attack >= Enemy.LifePoints) rebornTarget = bestAttack; + if (rebornTarget == null && arianna != null && Duel.Player == 0 && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant)) rebornTarget = arianna; + if (rebornTarget == null && bestAttack != null) rebornTarget = bestAttack; + if (rebornTarget != null) + { + AI.SelectCard(rebornTarget); + AI.SelectNextCard(FurnitureGetCost()); + activatedCardIdList.Contains(Card.Id); + banSpSummonExceptFiendCount = Math.Max(1, banSpSummonExceptFiendCount); + return true; + } + } + + return false; + } + + public bool RelinquishedAnimaSpSummon() + { + if (CheckWhetherNegated()) return false; + // summon to use effect + ClientCard enemyLeftEx = Enemy.MonsterZone[6]; + if (enemyLeftEx != null && enemyLeftEx.HasLinkMarker((int)CardLinkMarker.Top) && !enemyLeftEx.IsShouldNotBeTarget() && !enemyLeftEx.IsShouldNotBeMonsterTarget()) + { + ClientCard selfMonsterZone1 = Bot.MonsterZone[1]; + if (selfMonsterZone1 == null) + { + AI.SelectMaterials(CardId.LabrynthCooclock); + AI.SelectPlace(Zones.z1); + return true; + } + else if (!selfMonsterZone1.HasType(CardType.Xyz | CardType.Link | CardType.Token) && selfMonsterZone1.Level == 1) + { + AI.SelectMaterials(selfMonsterZone1); + AI.SelectPlace(Zones.z1); + return true; + } + } + ClientCard enemyRightEx = Enemy.MonsterZone[5]; + if (enemyRightEx != null && enemyRightEx.HasLinkMarker((int)CardLinkMarker.Top) && !enemyRightEx.IsShouldNotBeTarget() && !enemyRightEx.IsShouldNotBeMonsterTarget()) + { + ClientCard selfMonsterZone3 = Bot.MonsterZone[3]; + if (selfMonsterZone3 == null) + { + AI.SelectMaterials(CardId.LabrynthCooclock); + AI.SelectPlace(Zones.z3); + return true; + } + else if (!selfMonsterZone3.HasType(CardType.Xyz | CardType.Link | CardType.Token) && selfMonsterZone3.Level == 1) + { + AI.SelectMaterials(selfMonsterZone3); + AI.SelectPlace(Zones.z3); + return true; + } + } + + if (Bot.MonsterZone[5] != null || Bot.MonsterZone[6] != null) return false; + ClientCard enemyMonsterLeft = Enemy.MonsterZone[3]; + ClientCard enemyMonsterRight = Enemy.MonsterZone[1]; + if (Enemy.MonsterZone[6] != null) enemyMonsterLeft = null; + if (enemyMonsterLeft != null && enemyMonsterLeft.IsFacedown()) enemyMonsterLeft = null; + if (enemyMonsterLeft != null && (enemyMonsterLeft.IsShouldNotBeMonsterTarget() || enemyMonsterLeft.IsShouldNotBeTarget())) enemyMonsterLeft = null; + + if (Enemy.MonsterZone[5] != null) enemyMonsterRight = null; + if (enemyMonsterRight != null && (enemyMonsterRight.IsShouldNotBeMonsterTarget() || enemyMonsterRight.IsShouldNotBeTarget())) enemyMonsterRight = null; + if (enemyMonsterRight != null && enemyMonsterRight.IsFacedown()) enemyMonsterRight = null; + + int place = -1; + if (enemyMonsterLeft != null && enemyMonsterRight == null) place = Zones.z5; + if (enemyMonsterLeft == null && enemyMonsterRight != null) place = Zones.z6; + if (enemyMonsterLeft != null && enemyMonsterRight != null) + { + if (enemyMonsterLeft.IsFloodgate() && !enemyMonsterRight.IsFloodgate()) place = Zones.z5; + else if (!enemyMonsterLeft.IsFloodgate() && enemyMonsterRight.IsFloodgate()) place = Zones.z6; + else + { + if (enemyMonsterLeft.GetDefensePower() >= enemyMonsterRight.GetDefensePower()) place = Zones.z5; + else place = Zones.z6; + } + } + if (place >= 0) + { + AI.SelectMaterials(Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz | CardType.Link | CardType.Token) && card.Level == 1) + .OrderBy(card => card.Attack).ToList()); + AI.SelectPlace(place); + return true; + } + + // summon for little knight + if (Bot.HasInExtra(CardId.SPLittleKnight) && Bot.GetMonsters().Count(card => card.IsFaceup()) >= 2 + && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link))) + { + if (GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0) + { + AI.SelectMaterials(Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz | CardType.Link | CardType.Token) && card.Level == 1) + .OrderBy(card => card.Attack).ToList()); + return true; + } + } + return false; + } + public bool RelinquishedAnimaActivate() + { + if (CheckWhetherNegated()) return false; + activatedCardIdList.Add(Card.Id); + Dictionary placeList = new Dictionary{ {1, 6}, {3, 5}, {5, 3}, {6, 1} }; + foreach (KeyValuePair placePair in placeList) + { + if (Bot.MonsterZone[placePair.Key] == Card && Enemy.MonsterZone[placePair.Value] != null) + { + currentDestroyCardList.Add(Enemy.MonsterZone[placePair.Value]); + break; + } + } + return true; + } + + public bool MonsterRepos() + { + int selfAttack = Card.Attack + 1; + + if (selfAttack <= 1) + return !Card.IsDefense(); + + int bestAttack = 0; + foreach (ClientCard card in Bot.GetMonsters()) + { + int attack = card.Attack; + if (attack >= bestAttack) + { + bestAttack = attack; + } + } + + bool enemyBetter = Util.IsAllEnemyBetterThanValue(bestAttack, true); + + if (Card.IsAttack() && enemyBetter) + return true; + if (Card.IsDefense() && !enemyBetter) + return true; + return false; + } + + public bool ReposForLabrynth() + { + if (!activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth)) + return Card.IsFacedown(); + return false; + } + + public bool SpellSetCheck() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + if (Card.IsCode(CardId.BigWelcomeLabrynth) && Bot.HasInSpellZone(Card.Id)) return false; + if (Card.IsCode(CardId.TransactionRollback) && !Bot.HasInSpellZone(CardId.TransactionRollback)) + { + // check enemy grave trap + bool haveCopyTrap = false; + if (Enemy.Graveyard.Any(card => card.IsCode( + CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth, _CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, CardId.DestructiveDarumaKarmaCannon, + _CardId.CompulsoryEvacuationDevice, _CardId.BreakthroughSkill + ))) + { + haveCopyTrap = true; + } + + if (!haveCopyTrap && !Bot.HasInHand(CardId.UnchainedSoulOfSharvara)) return false; + } + if (Card.IsCode(CardId.EscapeOfTheUnchained) && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained))) return false; + + // select place + if (Card.IsTrap() || Card.HasType(CardType.QuickPlay)) + { + List avoidList = new List(); + int setFornfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoidList.Add(4 - i); + setFornfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setFornfiniteImpermanence); + return true; + } else + { + SelectSTPlace(Card, false, avoidList); + return true; + } + } else + { + SelectSTPlace(); + } + return true; + } + + else if (Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true) || Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true)) + { + if (Card.IsSpell() && !Bot.HasInSpellZone(Card.Id)) + { + SelectSTPlace(); + return true; + } + } + + return false; + } + + public bool SpellSetForCooClockCheck() + { + // set to destroy for Sharvara + if (Card.IsCode(CardId.PotOfExtravagance, CardId.TransactionRollback, CardId.WelcomeLabrynth) && Bot.HasInHand(CardId.UnchainedSoulOfSharvara) + && !activatedCardIdList.Contains(CardId.UnchainedSoulOfSharvara)) + { + SelectSTPlace(Card, false); + return true; + } + // set to activate by cooclock + bool haveLabrynth = Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)); + if (!cooclockAffected || (haveLabrynth && Bot.HasInHand(CardId.LabrynthCooclock) && !activatedCardIdList.Contains(CardId.LabrynthCooclock))) return false; + if (!Card.IsCode(CardId.BigWelcomeLabrynth, CardId.WelcomeLabrynth, _CardId.InfiniteImpermanence, _CardId.DimensionalBarrier)) return false; + if (haveLabrynth) + { + SelectSTPlace(Card, true); + return true; + } + if (!Card.IsCode(CardId.BigWelcomeLabrynth, CardId.WelcomeLabrynth)) return false; + if (!summoned && Bot.Hand.Any(card => card.IsMonster() && card.Level <= 4 && card.HasSetcode(SetcodeLabrynth))) + { + SelectSTPlace(Card, true); + return true; + } + + return false; + } + } +} diff --git a/Game/GameBehavior.cs b/Game/GameBehavior.cs index 4f7732aa..d6fe9275 100644 --- a/Game/GameBehavior.cs +++ b/Game/GameBehavior.cs @@ -736,6 +736,8 @@ private void OnMove(BinaryReader packet) (CardLocation)previous.location + " move to " + (CardLocation)current.location + ")"); } } + + _ai.OnMove(card, previous.controler, previous.location, current.controler, current.location); } private void OnSwap(BinaryReader packet) @@ -814,8 +816,10 @@ private void OnChaining(BinaryReader packet) if (card.Id == 0) card.SetId(cardId); int cc = GetLocalPlayer(packet.ReadByte()); + int pcl = info.location; if (_debug) - if (card != null) Logger.WriteLine("(" + cc.ToString() + " 's " + (card.Name ?? "UnKnowCard") + " activate effect)"); + if (card != null) Logger.WriteLine("(" + cc.ToString() + " 's " + (card.Name ?? "UnKnowCard") + " activate effect from " + (CardLocation)pcl + ")"); + _duel.LastChainLocation = (CardLocation)pcl; _ai.OnChaining(card, cc); //_duel.ChainTargets.Clear(); _duel.ChainTargetOnly.Clear(); @@ -830,8 +834,10 @@ private void OnChainEnd(BinaryReader packet) _duel.MainPhaseEnd = false; _ai.OnChainEnd(); _duel.LastChainPlayer = -1; + _duel.LastChainLocation = 0; _duel.CurrentChain.Clear(); _duel.ChainTargets.Clear(); + _duel.LastChainTargets.Clear(); _duel.ChainTargetOnly.Clear(); } @@ -948,6 +954,7 @@ private void OnUpdateData(BinaryReader packet) private void OnBecomeTarget(BinaryReader packet) { + _duel.LastChainTargets.Clear(); int count = packet.ReadInt32(); for (int i = 0; i < count; ++i) { @@ -957,6 +964,7 @@ private void OnBecomeTarget(BinaryReader packet) if (_debug) Logger.WriteLine("(" + (CardLocation)info.location + " 's " + (card.Name ?? "UnKnowCard") + " become target)"); _duel.ChainTargets.Add(card); + _duel.LastChainTargets.Add(card); _duel.ChainTargetOnly.Add(card); } } diff --git a/WindBot.csproj b/WindBot.csproj index 5b50096e..e284b92a 100644 --- a/WindBot.csproj +++ b/WindBot.csproj @@ -57,7 +57,6 @@ - @@ -67,6 +66,8 @@ + + diff --git a/bots.json b/bots.json index 145f1bd5..d4f6c616 100644 --- a/bots.json +++ b/bots.json @@ -71,6 +71,12 @@ "difficulty": 2, "masterRules": [ 3, 4, 5 ] }, + { + "name": "Exosister", + "deck": "Exosister", + "difficulty": 2, + "masterRules": [ 3, 4, 5] + }, { "name": "Familiar Possessed", "deck": "FamiliarPossessed",