diff --git a/Decks/AI_Maliss.ydk b/Decks/AI_Maliss.ydk new file mode 100644 index 00000000..6c185722 --- /dev/null +++ b/Decks/AI_Maliss.ydk @@ -0,0 +1,58 @@ +#created by MDPro3 +#main +68337209 +24224830 +20726052 +23434538 +30118811 +30118811 +75500286 +20938824 +10045474 +24224830 +96676583 +14558127 +27204311 +32061192 +20938824 +40366667 +40366667 +10045474 +68337209 +30118811 +94722358 +40366667 +34267821 +96676583 +3723262 +14558127 +23434538 +32061192 +10045474 +93453053 +69272449 +20938824 +69272449 +65681983 +69272449 +14558127 +32061192 +73628505 +68337209 +96676583 +#extra +39138610 +5043010 +86066372 +4280258 +21848500 +46947713 +95454996 +68059897 +29301450 +59859086 +52698008 +98978921 +30342076 +24842059 +60303245 \ No newline at end of file diff --git a/Game/AI/Decks/MalissExecutor.cs b/Game/AI/Decks/MalissExecutor.cs new file mode 100644 index 00000000..b26c01f8 --- /dev/null +++ b/Game/AI/Decks/MalissExecutor.cs @@ -0,0 +1,4207 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Claims; +using System.Security.Cryptography; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; +using YGOSharp.Network.Enums; +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Maliss", "AI_Maliss")] + public class MalissExecutor : DefaultExecutor + { + public class CardId + { + public const int DominusImpulse = 40366667; + public const int TERRAFORMING = 73628505; + //public const int AllureOfDarkness = 1475311; + public const int GoldSarcophagus = 75500286; + + // Cyberse / utility + public const int BackupIgnister = 30118811; + public const int WizardIgnister = 3723262; + + // Main Maliss pieces + public const int MalissP_Dormouse = 32061192; // Maliss

Dormouse + public const int MalissP_WhiteRabbit = 69272449; // Maliss

White Rabbit + public const int MalissP_ChessyCat = 96676583; // Maliss

Chessy Cat + public const int MalissP_MarchHare = 20938824; // Maliss

March Hare + public const int MalissC_GWC06 = 20726052; // Maliss GWC-06 + //public const int MalissC_TB11 = 57111661; // Maliss TB-11 + public const int MalissC_MTP07 = 94722358; // Maliss MTP-07 + public const int MalissQ_RedRansom = 68059897; // Maliss Red Ransom + public const int MalissQ_WhiteBinder = 95454996; // Maliss White Binder + public const int MalissQ_HeartsCrypter = 21848500; // Maliss Hearts Crypter + public const int MalissInTheMirror = 93453053; // Maliss in the Mirror (Spell) + public const int MalissInUnderground = 68337209; // Maliss in Underground + + + // === Extra Deck === + public const int Linguriboh = 24842059; + public const int LinkDecoder = 30342076; + public const int SP_LITTLE_KNIGHT = 29301450; + public const int SALAMANGREAT_ALMIRAJ = 60303245; + public const int SplashMage = 59859086; // Splash Mage + public const int CyberseWicckid = 52698008; // Cyberse Wicckid + public const int TranscodeTalker = 46947713; // Transcode Talker + public const int AlliedCodeTalkerIgnister = 39138610; // Allied Code Talker @Ignister + public const int FirewallDragon = 5043010; // Firewall Dragon + public const int LinkSpider = 98978921; // Link Spider + public const int HaggardLizardose = 9763474; // Haggard Lizardose + public const int AccesscodeTalker = 86066372; // Accesscode Talker + public const int Apollousa = 4280258; + + // === Handtraps / Others (blacklist/targets etc.) === + public const int Lancea = 34267821; + public const int Fuwalos = 42141493; + 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 DivineArsenalAAZEUS_SkyThunder = 90448279; + 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 GhostMournerMoonlitChill = 52038441; + public const int NibiruThePrimalBeing = 27204311; + } + const int SetcodeMaliss = 0x1bf; + const int SetcodeTimeLord = 0x4a; + const int SetcodePhantom = 0xdb; + const int SetcodeOrcust = 0x11b; + const int SetcodeHorus = 0x19d; + const int SetcodeDarkWorld = 0x6; + const int SetcodeSkyStriker = 0x115; + + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.MalissP_ChessyCat, CardId.MalissP_MarchHare,CardId.MalissP_WhiteRabbit,CardId.MalissInUnderground, + CardId.BackupIgnister, CardId.MalissP_Dormouse, + _CardId.AshBlossom,_CardId.InfiniteImpermanence,CardId.DominusImpulse } }, + {2, new List { _CardId.MaxxC, _CardId.CalledByTheGrave}}, + {1, new List { CardId.GoldSarcophagus, CardId.TERRAFORMING, + CardId.MalissC_GWC06, CardId.Lancea, CardId.MalissC_MTP07, + _CardId.CrossoutDesignator, CardId.MalissInTheMirror, CardId.WizardIgnister, + CardId.NibiruThePrimalBeing }} + }; + + List notToNegateIdList = new List { 58699500, 20343502, 19403423 }; + List notToDestroySpellTrap = new List { 50005218, 6767771 }; + 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 + }; + + public MalissExecutor(GameAI ai, Duel duel) : base(ai, duel) + { + // Must Set First + AddExecutor(ExecutorType.SpellSet, CardId.MalissC_GWC06, SpellSetCheck); + AddExecutor(ExecutorType.SpellSet, CardId.MalissC_MTP07, SpellSetCheck); + + + AddExecutor(ExecutorType.Activate, CardId.AccesscodeTalker, Accesscode_OnSummon_AtkUp); + AddExecutor(ExecutorType.Activate, CardId.AccesscodeTalker, Accesscode_Destroy_Ignition); + + // ===== Generic counters ===== + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + AddExecutor(ExecutorType.Activate, _CardId.CalledByTheGrave, CalledbytheGraveActivate); + AddExecutor(ExecutorType.Activate, _CardId.CrossoutDesignator, CrossoutDesignatorActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, CardId.Apollousa, DontSelfNG); + AddExecutor(ExecutorType.Activate, CardId.DominusImpulse, DontSelfNG); + AddExecutor(ExecutorType.Activate, CardId.AlliedCodeTalkerIgnister, Allied_NegateBanish); + AddExecutor(ExecutorType.Activate, CardId.FirewallDragon, FirewallBounce_OnOppSummon); + AddExecutor(ExecutorType.Activate, CardId.MalissC_MTP07, MTP07_OppTurn_RemoveEnemyOnly); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_HeartsCrypter, HC_Quick_ReturnBanished_AndBanishField); + AddExecutor(ExecutorType.Activate, CardId.MalissC_GWC06, GWC06_OppTurn_ReviveWB_HC); + AddExecutor(ExecutorType.Activate, CardId.SP_LITTLE_KNIGHT, ActLittleKnight); + + + AddExecutor(ExecutorType.Activate, CardId.MalissQ_RedRansom, RR_SS_FromBanished); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_HeartsCrypter, HC_OnBanished_SpecialSummon); + AddExecutor(ExecutorType.Activate, CardId.MalissInTheMirror, Mirror_Banish); + + // Plan#1 + // --- Start with Dormouse --- + AddExecutor(ExecutorType.Summon, CardId.MalissP_Dormouse, Step1_Dormouse_NormalSummon); + AddExecutor(ExecutorType.Summon, CardId.MalissP_WhiteRabbit, Step1_WhiteRabbit_NormalSummon); + AddExecutor(ExecutorType.Summon, CardId.MalissP_ChessyCat, TwoCC_NormalSummon); + AddExecutor(ExecutorType.Summon, CardId.MalissP_ChessyCat, Emergency_NormalCat); + AddExecutor(ExecutorType.Summon, CardId.MalissP_MarchHare, NSMH); + AddExecutor(ExecutorType.Summon, CardId.BackupIgnister, NSBackup_L); + AddExecutor(ExecutorType.Summon, CardId.BackupIgnister, NSBackup); + AddExecutor(ExecutorType.Activate, CardId.MalissP_Dormouse, Dormouse_SS_FromBanished); + AddExecutor(ExecutorType.Activate, CardId.MalissP_Dormouse, Dormouse_ForMH); + AddExecutor(ExecutorType.Activate, CardId.MalissP_Dormouse, Dormouse_Banish_Anytime); + AddExecutor(ExecutorType.Activate, CardId.MalissP_WhiteRabbit, Step1_WhiteRabbit_SS_FromBanished); + AddExecutor(ExecutorType.Activate, CardId.MalissP_WhiteRabbit, Step1_WhiteRabbit_SetTrapOnSummon); + AddExecutor(ExecutorType.Activate, CardId.MalissP_MarchHare, Step1_MH_FromHand); + AddExecutor(ExecutorType.Activate, CardId.MalissP_MarchHare, returnFromBanish); + AddExecutor(ExecutorType.SpSummon, CardId.Linguriboh, LinguribohMHLine); + AddExecutor(ExecutorType.SpSummon, CardId.LinkDecoder, Step1_SSLinkDecoder); + AddExecutor(ExecutorType.SpSummon, CardId.MalissQ_RedRansom, Step2N_LinkSummon_RedRansom); + AddExecutor(ExecutorType.SpSummon, CardId.MalissQ_RedRansom, Step2_LinkSummon_RedRansom); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_RedRansom, Step2_RedRansom_Search); + AddExecutor(ExecutorType.Activate, CardId.MalissP_ChessyCat, AnyDraw); + AddExecutor(ExecutorType.SpSummon, CardId.Apollousa, Link_Apo); + AddExecutor(ExecutorType.SpSummon, CardId.CyberseWicckid, Step2N_RRtoWicckid); + AddExecutor(ExecutorType.SpSummon, CardId.LinkDecoder, Step_SummonLinkDecoderToWicckid); + AddExecutor(ExecutorType.Activate, CardId.CyberseWicckid, Wicckid_SearchTuner); + AddExecutor(ExecutorType.SpSummon, CardId.SplashMage, Step_SplashToWB); + AddExecutor(ExecutorType.Activate, CardId.SplashMage, Step2N_SplashMage_ReviveP); + AddExecutor(ExecutorType.Activate, CardId.BackupIgnister, Flow3_BackupIgnister_AfterMakeIt3); + AddExecutor(ExecutorType.Activate, CardId.BackupIgnister, OneBody_Backup_SearchWizard); + AddExecutor(ExecutorType.SpSummon, CardId.MalissQ_WhiteBinder, Step2N_LinkSummon_WB); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_WhiteBinder, WB_OnSummon_BanishGY); + AddExecutor(ExecutorType.Activate, CardId.MalissC_GWC06, GWC06_MyTurn_Extend); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_WhiteBinder, WB_SetMalissTrap); + + // === STEP2: 2 bodies -> Splash Mage -> revive P -> Red Ransom -> search === + AddExecutor(ExecutorType.Activate, CardId.WizardIgnister, Step2_Fallback_Wizard_AfterSplashNegated); + AddExecutor(ExecutorType.Activate, CardId.BackupIgnister, Step2_Fallback_Backup_AfterSplashNegated); + + AddExecutor(ExecutorType.Activate, CardId.WizardIgnister, Flow3_WizardIgnister_AfterMakeIt3); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_WhiteBinder, WB_OnBanished_SelfSS); + AddExecutor(ExecutorType.Activate, CardId.MalissP_MarchHare, ssFromHandMH); + AddExecutor(ExecutorType.SpSummon, CardId.MalissQ_HeartsCrypter, Step_LinkSummon_HeartsCrypter); + AddExecutor(ExecutorType.SpSummon, CardId.AlliedCodeTalkerIgnister, Flow3_Link_Allied); + AddExecutor(ExecutorType.Activate, CardId.AlliedCodeTalkerIgnister, Allied_OnSummonTrigger); + + // Plan#2 White Rabbit --- + AddExecutor(ExecutorType.Activate, CardId.MalissC_MTP07, MTP07_ForMH); + AddExecutor(ExecutorType.SpSummon, CardId.FirewallDragon, Flow3_Link_Firewall); + AddExecutor(ExecutorType.SpSummon, CardId.MalissQ_WhiteBinder, Step_WicckidPlusOneToWB); + AddExecutor(ExecutorType.Activate, CardId.MalissP_ChessyCat, ChessyCat_SS_FromBanished); + + + AddExecutor(ExecutorType.SpSummon, CardId.CyberseWicckid, Step_RRtoWicckid); + AddExecutor(ExecutorType.Activate, CardId.MalissInUnderground, Flow3_UnderGround_Available_SSAnyPawn); + AddExecutor(ExecutorType.Activate, CardId.LinkDecoder, LinkDecoder_ReviveFromGY); + + // Fallback Lancea + AddExecutor(ExecutorType.SpSummon, CardId.TranscodeTalker, SummonTranscode); + AddExecutor(ExecutorType.Activate, CardId.TranscodeTalker, Transcode_ReviveLink3OrLower); + + // Emergency Start + AddExecutor(ExecutorType.Activate, CardId.TERRAFORMING, Terra_GrabUnderground); + AddExecutor(ExecutorType.Activate, CardId.GoldSarcophagus, GoldSarc_StartPiece); + AddExecutor(ExecutorType.Activate, CardId.MalissInUnderground, Underground_ActivateStarter); + + AddExecutor(ExecutorType.SpSummon, CardId.SplashMage, Step_SplashToRR); + AddExecutor(ExecutorType.Activate, CardId.SplashMage, Step2_SplashMage_ReviveP); + + // Fallback: Try to Start + AddExecutor(ExecutorType.SpSummon, CardId.LinkSpider); + AddExecutor(ExecutorType.Summon, _CardId.AshBlossom, Emergency_NS); + AddExecutor(ExecutorType.Summon, _CardId.MaxxC, Emergency_NS); + AddExecutor(ExecutorType.SpSummon, CardId.Linguriboh, OneBody_Link1_Linguriboh); + AddExecutor(ExecutorType.SpSummon, CardId.SALAMANGREAT_ALMIRAJ, OneBody_Link1_Almiraj); + AddExecutor(ExecutorType.Activate, CardId.BackupIgnister, OneBody_Backup_SS); + AddExecutor(ExecutorType.Activate, CardId.BackupIgnister, OneBody_Backup_SearchWizard); + AddExecutor(ExecutorType.Activate, CardId.WizardIgnister, OneBody_Wizard_SS); + + + //Finisher + AddExecutor(ExecutorType.SpSummon, CardId.AccesscodeTalker, Flow3_Link_Accesscode); + + // Turn3 or More + AddExecutor(ExecutorType.SpSummon, CardId.Linguriboh, T3Allow); + AddExecutor(ExecutorType.SpSummon, CardId.SALAMANGREAT_ALMIRAJ, T3Allow); + AddExecutor(ExecutorType.SpSummon, CardId.TranscodeTalker, EmerTranscode); + AddExecutor(ExecutorType.SpSummon, CardId.AlliedCodeTalkerIgnister, Emer_Allied); + AddExecutor(ExecutorType.SpSummon, CardId.AlliedCodeTalkerIgnister, Emer_Allied2); + + // >>> Fallback: S:P Little Knight + AddExecutor(ExecutorType.SpSummon, CardId.SP_LITTLE_KNIGHT, SummonLittleKnightFast); + AddExecutor(ExecutorType.SpSummon, CardId.SP_LITTLE_KNIGHT, SPEmer); + + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); + AddExecutor(ExecutorType.Repos, MonsterRepos); + + } + // Maliss Flags + bool usedNormalSummon = false; + bool ssDormouse = false; + bool ssWhiteRabbit = false; + bool ssChessyCat = false; + bool ssMarchHare = false; + bool ActiveMarchHare = false; + bool ssRRThisTurn = false; + bool ssWBThisTurn = false; + bool ssHCThisTurn = false; + bool enemyActivateLancea = false; + bool enemyActivateFuwalos = false; + bool ActiveUnderground = false; + bool blockWicckid = false; + bool mtp07SetThisTurn = false; + bool gwc06SetThisTurn = false; + bool splashNegatedThisTurn = false; + bool Allied_End = false; + bool fullBoard1 = false; + bool goldstart = false; + bool undergroundstart = false; + bool nsplan = false; + bool nsBackupplan = false; + bool NSDorMouse = false; + bool nsLanceaplan = false; + + int myTurnCount = 0; + bool avoidLinkedZones = false; + bool wantLinkedToWicckid = false; + private int? _wicckidEmzIndex = null; + private int _transcodeZoneMask = 0; + + const int MZ0 = 1 << 0; + const int MZ1 = 1 << 1; + const int MZ2 = 1 << 2; + const int MZ3 = 1 << 3; + const int MZ4 = 1 << 4; + const int EMZ_L = (1 << 5); + const int EMZ_R = (1 << 6); + const int EMZ_ALL = EMZ_L | EMZ_R; + int _wicckidEmzBit = 0; + int _forceTranscodeBit = 0; + + // Step Flag + bool step1Done = false; + bool step2Done = false; + int lastRevivedIdBySplash = 0; + bool coreSetupComplete = false; + bool madeIt3 = false; + bool resultSuccessFlag = false; + private bool _didSplashToRR; + private bool _didRRtoWicckid; + private bool _didSummonToWicckidArrow; + private bool _didWBFromWicckid; + private bool _finishPlanDecided; + private bool _preferWicckidArrows; + private bool _rrSelfSSPlacing = false; + private enum FinishPlan { FW_HC_Allied, HC_Allied, AlliedOnly } + private FinishPlan _finishPlan; + static bool IsEmzSeq(int seq) => seq >= 5; + static int BitOfSeq(int seq) => (1 << seq); + static int LowestBit(int m) => m & -m; + + private bool _oppJustActivatedPersistentSpell; + private bool _oppJustSummoned; + private bool _oppJustSet; + private int _prefWindowTTL; + + private int _enemyMonsterCountSnap; + private int _enemyFacedownSTSnap; + + //==================== Default code ==================== + #region Default Code Start Here + private int _totalAttack; + private int _totalBotAttack; + bool enemyActivateMaxxC = false; + bool enemyActivateLockBird = false; + int dimensionShifterCount = 0; + bool enemyActivateInfiniteImpermanenceFromHand = false; + List infiniteImpermanenceList = new List(); + List currentNegateCardList = new List(); + List currentDestroyCardList = new List(); + List sendToGYThisTurn = new List(); + List activatedCardIdList = new List(); + List enemyPlaceThisTurn = new List(); + List escapeTargetList = new List(); + List summonThisTurn = new 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 override bool OnSelectHand() { return true; } + + 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 int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) + { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + 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 CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && Bot.GetMonsters().Any(card => card.IsFaceup())) + { + return true; + } + return false; + } + + 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; + } + private int GetMyLinkedMMZMask() + { + int mask = 0; + foreach (var m in Bot.GetMonsters()) + { + if (m == null || !m.IsFaceup() || !m.HasType(CardType.Link)) continue; + mask |= m.GetLinkedZones(); + } + mask &= 0x1F; + return mask; + } + private bool IsPawnId(int id) + { + return id == CardId.MalissP_Dormouse + || id == CardId.MalissP_WhiteRabbit + || id == CardId.MalissP_ChessyCat + || id == CardId.MalissP_MarchHare; + } + private int GetQueenLinkedMMZMask() + { + int mask = 0; + foreach (var m in Bot.GetMonsters()) + { + if (m == null || !m.IsFaceup()) continue; + + if (m.IsCode(CardId.MalissQ_RedRansom) + || m.IsCode(CardId.MalissQ_WhiteBinder) + || m.IsCode(CardId.MalissQ_HeartsCrypter)) + { + mask |= m.GetLinkedZones(); + } + } + mask &= 0x1F; + return mask; + } + public override int OnSelectPlace(int cardId, int player, CardLocation location, int available) + { + if (player == 0 && location == CardLocation.MonsterZone) + { + int MAIN_MASK = + (int)Zones.z0 | + (int)Zones.z1 | + (int)Zones.z2 | + (int)Zones.z3 | + (int)Zones.z4; + + int emzMask = available & ~MAIN_MASK; + int mainMask = available & MAIN_MASK; + if (IsPawnId(cardId)) + { + int queenMask = GetQueenLinkedMMZMask(); + int queenChoices = queenMask & available & MAIN_MASK; + + if (queenChoices != 0) + { + int pick = FirstBitFromOrder( + queenChoices, + new[] { (int)Zones.z2, (int)Zones.z1, (int)Zones.z3, (int)Zones.z0, (int)Zones.z4 } + ); + AI.SelectPlace(pick); + return pick; + } + } + if (cardId == CardId.AlliedCodeTalkerIgnister) + { + var fw = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.FirewallDragon)); + int emzAvail = available & EMZ_ALL; + + if (fw != null && fw.IsFaceup()) + { + bool firewallCenter = fw.Controller == 0 && fw.Location == CardLocation.MonsterZone && fw.Sequence == 2; + + int linkedAll = fw.GetLinkedZones(); + int linkedChoices = linkedAll & available; + + int linkedEmzChoices = linkedChoices & EMZ_ALL; + int pick; + + if (linkedEmzChoices != 0) + { + if (firewallCenter && (linkedEmzChoices & EMZ_L) != 0 && (linkedEmzChoices & EMZ_R) != 0) + { + int leftFree = 0; + if ((available & (int)Zones.z0) != 0) leftFree++; + if ((available & (int)Zones.z1) != 0) leftFree++; + + int rightFree = 0; + if ((available & (int)Zones.z3) != 0) rightFree++; + if ((available & (int)Zones.z4) != 0) rightFree++; + + if (leftFree > rightFree) + pick = EMZ_L; + else if (rightFree > leftFree) + pick = EMZ_R; + else + pick = FirstBitFromOrder(linkedEmzChoices, new[] { EMZ_L, EMZ_R }); + } + else + { + pick = FirstBitFromOrder(linkedEmzChoices, new[] { EMZ_L, EMZ_R }); + } + AI.SelectPlace(pick); + return pick; + } + if (emzAvail != 0) + { + if (firewallCenter && (emzAvail & EMZ_L) != 0 && (emzAvail & EMZ_R) != 0) + { + int leftFree = 0; + if ((available & (int)Zones.z0) != 0) leftFree++; + if ((available & (int)Zones.z1) != 0) leftFree++; + + int rightFree = 0; + if ((available & (int)Zones.z3) != 0) rightFree++; + if ((available & (int)Zones.z4) != 0) rightFree++; + + if (leftFree > rightFree) + pick = EMZ_L; + else if (rightFree > leftFree) + pick = EMZ_R; + else + pick = FirstBitFromOrder(emzAvail, new[] { EMZ_L, EMZ_R }); + } + else + { + pick = FirstBitFromOrder(emzAvail, new[] { EMZ_L, EMZ_R }); + } + + AI.SelectPlace(pick); + return pick; + } + if (linkedChoices != 0) + { + pick = FirstBitFromOrder( + linkedChoices, + new[] { (int)Zones.z2, (int)Zones.z1, (int)Zones.z3, (int)Zones.z0, (int)Zones.z4 } + ); + AI.SelectPlace(pick); + return pick; + } + } + int emzOnly = available & EMZ_ALL; + if (emzOnly != 0) + { + int pick = FirstBitFromOrder(emzOnly, new[] { EMZ_L, EMZ_R }); + AI.SelectPlace(pick); + return pick; + } + return PreferSafeSummonZones(available); + } + if (cardId == CardId.CyberseWicckid) + { + int picked = ChooseAndRememberWicckidEmz(available); + if (picked != 0) return picked; + return 0; + } + if (cardId == CardId.TranscodeTalker) + { + int wanted = _forceTranscodeBit != 0 ? _forceTranscodeBit : _wicckidEmzBit; + + if (wanted != 0 && (available & wanted) != 0) + return wanted; + + int anyEmz = available & EMZ_ALL; + if (anyEmz != 0) + return (anyEmz & EMZ_L) != 0 ? EMZ_L : EMZ_R; + + return 0; + } + if (cardId == CardId.MalissQ_RedRansom && _rrSelfSSPlacing) + { + int prefer = (int)Zones.z1 | (int)Zones.z3; + int wmask = GetLinkedMaskFor(GetWicckid()); + int choices = (available & prefer) & ~wmask; + if (choices != 0) + { + int pick = FirstBitFromOrder(choices, new[] { (int)Zones.z1, (int)Zones.z3 }); + AI.SelectPlace(pick); + _rrSelfSSPlacing = false; + return pick; + } + } + if (cardId == CardId.LinkDecoder) + { + var trans = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.TranscodeTalker)); + int tmask = GetLinkedMaskFor(trans) & 0x1F; + int safe = (available & 0x1F) & ~tmask; + if (safe != 0) + { + int pick = FirstBitFromOrder(safe, new[] { (int)Zones.z2, (int)Zones.z1, (int)Zones.z3, (int)Zones.z0, (int)Zones.z4 }); + AI.SelectPlace(pick); + return pick; + } + } + if (cardId == CardId.FirewallDragon) + { + var trans = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.TranscodeTalker)); + int underTrans = 0; + if (trans != null) + { + int tmask = GetLinkedMaskFor(trans) & 0x1F; + if ((tmask & (int)Zones.z1) != 0) underTrans |= (int)Zones.z1; + if ((tmask & (int)Zones.z3) != 0) underTrans |= (int)Zones.z3; + } + int choices = (available & underTrans); + if (choices != 0) + { + int pick = FirstBitFromOrder(choices, new[] { (int)Zones.z1, (int)Zones.z3 }); + AI.SelectPlace(pick); + return pick; + } + } + + if (cardId == CardId.TranscodeTalker || + cardId == CardId.AccesscodeTalker || + cardId == CardId.AlliedCodeTalkerIgnister || + cardId == CardId.MalissQ_WhiteBinder || + cardId == CardId.MalissQ_HeartsCrypter) + { + return PreferSafeSummonZones(available); + } + int linked = (GetMyLinkedMMZMask() & available) & 0x1F; + int unlinked = (available & 0x1F) & ~linked; + + int choose; + if (avoidLinkedZones && unlinked != 0) + choose = LowestBit(unlinked); // Not linked zone + else if (linked != 0) + choose = LowestBit(linked); // Default + else + choose = LowestBit(available & 0x1F); + + AI.SelectPlace(choose); + return choose; + } + SelectSTPlace(Card, true); + 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 bool AshBlossomActivate() + { + if (CheckWhetherNegated(true) || !CheckLastChainShouldNegated()) return false; + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + if (CheckAtAdvantage() && Duel.Turn > 1) + { + return false; + } + } + return DefaultAshBlossomAndJoyousSpring(); + } + + public bool MaxxCActivate() + { + if (CheckWhetherNegated(true) || Duel.LastChainPlayer == 0) return false; + return DefaultMaxxC(); + } + + public bool InfiniteImpermanenceActivate() + { + if (CheckWhetherNegated()) return false; + 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(); + + 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(); + 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; + + 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()) + { + AI.SelectCard(card); + return true; + } + } + } + return true; + } + + public bool CrossoutDesignatorActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard() != null) + { + int code = Util.GetLastChainCard().Id; + int alias = Util.GetLastChainCard().Alias; + if (alias != 0 && alias - code < 10) code = alias; + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + if (CheckRemainInDeck(code) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectAnnounceID(code); + currentNegateCardList.AddRange(Enemy.MonsterZone.Where(c => c != null && c.IsFaceup() && c.IsCode(code))); + return true; + } + } + return false; + } + + public bool CalledbytheGraveActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) + { + return false; + } + if (Duel.LastChainPlayer == 1) + { + if (Util.GetLastChainCard().IsMonster()) + { + int code = Util.GetLastChainCard().GetOriginCode(); + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC) && CheckAtAdvantage() && Duel.Turn > 1) + { + return false; + } + ClientCard graveTarget = Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.GetOriginCode() == code); + if (graveTarget != null) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(graveTarget); + currentDestroyCardList.Add(graveTarget); + return true; + } + } + + foreach (ClientCard graveCard in Enemy.Graveyard) + { + if (Duel.ChainTargets.Contains(graveCard) && graveCard.IsMonster()) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + int code = graveCard.Id; + AI.SelectCard(graveCard); + currentDestroyCardList.Add(graveCard); + return true; + } + } + + 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); + currentDestroyCardList.Add(enemyMonsters[0]); + return true; + } + } + } + + if (Duel.LastChainPlayer == 1) return false; + List targets = GetDangerousCardinEnemyGrave(true); + if (targets.Count > 0) + { + int code = targets[0].Id; + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(code); + currentDestroyCardList.Add(targets[0]); + return true; + } + + return false; + } + + public bool SpellSetCheck() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + List onlyOneSetList = new List { }; + if (onlyOneSetList.Contains(Card.Id) && Bot.HasInSpellZone(Card.Id)) + { + return false; + } + + 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(); + } + if (Card.IsCode(CardId.MalissC_MTP07)) mtp07SetThisTurn = true; + if (Card.IsCode(CardId.MalissC_GWC06)) gwc06SetThisTurn = true; + return true; + } + + return false; + } + + public List GetDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && (card.HasSetcode(SetcodeOrcust) || card.HasSetcode(SetcodePhantom) || card.HasSetcode(SetcodeHorus) || card.HasSetcode(SetcodeDarkWorld) || card.HasSetcode(SetcodeSkyStriker))).ToList(); + List dangerMonsterIdList = new List { 99937011, 63542003, 9411399, 28954097, 30680659, 32731036 }; + result.AddRange(Enemy.Graveyard.GetMatchingCards(card => dangerMonsterIdList.Contains(card.Id))); + return result; + } + + public bool CheckWhetherNegated(bool disablecheck = true, bool toFieldCheck = false, CardType type = 0) + { + bool isMonster = type == 0 && Card.IsMonster(); + isMonster |= ((int)type & (int)CardType.Monster) != 0; + bool isSpellOrTrap = type == 0 && (Card.IsSpell() || Card.IsTrap()); + isSpellOrTrap |= (((int)type & (int)CardType.Spell) != 0) || (((int)type & (int)CardType.Trap) != 0); + bool isCounter = ((int)type & (int)CardType.Counter) != 0; + if (isSpellOrTrap && toFieldCheck && CheckSpellWillBeNegate(isCounter)) + return true; + if (DefaultCheckWhetherCardIsNegated(Card)) return true; + if (isMonster && (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)) return true; + } + if (disablecheck) return (Card.Location == CardLocation.MonsterZone || Card.Location == CardLocation.SpellZone) && Card.IsDisabled() && Card.IsFaceup(); + return false; + } + + public bool CheckNumber41(ClientCard card) + { + return card != null && card.IsFaceup() && card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled(); + } + + 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 bool CheckSpellWillBeNegate(bool isCounter = false, ClientCard target = null) + { + if (target == null) target = Card; + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + 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; + } + return false; + } + + 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; + if (DefaultCheckWhetherCardIsNegated(lastcard)) return false; + if (Duel.Turn == 1 && lastcard.IsCode(_CardId.MaxxC)) return false; + + return true; + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + ClientCard floodagateCard = Enemy.GetMonsters().Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsFloodgate() && c.IsFaceup() + && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (floodagateCard != null) return floodagateCard; + + ClientCard dangerCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (dangerCard != null) return dangerCard; + + ClientCard invincibleCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (invincibleCard != null) return invincibleCard; + + ClientCard equippedCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.EquipCards.Count > 0 && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (equippedCard != null) return equippedCard; + + ClientCard enemyExtraMonster = Enemy.MonsterZone.Where(c => c != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && (c.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) || (c.HasType(CardType.Link) && c.LinkCount >= 2)) + && CheckCanBeTargeted(c, canBeTarget, selfType) && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (enemyExtraMonster != null) return enemyExtraMonster; + + if (attack >= 0) + { + if (attack == 0) + attack = Util.GetBestAttack(Bot); + ClientCard betterCard = Enemy.MonsterZone.Where(card => card != null + && card.GetDefensePower() >= attack && card.GetDefensePower() > 0 && card.IsAttack() && CheckCanBeTargeted(card, canBeTarget, selfType) + && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (betterCard != null) return betterCard; + } + return null; + } + + 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())) return false; + } + return true; + } + + public bool CheckShouldNotIgnore(ClientCard cards, bool ignore = false) + { + return !ignore || (!currentDestroyCardList.Contains(cards) && !currentNegateCardList.Contains(cards)); + } + + 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 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)) && enemyPlaceThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && !enemyPlaceThisTurn.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; + } + + ClientCard target = Enemy.MonsterZone.FirstOrDefault(card => card?.Data != null + && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && !card.IsShouldNotBeTarget() + && CheckCanBeTargeted(card, canBeTarget, selfType) + && !currentNegateCardList.Contains(card)); + if (target != null) + { + resultList.Add(target); + } + + foreach (ClientCard chainingCard in Duel.CurrentChain) + { + if (chainingCard.Location == CardLocation.MonsterZone && chainingCard.Controller == 1 && !chainingCard.IsDisabled() + && CheckCanBeTargeted(chainingCard, canBeTarget, selfType) && !currentNegateCardList.Contains(chainingCard)) + { + resultList.Add(chainingCard); + } + } + + return resultList; + } + + 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(); + 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; + } + + private int LinkVal(ClientCard c) => (c != null && c.HasType(CardType.Link)) ? Math.Max(1, c.LinkCount) : 1; + private static readonly int EMZ_LEFT = 5; + private static readonly int EMZ_RIGHT = 6; + private bool IsInEMZ(ClientCard c) + { + var mz = Bot.MonsterZone; + return (mz.Length > 5 && mz[5] == c) || (mz.Length > 6 && mz[6] == c); + } + + private bool HasFreeEMZ() + { + var mz = Bot.MonsterZone; + bool slot5Free = mz.Length > 5 && mz[5] == null; + bool slot6Free = mz.Length > 6 && mz[6] == null; + return slot5Free || slot6Free; + } + #endregion + + #region work space #1 + public override void OnChainSolved(int chainIndex) + { + ClientCard currentCard = Duel.GetCurrentSolvingChainCard(); + var solving = Duel.GetCurrentSolvingChainCard(); + bool neg = Duel.IsCurrentSolvingChainNegated(); + if (currentCard != null && !Duel.IsCurrentSolvingChainNegated() && currentCard.Controller == 1) + { + if (currentCard.IsCode(CardId.Lancea)) enemyActivateLancea = true; + if (currentCard.IsCode(_CardId.MaxxC)) enemyActivateMaxxC = true; + if (currentCard.IsCode(CardId.Fuwalos)) enemyActivateFuwalos = true; + if (currentCard.IsCode(_CardId.LockBird)) enemyActivateLockBird = true; + if (currentCard.IsCode(_CardId.InfiniteImpermanence)) + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == currentCard) + { + infiniteImpermanenceList.Add(4 - i); + break; + } + } + } + var last = Duel.GetCurrentSolvingChainCard(); + if (last != null) + { + if (last.IsSpell() && (last.HasType(CardType.Field) || last.HasType(CardType.Continuous) || last.HasType(CardType.Equip))) + _oppJustActivatedPersistentSpell = true; + _prefWindowTTL = Math.Max(_prefWindowTTL, 2); + } + } + if (currentCard != null && currentCard.Controller == 0 && currentCard.IsCode(CardId.SplashMage)) + { + if (Duel.IsCurrentSolvingChainNegated()) + splashNegatedThisTurn = true; + } + } + public override void OnChainEnd() + { + escapeTargetList.Clear(); + currentNegateCardList.Clear(); + currentDestroyCardList.Clear(); + enemyActivateInfiniteImpermanenceFromHand = false; + _oppJustActivatedPersistentSpell = false; + int curMon = Enemy.GetMonsterCount(); + if (curMon > _enemyMonsterCountSnap) _oppJustSummoned = true; + _enemyMonsterCountSnap = curMon; + int curFD = Enemy.SpellZone.Count(c => c != null && c.IsFacedown()); + if (curFD > _enemyFacedownSTSnap) _oppJustSet = true; + _enemyFacedownSTSnap = curFD; + for (int idx = enemyPlaceThisTurn.Count - 1; idx >= 0; idx--) + { + ClientCard checkTarget = enemyPlaceThisTurn[idx]; + if (checkTarget == null || (checkTarget.Location != CardLocation.SpellZone && checkTarget.Location != CardLocation.MonsterZone)) + { + enemyPlaceThisTurn.RemoveAt(idx); + } + } + base.OnChainEnd(); + } + public override void OnNewTurn() + { + if (Duel.Player == 0) + { + myTurnCount++; + } + enemyActivateLancea = false; //added for Maliss + enemyActivateFuwalos = false; //added + enemyActivateMaxxC = false; + enemyActivateLockBird = false; + enemyActivateInfiniteImpermanenceFromHand = false; + if (dimensionShifterCount > 0) dimensionShifterCount--; + infiniteImpermanenceList.Clear(); + currentNegateCardList.Clear(); + currentDestroyCardList.Clear(); + sendToGYThisTurn.Clear(); + activatedCardIdList.Clear(); + enemyPlaceThisTurn.Clear(); + summonThisTurn.Clear(); + + // reset Maliss flags + usedNormalSummon = false; + ssChessyCat = false; + ssDormouse = false; + ssMarchHare = false; + ssWhiteRabbit = false; + ActiveMarchHare = false; + ActiveUnderground = false; + step1Done = false; + step2Done = false; + lastRevivedIdBySplash = 0; + mtp07SetThisTurn = false; + gwc06SetThisTurn = false; + splashNegatedThisTurn = false; + ssRRThisTurn = false; + ssWBThisTurn = false; + ssHCThisTurn = false; + _didSplashToRR = _didRRtoWicckid = _didSummonToWicckidArrow = _didWBFromWicckid = false; + _finishPlanDecided = false; + _preferWicckidArrows = false; + _rrSelfSSPlacing = false; + _forceTranscodeBit = 0; + _oppJustActivatedPersistentSpell = false; + _oppJustSummoned = false; + _oppJustSet = false; + _enemyMonsterCountSnap = Enemy.GetMonsterCount(); + _enemyFacedownSTSnap = Enemy.SpellZone.Count(c => c != null && c.IsFacedown()); + _prefWindowTTL = 0; + fullBoard1 = false; + Allied_End = false; + nsplan = false; + nsBackupplan = false; + NSDorMouse = false; + nsLanceaplan = false; + base.OnNewTurn(); + } + public override bool OnSelectYesNo(int desc) + { + if (desc == Util.GetStringId(CardId.MalissQ_WhiteBinder, 0)) + { + bool anyGY = (Bot.Graveyard.Count > 0) || (Enemy.Graveyard.Count > 0); + return anyGY; + } + return base.OnSelectYesNo(desc); + } + private bool DontSelfNG() { return Duel.LastChainPlayer != 0; } + + public override IList OnSelectCard(IList cards, int min, int max, int hint, bool cancelable) + { + var solving = Duel.GetCurrentSolvingChainCard(); + if (cards != null && cards.Count > 0 && solving != null) + { + if (solving.IsCode(CardId.MalissQ_RedRansom)) + { + var searchPool = cards + .Where(c => c != null && + (c.IsCode(CardId.MalissInUnderground) || + c.IsCode(CardId.MalissInTheMirror))) + .ToList(); + + if (searchPool.Count > 0) + { + bool shouldUG = ShouldSearchUnderground(); + int chooseId = 0; + if (shouldUG && searchPool.Any(c => c.IsCode(CardId.MalissInUnderground))) + { + chooseId = CardId.MalissInUnderground; + } + else if (searchPool.Any(c => c.IsCode(CardId.MalissInTheMirror))) + { + chooseId = CardId.MalissInTheMirror; + } + if (chooseId != 0) + { + var pick = searchPool.First(c => c.IsCode(chooseId)); + return new List { pick }; + } + } + } + if (hint == HintMsg.Set && solving.IsCode(CardId.MalissQ_WhiteBinder)) + { + ClientCard pick = null; + pick = cards.FirstOrDefault(c => c.Id == CardId.MalissC_GWC06 && c.Location == CardLocation.Deck); + if (pick == null) + { + pick = cards.FirstOrDefault(c => c.Id == CardId.MalissC_GWC06); + } + if (pick == null) + { + pick = cards.FirstOrDefault(c => c.Id == CardId.MalissC_MTP07); + } + if (pick != null) + { + if (pick.Id == CardId.MalissC_GWC06) + gwc06SetThisTurn = true; + else if (pick.Id == CardId.MalissC_MTP07) + mtp07SetThisTurn = true; + + return new List { pick }; + } + + } + } + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + #endregion + + + #region work space #2 + private int GetMMZCount() => Bot.MonsterZone.Take(5).Count(c => c != null); + private bool HasFreeMMZ() => GetMMZCount() < 5; + private bool HaveTwoBodies() => Bot.GetMonsterCount() >= 2; + private bool ShouldFastEndToSPLK() => + enemyActivateMaxxC || enemyActivateFuwalos; + private bool Step1Complete() + { + return Bot.HasInMonstersZone(CardId.MalissP_Dormouse) + && Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit); + } + private bool CanStartStep1() + { + if (enemyActivateLancea || ShouldFastEndToSPLK()) return false; + if (HaveTwoBodies()) return false; + if (!HasFreeMMZ()) return false; + bool haveStarterInHand = + Bot.HasInHand(CardId.MalissP_Dormouse) || + Bot.HasInHand(CardId.MalissP_WhiteRabbit) || + Bot.HasInHand(CardId.GoldSarcophagus) || + Bot.HasInHand(CardId.TERRAFORMING) || + Bot.HasInHand(CardId.MalissInUnderground); + return haveStarterInHand; + } + private bool CanContinueStep1() + { + if (enemyActivateLancea) return false; + return !HaveTwoBodies() && HasFreeMMZ(); + } + private int PickMalissTrapToSet() + { + int pref; + if (Duel.Player == 0 && Bot.HasInMonstersZone(CardId.MalissP_Dormouse) && Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit)) + { + pref = CardId.MalissC_GWC06; + } + else if (Duel.Player == 0 && (Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_RedRansom) || Bot.HasInBanished(CardId.MalissQ_RedRansom))) + { + pref = CardId.MalissC_GWC06; + } + else if (Duel.Player == 0 && Bot.HasInHand(CardId.MalissP_MarchHare) && !ActiveMarchHare && !ssWhiteRabbit) + { + pref = CardId.MalissC_GWC06; + } + else if (Duel.Player == 0 && nsBackupplan) + { + pref = CardId.MalissC_GWC06; + } + else if (Duel.Player == 1) + { + pref = CardId.MalissC_MTP07; + } + else + { + pref = CardId.MalissC_MTP07; + } + if (CheckRemainInDeck(pref) > 0) + { + return pref; + } + + return 0; + } + + private bool ActLittleKnight() + { + if (ActivateDescription == -1 || ActivateDescription == Util.GetStringId(CardId.SP_LITTLE_KNIGHT, 0)) + { + 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.SP_LITTLE_KNIGHT, 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(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 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; + } + + private bool Step1_Dormouse_NormalSummon() + { + if (!CanStartStep1()) return false; + if (usedNormalSummon) return false; + usedNormalSummon = true; + NSDorMouse = true; + return true; + } + private bool Dormouse_ForMH() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!CanContinueStep1()) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + int pick = 0; + if (goldstart || undergroundstart) + { + pick = (CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0) + ? CardId.MalissP_WhiteRabbit + : CardId.MalissP_ChessyCat; + } + else + { + pick = (CheckRemainInDeck(CardId.MalissP_MarchHare) > 0) + ? CardId.MalissP_MarchHare + : (CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0) + ? CardId.MalissP_WhiteRabbit + : CardId.MalissP_ChessyCat; + } + if (pick == 0) return false; + AI.SelectCard(pick); + return true; + } + + private bool Step1_WhiteRabbit_SS_FromBanished() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (enemyActivateLancea) return false; + if (Card.Id != CardId.MalissP_WhiteRabbit) return false; + if (Card.Location != CardLocation.Removed) return false; + if (Bot.LifePoints <= 300) + { + return false; + } + ssWhiteRabbit = true; + return true; + } + + private bool Step1_WhiteRabbit_SetTrapOnSummon() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissP_WhiteRabbit) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + + int trapToSet = PickMalissTrapToSet(); + if (trapToSet == 0) return false; + + if (trapToSet == CardId.MalissC_GWC06) + gwc06SetThisTurn = true; + + if (trapToSet == CardId.MalissC_MTP07) + mtp07SetThisTurn = true; + + AI.SelectCard(trapToSet); + SelectSafeSTZoneAwayFromImperm(); + if (Step1Complete()) step1Done = true; + return true; + } + + private bool Step1_WhiteRabbit_NormalSummon() + { + if (!CanStartStep1()) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse)) return false; + if (usedNormalSummon) return false; + usedNormalSummon = true; + return true; + } + + private bool Dormouse_SS_FromBanished() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissP_Dormouse) return false; + if (Card.Location != CardLocation.Removed) return false; + if (enemyActivateLancea) return false; + if (Bot.LifePoints <= 300) + { + return false; + } + ssDormouse = true; + return true; + } + private bool ChessyCat_SS_FromBanished() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissP_ChessyCat) return false; + if (Card.Location != CardLocation.Removed) return false; + if (enemyActivateLancea) return false; + if (Bot.LifePoints <= 300) + { + return false; + } + ssChessyCat = true; + return true; + } + + private bool SummonLittleKnightFast() + { + if (!(Bot.HasInMonstersZone(CardId.LinkSpider)|| Bot.HasInMonstersZone(CardId.Linguriboh))) return false; + if (!HaveTwoBodies()) return false; + + var mats = Bot.GetMonsters() + .Where(c => c != null && c.IsFaceup() && c.HasType(CardType.Effect)) + .OrderBy(c => c.Attack) + .Take(2).ToList(); + + if (mats.Count < 2) return false; + + AI.SelectMaterials(mats); + step1Done = true; + return true; + } + private bool SPEmer() + { + if (Bot.HasInMonstersZone(CardId.FirewallDragon) || + Bot.HasInMonstersZone(CardId.AlliedCodeTalkerIgnister) || + Bot.HasInMonstersZone(CardId.Apollousa) || + Bot.HasInMonstersZone(CardId.AccesscodeTalker)) + { + return false; + } + if (!HaveTwoBodies()) return false; + + var mats = PickLinkMatsMinCount( + targetLink: 2, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 2, + avoidIds: new[] { CardId.Apollousa, CardId.AlliedCodeTalkerIgnister, CardId.AccesscodeTalker } + ); + if (mats.Count != 2) + { + return false; + } + + AI.SelectMaterials(mats); + return true; + } + + private bool GoldSarc_StartPiece() + { + if (CheckSpellWillBeNegate()) return false; + if (enemyActivateLancea) { return false; } + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit)) { return false; } + int pick = 0; + if (!Bot.HasInMonstersZone(CardId.MalissP_Dormouse) && CheckRemainInDeck(CardId.MalissP_Dormouse) > 0 && !ssDormouse) + { pick = CardId.MalissP_Dormouse; } + else if (!Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit) && CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0 && !ssWhiteRabbit) + { pick = CardId.MalissP_WhiteRabbit; } + else if (madeIt3 && !ssChessyCat) + { pick = CardId.MalissP_ChessyCat; } + else { return false; } + if (pick == 0) return false; + + + AI.SelectCard(pick); + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + goldstart = true; + return true; + } + private bool ExistsForUnderground(int id) + { + return CheckRemainInDeck(id) > 0 + || Bot.HasInHand(id) + || Bot.HasInGraveyard(id); + } + private bool Underground_ActivateStarter() + { + if (enemyActivateLancea) return false; + if (Bot.GetMonsterCount() != 0) return false; + if (step1Done) return false; + + int pick = 0; + if (ExistsForUnderground(CardId.MalissP_Dormouse)) + pick = CardId.MalissP_Dormouse; + else if (ExistsForUnderground(CardId.MalissP_WhiteRabbit)) + pick = CardId.MalissP_WhiteRabbit; + else if (ExistsForUnderground(CardId.MalissP_ChessyCat)) + pick = CardId.MalissP_ChessyCat; + + if (pick == 0) return false; + + AI.SelectYesNo(true); + AI.SelectCard(pick); + ActiveUnderground = true; + undergroundstart = true; + return true; + } + + private bool Terra_GrabUnderground() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (CheckSpellWillBeNegate()) return false; + if (ActiveUnderground) return false; + if (Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInSpellZone(CardId.MalissInUnderground)) + return false; + AI.SelectCard(CardId.MalissInUnderground); + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + return true; + } + private bool HaveBackupOrWizardInHand() + { + return Bot.HasInHand(CardId.BackupIgnister) || Bot.HasInHand(CardId.WizardIgnister); + } + private bool HaveMHInHand() + { + return Bot.HasInHand(CardId.MalissP_MarchHare); + } + private bool IsMalissBody(ClientCard c) + { + return c != null && c.IsFaceup() && c.HasSetcode(SetcodeMaliss) + && c.IsCode(CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare); + } + private bool Emergency_NormalCat() + { + if (Bot.GetMonsterCount() != 0) return false; + if (usedNormalSummon) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit) || + Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInHand(CardId.TERRAFORMING) || + Bot.HasInHand(CardId.GoldSarcophagus)) return false; + if (!HaveMHInHand()) + { + return false; + } + + usedNormalSummon = true; + return true; + } + private bool OneBody_Link1_Linguriboh() + { + if (!HaveBackupOrWizardInHand()) return false; + if (Bot.HasInMonstersZone(CardId.Linguriboh)) return false; + if (Bot.GetMonsterCount() != 1) return false; + return true; + } + private bool OneBody_Link1_Almiraj() + { + if (!HaveBackupOrWizardInHand()) return false; + if (Bot.HasInMonstersZone(CardId.SALAMANGREAT_ALMIRAJ)) return false; + if (Bot.GetMonsterCount() != 1) return false; + return true; + } + private bool OneBody_Backup_SS() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + bool haveLinkAnchor = Bot.HasInMonstersZone(CardId.Linguriboh) || Bot.HasInMonstersZone(CardId.SplashMage); + if (!haveLinkAnchor) return false; + if (Card.Location != CardLocation.Hand) return false; + + avoidLinkedZones = true; + return true; + } + private bool OneBody_Backup_SearchWizard() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!Card.IsCode(CardId.BackupIgnister)) return false; + + if (Bot.Hand.Count == 0) return false; + + bool haveWizard = Bot.HasInHand(CardId.WizardIgnister); + + int searchId = 0; + if (haveWizard && CheckRemainInDeck(CardId.MalissP_MarchHare) > 0 && Bot.Hand.Count > 0) + searchId = CardId.MalissP_MarchHare; + else if (CheckRemainInDeck(CardId.MalissP_Dormouse) > 0 && nsplan && Bot.HasInMonstersZone(CardId.SALAMANGREAT_ALMIRAJ)) + searchId = CardId.MalissP_Dormouse; + else if (CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0 && nsBackupplan) + searchId = CardId.MalissP_WhiteRabbit; + else if (!haveWizard && CheckRemainInDeck(CardId.WizardIgnister) > 0 && Bot.Hand.Count > 0) + searchId = CardId.WizardIgnister; + else if (CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0) + searchId = CardId.MalissP_WhiteRabbit; + else + return false; + + AI.SelectCard(searchId); + + var hand = Bot.Hand.Where(h => h != null).ToList(); + var candidates = hand.Where(h => h.Id != CardId.WizardIgnister).ToList(); + var discards = new List(hand.Count); + if (searchId == CardId.MalissP_Dormouse) + { + discards.AddRange(candidates.Where(c => c.Id == CardId.MalissP_Dormouse)); + } + IEnumerable OthersExcludingTarget() + { + if (searchId == CardId.MalissP_MarchHare) + return candidates.Where(c => c.Id != CardId.MalissP_MarchHare); + return candidates; + } + var othersExcludingTarget = OthersExcludingTarget().ToList(); + var dupGroups = othersExcludingTarget.GroupBy(c => c.Id) + .Where(g => g.Count() >= 2); + + discards.AddRange(dupGroups.SelectMany(g => g)); + + int[] lowValueSinglesOrder = { CardId.NibiruThePrimalBeing, + CardId.Lancea, + CardId.TERRAFORMING, + CardId.GoldSarcophagus + }; + + foreach (var id in lowValueSinglesOrder) + { + discards.AddRange(othersExcludingTarget + .Where(c => c.Id == id && dupGroups.All(g => g.Key != id))); + } + + var already = new HashSet(discards); + discards.AddRange(othersExcludingTarget.Where(c => !already.Contains(c))); + + discards = discards.Where(c => c != null).Distinct().ToList(); + + if (searchId == CardId.MalissP_Dormouse) + { + AI.SelectNextCard(searchId); + } + if (searchId == CardId.MalissP_WhiteRabbit) + { + AI.SelectNextCard(searchId); + } + else if (discards != null) + { + AI.SelectNextCard(discards); + } + else + { + return false; + } + + avoidLinkedZones = true; + if (Bot.HasInMonstersZone(CardId.Linguriboh)) blockWicckid = true; + if (GetMMZCount() >= 5 && Bot.HasInHand(CardId.WizardIgnister)) { fullBoard1 = true; } + return true; + } + private bool OneBody_Wizard_SS() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!Bot.HasInMonstersZone(CardId.Linguriboh) && !Bot.HasInMonstersZone(CardId.SALAMANGREAT_ALMIRAJ)) return false; + if (Card.Location != CardLocation.Hand) return false; + + var revive = PickGYCybersePriority(); + if (revive == null) return false; + + avoidLinkedZones = true; + blockWicckid = true; + + AI.SelectCard(revive); + return true; + } + private ClientCard PickGYCybersePriority() + { + var m = PickGYMalissPriority(); + if (m != null) return m; + + var list = Bot.Graveyard.GetMatchingCards(c => + c != null && c.IsMonster() && c.HasRace(CardRace.Cyberse) && c.Level <= 4).ToList(); + return list.FirstOrDefault(); + } + private ClientCard PickGYMalissPriority() + { + int[] pref = { + CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare + }; + foreach (var id in pref) + { + var c = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(id) && g.IsMonster()); + if (c != null) return c; + } + return null; + } + private bool TwoCC_NormalSummon() + { + if (usedNormalSummon) return false; + if (Bot.GetMonsterCount() != 0) return false; + + bool haveOtherStarter = + Bot.HasInHand(CardId.MalissP_Dormouse) || + Bot.HasInHand(CardId.MalissP_WhiteRabbit) || + Bot.HasInHand(CardId.GoldSarcophagus) || + Bot.HasInHand(CardId.MalissInUnderground); + + if (haveOtherStarter) return false; + + if (Bot.Hand.GetMatchingCards(c => c != null && c.IsCode(CardId.MalissP_ChessyCat)).Count >= 2) + { + usedNormalSummon = true; + return true; + } + return false; + } + private bool IsMalissCost(ClientCard card) + { + if (card == null) return false; + + if (card.IsCode(CardId.MalissP_ChessyCat)) + return true; + if (card.IsCode(CardId.MalissP_WhiteRabbit) && NSDorMouse && !ssWhiteRabbit) + return true; + if (card.IsCode(CardId.MalissP_WhiteRabbit) && ssWhiteRabbit) + return true; + if (card.IsCode(CardId.MalissP_Dormouse) && ssDormouse) + return true; + if (card.IsCode(CardId.MalissP_MarchHare) && ActiveMarchHare) + return true; + + return false; + } + private bool IsMalissCost2(ClientCard card) + { + if (card == null) return false; + + return + card.IsCode(CardId.MalissP_ChessyCat) || + card.IsCode(CardId.MalissP_WhiteRabbit) || + card.IsCode(CardId.MalissP_MarchHare) || + card.IsCode(CardId.MalissInUnderground); + } + private bool AnyDraw() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissP_ChessyCat) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + if (CheckWhetherNegated()) return false; + + ClientCard target = null; + + target = Bot.Hand.FirstOrDefault(c => c != null && c.IsCode(CardId.MalissInTheMirror)); + if (target == null) + { + target = Bot.Hand.FirstOrDefault(IsMalissCost); + } + if (target == null) + { + var malissDupGroup = Bot.Hand + .Where(IsMalissCost2) + .GroupBy(c => c.Id) + .FirstOrDefault(g => g.Count() >= 2); + + if (malissDupGroup != null) + { + target = malissDupGroup.First(); + } + } + if (target == null) + { + return false; + } + AI.SelectCard(target); + return true; + } + private int PickTB11CostCandidateId() + { + var field = Bot.GetMonsters() + .Where(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeMaliss)) + .ToList(); + + field = field.Where(c => + !(c.IsCode(CardId.MalissP_WhiteRabbit) && ssWhiteRabbit) && + !(c.IsCode(CardId.MalissP_ChessyCat) && ssChessyCat) && + !(c.IsCode(CardId.MalissP_MarchHare) && ssMarchHare) && + !(c.IsCode(CardId.MalissP_Dormouse) && ssDormouse) && + !(c.IsCode(CardId.MalissQ_RedRansom) && ssRRThisTurn) && + !(c.IsCode(CardId.MalissQ_WhiteBinder) && ssWBThisTurn) && + !(c.IsCode(CardId.MalissQ_HeartsCrypter) && ssHCThisTurn) + ).ToList(); + + int[] pref = { + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare, + CardId.MalissP_Dormouse, + CardId.MalissQ_RedRansom, + CardId.MalissQ_WhiteBinder, + CardId.MalissQ_HeartsCrypter + }; + foreach (var id in pref) + if (field.Any(c => c.IsCode(id))) return id; + + return 0; + } + private int PickPFromGYForSplash() + { + if (enemyActivateLancea) + { + int[] pref2 = { + CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare, + CardId.BackupIgnister, + CardId.WizardIgnister + }; + foreach (var id in pref2) + if (Bot.HasInGraveyard(id)) return id; + } + int[] pref = { + CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare + }; + foreach (var id in pref) + if (Bot.HasInGraveyard(id)) return id; + return 0; + } + private bool HaveUndergroundOnHandOrField() + { + return Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInSpellZone(CardId.MalissInUnderground); + } + private bool ShouldSearchUnderground() + { + if (ActiveUnderground) return false; + if (HaveUndergroundOnHandOrField()) return false; + return CheckRemainInDeck(CardId.MalissInUnderground) > 0; + } + private bool Step2_RedRansom_Search() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + int chooseId = 0; + bool shouldUG = ShouldSearchUnderground(); + if (shouldUG) + { + chooseId = CardId.MalissInUnderground; + } + else if (CheckRemainInDeck(CardId.MalissInTheMirror) > 0) + { + chooseId = CardId.MalissInTheMirror; + } + if (chooseId == 0) return false; + + //AI.SelectCard(chooseId); go to onselectcard + step2Done = true; + avoidLinkedZones = false; + coreSetupComplete = true; + return true; + } + private List PickMaterialsForRedRansom() + { + var splash = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(CardId.SplashMage)); + if (splash == null) return new List(); + + ClientCard revived = null; + if (lastRevivedIdBySplash != 0) + revived = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(lastRevivedIdBySplash)); + + if (revived == null) + revived = Bot.MonsterZone.FirstOrDefault(c => + c != null && c.IsFaceup() && c.HasSetcode(0x1bf) && c != splash && !c.HasType(CardType.Link)); + + if (revived == null) return new List(); + return new List { splash, revived }; + } + private bool Step2_SplashMage_ReviveP() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (step2Done) return false; + + if (!Bot.HasInMonstersZone(CardId.SplashMage)) return false; + + int pick = PickPFromGYForSplash(); + if (pick == 0) return false; + + AI.SelectCard(pick); + lastRevivedIdBySplash = pick; + return true; + } + private bool Step2N_SplashMage_ReviveP() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!Bot.HasInMonstersZone(CardId.SplashMage)) return false; + + int pick = PickPFromGYForSplash(); + if (pick == 0) return false; + + AI.SelectCard(pick); + lastRevivedIdBySplash = pick; + return true; + } + private bool Step2_LinkSummon_RedRansom() + { + if (step2Done) return false; + + var mats = PickMaterialsForRedRansom(); + if (mats.Count != 2) return false; + + if (Util.GetBotAvailZonesFromExtraDeck(mats) == 0) return false; + + AI.SelectMaterials(mats); + madeIt3 = true; + return true; + } + private bool Step2N_LinkSummon_WB() + { + var mats = PickMaterialsForRedRansom(); + if (mats.Count != 2) return false; + + if (Util.GetBotAvailZonesFromExtraDeck(mats) == 0) return false; + + AI.SelectMaterials(mats); + return true; + } + private bool Step2N_LinkSummon_RedRansom() + { + if (!Bot.HasInMonstersZone(CardId.MalissP_MarchHare) || Bot.GetMonsterCount() < 3) + { + return false; + } + madeIt3 = true; + return true; + } + private bool Step2_Fallback_Wizard_AfterSplashNegated() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!splashNegatedThisTurn) return false; + if (Card.Location != CardLocation.Hand) return false; + + var revive = PickGYMalissPriority(); + if (revive == null) return false; + + avoidLinkedZones = true; + blockWicckid = true; + AI.SelectCard(revive); + return true; + } + private bool Step2_Fallback_Backup_AfterSplashNegated() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!splashNegatedThisTurn) return false; + if (Card.Location != CardLocation.Hand) return false; + + if (GetMMZCount() >= 5) return false; + + int want = !Bot.HasInHand(CardId.WizardIgnister) && CheckRemainInDeck(CardId.WizardIgnister) > 0 + ? CardId.WizardIgnister + : (CheckRemainInDeck(CardId.MalissP_MarchHare) > 0 ? CardId.MalissP_MarchHare : 0); + if (want == 0) return false; + + AI.SelectCard(want); + + var discard = Bot.Hand.FirstOrDefault(h => h != null && h.Id != CardId.WizardIgnister && h != Card); + if (discard != null) AI.SelectNextCard(discard); + + avoidLinkedZones = true; + blockWicckid = true; + return true; + } + #endregion + + #region FLOW 3: Core → Results (UG / Mirror / TP11 → Make it 3!! → Finishers) + private bool HaveUG() => Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInSpellZone(CardId.MalissInUnderground); + private bool HasSelfSSAvailable(int id) + { + if (id == CardId.MalissP_Dormouse) return !ssDormouse; + if (id == CardId.MalissP_WhiteRabbit) return !ssWhiteRabbit; + if (id == CardId.MalissP_ChessyCat) return !ssChessyCat; + if (id == CardId.MalissP_MarchHare) return !ActiveMarchHare; + return true; + } + private int PickUG_DHG_DormouseFirst() + { + int[] pref = { + CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare + }; + + foreach (var id in pref) + { + if (!HasSelfSSAvailable(id)) continue; + if (ExistsForUnderground(id)) + return id; + } + return 0; + } + + private bool Flow3_UnderGround_Available_SSAnyPawn() + { + if (!step2Done) return false; + if (!HaveUG()) return false; + if (Card.Id != CardId.MalissInUnderground) return false; + if (resultSuccessFlag) return false; + if (GetMMZCount() >= 5) + { + AI.SelectYesNo(false); + return true; + } + if (enemyActivateLancea) + { + AI.SelectYesNo(false); + return true; + } + + int pick = 0; + + if (CheckRemainInDeck(CardId.MalissInTheMirror) > 0 && nsplan) + { + pick = CardId.MalissInTheMirror; + } + else if (CheckRemainInDeck(CardId.MalissInTheMirror) > 0 && NSDorMouse) + { + pick = CardId.MalissInTheMirror; + } + else + { + pick = PickUG_DHG_DormouseFirst(); + } + + if (pick == 0) + { + AI.SelectYesNo(false); + return true; + } + + AI.SelectYesNo(true); + AI.SelectCard(pick); + resultSuccessFlag = true; + return true; + } + private bool Mirror_Banish() + { + if (DescIs(CardId.MalissInTheMirror, 1)) + { + var gy = PickMirrorGYTargetForSearch(); + if (gy == null) return false; + + AI.SelectCard(gy); + + var wants = Mirror_SearchOrderForType(false, gy.IsMonster()); + AI.SelectNextCard(wants); + return true; + } + + if (CheckSpellWillBeNegate()) return false; + if (CheckWhetherNegated()) return false; + var cost = PickMirrorCostCandidate(); + if (cost == null) return false; + foreach (ClientCard m in Enemy.GetMonsters()) + { + if (m.IsMonsterShouldBeDisabledBeforeItUseEffect() && !m.IsDisabled() && Duel.LastChainPlayer != 0) + { + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + AI.SelectCard(m); + AI.SelectNextCard(cost); + return true; + } + } + ClientCard LastChainCard = Util.GetLastChainCard(); + if ((LastChainCard == null || LastChainCard.Controller != 1 || LastChainCard.Location != CardLocation.MonsterZone + || LastChainCard.IsDisabled() || LastChainCard.IsShouldNotBeTarget() || LastChainCard.IsShouldNotBeSpellTrapTarget())) + return false; + + 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()) + { + AI.SelectCard(card); + } + } + } + AI.SelectNextCard(cost); + return true; + } + private bool Flow3_Link_Accesscode() + { + if (Bot.HasInExtra(CardId.AlliedCodeTalkerIgnister)) return false; + if (BlockAccesscodeOnT1()) return false; + + var avoid = new[] { CardId.AlliedCodeTalkerIgnister, CardId.FirewallDragon }; + + var mats = PickLinkMatsMinCount( + targetLink: 4, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 4, + avoidIds: avoid + ); + + if (mats.Count < 2) return false; + AI.SelectMaterials(mats); + return true; + } + private bool Flow3_BackupIgnister_AfterMakeIt3() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!madeIt3) return false; + if (Card.Location != CardLocation.Hand) return false; + + avoidLinkedZones = true; + return true; + } + private bool Flow3_WizardIgnister_AfterMakeIt3() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!madeIt3) return false; + if (Card.Location != CardLocation.Hand) return false; + + var revive = PickGYMalissPriority(); + if (revive == null) return false; + + avoidLinkedZones = true; + AI.SelectCard(revive); + return true; + } + private bool RR_SS_FromBanished() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissQ_RedRansom) return false; + if (Card.Location != CardLocation.Removed) return false; + if (GetMMZCount() >= 5) return false; + if (Bot.LifePoints <= 900) + { + return false; + } + + int pickId = 0; + + int[] pawnOrder = { + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_Dormouse + }; + + foreach (var id in pawnOrder) + { + if (CheckRemainInDeck(id) > 0 && PawnSelfSS_AvailableId(id)) + { + pickId = id; + break; + } + } + + + bool canBanish = (pickId != 0); + AI.SelectYesNo(canBanish); + if (canBanish) + { + AI.SelectCard(pickId); + } + _rrSelfSSPlacing = true; + ssRRThisTurn = true; + return true; + } + private bool Wicckid_SearchTuner() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (CheckRemainInDeck(CardId.BackupIgnister) <= 0) return false; + var cost = PickGYCyberseForWicckidCost_Safe(); + if (cost == null) + { + return false; + } + + AI.SelectCard(cost); + AI.SelectNextCard(CardId.BackupIgnister); + avoidLinkedZones = false; + return true; + } + private bool LinkDecoder_ReviveFromGY() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Grave) { return false; } + if (Allied_End) { return false; } + return true; + } + private bool Transcode_ReviveLink3OrLower() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + + var prefer = Bot.Graveyard.GetFirstMatchingCard(c => c.IsCode(CardId.CyberseWicckid)) + ?? Bot.Graveyard.GetFirstMatchingCard(c => c.IsCode(CardId.SplashMage)) + ?? Bot.Graveyard.GetFirstMatchingCard(c => c.IsCode(CardId.MalissQ_RedRansom)) + ?? Bot.Graveyard.GetFirstMatchingCard(c => c.IsCode(CardId.MalissQ_WhiteBinder)) + ?? Bot.Graveyard + .GetMatchingCards(c => c.IsMonster() && c.HasType(CardType.Link) && c.LinkCount <= 3 && !c.IsCode(CardId.TranscodeTalker)) + .OrderByDescending(c => c.Attack).FirstOrDefault(); + + if (prefer == null) return false; + AI.SelectCard(prefer); + avoidLinkedZones = false; + return true; + } + private bool Allied_NegateBanish() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (CheckWhetherNegated(true) || !CheckLastChainShouldNegated()) return false; + var allied = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.AlliedCodeTalkerIgnister)); + if (allied == null || allied.IsDisabled()) return false; + bool haveAnyLink = Bot.GetMonsters().Any(m => m != null && m.HasType(CardType.Link) && !m.IsCode(CardId.AlliedCodeTalkerIgnister)); + if (!haveAnyLink) return false; + + var cheapLinks = new List { + CardId.CyberseWicckid, + CardId.MalissQ_WhiteBinder, + CardId.TranscodeTalker, + CardId.MalissQ_RedRansom + }; + AI.SelectCard(cheapLinks.ToArray()); + return true; + } + private ClientCard GetWicckid() + { + return Bot.MonsterZone.GetFirstMatchingFaceupCard(c => c != null && c.IsCode(CardId.CyberseWicckid)); + } + private int GetLinkedMaskFor(ClientCard link) + { + if (link == null || !link.IsFaceup() || !link.HasType(CardType.Link)) return 0; + return link.GetLinkedZones() & 0x1F; + } + private bool PawnSelfSS_AvailableId(int id) + { + if (id == CardId.MalissP_Dormouse) return !ssDormouse; + if (id == CardId.MalissP_WhiteRabbit) return !ssWhiteRabbit; + if (id == CardId.MalissP_ChessyCat) return !ssChessyCat; + if (id == CardId.MalissP_MarchHare) return !ActiveMarchHare; + return false; + } + private bool QueenSelfSS_AvailableId(int id) + { + if (id == CardId.MalissQ_HeartsCrypter) return !ssHCThisTurn; + if (id == CardId.MalissQ_WhiteBinder) return !ssWBThisTurn; + if (id == CardId.MalissQ_RedRansom) return !ssRRThisTurn; + return false; + } + private bool Dormouse_Banish_Anytime() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissP_Dormouse) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (enemyActivateLancea) return false; + if (!HasFreeMMZ()) return false; + + int pick = 0; + + if (CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0 && PawnSelfSS_AvailableId(CardId.MalissP_WhiteRabbit)) + pick = CardId.MalissP_WhiteRabbit; + else if (CheckRemainInDeck(CardId.MalissP_ChessyCat) > 0 && PawnSelfSS_AvailableId(CardId.MalissP_ChessyCat)) + pick = CardId.MalissP_ChessyCat; + + if (pick == 0) return false; + + AI.SelectCard(pick); + return true; + } + private bool IsCyberse(ClientCard c) => c != null && c.HasType(CardType.Monster) && c.HasRace(CardRace.Cyberse); + private bool RR_HOPT_Spent_ThisTurn() => ssRRThisTurn; + private bool RR_CanStillSS_ThisTurn() + { + if (RR_HOPT_Spent_ThisTurn()) return false; + return true; + } + private int Score_WicckidCost(ClientCard c) + { + if (c == null) return int.MinValue; + if (c.IsCode(CardId.MalissQ_RedRansom) && RR_HOPT_Spent_ThisTurn()) return -999; + if (c.IsCode(CardId.MalissQ_RedRansom) && RR_CanStillSS_ThisTurn()) return 1000; + + if (c.IsCode(CardId.Linguriboh) || c.IsCode(CardId.SALAMANGREAT_ALMIRAJ)) return 120; + + if (c.IsCode(CardId.MalissP_Dormouse) && ssDormouse) return 90; + if (c.IsCode(CardId.MalissP_WhiteRabbit) && ssWhiteRabbit) return 70; + if (c.IsCode(CardId.MalissP_ChessyCat) && ssChessyCat) return 60; + if (c.IsCode(CardId.MalissP_MarchHare) && ssMarchHare) return 60; + + if (c.IsCode(CardId.BackupIgnister)) return 20; + if (c.IsCode(CardId.WizardIgnister)) return 20; + return 30; + } + private ClientCard PickGYCyberseForWicckidCost_Safe() + { + var gy = (Bot.Graveyard ?? new List()) + .Where(IsCyberse) + .ToList(); + if (gy.Count == 0) return null; + + ClientCard best = null; + int bestScore = int.MinValue; + foreach (var c in gy) + { + int sc = Score_WicckidCost(c); + if (sc > bestScore) { best = c; bestScore = sc; } + } + return best; + } + private bool BlockAccesscodeOnT1() + { + bool blocked = (Duel.Player == 0 && Duel.Turn == 1); + return blocked; + } + private bool Allied_OnSummonTrigger() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + var me = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.AlliedCodeTalkerIgnister)); + if (me == null) return false; + + bool has2300 = Bot.Graveyard.Any(c => c != null && c.IsMonster() && c.HasRace(CardRace.Cyberse) && c.Attack == 2300); + if (!has2300) return false; + + var prefer = new List { + CardId.MalissQ_RedRansom, + CardId.MalissQ_WhiteBinder, + CardId.TranscodeTalker + }; + AI.SelectCard(prefer.ToArray()); + return true; + } + private bool ShouldSummonTranscode() + { + bool haveSplashGY = Bot.HasInGraveyard(CardId.SplashMage); + bool haveRR = Bot.HasInMonstersZone(CardId.MalissQ_RedRansom); + bool haveL1 = Bot.HasInMonstersZone(CardId.LinkDecoder) || Bot.HasInGraveyard(CardId.LinkDecoder); + + return haveSplashGY && (haveRR || haveL1); + } + private bool Step_SplashToRR() + { + if (_didSplashToRR) return false; + if (Bot.HasInMonstersZone(CardId.LinkDecoder)) return false; + + if (!Bot.HasInExtra(CardId.MalissQ_RedRansom)) return false; + + var mats = PickLinkMatsMinCount( + targetLink: 2, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 2, + avoidIds: new[] { CardId.CyberseWicckid } + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + + _didSplashToRR = true; + return true; + } + private bool Step_SplashToWB() + { + if (_didSplashToRR) return false; + + if (!Bot.HasInExtra(CardId.MalissQ_WhiteBinder)) return false; + + var mats = PickLinkMatsMinCount( + targetLink: 2, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 2, + avoidIds: new[] { CardId.CyberseWicckid } + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + + _didSplashToRR = true; + return true; + } + private bool Step_RRtoWicckid() + { + if(enemyActivateLancea) return false; + if (blockWicckid) return true; + if (!_didSplashToRR || _didRRtoWicckid) return false; + if (!Bot.HasInExtra(CardId.CyberseWicckid)) return false; + + var rr = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.MalissQ_RedRansom)); + if (rr == null) return false; + var emzOccupant = Bot.GetMonsters().FirstOrDefault(m => m != null && m != rr && IsInEMZ(m)); + var buddy = emzOccupant ?? Bot.MonsterZone.FirstOrDefault(m => m != null && m != rr); + if (buddy == null) return false; + bool emzWillBeFree = HasFreeEMZ() || IsInEMZ(rr) || IsInEMZ(buddy); + if (!emzWillBeFree) return false; + AI.SelectCard(CardId.CyberseWicckid); + AI.SelectMaterials(new List { rr, buddy }); + wantLinkedToWicckid = true; + _preferWicckidArrows = true; + _didRRtoWicckid = true; + return true; + } + private bool Step2N_RRtoWicckid() + { + if (enemyActivateLancea) return false; + if (blockWicckid) return false; + var rr = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.MalissQ_RedRansom)); + if (rr == null) return false; + var emzOccupant = Bot.GetMonsters().FirstOrDefault(m => m != null && m != rr && IsInEMZ(m)); + var buddy = emzOccupant ?? Bot.MonsterZone.FirstOrDefault(m => m != null && m != rr); + if (buddy == null) return false; + bool emzWillBeFree = HasFreeEMZ() || IsInEMZ(rr) || IsInEMZ(buddy); + if (!emzWillBeFree) return false; + AI.SelectCard(CardId.CyberseWicckid); + AI.SelectMaterials(new List { rr, buddy }); + wantLinkedToWicckid = true; + _preferWicckidArrows = true; + _didRRtoWicckid = true; + return true; + } + private bool Step_SummonLinkDecoderToWicckid() + { + if (!_didRRtoWicckid || _didSummonToWicckidArrow) return false; + + var wic = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.CyberseWicckid)); + if (wic == null) return false; + + _preferWicckidArrows = true; + avoidLinkedZones = false; + + return true; + } + private bool Step1_SSLinkDecoder() + { + if ((Bot.HasInMonstersZone(CardId.MalissP_Dormouse) || + Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit) || + Bot.HasInMonstersZone(CardId.MalissP_ChessyCat) || + Bot.HasInMonstersZone(CardId.BackupIgnister)) && Bot.GetMonsterCount() == 1 && Bot.HasInHand(CardId.MalissP_MarchHare)) + { + return true; + } + return false; + } + private bool IsMalissMonster(ClientCard c) => c != null && c.IsMonster() && c.HasSetcode(0x1bf); + private bool CanMakeLinkNWithFlexibleTwo(ClientCard a, ClientCard b, int target) + { + int a1 = 1, aL = (a != null && a.HasType(CardType.Link)) ? Math.Max(1, a.LinkCount) : 1; + int b1 = 1, bL = (b != null && b.HasType(CardType.Link)) ? Math.Max(1, b.LinkCount) : 1; + return (a1 + b1 == target) || (a1 + bL == target) || (aL + b1 == target) || (aL + bL == target); + } + private bool Step_WicckidPlusOneToWB() + { + const int WB_LINK = 3; + if (!Bot.HasInExtra(CardId.MalissQ_WhiteBinder)) return false; + + var wic = GetWicckid(); + if (wic != null) + { + var partners = Bot.GetMonsters() + .Where(m => m != null && m.IsFaceup() && m != wic && m.HasType(CardType.Effect)) + .ToList(); + + Func okPair = p => (IsMalissMonster(p) || IsMalissMonster(wic)) && CanMakeLinkNWithFlexibleTwo(wic, p, WB_LINK); + + var p2 = partners.FirstOrDefault(p => !p.IsCode(CardId.MalissQ_RedRansom) && okPair(p)) + ?? partners.FirstOrDefault(p => p.IsCode(CardId.MalissQ_RedRansom) && okPair(p)); + + if (p2 != null) + { + AI.SelectMaterials(new List { wic, p2 }); + _didWBFromWicckid = true; + EnsureFinishPlanAfterWB(); + return true; + } + } + + var mats = PickLinkMatsMinCount( + targetLink: WB_LINK, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 3, + avoidIds: new[] { CardId.Apollousa, CardId.FirewallDragon, CardId.AlliedCodeTalkerIgnister }, + requireMaliss: true + ); + if (mats.Count == 0) return false; + + AI.SelectMaterials(mats); + _didWBFromWicckid = true; + EnsureFinishPlanAfterWB(); + return true; + } + private void EnsureFinishPlanAfterWB() + { + if (_finishPlanDecided) return; + + int free = Bot.MonsterZone.Count(m => m != null && !m.IsCode(CardId.MalissQ_RedRansom)); + bool canFW = Bot.HasInExtra(CardId.FirewallDragon); + bool canHC = Bot.HasInExtra(CardId.MalissQ_HeartsCrypter); + bool canAll = Bot.HasInExtra(CardId.AlliedCodeTalkerIgnister); + bool reachAllied = CanReachAlliedNow(); + + if (canFW && canHC && canAll && free >= 3 && reachAllied) + _finishPlan = FinishPlan.FW_HC_Allied; + else if (canHC && canAll && free >= 2 && reachAllied) + _finishPlan = FinishPlan.HC_Allied; + else + _finishPlan = FinishPlan.AlliedOnly; + + _finishPlanDecided = true; + } + private bool ssFromHandMH() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Hand) return false; + if (GetMMZCount() > 3) return false; + if (enemyActivateLancea) return false; + + if (Duel.Player == 0) + { + var gy = Bot.Graveyard.GetMatchingCards(c => c != null && c.HasSetcode(0x1bf) && c != Card).ToList(); + + Func, ClientCard> pickP1 = (src) => + { + int[] pref = { + CardId.MalissQ_WhiteBinder, + CardId.MalissQ_HeartsCrypter, + CardId.MalissQ_RedRansom + }; + foreach (var id in pref) + { + var c = src.FirstOrDefault(x => x.HasType(CardType.Monster) && x.Id == id && QueenSelfSS_AvailableId(x.Id)); + if (c != null) return c; + } + return src.FirstOrDefault(x => x.HasType(CardType.Monster) && QueenSelfSS_AvailableId(x.Id)); + }; + ClientCard pick = pickP1(gy); + if (pick == null) return false; + AI.SelectCard(pick); + ssMarchHare = true; + return DontSelfNG(); + } + else + { + if (Bot.HasInGraveyard(CardId.MalissQ_WhiteBinder) && !ssWBThisTurn) + { + var Target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissQ_WhiteBinder)); + if (Target == null) return false; + AI.SelectCard(Target); + } + else if (Bot.HasInGraveyard(CardId.MalissQ_HeartsCrypter) && !ssHCThisTurn) + { + var Target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissQ_HeartsCrypter)); + if (Target == null) return false; + AI.SelectCard(Target); + } + else if (Bot.HasInGraveyard(CardId.MalissQ_RedRansom) && !ssRRThisTurn) + { + var Target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissQ_RedRansom)); + if (Target == null) return false; + AI.SelectCard(Target); + } + else + { + return false; + } + + ssMarchHare = true; + return DontSelfNG(); + } + } + private bool Step1_MH_FromHand() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Hand) return false; + if (Bot.GetMonsterCount() > 1) return false; + if (enemyActivateLancea) return false; + + if (Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit) && Bot.HasInGraveyard(CardId.MalissC_MTP07)) + { + var target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissC_MTP07)); + if (target == null) return false; + AI.SelectCard(target); + ssMarchHare = true; + return DontSelfNG(); + } + if (Bot.HasInMonstersZone(CardId.LinkDecoder) && Bot.HasInGraveyard(CardId.MalissP_Dormouse)) + { + var target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissP_Dormouse)); + if (target == null) return false; + AI.SelectCard(target); + ssMarchHare = true; + return DontSelfNG(); + } + if (Bot.HasInMonstersZone(CardId.LinkDecoder) && Bot.HasInGraveyard(CardId.MalissP_WhiteRabbit)) + { + var target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissP_WhiteRabbit)); + if (target == null) return false; + AI.SelectCard(target); + ssMarchHare = true; + return DontSelfNG(); + } + if (Bot.HasInMonstersZone(CardId.LinkDecoder) && Bot.HasInGraveyard(CardId.MalissP_ChessyCat)) + { + var target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissP_ChessyCat)); + if (target == null) return false; + AI.SelectCard(target); + ssMarchHare = true; + return DontSelfNG(); + } + if (Bot.HasInMonstersZone(CardId.Linguriboh) && Bot.HasInGraveyard(CardId.MalissP_MarchHare)) + { + var target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissP_MarchHare)); + if (target == null) return false; + AI.SelectCard(target); + ssMarchHare = true; + return DontSelfNG(); + } + + return false; + } + private bool returnFromBanish() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Removed) return false; + + var mh = Bot.Banished.GetFirstMatchingCard( + c => c.IsFaceup() && c.IsCode(CardId.MalissP_MarchHare)); + + if (mh == null) return false; + + AI.SelectCard(mh); + ActiveMarchHare = true; + return true; + } + private bool WB_OnSummon_BanishGY() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + + const int MAX_PICKS = 3; + var picks = new List(); + + if (Duel.Player == 0) + { + int freeMMZ = Math.Max(0, 5 - GetMMZCount()); + int myPawnCount = Bot.Graveyard + .GetMatchingCards(g => IsMalissPawn(g)) + .Count; + bool canUseOwnPawn = myPawnCount > 1; + + if (!ActiveMarchHare && canUseOwnPawn) + { + var mh = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_MarchHare)); + if (mh != null) picks.Add(mh); + } + + if (freeMMZ > 0 && !ssRRThisTurn && !Allied_End) + { + var rr = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissQ_RedRansom)); + if (rr != null && !picks.Contains(rr) && !ShouldSkipBanishing(rr)) + { picks.Add(rr); freeMMZ--; } + } + + if (freeMMZ > 0 && !ssHCThisTurn && !Allied_End) + { + var hc = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissQ_HeartsCrypter)); + if (hc != null && !picks.Contains(hc) && !ShouldSkipBanishing(hc)) + { picks.Add(hc); freeMMZ--; } + } + + if (freeMMZ > 0 && !ssWhiteRabbit && !Allied_End && canUseOwnPawn) + { + var wr = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_WhiteRabbit)); + if (wr != null && !picks.Contains(wr) && !ShouldSkipBanishing(wr)) + { picks.Add(wr); freeMMZ--; } + } + + if (freeMMZ > 0 && !ssDormouse && !Allied_End && canUseOwnPawn) + { + var dm = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_Dormouse)); + if (dm != null && !picks.Contains(dm) && !ShouldSkipBanishing(dm)) + { picks.Add(dm); freeMMZ--; } + } + + if (freeMMZ > 0 && !ssChessyCat && !Allied_End && canUseOwnPawn) + { + var cc = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_ChessyCat)); + if (cc != null && !picks.Contains(cc) && !ShouldSkipBanishing(cc)) + { picks.Add(cc); freeMMZ--; } + } + + if (freeMMZ >= 0 && canUseOwnPawn) + { + var target = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_ChessyCat) || g.IsCode(CardId.MalissP_Dormouse)); + picks.Add(target); + } + + picks = picks.Where(c => c != null).Distinct().Take(MAX_PICKS).ToList(); + + var threats = PickEnemyGYThreats(MAX_PICKS - picks.Count); + foreach (var t in threats) + { + if (picks.Count >= MAX_PICKS) break; + if (!picks.Contains(t)) picks.Add(t); + } + } + else + { + int freeMMZ = Math.Max(0, 5 - GetMMZCount()); + + if (!Bot.HasInSpellZone(CardId.MalissC_MTP07) && freeMMZ > 0 && !ssWhiteRabbit) + { + var wr = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_WhiteRabbit)); + if (wr != null) { picks.Add(wr); freeMMZ--; } + } + + if (picks.Count >= 0 && !Bot.HasInHand(CardId.MalissP_MarchHare) && !ActiveMarchHare) + { + var mh = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_MarchHare)); + if (mh != null) picks.Add(mh); + } + + if (freeMMZ > 0 && !ssRRThisTurn) + { + var rr = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissQ_RedRansom)); + if (rr != null && !picks.Contains(rr) && !ShouldSkipBanishing(rr)) + { picks.Add(rr); freeMMZ--; } + } + + if (freeMMZ > 0 && !ssHCThisTurn) + { + var hc = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissQ_HeartsCrypter)); + if (hc != null && !picks.Contains(hc) && !ShouldSkipBanishing(hc)) + { picks.Add(hc); freeMMZ--; } + } + + var threats = PickEnemyGYThreats(MAX_PICKS - picks.Count); + foreach (var t in threats) + { + if (picks.Count >= MAX_PICKS) break; + if (!picks.Contains(t)) picks.Add(t); + } + } + + if (picks.Count == 0) return false; + + if (picks.Count > MAX_PICKS) picks = picks.Take(MAX_PICKS).ToList(); + if (picks.Count < MAX_PICKS) + { + var more = PickEnemyGYThreats(MAX_PICKS - picks.Count); + foreach (var t in more) + if (!picks.Contains(t)) picks.Add(t); + } + AI.SelectCard(picks); + return true; + } + private bool IsMalissPawn(ClientCard c) + { + return c != null && ( + c.IsCode(CardId.MalissP_ChessyCat) || + c.IsCode(CardId.MalissP_WhiteRabbit) || + c.IsCode(CardId.MalissP_Dormouse) || + c.IsCode(CardId.MalissP_MarchHare) + ); + } + private List PickEnemyGYThreats(int need) + { + var result = new List(); + if (need <= 0) return result; + + var danger = GetDangerousCardinEnemyGrave(false); + foreach (var c in danger) + { + if (result.Count >= need) break; + if (!result.Contains(c)) result.Add(c); + } + + if (result.Count < need) + { + for (int i = Enemy.Graveyard.Count - 1; i >= 0 && result.Count < need; i--) + { + var c = Enemy.Graveyard[i]; + if (c == null) continue; + if (!result.Contains(c)) result.Add(c); + } + } + + if (result.Count < need) + { + foreach (var c in Enemy.Graveyard) + { + if (result.Count >= need) break; + if (!result.Contains(c)) result.Add(c); + } + } + + return result; + } + private bool WB_OnBanished_SelfSS() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Removed) return false; + if (GetMMZCount() >= 5) return false; + if (Bot.LifePoints <= 900) + { + return false; + } + AI.SelectYesNo(true); + ssWBThisTurn = true; + return true; + } + private int PickMalissTrapForWB() + { + if (CheckRemainInDeck(CardId.MalissC_GWC06) > 0 || Bot.HasInGraveyard(CardId.MalissC_GWC06)) + return CardId.MalissC_GWC06; + + if (CheckRemainInDeck(CardId.MalissC_MTP07) > 0 || Bot.HasInGraveyard(CardId.MalissC_MTP07)) + return CardId.MalissC_MTP07; + + return 0; + } + private bool WB_SetMalissTrap() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (ActivateDescription != Util.GetStringId(CardId.MalissQ_WhiteBinder, 1)) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + + + SelectSafeSTZoneAwayFromImperm(); + return true; + } + private List PickLinkMatsMinCount(int targetLink, + Func isEligible, + int minCount, + int maxCount, + IEnumerable avoidIds = null, + bool requireMaliss = false) + { + int LinkValOf(ClientCard m) => m.HasType(CardType.Link) ? Math.Max(1, m.LinkCount) : 1; + bool IsOneVal(ClientCard m) => !m.HasType(CardType.Link) || Math.Max(1, m.LinkCount) == 1; + + var all = Bot.GetMonsters() + .Where(m => m != null && m.IsFaceup() && isEligible(m)) + .ToList(); + if (all.Count < minCount) return new List(); + + var avoid = new HashSet(avoidIds ?? Enumerable.Empty()); + + List OrderForFirst(IEnumerable src) => src.OrderByDescending(LinkValOf) + .ThenBy(m => avoid.Contains(m.Id) ? 1 : 0) + .ThenBy(m => m.Attack) + .ToList(); + + List OrderForLater(IEnumerable src) => src.OrderBy(m => IsOneVal(m) ? 0 : 1) + .ThenBy(m => m.HasType(CardType.Link) ? (Math.Max(1, m.LinkCount) == 1 ? 0 : 1) : -1) + .ThenBy(LinkValOf) + .ThenBy(m => avoid.Contains(m.Id) ? 1 : 0) + .ThenBy(m => m.Attack) + .ToList(); + + var poolPreferred = all.Where(m => !avoid.Contains(m.Id)).ToList(); + var poolFallback = all.ToList(); + + int firstMaxAllowed = targetLink - Math.Max(0, minCount - 1); + + List TryPick(List pool) + { + var chosen = new List(); + bool Dfs(List avail, int sum) + { + if (sum > targetLink || chosen.Count > maxCount) return false; + + if (chosen.Count >= minCount && sum == targetLink) + { + if (requireMaliss && !chosen.Any(IsMalissBody)) return false; + if (Util.GetBotAvailZonesFromExtraDeck(chosen) == 0) return false; + return true; + } + + int need = Math.Max(0, minCount - chosen.Count); + if (sum + need > targetLink) return false; + + var ordered = (chosen.Count == 0) + ? OrderForFirst(avail) + : OrderForLater(avail); + + for (int i = 0; i < ordered.Count; i++) + { + var m = ordered[i]; + var nextAvail = ordered.Where((x, idx) => idx != i).ToList(); + int lv = LinkValOf(m); + + int[] values = (chosen.Count == 0) + ? (lv > 1 ? new[] { Math.Min(lv, firstMaxAllowed), 1 } : new[] { 1 }) + : (lv > 1 ? new[] { 1, lv } : new[] { 1 }); + + foreach (var v in values.Distinct()) + { + int newSum = sum + v; + if (newSum > targetLink) continue; + + int remMin = Math.Max(0, minCount - (chosen.Count + 1)); + if (newSum + remMin > targetLink) continue; + + chosen.Add(m); + if (Dfs(nextAvail, newSum)) return true; + chosen.RemoveAt(chosen.Count - 1); + } + } + return false; + } + + if (Dfs(OrderForFirst(pool), 0)) return chosen; + return null; + } + + var pick = TryPick(poolPreferred); + if (pick != null && pick.Count > 0) return pick; + + pick = TryPick(poolFallback); + return pick ?? new List(); + } + private bool Link_Apo() + { + if (!(Bot.HasInMonstersZone(CardId.MalissQ_RedRansom) && + Bot.HasInMonstersZone(CardId.LinkDecoder))) return false; + + + var mats = PickLinkMatsMinCount( + targetLink: 4, + isEligible: m => m.HasType(CardType.Monster), + minCount: 2, + maxCount: 2, + avoidIds: new[] { CardId.TranscodeTalker, CardId.AlliedCodeTalkerIgnister, CardId.AccesscodeTalker } + ); + if (mats.Count == 0) return false; + AI.SelectMaterials(mats); + blockWicckid = true; + return true; + } + private bool Flow3_Link_Firewall() + { + if (Bot.HasInMonstersZone(CardId.MalissQ_HeartsCrypter) && Bot.HasInMonstersZone(CardId.LinkDecoder) && Bot.HasInMonstersZone(CardId.CyberseWicckid)) + { + var mats = PickLinkMatsMinCount( + targetLink: 4, + isEligible: m => m.HasType(CardType.Monster), + minCount: 2, + maxCount: 2, + avoidIds: new[] { CardId.CyberseWicckid, CardId.Apollousa, CardId.TranscodeTalker, CardId.AlliedCodeTalkerIgnister, CardId.AccesscodeTalker } + ); + if (mats.Count == 0) return false; + AI.SelectMaterials(mats); + return true; + } + return false; + } + private bool Step_LinkSummon_HeartsCrypter() + { + if((Bot.HasInMonstersZone(CardId.MalissQ_WhiteBinder) && Bot.HasInMonstersZone(CardId.MalissQ_RedRansom) && + Bot.HasInMonstersZone(CardId.Apollousa) && Bot.GetMonsterCount() < 5)) return false; + var cand = Bot.GetMonsters() + .Where(c => c != null && c.IsFaceup() && c.HasType(CardType.Effect)) + .ToList(); + if (cand.Count < 3) return false; + + bool IsMaliss(ClientCard m) => m.HasSetcode(0x1bf); + + var avoid = new HashSet { CardId.CyberseWicckid, CardId.Apollousa, CardId.AlliedCodeTalkerIgnister, CardId.AccesscodeTalker, CardId.FirewallDragon, CardId.TranscodeTalker }; + + var ordered = cand + .OrderBy(m => avoid.Contains(m.Id) ? 2 : 0) + .ThenBy(m => m.HasType(CardType.Link) ? 1 : 0) + .ThenBy(m => m.Attack) + .ToList(); + + List mats = ordered.Take(3).ToList(); + if (!mats.Any(IsMaliss)) + { + var maliss = ordered.FirstOrDefault(IsMaliss); + if (maliss == null) return false; + mats[2] = maliss; + } + + if (Util.GetBotAvailZonesFromExtraDeck(mats) == 0) return false; + AI.SelectMaterials(mats); + return true; + } + private bool HC_Quick_ReturnBanished_AndBanishField() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (ActivateDescription != Util.GetStringId(CardId.MalissQ_HeartsCrypter, 0)) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + if (CheckWhetherNegated()) return false; + + RefreshNoChainWindows(); + + var banishedMaliss = GetBanishedMaliss(); + if (banishedMaliss.Count == 0) return false; + bool haveReturn = banishedMaliss.Count > 0; + + bool mustNow = GetProblematicEnemyCardList( + canBeTarget: true, + selfType: CardType.Monster | CardType.Spell | CardType.Trap + ).Count > 0; + + if (Duel.Player == 0) + { + if (!haveReturn && !mustNow) return false; + } + else + { + if (!mustNow && !IsPreferredRemovalTiming()) return false; + } + + if (!haveReturn) return false; + var ret = PickBanishedMalissForHC(banishedMaliss); + if (ret == null) return false; + + var fieldTargets = GetProblematicEnemyCardList(true, selfType: CardType.Monster | CardType.Spell | CardType.Trap); + if (fieldTargets.Count == 0) + { + var any = GetBestEnemyCard(false, true); + if (any != null) fieldTargets.Add(any); + } + if (fieldTargets.Count == 0) return false; + + AI.SelectCard(ret); + AI.SelectNextCard(fieldTargets); + ConsumePreferredWindow(); + return DontSelfNG(); + } + private List GetBanishedMaliss() + { + return Bot.Banished.GetMatchingCards(c => + c != null && c.IsFaceup() && c.HasSetcode(0x1bf)).ToList(); + } + private ClientCard PickBanishedMalissForHC(List cand) + { + cand = cand + .Where(c => !c.IsCode(CardId.MalissQ_WhiteBinder) && !c.IsCode(CardId.MalissQ_RedRansom)) + .ToList(); + if (cand.Count == 0) return null; + + int Score(ClientCard c) + { + if (c.IsCode(CardId.MalissInTheMirror)) return 100; + if (c.IsCode(CardId.MalissC_MTP07)) return 95; + if (c.IsCode(CardId.MalissC_GWC06)) return 90; + if (c.IsCode(CardId.MalissInUnderground)) return 85; + if (c.IsCode(CardId.MalissP_MarchHare)) return 80; + if (c.IsCode(CardId.MalissP_ChessyCat)) return 75; + if (c.IsCode(CardId.MalissP_WhiteRabbit)) return 70; + if (c.IsCode(CardId.MalissP_Dormouse)) return 65; + return 50; + } + return cand.OrderByDescending(Score).First(); + } + private bool HC_OnBanished_SpecialSummon() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Removed) return false; + + if (GetMMZCount() >= 5) + { + return false; + } + + if (Bot.LifePoints <= 900) + { + return false; + } + + AI.SelectYesNo(true); + ssHCThisTurn = true; + return true; + } + private bool HasMalissLinkFaceup() + { + return Bot.GetMonsters().Any(c => c != null && c.IsFaceup() && c.HasSetcode(0x1bf) && c.HasType(CardType.Link)); + } + private bool CanReachAlliedNow() + { + var mats = PickLinkMatsMinCount( + targetLink: 5, + isEligible: m => m.HasType(CardType.Effect), + minCount: 3, + maxCount: 5, + avoidIds: new[] { CardId.Apollousa, CardId.FirewallDragon, CardId.AccesscodeTalker } + ); + return mats.Count > 0; + } + private bool Flow3_Link_Allied() + { + if (!(Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_RedRansom) && Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_WhiteBinder))) return false; + var mats = PickLinkMatsMinCount( + targetLink: 5, + isEligible: m => m.HasType(CardType.Effect), + minCount: 3, + maxCount: 5, + avoidIds: new[] { CardId.Apollousa, CardId.FirewallDragon, CardId.AccesscodeTalker } + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + Allied_End = true; + return true; + } + private bool Emer_Allied() + { + if(!enemyActivateLancea) return false; + if (!(Bot.HasInMonstersZone(CardId.TranscodeTalker) && Bot.HasInMonstersZone(CardId.SplashMage))) return false; + var mats = PickLinkMatsMinCount( + targetLink: 5, + isEligible: m => m.HasType(CardType.Effect), + minCount: 3, + maxCount: 3 + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + Allied_End = true; + return true; + } + private bool Emer_Allied2() + { + var myMonsters = Bot.GetMonsters().Where(m => m != null).ToList(); + if (myMonsters.Count != 3) return false; + + var link3List = myMonsters + .Where(m => m.HasType(CardType.Link) && m.LinkCount == 3) + .ToList(); + + if (link3List.Count != 1) return false; + + bool haveKeyInGY = + Bot.HasInGraveyard(CardId.MalissQ_RedRansom) || + Bot.HasInGraveyard(CardId.MalissQ_WhiteBinder) || + Bot.HasInGraveyard(CardId.TranscodeTalker); + + if (!haveKeyInGY) return false; + + var mats = PickLinkMatsMinCount( + targetLink: 5, + isEligible: m => m.HasType(CardType.Effect), + minCount: 3, + maxCount: 3 + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + Allied_End = true; + return true; + } + private ClientCard FindGWC06TargetByOrder(params int[] ids) + { + foreach (var id in ids) + { + var gy = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(id)); + if (gy != null) return gy; + + var ban = Bot.Banished.GetFirstMatchingCard(c => c != null && c.IsCode(id) && c.IsFaceup()); + if (ban != null) return ban; + } + return null; + } + private int PickGWC06CostCandidateId() + { + if (Bot.HasInMonstersZone(CardId.MalissQ_WhiteBinder) && !ssWBThisTurn) + return CardId.MalissQ_WhiteBinder; + if (Bot.HasInMonstersZone(CardId.MalissQ_RedRansom) && !ssRRThisTurn) + return CardId.MalissQ_RedRansom; + + int card = PickTB11CostCandidateId(); + if (card != 0) return card; + + return 0; + } + private ClientCard PickGWC06TargetExtend() + { + if (Duel.Turn > 2) + { + return FindGWC06TargetByOrder( + CardId.MalissQ_HeartsCrypter, + CardId.MalissQ_RedRansom, + CardId.MalissQ_WhiteBinder + ); + } + else { + return FindGWC06TargetByOrder( + CardId.MalissQ_WhiteBinder, + CardId.MalissQ_RedRansom, + CardId.MalissQ_HeartsCrypter, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_Dormouse, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare + ); + } + } + private bool GWC06_MyTurn_Extend() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Duel.Player == 0 && + GetMMZCount() >= 4 && + Bot.HasInMonstersZone(CardId.FirewallDragon) && + (Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_RedRansom) || + Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_WhiteBinder) || + Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_HeartsCrypter))) return false; + if (Duel.Player != 0) return false; + if (!(Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2)) return false; + if (CheckSpellWillBeNegate()) return false; + if (GetMMZCount() >= 5) return false; + if (!Bot.HasInMonstersZone(CardId.MalissQ_WhiteBinder) && !(Bot.HasInMonstersZone(CardId.MalissQ_RedRansom) && Bot.GetMonsterCount() == 1)) return false; + var target = PickGWC06TargetExtend(); + if (target == null) return false; + if (gwc06SetThisTurn) + { + int costId = PickGWC06CostCandidateId(); + if (costId == 0) return false; + AI.SelectCard(costId); + AI.SelectNextCard(target); + return DontSelfNG(); + } + AI.SelectCard(target); + return DontSelfNG(); + } + private bool GWC06_OppTurn_ReviveWB_HC() + { + if (Duel.Player != 1) return false; + if (!Bot.HasInSpellZone(CardId.MalissC_GWC06)) return false; + if (CheckSpellWillBeNegate()) return false; + if (GetMMZCount() >= 5) return false; + + var target = FindGWC06TargetByOrder( + CardId.MalissQ_WhiteBinder, + CardId.MalissQ_HeartsCrypter + ); + if (target == null) return false; + + if (gwc06SetThisTurn) + { + int costId = PickGWC06CostCandidateId(); + if (costId == 0) return false; + AI.SelectCard(costId); + AI.SelectNextCard(target); + return DontSelfNG(); + } + + AI.SelectCard(target); + return DontSelfNG(); + } + private static int FirstBit(int mask) + { + for (int i = 0; i < 32; i++) + { + int b = 1 << i; + if ((mask & b) != 0) return b; + } + return 0; + } + private static int FirstBitFromOrder(int mask, int[] order) + { + foreach (var b in order) + if ((mask & b) != 0) return b; + return FirstBit(mask); + } + private int PreferSafeSummonZones(int available) + { + int MAIN_MASK = + (int)Zones.z0 | + (int)Zones.z1 | + (int)Zones.z2 | + (int)Zones.z3 | + (int)Zones.z4; + + int emzMask = available & ~MAIN_MASK; + + if (emzMask != 0) + return FirstBit(emzMask); + + int enemyPointed = 0; + try + { + enemyPointed = Enemy.GetLinkedZones(); + } + catch { } + + int safeMain = (available & MAIN_MASK) & ~enemyPointed; + + if (safeMain != 0) + { + return FirstBitFromOrder( + safeMain, + new[] { (int)Zones.z2, (int)Zones.z1, (int)Zones.z3, (int)Zones.z0, (int)Zones.z4 } + ); + } + + return FirstBit(available); + } + private int PickMTP07CostCandidateId() + { + return PickTB11CostCandidateId(); + } + private ClientCard PickMTP07EnemyRemovalTarget() + { + var list = GetProblematicEnemyCardList(canBeTarget: true, ignoreSpells: false, selfType: CardType.Trap); + if (list.Count > 0) return list[0]; + + var m = GetBestEnemyMonster(onlyFaceup: false, canBeTarget: true); + if (m != null) return m; + + var s = GetBestEnemySpell(onlyFaceup: false, canBeTarget: true); + if (s != null) return s; + + return Enemy.GetMonsters().FirstOrDefault(c => c != null) + ?? Enemy.GetSpells().FirstOrDefault(c => c != null); + } + private bool MTP07_ForMH() + { + if (!(Bot.GetMonsterCount() == 1 && Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit))) return false; + if (CheckSpellWillBeNegate()) return false; + if (CheckWhetherNegated()) return false; + + int searchId = PickMTP07SearchId(); + if (searchId == 0) return false; + + if (mtp07SetThisTurn) + { + int costId = PickMTP07CostCandidateId(); + if (costId == 0) return false; + AI.SelectCard(costId); + } + AI.SelectNextCard(searchId); + return DontSelfNG(); + } + private bool MTP07_OppTurn_RemoveEnemyOnly() + { + if (Duel.Player != 1) return false; + if (!HasMalissLinkFaceup()) return false; + if (CheckSpellWillBeNegate()) return false; + if (CheckWhetherNegated()) return false; + var urgent = GetProblematicEnemyCardList(canBeTarget: true, ignoreSpells: false, selfType: CardType.Trap); + if (urgent.Count == 0 && !IsPreferredRemovalTiming()) return false; + bool preBattle = Duel.Phase == DuelPhase.Main1 && Enemy.GetMonsterCount() > 0; + if (urgent.Count == 0 && !(IsPreferredRemovalTiming() || preBattle)) return false; + int searchId = PickMTP07SearchId(); + if (searchId == 0) return false; + ClientCard target = null; + if (urgent.Count > 0) + { + target = urgent[0]; + } + else if (preBattle) + { + target = Enemy.MonsterZone + .Where(c => c != null && c.IsFaceup()) + .OrderByDescending(c => c.Attack) + .FirstOrDefault(); + } + else + { + target = PickMTP07EnemyRemovalTarget(); + } + if (target == null) return false; + if (mtp07SetThisTurn) + { + int costId = PickMTP07CostCandidateId(); + if (costId == 0) return false; + AI.SelectCard(costId); + AI.SelectNextCard(searchId); + AI.SelectNextCard(target); + ConsumePreferredWindow(); + return DontSelfNG(); + } + AI.SelectCard(searchId); + AI.SelectNextCard(target); + ConsumePreferredWindow(); + return DontSelfNG(); + } + private bool Emergency_NS() + { + if (usedNormalSummon) return false; + if (Bot.GetMonsterCount() != 0) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit) + || Bot.HasInHand(CardId.MalissP_ChessyCat) + || Bot.HasInHand(CardId.GoldSarcophagus) || Bot.HasInHand(CardId.MalissInUnderground)) return false; + if (!Bot.HasInHand(CardId.BackupIgnister)) return false; + usedNormalSummon = true; + nsplan = true; + return true; + } + private bool IsWicInEMZ(ClientCard wic) + { + return wic != null + && wic.Location == CardLocation.MonsterZone + && (wic.Sequence == EMZ_LEFT || wic.Sequence == EMZ_RIGHT); + } + private IEnumerable GetWicDownSeq(ClientCard wic) + { + if (!IsWicInEMZ(wic)) yield break; + if (wic.Sequence == EMZ_LEFT) { yield return 1; yield return 2; } + if (wic.Sequence == EMZ_RIGHT) { yield return 3; yield return 4; } + } + private bool IsUnderWic(ClientCard wic, ClientCard m) + { + if (wic == null || m == null) return false; + if (m.Controller != wic.Controller) return false; + if (m.Location != CardLocation.MonsterZone) return false; + if (m.Sequence < 0 || m.Sequence > 4) return false; + return GetWicDownSeq(wic).Contains(m.Sequence); + } + private ClientCard PickUnderlingForTranscode(ClientCard wic, System.Collections.Generic.IList pool) + { + if (!IsWicInEMZ(wic)) return null; + var underlings = pool.Where(x => x != null && x != wic && x.IsFaceup() + && x.Location == CardLocation.MonsterZone + && x.Controller == wic.Controller + && x.Sequence >= 0 && x.Sequence <= 4 + && IsUnderWic(wic, x) + && (x.HasType(CardType.Link) || x.HasType(CardType.Effect)) + && !x.HasType(CardType.Token)) + .ToList(); + + if (underlings.Count == 0) return null; + + var preferLD = underlings.FirstOrDefault(x => x.IsCode(CardId.LinkDecoder)); + if (preferLD != null) return preferLD; + return underlings + .OrderByDescending(x => x.HasType(CardType.Link) ? 2 : 1) + .ThenByDescending(x => x.Attack) + .FirstOrDefault(); + } + private static readonly int[] PreferCenterMainSeq = new[] { 2, 1, 3, 0, 4 }; + + private bool SummonTranscode() + { + if (!enemyActivateLancea) return false; + var wic = Bot.GetMonsters().FirstOrDefault( + x => x != null && x.IsFaceup() && x.IsCode(CardId.CyberseWicckid)); + + if (!IsWicInEMZ(wic)) return false; + + var under = PickUnderlingForTranscode(wic, Bot.GetMonsters()); + if (under == null) return false; + + SelectLinkMaterialsPair(wic, under); + + int emz = EmzBitFor(wic); + if (emz != 0) AI.SelectPlace(emz); + + return true; + } + private void SelectLinkMaterialsPair(ClientCard a, ClientCard b) + { + try + { + var mats = new System.Collections.Generic.List { a, b }; + AI.SelectMaterials(mats); + return; + } + catch { } + + AI.SelectCard(a); + AI.SelectNextCard(b); + } + private int EmzBitFor(ClientCard link) + { + if (link == null || link.Location != CardLocation.MonsterZone) return 0; + if (link.Sequence == EMZ_LEFT) return 1 << EMZ_LEFT; // 1<<5 + if (link.Sequence == EMZ_RIGHT) return 1 << EMZ_RIGHT; // 1<<6 + return 0; + } + int ChooseAndRememberWicckidEmz(int available) + { + int emzAvail = available & EMZ_ALL; + if (emzAvail == 0) return 0; + + int best = 0; + int bestScore = int.MinValue; + + foreach (int emz in new[] { EMZ_L, EMZ_R }) + { + if ((emzAvail & emz) == 0) continue; + + int score = 0; + int down = DownBitOfEmz(emz); + + if (down == MZ1 && IsMainFreeSeq(1)) score += 10; + if (down == MZ3 && IsMainFreeSeq(3)) score += 10; + + if (score > bestScore) + { + bestScore = score; + best = emz; + } + } + + if (best == 0) + best = (emzAvail & EMZ_L) != 0 ? EMZ_L : EMZ_R; + + _wicckidEmzBit = best; + return best; + } + bool IsMainFreeSeq(int seq) + { + var ms = Bot.MonsterZone.GetMonsters(); + var occupied = ms.Any(m => m != null && m.Controller == 0 && m.Sequence == seq); + return !occupied; + } + int DownBitOfEmz(int emzBit) + { + if (emzBit == EMZ_L) return MZ1; + if (emzBit == EMZ_R) return MZ3; + return 0; + } + private bool IsPreferredRemovalTiming() + { + if (Duel.Player != 1) return false; + if (_prefWindowTTL > 0) return true; + if (_oppJustActivatedPersistentSpell || _oppJustSummoned || _oppJustSet) return true; + + return false; + } + private void ConsumePreferredWindow() + { + _prefWindowTTL = 0; + _oppJustActivatedPersistentSpell = false; + _oppJustSummoned = false; + _oppJustSet = false; + } + private void RefreshNoChainWindows() + { + bool oppMain = (Duel.Player == 1) && (Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2); + + int curMon = Enemy.GetMonsterCount(); + if (curMon > _enemyMonsterCountSnap) + { + _oppJustSummoned = true; + if (oppMain) _prefWindowTTL = Math.Max(_prefWindowTTL, 2); + } + _enemyMonsterCountSnap = curMon; + + int curFD = Enemy.SpellZone.Count(c => c != null && c.IsFacedown()); + if (curFD > _enemyFacedownSTSnap) + { + _oppJustSet = true; + if (oppMain) _prefWindowTTL = Math.Max(_prefWindowTTL, 2); + } + _enemyFacedownSTSnap = curFD; + + if (oppMain) + { + if (_prefWindowTTL > 0) _prefWindowTTL--; + if (_prefWindowTTL == 0) + { + _oppJustActivatedPersistentSpell = false; + _oppJustSummoned = false; + _oppJustSet = false; + } + } + else + { + _prefWindowTTL = 0; + _oppJustActivatedPersistentSpell = false; + _oppJustSummoned = false; + _oppJustSet = false; + } + } + private bool FirewallBounce_OnOppSummon() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (ActivateDescription != Util.GetStringId(CardId.FirewallDragon, 0)) + return false; + + if (Duel.LastSummonPlayer != 1) + return false; + + var picks = new List(); + + var negateList = GetMonsterListForTargetNegate(); + if (negateList != null) + { + foreach (var c in negateList) + { + if (c == null) continue; + if (c.Controller != 1) continue; + if (!c.IsMonster()) continue; + if (!c.IsFaceup()) continue; + if (!picks.Contains(c)) + picks.Add(c); + } + } + foreach (var m in Enemy.GetMonsters().OrderByDescending(x => x.Attack)) + { + if (m == null) continue; + if (!m.IsMonster()) continue; + if (!m.IsFaceup()) continue; + if (!picks.Contains(m)) + picks.Add(m); + } + + if (picks.Count == 0) + return false; + + AI.SelectCard(picks); + return true; + } + private bool Accesscode_OnSummon_AtkUp() + { + var list = new List(); + int[] prefer = { + CardId.MalissQ_HeartsCrypter, + CardId.MalissQ_WhiteBinder, + CardId.MalissQ_RedRansom, + CardId.TranscodeTalker + }; + foreach (var id in prefer) + { + var hit = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(id)); + if (hit != null) list.Add(hit); + } + + AI.SelectCard(list); + return true; + } + + private bool Accesscode_Destroy_Ignition() + { + if (ActivateDescription != Util.GetStringId(CardId.AccesscodeTalker, 1)) + return false; + + if (Enemy.GetFieldCount() == 0) + return false; + + IEnumerable costPool = + (Bot.GetGraveyardMonsters() ?? Enumerable.Empty()) + .Where(c => c.HasType(CardType.Link)) + .Concat((Bot.GetMonsters() ?? Enumerable.Empty()) + .Where(c => c.HasType(CardType.Link) && c != Card)); + + var costList = costPool + .OrderBy(c => c.Location != CardLocation.Grave) + .ThenBy(c => c.LinkCount) + .ThenBy(c => c.Attack) + .ToList(); + + if (costList.Count == 0) + return false; + + var targets = new List(); + + targets.AddRange(Enemy.GetSpells() + .Where(s => s != null && s.IsFacedown())); + targets.AddRange(Enemy.GetSpells() + .Where(s => s != null && !s.IsFacedown())); + targets.AddRange(Enemy.GetMonsters() + .OrderByDescending(m => m.Attack)); + + if (targets.Count == 0) + return false; + + AI.SelectCard(costList); + AI.SelectNextCard(targets); + + return true; + } + private bool AlreadySSFromBanishThisTurn(ClientCard c) + { + if (c == null) return false; + if (c.Controller != 0) return false; + + if (c.IsCode(CardId.MalissP_Dormouse)) return ssDormouse; + if (c.IsCode(CardId.MalissP_WhiteRabbit)) return ssWhiteRabbit; + if (c.IsCode(CardId.MalissP_ChessyCat)) return ssChessyCat; + + if (c.IsCode(CardId.MalissQ_RedRansom)) return ssRRThisTurn; + if (c.IsCode(CardId.MalissQ_HeartsCrypter)) return ssHCThisTurn; + if (c.IsCode(CardId.MalissQ_WhiteBinder)) return ssWBThisTurn; + + return false; + } + private bool ShouldSkipBanishing(ClientCard c) + { + if (c == null) return true; + if (c.Controller != 0) return false; + if (AlreadySSFromBanishThisTurn(c)) return true; + if (c.IsCode(CardId.MalissP_MarchHare) && ActiveMarchHare) return true; + return false; + } + private bool DescIs(int cardId, params int[] idx) + { + if (ActivateDescription == -1) return true; + foreach (var i in idx) + if (ActivateDescription == Util.GetStringId(cardId, i)) return true; + return false; + } + + private ClientCard PickMirrorCostCandidate() + { + int[] handPref = { + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_Dormouse, + CardId.MalissP_MarchHare + }; + foreach (var id in handPref) + { + var h = Bot.Hand.GetFirstMatchingCard(c => c != null && c.IsCode(id)); + if (h != null) return h; + } + + var fieldP = Bot.GetMonsters() + .Where(c => c != null && c.HasSetcode(0x1bf) && !c.HasType(CardType.Link)) + .OrderBy(c => c.Attack).FirstOrDefault(); + if (fieldP != null) return fieldP; + + int[] avoid = { + CardId.MalissQ_HeartsCrypter, CardId.MalissQ_WhiteBinder, CardId.MalissQ_RedRansom + }; + var weakLink = Bot.GetMonsters() + .Where(c => c != null && c.HasSetcode(0x1bf) && c.HasType(CardType.Link) && !avoid.Contains(c.Id)) + .OrderBy(c => c.Attack).FirstOrDefault(); + return weakLink; + } + + private int[] Mirror_SearchOrderForType(bool isTrap, bool isMon) + { + if (isTrap) + return new[] { CardId.MalissC_GWC06, CardId.MalissC_MTP07 }; + if (isMon) + { + if (!Bot.HasInHand(CardId.MalissP_WhiteRabbit) && NSDorMouse) + { + return new[] { CardId.MalissP_WhiteRabbit, CardId.MalissP_MarchHare, CardId.MalissP_Dormouse, CardId.MalissP_ChessyCat }; + } + else + { + return new[] { CardId.MalissP_MarchHare, CardId.MalissP_Dormouse, CardId.MalissP_WhiteRabbit, CardId.MalissP_ChessyCat }; + } + } + return new[] { CardId.MalissP_MarchHare, CardId.MalissP_Dormouse, CardId.MalissP_WhiteRabbit, CardId.MalissP_ChessyCat }; + } + + private ClientCard PickMirrorGYTargetForSearch() + { + if (NSDorMouse && !ssRRThisTurn) + { + var rr = Bot.Graveyard.GetFirstMatchingCard( + c => c != null && c.IsCode(CardId.MalissQ_RedRansom)); + + if (rr != null) + return rr; + } + int[] monPref = { CardId.MalissP_MarchHare, CardId.MalissP_Dormouse, CardId.MalissP_WhiteRabbit, CardId.MalissP_ChessyCat }; + foreach (var id in monPref) + { + var m = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(id)); + if (m != null && CheckRemainInDeck(id) > 0) return m; + } + return null; + } + private int PickMTP07SearchId() + { + if (CheckRemainInDeck(CardId.MalissP_MarchHare) > 0) + return CardId.MalissP_MarchHare; + + int[] pawnPref = { + CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat + }; + foreach (var id in pawnPref) + if (CheckRemainInDeck(id) > 0) return id; + + return 0; + } + private void SelectSafeSTZoneAwayFromImperm() + { + var safeCols = Enumerable.Range(0, 5) + .Where(seq => + { + if (Bot.SpellZone[seq] != null) return false; + if (infiniteImpermanenceList.Contains(seq)) return false; + return true; + }) + .ToList(); + + if (safeCols.Count == 0) + { + safeCols = Enumerable.Range(0, 5) + .Where(seq => Bot.SpellZone[seq] == null) + .ToList(); + } + + int mask = 0; + foreach (int seq in safeCols) mask |= (1 << seq); + AI.SelectPlace(mask); + } + private bool T3Allow() + { + if (Duel.Player != 0) return false; + if (myTurnCount < 2) return false; + if (!HaveBackupOrWizardInHand() || !Bot.HasInHand(CardId.MalissP_MarchHare)) return false; + return true; + } + private bool NSBackup() + { + if (Bot.GetMonsterCount() != 0) return false; + if (usedNormalSummon) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit) || Bot.HasInHand(CardId.MalissP_ChessyCat) || + Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInHand(CardId.TERRAFORMING) || + Bot.HasInHand(CardId.GoldSarcophagus)) return false; + nsBackupplan = true; + return true; + } + private bool NSBackup_L() + { + if (Bot.GetMonsterCount() != 0) return false; + if (usedNormalSummon) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit) || Bot.HasInHand(CardId.MalissP_ChessyCat) || + Bot.HasInHand(CardId.MalissP_MarchHare) || Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInHand(CardId.TERRAFORMING) || + Bot.HasInHand(CardId.GoldSarcophagus)) return false; + nsLanceaplan = true; + return true; + } + + private bool NSMH() + { + int mhCount = Bot.Hand.GetMatchingCards(c => c != null && c.IsCode(CardId.MalissP_MarchHare)).Count; + if (mhCount <2 ) return false; + if (Bot.GetMonsterCount() != 0) return false; + if (usedNormalSummon) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit) || Bot.HasInHand(CardId.MalissP_ChessyCat) || + Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInHand(CardId.TERRAFORMING) || + Bot.HasInHand(CardId.GoldSarcophagus)) return false; + return true; + } + private bool LinguribohMHLine() + { + if (!(Bot.HasInHand(CardId.MalissP_MarchHare) && Bot.HasInMonstersZone(CardId.MalissP_MarchHare))) return false; + if (Bot.GetMonsterCount() != 1) return false; + return true; + } + private bool EmerTranscode() + { + if (!enemyActivateLancea) return false; + if (Bot.GetMonsterCount() < 3) return false; + var mats = PickLinkMatsMinCount( + targetLink: 3, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 2 + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + return true; + } + #endregion + + // ======================= END OF LIFE ==================== + } +} diff --git a/WindBot.csproj b/WindBot.csproj index af5e94f7..3a304aaa 100644 --- a/WindBot.csproj +++ b/WindBot.csproj @@ -68,6 +68,7 @@ +