diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..616af734
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+
+[*.yml]
+indent_size = 2
diff --git a/.gitignore b/.gitignore
index 0c3d6b03..3314c9f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ x64/
build/
[Bb]in/
[Oo]bj/
+output/
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
!packages/*/build/
diff --git a/.travis.yml b/.travis.yml
index 032ddc5b..c7778071 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,16 +2,50 @@ os: windows
language: cpp
git:
depth: 1
-script:
-- export PATH="/c/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/MSBuild/15.0/Bin":$PATH
-- msbuild.exe -m -p:Configuration=Release WindBot.sln
-after_success:
-- cd bin
-- mv Release WindBot
-- git init
-- git checkout --orphan $DEPLOY_BRANCH
-- git config user.email deploy@travis-ci.org
-- git config user.name "Deployment Bot (from Travis CI)"
-- git add -A WindBot
-- git commit -qm "Deploy $DEPLOY_REPO to $DEPLOY_REPO:$DEPLOY_BRANCH"
-- git push -qf https://$DEPLOY_TOKEN@github.com/$DEPLOY_REPO.git $DEPLOY_BRANCH:$DEPLOY_BRANCH
+env:
+ global:
+ - ARTIFACT="WindBotIgnite-Release-$(date +%Y%m%d)-$TRAVIS_COMMIT.zip"
+ # Visual Studio Installer location for the Android SDK
+ - ANDROID_SDK_ROOT='C:\Program Files (x86)\Android\android-sdk'
+ # Visual Studio Installer location for an OpenJDK for Android development
+ - JAVA_HOME='C:\Program Files\Android\jdk\microsoft_dist_openjdk_1.8.0.25'
+jobs:
+ include:
+ - name: "Desktop exe"
+ script: dotnet build WindBot.csproj --configuration=Release
+ env: DESKTOP_BUILD=1
+ before_deploy:
+ - cd bin && mv Release WindBot
+ - 7z a -tzip "$ARTIFACT" WindBot
+ - mv $ARTIFACT .. && cd ..
+ - name: "Android aar"
+ install:
+ - choco install nuget.commandline visualstudio2017-workload-xamarinbuildtools visualstudio2017-workload-nativemobile visualstudio2017-workload-netcrossplat
+ - ./ci/install-sdk-ndk.sh
+ - choco install mono --x86 # Unspecified dependency for Embeddinator; 64-bit does not work
+ script:
+ - export PATH="/c/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/MSBuild/15.0/Bin":$PATH
+ - nuget restore WindBot.sln
+ - msbuild.exe -m -p:Configuration=Release WindBot.sln
+ before_deploy:
+ - cd bin && mkdir -p WindBot
+ - cp -r Release/COPYING Release/LICENSE Release/bots.json Release/Decks/ Release/Dialogs/ WindBot/
+ - 7z a WindBotIgnite-Resources.7z WindBot && cd ..
+ - mv output/libWindbot.aar bin/WindBotIgnite-Resources.7z .
+before_script: cp Dialogs/default.json .
+deploy:
+- provider: script
+ skip_cleanup: true
+ script: bash ./ci/deploy.sh
+ on:
+ repo: ProjectIgnis/windbot
+ condition: $DESKTOP_BUILD == 1
+- provider: releases
+ skip_cleanup: true
+ api_key: $RELEASES_TOKEN
+ file:
+ - $ARTIFACT
+ - libWindbot.aar
+ - WindBotIgnite-Resources.7z
+ on:
+ tags: true
diff --git a/BotWrapper/bot.conf b/BotWrapper/bot.conf
index 56296ab2..6e8c6511 100644
--- a/BotWrapper/bot.conf
+++ b/BotWrapper/bot.conf
@@ -2,7 +2,7 @@
# !name
# command
# description
-# flags (avail flags: SUPPORT_MASTER_RULE_3, SUPPORT_NEW_MASTER_RULE)
+# flags (avail flags: SUPPORT_MASTER_RULE_3, SUPPORT_NEW_MASTER_RULE, SUPPORT_MASTER_RULE_2020)
!随机-非常简单
Random=AI_LV1
@@ -86,7 +86,7 @@ AI_ANTI_META SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2
!尼亚-淘气仙星
Name=尼亚 Deck=Trickstar Dialog=near.zh-CN
-淘气仙星卡组。
+旧式淘气仙星卡组。
AI_LV3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
!尼亚-幻变骚灵
@@ -101,7 +101,7 @@ AI_LV3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
!复制梁龙-自奏圣乐
Name=复制梁龙 Deck=Orcust Dialog=anothercopy.zh-CN
-自奏圣乐卡组。
+旧式自奏圣乐卡组。
AI_LV3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
!复制梁龙-转生炎兽
@@ -122,7 +122,12 @@ AI_LV1 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
!永远之魂-荷鲁斯
Name=永远之魂 Deck=Horus Dialog=soul.zh-CN
老式龙族卡组。
-AI_LV1 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE
+AI_LV1 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
+
+!比特机灵-微风
+Name=比特机灵 Deck=PureWinds Dialog=zh-CN
+风属性卡组。
+AI_LV2 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
!试作型机器人1732
Name=试作型机器人1732 Deck=ST1732 Dialog=zh-CN
@@ -157,9 +162,14 @@ AI_ANTI_META SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2
!報社鬥士
Name=報社鬥士 Deck=GrenMajuThunderBoarder Dialog=kiwi.zh-TW
红莲雷王滑板卡组。
-AI_ANTI_META SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
+AI_ANTI_META SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
!我太帅了
Name=我太帅了 Deck=Dragun Dialog=smart.zh-CN
超魔导真红眼龙骑士卡组。
-AI_LV3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
+AI_LV3 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
+
+!玻璃女巫
+Name=玻璃女巫 Deck=Witchcraft Dialog=verre.zh-CN
+魔女术卡组。
+AI_LV3 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020
diff --git a/Decks/AI_ABC.ydk b/Decks/AI_ABC.ydk
new file mode 100644
index 00000000..edbd8f37
--- /dev/null
+++ b/Decks/AI_ABC.ydk
@@ -0,0 +1,59 @@
+#created by FlameFlash
+#main
+99249638
+99249638
+46659709
+46659709
+46659709
+65367484
+65367484
+65367484
+43147039
+89132148
+30012506
+30012506
+30012506
+77411244
+77411244
+77411244
+3405259
+3405259
+3405259
+39890958
+14558128
+32807846
+73628505
+73628505
+73628505
+12524259
+12524259
+12524259
+24224830
+24224830
+24224830
+65681983
+65681983
+65681983
+66399653
+66399653
+66399653
+10045474
+10045474
+10045474
+#extra
+1561110
+1561110
+1561110
+75286621
+10443957
+10443957
+58069384
+58069384
+21887175
+4280258
+38342335
+2857636
+75452921
+83152482
+65741786
+!side
diff --git a/Decks/AI_Dogmatika.ydk b/Decks/AI_Dogmatika.ydk
new file mode 100644
index 00000000..91e18312
--- /dev/null
+++ b/Decks/AI_Dogmatika.ydk
@@ -0,0 +1,59 @@
+#created by AlphaKretin
+#main
+69680031
+95679145
+3717252
+60303688
+60303688
+60303688
+86120751
+86120751
+86120751
+14558127
+14558127
+14558127
+1984618
+1984618
+1984618
+25311006
+25311006
+25311006
+73628505
+74063034
+74063034
+24224830
+24224830
+24224830
+48130397
+48130397
+48130397
+47679935
+47679935
+47679935
+10045474
+10045474
+10045474
+82956214
+82956214
+82956214
+21011044
+41420027
+41420027
+41420027
+#extra
+75286621
+20366274
+41209827
+69946549
+41373230
+97300502
+50907446
+94977269
+80532587
+80532587
+13529466
+74586817
+98506199
+2220237
+60303245
+!side
diff --git a/Decks/AI_Mathmech.ydk b/Decks/AI_Mathmech.ydk
new file mode 100644
index 00000000..01ff5178
--- /dev/null
+++ b/Decks/AI_Mathmech.ydk
@@ -0,0 +1,59 @@
+#created by ...
+#main
+8567955
+8567955
+8567955
+89743495
+89743495
+27182739
+27182739
+27182739
+53577438
+53577438
+53577438
+16360142
+16360142
+16360142
+80965043
+80965043
+80965043
+52354896
+52354896
+12580477
+14025912
+14532163
+14532163
+35261759
+35261759
+35261759
+57160136
+57160136
+57160136
+70368879
+70368879
+70368879
+81439173
+93104632
+93104632
+8267140
+41410651
+36361633
+36361633
+36361633
+#extra
+42632209
+42632209
+42632209
+15248594
+15248594
+15248594
+61399402
+61399402
+61399402
+85692042
+85692042
+85692042
+88021907
+88021907
+88021907
+!side
diff --git a/Decks/AI_PureWinds.ydk b/Decks/AI_PureWinds.ydk
new file mode 100644
index 00000000..915a1173
--- /dev/null
+++ b/Decks/AI_PureWinds.ydk
@@ -0,0 +1,74 @@
+#created by ...
+#main
+71007216
+71007216
+71007216
+81275020
+71175527
+71175527
+71175527
+43722862
+43722862
+43722862
+53932291
+53932291
+65277087
+65277087
+65277087
+54455435
+54455435
+54455435
+91662792
+91662792
+91662792
+16725505
+70117860
+70117860
+70117860
+12580477
+27980138
+27980138
+58577036
+83764718
+8267140
+25789292
+67723438
+67723438
+8608979
+8608979
+8608979
+24590232
+40605147
+84749824
+#extra
+27315304
+50954680
+50954680
+50954680
+82044279
+82044279
+14577226
+29552709
+29552709
+64880894
+84766279
+42110604
+70913714
+30674956
+90512490
+!side
+19420830
+19420830
+19420830
+94145021
+94145021
+94145021
+25789292
+25789292
+43711255
+43711255
+43711255
+58921041
+58921041
+23002292
+23002292
diff --git a/Decks/AI_Timethief.ydk b/Decks/AI_Timethief.ydk
new file mode 100644
index 00000000..121c3427
--- /dev/null
+++ b/Decks/AI_Timethief.ydk
@@ -0,0 +1,59 @@
+#created by ...
+#main
+65367484
+65367484
+65367484
+56308388
+56308388
+56308388
+67696066
+82496097
+82496097
+82496097
+74578720
+74578720
+74578720
+19891131
+19891131
+19891131
+70368879
+70368879
+70368879
+81439173
+10877309
+10877309
+10877309
+81670445
+81670445
+81670445
+18678554
+18678554
+18678554
+26708437
+26708437
+26708437
+57319935
+57319935
+98827725
+98827725
+98827725
+76587747
+76587747
+76587747
+#extra
+56832966
+56832966
+56832966
+86532744
+86532744
+86532744
+84013237
+84013237
+84013237
+55285840
+55285840
+55285840
+59208943
+59208943
+59208943
+!side
diff --git a/Decks/AI_Witchcraft.ydk b/Decks/AI_Witchcraft.ydk
new file mode 100644
index 00000000..e7ee2790
--- /dev/null
+++ b/Decks/AI_Witchcraft.ydk
@@ -0,0 +1,81 @@
+#created by ...
+#main
+49036338
+71074418
+21522601
+84523092
+84523092
+21744288
+21744288
+21744288
+95245544
+95245544
+95245544
+14558127
+38814750
+38814750
+38814750
+23434538
+59851535
+64756282
+64756282
+10805153
+11110587
+11110587
+14532163
+14532163
+49238328
+49238328
+49238328
+54693926
+54693926
+57916305
+57916305
+57916305
+58577036
+58577036
+58577036
+73594093
+83301414
+83301414
+83301414
+24224830
+24224830
+24224830
+56894757
+65681983
+65681983
+70226289
+13758665
+19673561
+40252269
+40252269
+83289866
+83289866
+83289866
+87769556
+10045474
+10045474
+10045474
+55072170
+94553671
+94553671
+#extra
+27548199
+5041348
+5041348
+74586817
+98558751
+85289965
+38342335
+2857636
+2857636
+8802510
+50588353
+60303245
+94259633
+94259633
+94259633
+!side
+65681983
+40252269
diff --git a/Dialogs/smart.zh-CN.json b/Dialogs/smart.zh-CN.json
index 32d85559..d376c40f 100644
--- a/Dialogs/smart.zh-CN.json
+++ b/Dialogs/smart.zh-CN.json
@@ -21,7 +21,8 @@
"消失吧,杂鱼"
],
"ondirectattack": [
- "爸爸,饶命"
+ "爸爸,饶命",
+ "恐怖如斯"
],
"facedownmonstername": "怪兽",
"activate": [
diff --git a/Dialogs/verre.zh-CN.json b/Dialogs/verre.zh-CN.json
new file mode 100644
index 00000000..3c423a17
--- /dev/null
+++ b/Dialogs/verre.zh-CN.json
@@ -0,0 +1,54 @@
+{
+ "welcome": [
+ "啊~~让我再睡一会嘛~",
+ "你要加入公会吗?",
+ "AI功能正在测试中,遇到问题请及时反馈。"
+ ],
+ "deckerror": [
+ "我的卡组里{0}不能用,我回去睡觉了。"
+ ],
+ "duelstart": [
+ "好困……",
+ "啊呀,对面看上去好厉害的样子。"
+ ],
+ "newturn": [
+ "我的回合,抽卡!",
+ "魔力补充!"
+ ],
+ "endturn": [
+ "不想干活怎么办……",
+ "就这样吧,该你了。"
+ ],
+ "directattack": [
+ "{0},直接攻击!",
+ "不过如此嘛,直接攻击!",
+ "快走开,我要回去睡觉了。",
+ ],
+ "attack": [
+ "{0},攻击{1}!",
+ "{0},替我打倒{1}!"
+ ],
+ "ondirectattack": [
+ "啊啊……",
+ "啊啊啊……",
+ "{0}好可怕啊……",
+ "我累了……"
+ ],
+ "facedownmonstername": "怪兽",
+ "activate": [
+ "{0}的效果发动!"
+ ],
+ "summon": [
+ "召唤{0}!",
+ "出来吧,{0}!",
+ "{0},来帮我一下。"
+ ],
+ "setmonster": [
+ "……"
+ ],
+ "chaining": [
+ "发动{0}!",
+ "等一下,我发动{0}。",
+ "要不是有{0},我都快睡着了。"
+ ]
+}
diff --git a/ExecutorBase/ExecutorBase.csproj b/ExecutorBase/ExecutorBase.csproj
new file mode 100644
index 00000000..8e1b6cf7
--- /dev/null
+++ b/ExecutorBase/ExecutorBase.csproj
@@ -0,0 +1,94 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {A1583FD7-7985-47DD-A835-8134DBF5811C}
+ Library
+ Properties
+ ExecutorBase
+ ExecutorBase
+ v4.0
+ 512
+ true
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\Mono.Data.Sqlite.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Game/AI/AIUtil.cs b/ExecutorBase/Game/AI/AIUtil.cs
similarity index 96%
rename from Game/AI/AIUtil.cs
rename to ExecutorBase/Game/AI/AIUtil.cs
index eeeb8bf0..ae208cf2 100644
--- a/Game/AI/AIUtil.cs
+++ b/ExecutorBase/Game/AI/AIUtil.cs
@@ -1,446 +1,446 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using YGOSharp.OCGWrapper.Enums;
-namespace WindBot.Game.AI
-{
- public class AIUtil
- {
- public Duel Duel { get; private set; }
- public ClientField Bot { get; private set; }
- public ClientField Enemy { get; private set; }
-
- public AIUtil(Duel duel)
- {
- Duel = duel;
- Bot = Duel.Fields[0];
- Enemy = Duel.Fields[1];
- }
-
- ///
- /// Get the total ATK Monster of the player.
- ///
- public int GetTotalAttackingMonsterAttack(int player)
- {
- return Duel.Fields[player].GetMonsters().Where(m => m.IsAttack()).Sum(m => (int?)m.Attack) ?? 0;
- }
- ///
- /// Get the best ATK or DEF power of the field.
- ///
- /// Bot or Enemy.
- /// Only calculate attack.
- public int GetBestPower(ClientField field, bool onlyATK = false)
- {
- return field.MonsterZone.GetMonsters()
- .Where(card => !onlyATK || card.IsAttack())
- .Max(card => (int?)card.GetDefensePower()) ?? -1;
- }
-
- public int GetBestAttack(ClientField field)
- {
- return GetBestPower(field, true);
- }
-
- public bool IsOneEnemyBetterThanValue(int value, bool onlyATK)
- {
- return Enemy.MonsterZone.GetMonsters()
- .Any(card => card.GetDefensePower() > value && (!onlyATK || card.IsAttack()));
- }
-
- public bool IsAllEnemyBetterThanValue(int value, bool onlyATK)
- {
- List monsters = Enemy.MonsterZone.GetMonsters();
- return monsters.Count > 0 && monsters
- .All(card => card.GetDefensePower() > value && (!onlyATK || card.IsAttack()));
- }
-
- ///
- /// Deprecated, use IsOneEnemyBetter and IsAllEnemyBetter instead.
- ///
- public bool IsEnemyBetter(bool onlyATK, bool all)
- {
- if (all)
- return IsAllEnemyBetter(onlyATK);
- else
- return IsOneEnemyBetter(onlyATK);
- }
-
- ///
- /// Is there an enemy monster who has better power than the best power of the bot's?
- ///
- /// Only calculate attack.
- public bool IsOneEnemyBetter(bool onlyATK = false)
- {
- int bestBotPower = GetBestPower(Bot, onlyATK);
- return IsOneEnemyBetterThanValue(bestBotPower, onlyATK);
- }
-
- ///
- /// Do all enemy monsters have better power than the best power of the bot's?
- ///
- /// Only calculate attack.
- public bool IsAllEnemyBetter(bool onlyATK = false)
- {
- int bestBotPower = GetBestPower(Bot, onlyATK);
- return IsAllEnemyBetterThanValue(bestBotPower, onlyATK);
- }
-
- public ClientCard GetBestBotMonster(bool onlyATK = false)
- {
- return Bot.MonsterZone.GetMonsters()
- .Where(card => !onlyATK || card.IsAttack())
- .OrderByDescending(card => card.GetDefensePower())
- .FirstOrDefault();
- }
-
- public ClientCard GetWorstBotMonster(bool onlyATK = false)
- {
- return Bot.MonsterZone.GetMonsters()
- .Where(card => !onlyATK || card.IsAttack())
- .OrderBy(card => card.GetDefensePower())
- .FirstOrDefault();
- }
-
- public ClientCard GetOneEnemyBetterThanValue(int value, bool onlyATK = false, bool canBeTarget = false)
- {
- return Enemy.MonsterZone.GetMonsters()
- .FirstOrDefault(card => card.GetDefensePower() >= value && (!onlyATK || card.IsAttack()) && (!canBeTarget || !card.IsShouldNotBeTarget()));
- }
-
- public ClientCard GetOneEnemyBetterThanMyBest(bool onlyATK = false, bool canBeTarget = false)
- {
- int bestBotPower = GetBestPower(Bot, onlyATK);
- return GetOneEnemyBetterThanValue(bestBotPower, onlyATK, canBeTarget);
- }
-
- public ClientCard GetProblematicEnemyCard(int attack = 0, bool canBeTarget = false)
- {
- ClientCard card = Enemy.MonsterZone.GetFloodgate(canBeTarget);
- if (card != null)
- return card;
-
- card = Enemy.SpellZone.GetFloodgate(canBeTarget);
- if (card != null)
- return card;
-
- card = Enemy.MonsterZone.GetDangerousMonster(canBeTarget);
- if (card != null)
- return card;
-
- card = Enemy.MonsterZone.GetInvincibleMonster(canBeTarget);
- if (card != null)
- return card;
-
- if (attack == 0)
- attack = GetBestAttack(Bot);
- return GetOneEnemyBetterThanValue(attack, true, canBeTarget);
- }
-
- public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false)
- {
- ClientCard card = Enemy.MonsterZone.GetFloodgate(canBeTarget);
- if (card != null)
- return card;
-
- card = Enemy.MonsterZone.GetDangerousMonster(canBeTarget);
- if (card != null)
- return card;
-
- card = Enemy.MonsterZone.GetInvincibleMonster(canBeTarget);
- if (card != null)
- return card;
-
- if (attack == 0)
- attack = GetBestAttack(Bot);
- return GetOneEnemyBetterThanValue(attack, true, canBeTarget);
- }
-
- public ClientCard GetProblematicEnemySpell()
- {
- ClientCard card = Enemy.SpellZone.GetFloodgate();
- return card;
- }
-
- public ClientCard GetBestEnemyCard(bool onlyFaceup = false, bool canBeTarget = false)
- {
- ClientCard card = GetBestEnemyMonster(onlyFaceup, canBeTarget);
- if (card != null)
- return card;
-
- card = GetBestEnemySpell(onlyFaceup);
- if (card != null)
- return card;
-
- return null;
- }
-
- public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false)
- {
- ClientCard card = GetProblematicEnemyMonster(0, canBeTarget);
- if (card != null)
- return card;
-
- card = Enemy.MonsterZone.GetHighestAttackMonster(canBeTarget);
- if (card != null)
- return card;
-
- List monsters = Enemy.GetMonsters();
-
- // after GetHighestAttackMonster, the left monsters must be face-down.
- if (monsters.Count > 0 && !onlyFaceup)
- return monsters[0];
-
- return null;
- }
-
- public ClientCard GetWorstEnemyMonster(bool onlyATK = false)
- {
- return Enemy.MonsterZone.GetMonsters()
- .Where(card => !onlyATK || card.IsAttack())
- .OrderBy(card => card.GetDefensePower())
- .FirstOrDefault();
- }
-
- public ClientCard GetBestEnemySpell(bool onlyFaceup = false)
- {
- ClientCard card = GetProblematicEnemySpell();
- if (card != null)
- return card;
-
- var spells = Enemy.GetSpells();
-
- card = spells.FirstOrDefault(ecard => ecard.IsFaceup() && (ecard.HasType(CardType.Continuous) || ecard.HasType(CardType.Field)));
- if (card != null)
- return card;
-
- if (spells.Count > 0 && !onlyFaceup)
- return spells[0];
-
- return null;
- }
-
- public ClientCard GetPZone(int player, int id)
- {
- if (Duel.IsNewRule)
- {
- return Duel.Fields[player].SpellZone[id * 4];
- }
- else
- {
- return Duel.Fields[player].SpellZone[6 + id];
- }
- }
-
- public int GetStringId(int id, int option)
- {
- return id * 16 + option;
- }
-
- public bool IsTurn1OrMain2()
- {
- return Duel.Turn == 1 || Duel.Phase == DuelPhase.Main2;
- }
-
- public int GetBotAvailZonesFromExtraDeck(IList remove)
- {
- ClientCard[] BotMZone = (ClientCard[])Bot.MonsterZone.Clone();
- ClientCard[] EnemyMZone = (ClientCard[])Enemy.MonsterZone.Clone();
- for (int i = 0; i < 7; i++)
- {
- if (remove.Contains(BotMZone[i])) BotMZone[i] = null;
- if (remove.Contains(EnemyMZone[i])) EnemyMZone[i] = null;
- }
-
- if (!Duel.IsNewRule || Duel.IsNewRule2020)
- return Zones.MainMonsterZones;
-
- int result = 0;
-
- if (BotMZone[5] == null && BotMZone[6] == null)
- {
- if (EnemyMZone[5] == null)
- result |= Zones.z6;
- if (EnemyMZone[6] == null)
- result |= Zones.z5;
- }
-
- if (BotMZone[0] == null &&
- ((BotMZone[1]?.HasLinkMarker(CardLinkMarker.Left) ?? false) ||
- (BotMZone[5]?.HasLinkMarker(CardLinkMarker.BottomLeft) ?? false) ||
- (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.TopRight) ?? false)))
- result |= Zones.z0;
-
- if (BotMZone[1] == null &&
- ((BotMZone[0]?.HasLinkMarker(CardLinkMarker.Right) ?? false) ||
- (BotMZone[2]?.HasLinkMarker(CardLinkMarker.Left) ?? false) ||
- (BotMZone[5]?.HasLinkMarker(CardLinkMarker.Bottom) ?? false) ||
- (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.Top) ?? false)))
- result |= Zones.z1;
-
- if (BotMZone[2] == null &&
- ((BotMZone[1]?.HasLinkMarker(CardLinkMarker.Right) ?? false) ||
- (BotMZone[3]?.HasLinkMarker(CardLinkMarker.Left) ?? false) ||
- (BotMZone[5]?.HasLinkMarker(CardLinkMarker.BottomRight) ?? false) ||
- (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.TopLeft) ?? false) ||
- (BotMZone[6]?.HasLinkMarker(CardLinkMarker.BottomLeft) ?? false) ||
- (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.TopRight) ?? false)))
- result |= Zones.z2;
-
- if (BotMZone[3] == null &&
- ((BotMZone[2]?.HasLinkMarker(CardLinkMarker.Right) ?? false) ||
- (BotMZone[4]?.HasLinkMarker(CardLinkMarker.Left) ?? false) ||
- (BotMZone[6]?.HasLinkMarker(CardLinkMarker.Bottom) ?? false) ||
- (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.Top) ?? false)))
- result |= Zones.z3;
-
- if (BotMZone[4] == null &&
- ((BotMZone[3]?.HasLinkMarker(CardLinkMarker.Right) ?? false) ||
- (BotMZone[6]?.HasLinkMarker(CardLinkMarker.BottomRight) ?? false) ||
- (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.TopLeft) ?? false)))
- result |= Zones.z4;
-
- return result;
- }
-
- public int GetBotAvailZonesFromExtraDeck(ClientCard remove)
- {
- return GetBotAvailZonesFromExtraDeck(new[] { remove });
- }
-
- public int GetBotAvailZonesFromExtraDeck()
- {
- return GetBotAvailZonesFromExtraDeck(new List());
- }
-
- public bool IsChainTarget(ClientCard card)
- {
- return Duel.ChainTargets.Any(card.Equals);
- }
-
- public bool IsChainTargetOnly(ClientCard card)
- {
- return Duel.ChainTargetOnly.Count == 1 && card.Equals(Duel.ChainTargetOnly[0]);
- }
-
- public bool ChainContainsCard(int id)
- {
- return Duel.CurrentChain.Any(card => card.IsCode(id));
- }
-
- public bool ChainContainsCard(int[] ids)
- {
- return Duel.CurrentChain.Any(card => card.IsCode(ids));
- }
-
- public int ChainCountPlayer(int player)
- {
- return Duel.CurrentChain.Count(card => card.Controller == player);
- }
-
- public bool ChainContainPlayer(int player)
- {
- return Duel.CurrentChain.Any(card => card.Controller == player);
- }
-
- public bool HasChainedTrap(int player)
- {
- return Duel.CurrentChain.Any(card => card.Controller == player && card.HasType(CardType.Trap));
- }
-
- public ClientCard GetLastChainCard()
- {
- return Duel.CurrentChain.LastOrDefault();
- }
-
- ///
- /// Select cards listed in preferred.
- ///
- public IList SelectPreferredCards(ClientCard preferred, IList cards, int min, int max)
- {
- IList selected = new List();
- if (cards.IndexOf(preferred) > 0 && selected.Count < max)
- {
- selected.Add(preferred);
- }
-
- return selected;
- }
-
- ///
- /// Select cards listed in preferred.
- ///
- public IList SelectPreferredCards(int preferred, IList cards, int min, int max)
- {
- IList selected = new List();
- foreach (ClientCard card in cards)
- {
- if (card.IsCode(preferred) && selected.Count < max)
- selected.Add(card);
- }
-
- return selected;
- }
-
- ///
- /// Select cards listed in preferred.
- ///
- public IList SelectPreferredCards(IList preferred, IList cards, int min, int max)
- {
- IList selected = new List();
- IList avail = cards.ToList(); // clone
- while (preferred.Count > 0 && avail.IndexOf(preferred[0]) > 0 && selected.Count < max)
- {
- ClientCard card = preferred[0];
- preferred.Remove(card);
- avail.Remove(card);
- selected.Add(card);
- }
-
- return selected;
- }
-
- ///
- /// Select cards listed in preferred.
- ///
- public IList SelectPreferredCards(IList preferred, IList cards, int min, int max)
- {
- IList selected = new List();
- foreach (int id in preferred)
- {
- foreach (ClientCard card in cards)
- {
- if (card.IsCode(id) && selected.Count < max && selected.IndexOf(card) <= 0)
- selected.Add(card);
- }
- if (selected.Count >= max)
- break;
- }
-
- return selected;
- }
-
- ///
- /// Check and fix selected to make sure it meet the count requirement.
- ///
- public IList CheckSelectCount(IList _selected, IList cards, int min, int max)
- {
- var selected = _selected.ToList();
- if (selected.Count < min)
- {
- foreach (ClientCard card in cards)
- {
- if (!selected.Contains(card))
- selected.Add(card);
- if (selected.Count >= max)
- break;
- }
- }
- while (selected.Count > max)
- {
- selected.RemoveAt(selected.Count - 1);
- }
-
- return selected;
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using YGOSharp.OCGWrapper.Enums;
+namespace WindBot.Game.AI
+{
+ public class AIUtil
+ {
+ public Duel Duel { get; private set; }
+ public ClientField Bot { get; private set; }
+ public ClientField Enemy { get; private set; }
+
+ public AIUtil(Duel duel)
+ {
+ Duel = duel;
+ Bot = Duel.Fields[0];
+ Enemy = Duel.Fields[1];
+ }
+
+ ///
+ /// Get the total ATK Monster of the player.
+ ///
+ public int GetTotalAttackingMonsterAttack(int player)
+ {
+ return Duel.Fields[player].GetMonsters().Where(m => m.IsAttack()).Sum(m => (int?)m.Attack) ?? 0;
+ }
+ ///
+ /// Get the best ATK or DEF power of the field.
+ ///
+ /// Bot or Enemy.
+ /// Only calculate attack.
+ public int GetBestPower(ClientField field, bool onlyATK = false)
+ {
+ return field.MonsterZone.GetMonsters()
+ .Where(card => !onlyATK || card.IsAttack())
+ .Max(card => (int?)card.GetDefensePower()) ?? -1;
+ }
+
+ public int GetBestAttack(ClientField field)
+ {
+ return GetBestPower(field, true);
+ }
+
+ public bool IsOneEnemyBetterThanValue(int value, bool onlyATK)
+ {
+ return Enemy.MonsterZone.GetMonsters()
+ .Any(card => card.GetDefensePower() > value && (!onlyATK || card.IsAttack()));
+ }
+
+ public bool IsAllEnemyBetterThanValue(int value, bool onlyATK)
+ {
+ List monsters = Enemy.MonsterZone.GetMonsters();
+ return monsters.Count > 0 && monsters
+ .All(card => card.GetDefensePower() > value && (!onlyATK || card.IsAttack()));
+ }
+
+ ///
+ /// Deprecated, use IsOneEnemyBetter and IsAllEnemyBetter instead.
+ ///
+ public bool IsEnemyBetter(bool onlyATK, bool all)
+ {
+ if (all)
+ return IsAllEnemyBetter(onlyATK);
+ else
+ return IsOneEnemyBetter(onlyATK);
+ }
+
+ ///
+ /// Is there an enemy monster who has better power than the best power of the bot's?
+ ///
+ /// Only calculate attack.
+ public bool IsOneEnemyBetter(bool onlyATK = false)
+ {
+ int bestBotPower = GetBestPower(Bot, onlyATK);
+ return IsOneEnemyBetterThanValue(bestBotPower, onlyATK);
+ }
+
+ ///
+ /// Do all enemy monsters have better power than the best power of the bot's?
+ ///
+ /// Only calculate attack.
+ public bool IsAllEnemyBetter(bool onlyATK = false)
+ {
+ int bestBotPower = GetBestPower(Bot, onlyATK);
+ return IsAllEnemyBetterThanValue(bestBotPower, onlyATK);
+ }
+
+ public ClientCard GetBestBotMonster(bool onlyATK = false)
+ {
+ return Bot.MonsterZone.GetMonsters()
+ .Where(card => !onlyATK || card.IsAttack())
+ .OrderByDescending(card => card.GetDefensePower())
+ .FirstOrDefault();
+ }
+
+ public ClientCard GetWorstBotMonster(bool onlyATK = false)
+ {
+ return Bot.MonsterZone.GetMonsters()
+ .Where(card => !onlyATK || card.IsAttack())
+ .OrderBy(card => card.GetDefensePower())
+ .FirstOrDefault();
+ }
+
+ public ClientCard GetOneEnemyBetterThanValue(int value, bool onlyATK = false, bool canBeTarget = false)
+ {
+ return Enemy.MonsterZone.GetMonsters()
+ .FirstOrDefault(card => card.GetDefensePower() >= value && (!onlyATK || card.IsAttack()) && (!canBeTarget || !card.IsShouldNotBeTarget()));
+ }
+
+ public ClientCard GetOneEnemyBetterThanMyBest(bool onlyATK = false, bool canBeTarget = false)
+ {
+ int bestBotPower = GetBestPower(Bot, onlyATK);
+ return GetOneEnemyBetterThanValue(bestBotPower, onlyATK, canBeTarget);
+ }
+
+ public ClientCard GetProblematicEnemyCard(int attack = 0, bool canBeTarget = false)
+ {
+ ClientCard card = Enemy.MonsterZone.GetFloodgate(canBeTarget);
+ if (card != null)
+ return card;
+
+ card = Enemy.SpellZone.GetFloodgate(canBeTarget);
+ if (card != null)
+ return card;
+
+ card = Enemy.MonsterZone.GetDangerousMonster(canBeTarget);
+ if (card != null)
+ return card;
+
+ card = Enemy.MonsterZone.GetInvincibleMonster(canBeTarget);
+ if (card != null)
+ return card;
+
+ if (attack == 0)
+ attack = GetBestAttack(Bot);
+ return GetOneEnemyBetterThanValue(attack, true, canBeTarget);
+ }
+
+ public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false)
+ {
+ ClientCard card = Enemy.MonsterZone.GetFloodgate(canBeTarget);
+ if (card != null)
+ return card;
+
+ card = Enemy.MonsterZone.GetDangerousMonster(canBeTarget);
+ if (card != null)
+ return card;
+
+ card = Enemy.MonsterZone.GetInvincibleMonster(canBeTarget);
+ if (card != null)
+ return card;
+
+ if (attack == 0)
+ attack = GetBestAttack(Bot);
+ return GetOneEnemyBetterThanValue(attack, true, canBeTarget);
+ }
+
+ public ClientCard GetProblematicEnemySpell()
+ {
+ ClientCard card = Enemy.SpellZone.GetFloodgate();
+ return card;
+ }
+
+ public ClientCard GetBestEnemyCard(bool onlyFaceup = false, bool canBeTarget = false)
+ {
+ ClientCard card = GetBestEnemyMonster(onlyFaceup, canBeTarget);
+ if (card != null)
+ return card;
+
+ card = GetBestEnemySpell(onlyFaceup);
+ if (card != null)
+ return card;
+
+ return null;
+ }
+
+ public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false)
+ {
+ ClientCard card = GetProblematicEnemyMonster(0, canBeTarget);
+ if (card != null)
+ return card;
+
+ card = Enemy.MonsterZone.GetHighestAttackMonster(canBeTarget);
+ if (card != null)
+ return card;
+
+ List monsters = Enemy.GetMonsters();
+
+ // after GetHighestAttackMonster, the left monsters must be face-down.
+ if (monsters.Count > 0 && !onlyFaceup)
+ return monsters[0];
+
+ return null;
+ }
+
+ public ClientCard GetWorstEnemyMonster(bool onlyATK = false)
+ {
+ return Enemy.MonsterZone.GetMonsters()
+ .Where(card => !onlyATK || card.IsAttack())
+ .OrderBy(card => card.GetDefensePower())
+ .FirstOrDefault();
+ }
+
+ public ClientCard GetBestEnemySpell(bool onlyFaceup = false)
+ {
+ ClientCard card = GetProblematicEnemySpell();
+ if (card != null)
+ return card;
+
+ var spells = Enemy.GetSpells();
+
+ card = spells.FirstOrDefault(ecard => ecard.IsFaceup() && (ecard.HasType(CardType.Continuous) || ecard.HasType(CardType.Field)));
+ if (card != null)
+ return card;
+
+ if (spells.Count > 0 && !onlyFaceup)
+ return spells[0];
+
+ return null;
+ }
+
+ public ClientCard GetPZone(int player, int id)
+ {
+ if (Duel.IsNewRule)
+ {
+ return Duel.Fields[player].SpellZone[id * 4];
+ }
+ else
+ {
+ return Duel.Fields[player].SpellZone[6 + id];
+ }
+ }
+
+ public long GetStringId(long id, int option)
+ {
+ return (option & 0xfffff) | (id << 20);
+ }
+
+ public bool IsTurn1OrMain2()
+ {
+ return Duel.Turn == 1 || Duel.Phase == DuelPhase.Main2;
+ }
+
+ public int GetBotAvailZonesFromExtraDeck(IList remove)
+ {
+ ClientCard[] BotMZone = (ClientCard[])Bot.MonsterZone.Clone();
+ ClientCard[] EnemyMZone = (ClientCard[])Enemy.MonsterZone.Clone();
+ for (int i = 0; i < 7; i++)
+ {
+ if (remove.Contains(BotMZone[i])) BotMZone[i] = null;
+ if (remove.Contains(EnemyMZone[i])) EnemyMZone[i] = null;
+ }
+
+ if (!Duel.IsNewRule || Duel.IsNewRule2020)
+ return Zones.MainMonsterZones;
+
+ int result = 0;
+
+ if (BotMZone[5] == null && BotMZone[6] == null)
+ {
+ if (EnemyMZone[5] == null)
+ result |= Zones.z6;
+ if (EnemyMZone[6] == null)
+ result |= Zones.z5;
+ }
+
+ if (BotMZone[0] == null &&
+ ((BotMZone[1]?.HasLinkMarker(CardLinkMarker.Left) ?? false) ||
+ (BotMZone[5]?.HasLinkMarker(CardLinkMarker.BottomLeft) ?? false) ||
+ (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.TopRight) ?? false)))
+ result |= Zones.z0;
+
+ if (BotMZone[1] == null &&
+ ((BotMZone[0]?.HasLinkMarker(CardLinkMarker.Right) ?? false) ||
+ (BotMZone[2]?.HasLinkMarker(CardLinkMarker.Left) ?? false) ||
+ (BotMZone[5]?.HasLinkMarker(CardLinkMarker.Bottom) ?? false) ||
+ (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.Top) ?? false)))
+ result |= Zones.z1;
+
+ if (BotMZone[2] == null &&
+ ((BotMZone[1]?.HasLinkMarker(CardLinkMarker.Right) ?? false) ||
+ (BotMZone[3]?.HasLinkMarker(CardLinkMarker.Left) ?? false) ||
+ (BotMZone[5]?.HasLinkMarker(CardLinkMarker.BottomRight) ?? false) ||
+ (EnemyMZone[6]?.HasLinkMarker(CardLinkMarker.TopLeft) ?? false) ||
+ (BotMZone[6]?.HasLinkMarker(CardLinkMarker.BottomLeft) ?? false) ||
+ (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.TopRight) ?? false)))
+ result |= Zones.z2;
+
+ if (BotMZone[3] == null &&
+ ((BotMZone[2]?.HasLinkMarker(CardLinkMarker.Right) ?? false) ||
+ (BotMZone[4]?.HasLinkMarker(CardLinkMarker.Left) ?? false) ||
+ (BotMZone[6]?.HasLinkMarker(CardLinkMarker.Bottom) ?? false) ||
+ (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.Top) ?? false)))
+ result |= Zones.z3;
+
+ if (BotMZone[4] == null &&
+ ((BotMZone[3]?.HasLinkMarker(CardLinkMarker.Right) ?? false) ||
+ (BotMZone[6]?.HasLinkMarker(CardLinkMarker.BottomRight) ?? false) ||
+ (EnemyMZone[5]?.HasLinkMarker(CardLinkMarker.TopLeft) ?? false)))
+ result |= Zones.z4;
+
+ return result;
+ }
+
+ public int GetBotAvailZonesFromExtraDeck(ClientCard remove)
+ {
+ return GetBotAvailZonesFromExtraDeck(new[] { remove });
+ }
+
+ public int GetBotAvailZonesFromExtraDeck()
+ {
+ return GetBotAvailZonesFromExtraDeck(new List());
+ }
+
+ public bool IsChainTarget(ClientCard card)
+ {
+ return Duel.ChainTargets.Any(card.Equals);
+ }
+
+ public bool IsChainTargetOnly(ClientCard card)
+ {
+ return Duel.ChainTargetOnly.Count == 1 && card.Equals(Duel.ChainTargetOnly[0]);
+ }
+
+ public bool ChainContainsCard(int id)
+ {
+ return Duel.CurrentChain.Any(card => card.IsCode(id));
+ }
+
+ public bool ChainContainsCard(int[] ids)
+ {
+ return Duel.CurrentChain.Any(card => card.IsCode(ids));
+ }
+
+ public int ChainCountPlayer(int player)
+ {
+ return Duel.CurrentChain.Count(card => card.Controller == player);
+ }
+
+ public bool ChainContainPlayer(int player)
+ {
+ return Duel.CurrentChain.Any(card => card.Controller == player);
+ }
+
+ public bool HasChainedTrap(int player)
+ {
+ return Duel.CurrentChain.Any(card => card.Controller == player && card.HasType(CardType.Trap));
+ }
+
+ public ClientCard GetLastChainCard()
+ {
+ return Duel.CurrentChain.LastOrDefault();
+ }
+
+ ///
+ /// Select cards listed in preferred.
+ ///
+ public IList SelectPreferredCards(ClientCard preferred, IList cards, int min, int max)
+ {
+ IList selected = new List();
+ if (cards.IndexOf(preferred) > 0 && selected.Count < max)
+ {
+ selected.Add(preferred);
+ }
+
+ return selected;
+ }
+
+ ///
+ /// Select cards listed in preferred.
+ ///
+ public IList SelectPreferredCards(int preferred, IList cards, int min, int max)
+ {
+ IList selected = new List();
+ foreach (ClientCard card in cards)
+ {
+ if (card.IsCode(preferred) && selected.Count < max)
+ selected.Add(card);
+ }
+
+ return selected;
+ }
+
+ ///
+ /// Select cards listed in preferred.
+ ///
+ public IList SelectPreferredCards(IList preferred, IList cards, int min, int max)
+ {
+ IList selected = new List();
+ IList avail = cards.ToList(); // clone
+ while (preferred.Count > 0 && avail.IndexOf(preferred[0]) > 0 && selected.Count < max)
+ {
+ ClientCard card = preferred[0];
+ preferred.Remove(card);
+ avail.Remove(card);
+ selected.Add(card);
+ }
+
+ return selected;
+ }
+
+ ///
+ /// Select cards listed in preferred.
+ ///
+ public IList SelectPreferredCards(IList preferred, IList cards, int min, int max)
+ {
+ IList selected = new List();
+ foreach (int id in preferred)
+ {
+ foreach (ClientCard card in cards)
+ {
+ if (card.IsCode(id) && selected.Count < max && selected.IndexOf(card) <= 0)
+ selected.Add(card);
+ }
+ if (selected.Count >= max)
+ break;
+ }
+
+ return selected;
+ }
+
+ ///
+ /// Check and fix selected to make sure it meet the count requirement.
+ ///
+ public IList CheckSelectCount(IList _selected, IList cards, int min, int max)
+ {
+ var selected = _selected.ToList();
+ if (selected.Count < min)
+ {
+ foreach (ClientCard card in cards)
+ {
+ if (!selected.Contains(card))
+ selected.Add(card);
+ if (selected.Count >= max)
+ break;
+ }
+ }
+ while (selected.Count > max)
+ {
+ selected.RemoveAt(selected.Count - 1);
+ }
+
+ return selected;
+ }
+ }
}
\ No newline at end of file
diff --git a/Game/AI/CardContainer.cs b/ExecutorBase/Game/AI/CardContainer.cs
similarity index 97%
rename from Game/AI/CardContainer.cs
rename to ExecutorBase/Game/AI/CardContainer.cs
index 1b263e59..0a09a506 100644
--- a/Game/AI/CardContainer.cs
+++ b/ExecutorBase/Game/AI/CardContainer.cs
@@ -1,155 +1,155 @@
-using System.Collections.Generic;
-using YGOSharp.OCGWrapper.Enums;
-using System;
-using System.Linq;
-
-namespace WindBot.Game.AI
-{
- public static class CardContainer
- {
- public static int CompareCardAttack(ClientCard cardA, ClientCard cardB)
- {
- if (cardA.Attack < cardB.Attack)
- return -1;
- if (cardA.Attack == cardB.Attack)
- return 0;
- return 1;
- }
-
- public static int CompareCardLevel(ClientCard cardA, ClientCard cardB)
- {
- if (cardA.Level < cardB.Level)
- return -1;
- if (cardA.Level == cardB.Level)
- return 0;
- return 1;
- }
-
- public static int CompareDefensePower(ClientCard cardA, ClientCard cardB)
- {
- if (cardA == null && cardB == null)
- return 0;
- if (cardA == null)
- return -1;
- if (cardB == null)
- return 1;
- int powerA = cardA.GetDefensePower();
- int powerB = cardB.GetDefensePower();
- if (powerA < powerB)
- return -1;
- if (powerA == powerB)
- return 0;
- return 1;
- }
-
- public static ClientCard GetHighestAttackMonster(this IEnumerable cards, bool canBeTarget = false)
- {
- return cards
- .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget()))
- .OrderBy(card => card.Attack).FirstOrDefault();
- }
-
- public static ClientCard GetHighestDefenseMonster(this IEnumerable cards, bool canBeTarget = false)
- {
- return cards
- .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget()))
- .OrderBy(card => card.Defense).FirstOrDefault();
- }
-
- public static ClientCard GetLowestAttackMonster(this IEnumerable cards, bool canBeTarget = false)
- {
- return cards
- .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget()))
- .OrderByDescending(card => card.Attack).FirstOrDefault();
- }
-
- public static ClientCard GetLowestDefenseMonster(this IEnumerable cards, bool canBeTarget = false)
- {
- return cards
- .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget()))
- .OrderByDescending(card => card.Defense).FirstOrDefault();
- }
-
- public static bool ContainsMonsterWithLevel(this IEnumerable cards, int level)
- {
- return cards.Where(card => card?.Data != null).Any(card => !card.HasType(CardType.Xyz) && card.Level == level);
- }
-
- public static bool ContainsMonsterWithRank(this IEnumerable cards, int rank)
- {
- return cards.Where(card => card?.Data != null).Any(card => card.HasType(CardType.Xyz) && card.Rank == rank);
- }
-
- public static bool ContainsCardWithId(this IEnumerable cards, int id)
- {
- return cards.Where(card => card?.Data != null).Any(card => card.IsCode(id));
- }
-
- public static int GetCardCount(this IEnumerable cards, int id)
- {
- return cards.Where(card => card?.Data != null).Count(card => card.IsCode(id));
- }
-
- public static List GetMonsters(this IEnumerable cards)
- {
- return cards.Where(card => card?.Data != null && card.HasType(CardType.Monster)).ToList();
- }
-
- public static List GetFaceupPendulumMonsters(this IEnumerable cards)
- {
- return cards.Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && card.HasType(CardType.Pendulum)).ToList();
- }
-
- public static ClientCard GetInvincibleMonster(this IEnumerable cards, bool canBeTarget = false)
- {
- return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterInvincible() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget()));
- }
-
- public static ClientCard GetDangerousMonster(this IEnumerable cards, bool canBeTarget = false)
- {
- return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterDangerous() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget()));
- }
-
- public static ClientCard GetFloodgate(this IEnumerable cards, bool canBeTarget = false)
- {
- return cards.FirstOrDefault(card => card?.Data != null && card.IsFloodgate() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget()));
- }
-
- public static ClientCard GetFirstMatchingCard(this IEnumerable cards, Func filter)
- {
- return cards.FirstOrDefault(card => card?.Data != null && filter.Invoke(card));
- }
-
- public static ClientCard GetFirstMatchingFaceupCard(this IEnumerable cards, Func filter)
- {
- return cards.FirstOrDefault(card => card?.Data != null && card.IsFaceup() && filter.Invoke(card));
- }
-
- public static IList GetMatchingCards(this IEnumerable cards, Func filter)
- {
- return cards.Where(card => card?.Data != null && filter.Invoke(card)).ToList();
- }
-
- public static int GetMatchingCardsCount(this IEnumerable cards, Func filter)
- {
- return cards.Count(card => card?.Data != null && filter.Invoke(card));
- }
-
- public static bool IsExistingMatchingCard(this IEnumerable cards, Func filter, int count = 1)
- {
- return cards.GetMatchingCardsCount(filter) >= count;
- }
-
- public static ClientCard GetShouldBeDisabledBeforeItUseEffectMonster(this IEnumerable cards, bool canBeTarget = true)
- {
- return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget()));
- }
-
- public static IEnumerable> GetCombinations(this IEnumerable elements, int k)
- {
- return k == 0 ? new[] { new T[0] } :
- elements.SelectMany((e, i) =>
- elements.Skip(i + 1).GetCombinations(k - 1).Select(c => (new[] { e }).Concat(c)));
- }
- }
+using System.Collections.Generic;
+using YGOSharp.OCGWrapper.Enums;
+using System;
+using System.Linq;
+
+namespace WindBot.Game.AI
+{
+ public static class CardContainer
+ {
+ public static int CompareCardAttack(ClientCard cardA, ClientCard cardB)
+ {
+ if (cardA.Attack < cardB.Attack)
+ return -1;
+ if (cardA.Attack == cardB.Attack)
+ return 0;
+ return 1;
+ }
+
+ public static int CompareCardLevel(ClientCard cardA, ClientCard cardB)
+ {
+ if (cardA.Level < cardB.Level)
+ return -1;
+ if (cardA.Level == cardB.Level)
+ return 0;
+ return 1;
+ }
+
+ public static int CompareDefensePower(ClientCard cardA, ClientCard cardB)
+ {
+ if (cardA == null && cardB == null)
+ return 0;
+ if (cardA == null)
+ return -1;
+ if (cardB == null)
+ return 1;
+ int powerA = cardA.GetDefensePower();
+ int powerB = cardB.GetDefensePower();
+ if (powerA < powerB)
+ return -1;
+ if (powerA == powerB)
+ return 0;
+ return 1;
+ }
+
+ public static ClientCard GetHighestAttackMonster(this IEnumerable cards, bool canBeTarget = false)
+ {
+ return cards
+ .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget()))
+ .OrderBy(card => card.Attack).FirstOrDefault();
+ }
+
+ public static ClientCard GetHighestDefenseMonster(this IEnumerable cards, bool canBeTarget = false)
+ {
+ return cards
+ .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget()))
+ .OrderBy(card => card.Defense).FirstOrDefault();
+ }
+
+ public static ClientCard GetLowestAttackMonster(this IEnumerable cards, bool canBeTarget = false)
+ {
+ return cards
+ .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget()))
+ .OrderByDescending(card => card.Attack).FirstOrDefault();
+ }
+
+ public static ClientCard GetLowestDefenseMonster(this IEnumerable cards, bool canBeTarget = false)
+ {
+ return cards
+ .Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && !(canBeTarget && card.IsShouldNotBeTarget()))
+ .OrderByDescending(card => card.Defense).FirstOrDefault();
+ }
+
+ public static bool ContainsMonsterWithLevel(this IEnumerable cards, int level)
+ {
+ return cards.Where(card => card?.Data != null).Any(card => !card.HasType(CardType.Xyz) && card.Level == level);
+ }
+
+ public static bool ContainsMonsterWithRank(this IEnumerable cards, int rank)
+ {
+ return cards.Where(card => card?.Data != null).Any(card => card.HasType(CardType.Xyz) && card.Rank == rank);
+ }
+
+ public static bool ContainsCardWithId(this IEnumerable cards, int id)
+ {
+ return cards.Where(card => card?.Data != null).Any(card => card.IsCode(id));
+ }
+
+ public static int GetCardCount(this IEnumerable cards, int id)
+ {
+ return cards.Where(card => card?.Data != null).Count(card => card.IsCode(id));
+ }
+
+ public static List GetMonsters(this IEnumerable cards)
+ {
+ return cards.Where(card => card?.Data != null && card.HasType(CardType.Monster)).ToList();
+ }
+
+ public static List GetFaceupPendulumMonsters(this IEnumerable cards)
+ {
+ return cards.Where(card => card?.Data != null && card.HasType(CardType.Monster) && card.IsFaceup() && card.HasType(CardType.Pendulum)).ToList();
+ }
+
+ public static ClientCard GetInvincibleMonster(this IEnumerable cards, bool canBeTarget = false)
+ {
+ return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterInvincible() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget()));
+ }
+
+ public static ClientCard GetDangerousMonster(this IEnumerable cards, bool canBeTarget = false)
+ {
+ return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterDangerous() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget()));
+ }
+
+ public static ClientCard GetFloodgate(this IEnumerable cards, bool canBeTarget = false)
+ {
+ return cards.FirstOrDefault(card => card?.Data != null && card.IsFloodgate() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget()));
+ }
+
+ public static ClientCard GetFirstMatchingCard(this IEnumerable cards, Func filter)
+ {
+ return cards.FirstOrDefault(card => card?.Data != null && filter.Invoke(card));
+ }
+
+ public static ClientCard GetFirstMatchingFaceupCard(this IEnumerable cards, Func filter)
+ {
+ return cards.FirstOrDefault(card => card?.Data != null && card.IsFaceup() && filter.Invoke(card));
+ }
+
+ public static IList GetMatchingCards(this IEnumerable cards, Func filter)
+ {
+ return cards.Where(card => card?.Data != null && filter.Invoke(card)).ToList();
+ }
+
+ public static int GetMatchingCardsCount(this IEnumerable cards, Func filter)
+ {
+ return cards.Count(card => card?.Data != null && filter.Invoke(card));
+ }
+
+ public static bool IsExistingMatchingCard(this IEnumerable cards, Func filter, int count = 1)
+ {
+ return cards.GetMatchingCardsCount(filter) >= count;
+ }
+
+ public static ClientCard GetShouldBeDisabledBeforeItUseEffectMonster(this IEnumerable cards, bool canBeTarget = true)
+ {
+ return cards.FirstOrDefault(card => card?.Data != null && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && (!canBeTarget || !card.IsShouldNotBeTarget()));
+ }
+
+ public static IEnumerable> GetCombinations(this IEnumerable elements, int k)
+ {
+ return k == 0 ? new[] { new T[0] } :
+ elements.SelectMany((e, i) =>
+ elements.Skip(i + 1).GetCombinations(k - 1).Select(c => (new[] { e }).Concat(c)));
+ }
+ }
}
\ No newline at end of file
diff --git a/Game/AI/CardExecutor.cs b/ExecutorBase/Game/AI/CardExecutor.cs
similarity index 96%
rename from Game/AI/CardExecutor.cs
rename to ExecutorBase/Game/AI/CardExecutor.cs
index 75509a77..d5ec43dd 100644
--- a/Game/AI/CardExecutor.cs
+++ b/ExecutorBase/Game/AI/CardExecutor.cs
@@ -1,18 +1,18 @@
-using System;
-
-namespace WindBot.Game.AI
-{
- public class CardExecutor
- {
- public int CardId { get; private set; }
- public ExecutorType Type { get; private set; }
- public Func Func { get; private set; }
-
- public CardExecutor(ExecutorType type, int cardId, Func func)
- {
- CardId = cardId;
- Type = type;
- Func = func;
- }
- }
+using System;
+
+namespace WindBot.Game.AI
+{
+ public class CardExecutor
+ {
+ public int CardId { get; private set; }
+ public ExecutorType Type { get; private set; }
+ public Func Func { get; private set; }
+
+ public CardExecutor(ExecutorType type, int cardId, Func func)
+ {
+ CardId = cardId;
+ Type = type;
+ Func = func;
+ }
+ }
}
\ No newline at end of file
diff --git a/Game/AI/CardExtension.cs b/ExecutorBase/Game/AI/CardExtension.cs
similarity index 97%
rename from Game/AI/CardExtension.cs
rename to ExecutorBase/Game/AI/CardExtension.cs
index 8b30394b..3e45daf1 100644
--- a/Game/AI/CardExtension.cs
+++ b/ExecutorBase/Game/AI/CardExtension.cs
@@ -1,80 +1,80 @@
-using System;
-using WindBot.Game.AI.Enums;
-using YGOSharp.OCGWrapper.Enums;
-
-namespace WindBot.Game.AI
-{
- public static class CardExtension
- {
- ///
- /// Is this monster is invincible to battle?
- ///
- public static bool IsMonsterInvincible(this ClientCard card)
- {
- return !card.IsDisabled() && Enum.IsDefined(typeof(InvincibleMonster), card.Id);
- }
-
- ///
- /// Is this monster is dangerous to attack?
- ///
- public static bool IsMonsterDangerous(this ClientCard card)
- {
- return !card.IsDisabled() && Enum.IsDefined(typeof(DangerousMonster), card.Id);
- }
-
- ///
- /// Do this monster prevents activation of opponent's effect monsters in battle?
- ///
- public static bool IsMonsterHasPreventActivationEffectInBattle(this ClientCard card)
- {
- return !card.IsDisabled() && Enum.IsDefined(typeof(PreventActivationEffectInBattle), card.Id);
- }
-
- ///
- /// Is this card shouldn't be tried to be selected as target?
- ///
- public static bool IsShouldNotBeTarget(this ClientCard card)
- {
- return !card.IsDisabled() && !card.HasType(CardType.Normal) && Enum.IsDefined(typeof(ShouldNotBeTarget), card.Id);
- }
-
- ///
- /// Is this card shouldn't be tried to be selected as target of monster?
- ///
- public static bool IsShouldNotBeMonsterTarget(this ClientCard card)
- {
- return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldNotBeMonsterTarget), card.Id);
- }
-
- ///
- /// Is this card shouldn't be tried to be selected as target of spell & trap?
- ///
- public static bool IsShouldNotBeSpellTrapTarget(this ClientCard card)
- {
- return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldNotBeSpellTrapTarget), card.Id);
- }
-
- ///
- /// Is this monster should be disabled (with Breakthrough Skill) before it use effect and release or banish itself?
- ///
- public static bool IsMonsterShouldBeDisabledBeforeItUseEffect(this ClientCard card)
- {
- return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldBeDisabledBeforeItUseEffectMonster), card.Id);
- }
-
- public static bool IsFloodgate(this ClientCard card)
- {
- return Enum.IsDefined(typeof(Floodgate), card.Id);
- }
-
- public static bool IsOneForXyz(this ClientCard card)
- {
- return Enum.IsDefined(typeof(OneForXyz), card.Id);
- }
-
- public static bool IsFusionSpell(this ClientCard card)
- {
- return Enum.IsDefined(typeof(FusionSpell), card.Id);
- }
- }
+using System;
+using WindBot.Game.AI.Enums;
+using YGOSharp.OCGWrapper.Enums;
+
+namespace WindBot.Game.AI
+{
+ public static class CardExtension
+ {
+ ///
+ /// Is this monster is invincible to battle?
+ ///
+ public static bool IsMonsterInvincible(this ClientCard card)
+ {
+ return !card.IsDisabled() && Enum.IsDefined(typeof(InvincibleMonster), card.Id);
+ }
+
+ ///
+ /// Is this monster is dangerous to attack?
+ ///
+ public static bool IsMonsterDangerous(this ClientCard card)
+ {
+ return !card.IsDisabled() && Enum.IsDefined(typeof(DangerousMonster), card.Id);
+ }
+
+ ///
+ /// Do this monster prevents activation of opponent's effect monsters in battle?
+ ///
+ public static bool IsMonsterHasPreventActivationEffectInBattle(this ClientCard card)
+ {
+ return !card.IsDisabled() && Enum.IsDefined(typeof(PreventActivationEffectInBattle), card.Id);
+ }
+
+ ///
+ /// Is this card shouldn't be tried to be selected as target?
+ ///
+ public static bool IsShouldNotBeTarget(this ClientCard card)
+ {
+ return !card.IsDisabled() && !card.HasType(CardType.Normal) && Enum.IsDefined(typeof(ShouldNotBeTarget), card.Id);
+ }
+
+ ///
+ /// Is this card shouldn't be tried to be selected as target of monster?
+ ///
+ public static bool IsShouldNotBeMonsterTarget(this ClientCard card)
+ {
+ return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldNotBeMonsterTarget), card.Id);
+ }
+
+ ///
+ /// Is this card shouldn't be tried to be selected as target of spell & trap?
+ ///
+ public static bool IsShouldNotBeSpellTrapTarget(this ClientCard card)
+ {
+ return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldNotBeSpellTrapTarget), card.Id);
+ }
+
+ ///
+ /// Is this monster should be disabled (with Breakthrough Skill) before it use effect and release or banish itself?
+ ///
+ public static bool IsMonsterShouldBeDisabledBeforeItUseEffect(this ClientCard card)
+ {
+ return !card.IsDisabled() && Enum.IsDefined(typeof(ShouldBeDisabledBeforeItUseEffectMonster), card.Id);
+ }
+
+ public static bool IsFloodgate(this ClientCard card)
+ {
+ return Enum.IsDefined(typeof(Floodgate), card.Id);
+ }
+
+ public static bool IsOneForXyz(this ClientCard card)
+ {
+ return Enum.IsDefined(typeof(OneForXyz), card.Id);
+ }
+
+ public static bool IsFusionSpell(this ClientCard card)
+ {
+ return Enum.IsDefined(typeof(FusionSpell), card.Id);
+ }
+ }
}
\ No newline at end of file
diff --git a/Game/AI/CardSelector.cs b/ExecutorBase/Game/AI/CardSelector.cs
similarity index 96%
rename from Game/AI/CardSelector.cs
rename to ExecutorBase/Game/AI/CardSelector.cs
index 5a0cf2ba..fda0bb55 100644
--- a/Game/AI/CardSelector.cs
+++ b/ExecutorBase/Game/AI/CardSelector.cs
@@ -1,104 +1,104 @@
-using System.Collections.Generic;
-using YGOSharp.OCGWrapper.Enums;
-
-namespace WindBot.Game.AI
-{
- public class CardSelector
- {
- private enum SelectType
- {
- Card,
- Cards,
- Id,
- Ids,
- Location
- }
-
- private SelectType _type;
- private ClientCard _card;
- private IList _cards;
- private int _id;
- private IList _ids;
- private CardLocation _location;
-
- public CardSelector(ClientCard card)
- {
- _type = SelectType.Card;
- _card = card;
- }
-
- public CardSelector(IList cards)
- {
- _type = SelectType.Cards;
- _cards = cards;
- }
-
- public CardSelector(int cardId)
- {
- _type = SelectType.Id;
- _id = cardId;
- }
-
- public CardSelector(IList ids)
- {
- _type = SelectType.Ids;
- _ids = ids;
- }
-
- public CardSelector(CardLocation location)
- {
- _type = SelectType.Location;
- _location = location;
- }
-
- public IList Select(IList cards, int min, int max)
- {
- IList result = new List();
-
- switch (_type)
- {
- case SelectType.Card:
- if (cards.Contains(_card))
- result.Add(_card);
- break;
- case SelectType.Cards:
- foreach (ClientCard card in _cards)
- if (cards.Contains(card) && !result.Contains(card))
- result.Add(card);
- break;
- case SelectType.Id:
- foreach (ClientCard card in cards)
- if (card.IsCode(_id))
- result.Add(card);
- break;
- case SelectType.Ids:
- foreach (int id in _ids)
- foreach (ClientCard card in cards)
- if (card.IsCode(id) && !result.Contains(card))
- result.Add(card);
- break;
- case SelectType.Location:
- foreach (ClientCard card in cards)
- if (card.Location == _location)
- result.Add(card);
- break;
- }
-
- if (result.Count < min)
- {
- foreach (ClientCard card in cards)
- {
- if (!result.Contains(card))
- result.Add(card);
- if (result.Count >= min)
- break;
- }
- }
-
- while (result.Count > max)
- result.RemoveAt(result.Count - 1);
-
- return result;
- }
- }
+using System.Collections.Generic;
+using YGOSharp.OCGWrapper.Enums;
+
+namespace WindBot.Game.AI
+{
+ public class CardSelector
+ {
+ private enum SelectType
+ {
+ Card,
+ Cards,
+ Id,
+ Ids,
+ Location
+ }
+
+ private SelectType _type;
+ private ClientCard _card;
+ private IList _cards;
+ private int _id;
+ private IList _ids;
+ private CardLocation _location;
+
+ public CardSelector(ClientCard card)
+ {
+ _type = SelectType.Card;
+ _card = card;
+ }
+
+ public CardSelector(IList cards)
+ {
+ _type = SelectType.Cards;
+ _cards = cards;
+ }
+
+ public CardSelector(int cardId)
+ {
+ _type = SelectType.Id;
+ _id = cardId;
+ }
+
+ public CardSelector(IList ids)
+ {
+ _type = SelectType.Ids;
+ _ids = ids;
+ }
+
+ public CardSelector(CardLocation location)
+ {
+ _type = SelectType.Location;
+ _location = location;
+ }
+
+ public IList Select(IList cards, int min, int max)
+ {
+ IList result = new List();
+
+ switch (_type)
+ {
+ case SelectType.Card:
+ if (cards.Contains(_card))
+ result.Add(_card);
+ break;
+ case SelectType.Cards:
+ foreach (ClientCard card in _cards)
+ if (cards.Contains(card) && !result.Contains(card))
+ result.Add(card);
+ break;
+ case SelectType.Id:
+ foreach (ClientCard card in cards)
+ if (card.IsCode(_id))
+ result.Add(card);
+ break;
+ case SelectType.Ids:
+ foreach (int id in _ids)
+ foreach (ClientCard card in cards)
+ if (card.IsCode(id) && !result.Contains(card))
+ result.Add(card);
+ break;
+ case SelectType.Location:
+ foreach (ClientCard card in cards)
+ if (card.Location == _location)
+ result.Add(card);
+ break;
+ }
+
+ if (result.Count < min)
+ {
+ foreach (ClientCard card in cards)
+ {
+ if (!result.Contains(card))
+ result.Add(card);
+ if (result.Count >= min)
+ break;
+ }
+ }
+
+ while (result.Count > max)
+ result.RemoveAt(result.Count - 1);
+
+ return result;
+ }
+ }
}
\ No newline at end of file
diff --git a/Game/AI/DeckAttribute.cs b/ExecutorBase/Game/AI/DeckAttribute.cs
similarity index 96%
rename from Game/AI/DeckAttribute.cs
rename to ExecutorBase/Game/AI/DeckAttribute.cs
index 49bae161..89a87a9c 100644
--- a/Game/AI/DeckAttribute.cs
+++ b/ExecutorBase/Game/AI/DeckAttribute.cs
@@ -1,22 +1,22 @@
-using System;
-
-namespace WindBot.Game.AI
-{
- [AttributeUsage(AttributeTargets.Class)]
- public class DeckAttribute : Attribute
- {
- public string Name { get; private set; }
- public string File { get; private set; }
- public string Level { get; private set; }
-
- public DeckAttribute(string name, string file = null, string level = "Normal")
- {
- if (String.IsNullOrEmpty(file))
- file = name;
-
- Name = name;
- File = file;
- Level = level;
- }
- }
-}
+using System;
+
+namespace WindBot.Game.AI
+{
+ [AttributeUsage(AttributeTargets.Class)]
+ public class DeckAttribute : Attribute
+ {
+ public string Name { get; private set; }
+ public string File { get; private set; }
+ public string Level { get; private set; }
+
+ public DeckAttribute(string name, string file = null, string level = "Normal")
+ {
+ if (String.IsNullOrEmpty(file))
+ file = name;
+
+ Name = name;
+ File = file;
+ Level = level;
+ }
+ }
+}
diff --git a/Game/AI/DefaultExecutor.cs b/ExecutorBase/Game/AI/DefaultExecutor.cs
similarity index 97%
rename from Game/AI/DefaultExecutor.cs
rename to ExecutorBase/Game/AI/DefaultExecutor.cs
index 6c5be767..fd63c8a6 100644
--- a/Game/AI/DefaultExecutor.cs
+++ b/ExecutorBase/Game/AI/DefaultExecutor.cs
@@ -1,1094 +1,1098 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using YGOSharp.OCGWrapper.Enums;
-using WindBot;
-using WindBot.Game;
-using WindBot.Game.AI;
-
-namespace WindBot.Game.AI
-{
- public abstract class DefaultExecutor : Executor
- {
- protected class _CardId
- {
- public const int JizukirutheStarDestroyingKaiju = 63941210;
- public const int ThunderKingtheLightningstrikeKaiju = 48770333;
- public const int DogorantheMadFlameKaiju = 93332803;
- public const int RadiantheMultidimensionalKaiju = 28674152;
- public const int GadarlatheMysteryDustKaiju = 36956512;
- public const int KumongoustheStickyStringKaiju = 29726552;
- public const int GamecieltheSeaTurtleKaiju = 55063751;
- public const int SuperAntiKaijuWarMachineMechaDogoran = 84769941;
-
- public const int UltimateConductorTytanno = 18940556;
- public const int ElShaddollConstruct = 20366274;
- public const int AllyOfJusticeCatastor = 26593852;
-
- public const int DupeFrog = 46239604;
- public const int MaraudingCaptain = 2460565;
-
- public const int BlackRoseDragon = 73580471;
- public const int JudgmentDragon = 57774843;
- public const int TopologicTrisbaena = 72529749;
- public const int EvilswarmExcitonKnight = 46772449;
- public const int HarpiesFeatherDuster = 18144506;
- public const int DarkMagicAttack = 2314238;
- public const int MysticalSpaceTyphoon = 5318639;
- public const int CosmicCyclone = 8267140;
- public const int ChickenGame = 67616300;
-
- public const int CastelTheSkyblasterMusketeer = 82633039;
- public const int CrystalWingSynchroDragon = 50954680;
- public const int NumberS39UtopiaTheLightning = 56832966;
- public const int Number39Utopia = 84013237;
- public const int UltimayaTzolkin = 1686814;
- public const int MekkKnightCrusadiaAstram = 21887175;
-
- public const int MoonMirrorShield = 19508728;
- public const int PhantomKnightsFogBlade = 25542642;
-
- public const int VampireFraeulein = 6039967;
- public const int InjectionFairyLily = 79575620;
-
- public const int BlueEyesChaosMAXDragon = 55410871;
-
- public const int AshBlossom = 14558127;
- public const int MaxxC = 23434538;
- public const int LockBird = 94145021;
- public const int GhostOgreAndSnowRabbit = 59438930;
- public const int GhostBelle = 73642296;
- public const int EffectVeiler = 63845230;
- public const int ArtifactLancea = 34267821;
-
- public const int CalledByTheGrave = 24224830;
- public const int InfiniteImpermanence = 10045474;
- public const int GalaxySoldier = 46659709;
- public const int MacroCosmos = 30241314;
- public const int UpstartGoblin = 70368879;
- public const int CyberEmergency = 60600126;
-
- public const int EaterOfMillions = 63845230;
-
- public const int InvokedPurgatrio = 12307878;
- public const int ChaosAncientGearGiant = 51788412;
- public const int UltimateAncientGearGolem = 12652643;
-
- public const int RedDragonArchfiend = 70902743;
-
- public const int ImperialOrder = 61740673;
- public const int NaturiaBeast = 33198837;
- public const int AntiSpellFragrance = 58921041;
- }
-
- int HonestEffectCount = 0;
-
- protected DefaultExecutor(GameAI ai, Duel duel)
- : base(ai, duel)
- {
- AddExecutor(ExecutorType.Activate, _CardId.ChickenGame, DefaultChickenGame);
- }
-
- ///
- /// Decide which card should the attacker attack.
- ///
- /// Card that attack.
- /// Cards that defend.
- /// BattlePhaseAction including the target, or null (in this situation, GameAI will check the next attacker)
- public override BattlePhaseAction OnSelectAttackTarget(ClientCard attacker, IList defenders)
- {
- foreach (ClientCard defender in defenders)
- {
- attacker.RealPower = attacker.Attack;
- defender.RealPower = defender.GetDefensePower();
- if (!OnPreBattleBetween(attacker, defender))
- continue;
-
- if (attacker.RealPower > defender.RealPower || (attacker.RealPower >= defender.RealPower && attacker.IsLastAttacker && defender.IsAttack()))
- return AI.Attack(attacker, defender);
- }
-
- if (attacker.CanDirectAttack)
- return AI.Attack(attacker, null);
-
- return null;
- }
-
- ///
- /// Decide whether to declare attack between attacker and defender.
- /// Can be overrided to update the RealPower of attacker for cards like Honest.
- ///
- /// Card that attack.
- /// Card that defend.
- /// false if the attack shouldn't be done.
- public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender)
- {
- if (attacker.RealPower <= 0)
- return false;
-
- if (!attacker.IsMonsterHasPreventActivationEffectInBattle())
- {
- if (defender.IsMonsterInvincible() && defender.IsDefense())
- return false;
-
- if (defender.IsMonsterDangerous())
- {
- bool canIgnoreIt = !attacker.IsDisabled() && (
- attacker.IsCode(_CardId.UltimateConductorTytanno) && defender.IsDefense() ||
- attacker.IsCode(_CardId.ElShaddollConstruct) && defender.IsSpecialSummoned ||
- attacker.IsCode(_CardId.AllyOfJusticeCatastor) && !defender.HasAttribute(CardAttribute.Dark));
- if (!canIgnoreIt)
- return false;
- }
-
- foreach (ClientCard equip in defender.EquipCards)
- {
- if (equip.IsCode(_CardId.MoonMirrorShield) && !equip.IsDisabled())
- {
- return false;
- }
- }
-
- if (!defender.IsDisabled())
- {
- if (defender.IsCode(_CardId.MekkKnightCrusadiaAstram) && defender.IsAttack() && attacker.IsSpecialSummoned)
- return false;
-
- if (defender.IsCode(_CardId.CrystalWingSynchroDragon) && defender.IsAttack() && attacker.Level >= 5)
- return false;
-
- if (defender.IsCode(_CardId.AllyOfJusticeCatastor) && !attacker.HasAttribute(CardAttribute.Dark))
- return false;
-
- if (defender.IsCode(_CardId.NumberS39UtopiaTheLightning) && defender.IsAttack() && defender.HasXyzMaterial(2, _CardId.Number39Utopia))
- defender.RealPower = 5000;
-
- if (defender.IsCode(_CardId.VampireFraeulein))
- defender.RealPower += (Enemy.LifePoints > 3000) ? 3000 : (Enemy.LifePoints - 100);
-
- if (defender.IsCode(_CardId.InjectionFairyLily) && Enemy.LifePoints > 2000)
- defender.RealPower += 3000;
- }
- }
-
- if (!defender.IsMonsterHasPreventActivationEffectInBattle())
- {
- if (attacker.IsCode(_CardId.NumberS39UtopiaTheLightning) && !attacker.IsDisabled() && attacker.HasXyzMaterial(2, _CardId.Number39Utopia))
- attacker.RealPower = 5000;
-
- foreach (ClientCard equip in attacker.EquipCards)
- {
- if (equip.IsCode(_CardId.MoonMirrorShield) && !equip.IsDisabled())
- {
- attacker.RealPower = defender.RealPower + 100;
- }
- }
- }
-
- if (Enemy.HasInMonstersZone(_CardId.MekkKnightCrusadiaAstram, true) && !(defender).IsCode(_CardId.MekkKnightCrusadiaAstram))
- return false;
-
- if (Enemy.HasInMonstersZone(_CardId.DupeFrog, true) && !(defender).IsCode(_CardId.DupeFrog))
- return false;
-
- if (Enemy.HasInMonstersZone(_CardId.MaraudingCaptain, true) && !defender.IsCode(_CardId.MaraudingCaptain) && defender.Race == (int)CardRace.Warrior)
- return false;
-
- if (defender.IsCode(_CardId.UltimayaTzolkin) && !defender.IsDisabled() && Enemy.GetMonsters().Any(monster => !monster.Equals(defender) && monster.HasType(CardType.Synchro)))
- return false;
-
- if (defender.OwnTargets.Any(card => card.IsCode(_CardId.PhantomKnightsFogBlade) && !card.IsDisabled()))
- return false;
-
- return true;
- }
-
- ///
- /// Called when the AI has to select a card position.
- ///
- /// Id of the card to position on the field.
- /// List of available positions.
- /// Selected position, or 0 if no position is set for this card.
- public override CardPosition OnSelectPosition(int cardId, IList positions)
- {
- YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId);
- if (cardData != null)
- {
- if (cardData.Attack == 0)
- return CardPosition.FaceUpDefence;
- }
- return 0;
- }
-
- public override bool OnSelectBattleReplay()
- {
- if (Bot.BattlingMonster == null)
- return false;
- List defenders = new List(Duel.Fields[1].GetMonsters());
- defenders.Sort(CardContainer.CompareDefensePower);
- defenders.Reverse();
- BattlePhaseAction result = OnSelectAttackTarget(Bot.BattlingMonster, defenders);
- if (result != null && result.Action == BattlePhaseAction.BattleAction.Attack)
- {
- return true;
- }
- return false;
- }
-
- public override void OnNewTurn()
- {
- HonestEffectCount = 0;
- }
-
- ///
- /// Destroy face-down cards first, in our turn.
- ///
- protected bool DefaultMysticalSpaceTyphoon()
- {
- if (Duel.CurrentChain.Any(card => card.IsCode(_CardId.MysticalSpaceTyphoon)))
- {
- return false;
- }
-
- List spells = Enemy.GetSpells();
- if (spells.Count == 0)
- return false;
-
- ClientCard selected = Enemy.SpellZone.GetFloodgate();
-
- if (selected == null)
- {
- if (Duel.Player == 0)
- selected = spells.FirstOrDefault(card => card.IsFacedown());
- if (Duel.Player == 1)
- selected = spells.FirstOrDefault(card => card.HasType(CardType.Continuous) || card.HasType(CardType.Equip) || card.HasType(CardType.Field));
- }
-
- if (selected == null)
- return false;
- AI.SelectCard(selected);
- return true;
- }
-
- ///
- /// Destroy face-down cards first, in our turn.
- ///
- protected bool DefaultCosmicCyclone()
- {
- foreach (ClientCard card in Duel.CurrentChain)
- if (card.IsCode(_CardId.CosmicCyclone))
- return false;
- return (Bot.LifePoints > 1000) && DefaultMysticalSpaceTyphoon();
- }
-
- ///
- /// Activate if avail.
- ///
- protected bool DefaultGalaxyCyclone()
- {
- List spells = Enemy.GetSpells();
- if (spells.Count == 0)
- return false;
-
- ClientCard selected = null;
-
- if (Card.Location == CardLocation.Grave)
- {
- selected = Util.GetBestEnemySpell(true);
- }
- else
- {
- selected = spells.FirstOrDefault(card => card.IsFacedown());
- }
-
- if (selected == null)
- return false;
-
- AI.SelectCard(selected);
- return true;
- }
-
- ///
- /// Set the highest ATK level 4+ effect enemy monster.
- ///
- protected bool DefaultBookOfMoon()
- {
- if (Util.IsAllEnemyBetter(true))
- {
- ClientCard monster = Enemy.GetMonsters().GetHighestAttackMonster(true);
- if (monster != null && monster.HasType(CardType.Effect) && !monster.HasType(CardType.Link) && (monster.HasType(CardType.Xyz) || monster.Level > 4))
- {
- AI.SelectCard(monster);
- return true;
- }
- }
- return false;
- }
-
- ///
- /// Return problematic monster, and if this card become target, return any enemy monster.
- ///
- protected bool DefaultCompulsoryEvacuationDevice()
- {
- ClientCard target = Util.GetProblematicEnemyMonster(0, true);
- if (target != null)
- {
- AI.SelectCard(target);
- return true;
- }
- if (Util.IsChainTarget(Card))
- {
- ClientCard monster = Util.GetBestEnemyMonster(false, true);
- if (monster != null)
- {
- AI.SelectCard(monster);
- return true;
- }
- }
- return false;
- }
-
- ///
- /// Revive the best monster when we don't have better one in field.
- ///
- protected bool DefaultCallOfTheHaunted()
- {
- if (!Util.IsAllEnemyBetter(true))
- return false;
- ClientCard selected = Bot.Graveyard.GetMatchingCards(card => card.IsCanRevive()).OrderByDescending(card => card.Attack).FirstOrDefault();
- AI.SelectCard(selected);
- return true;
- }
-
- ///
- /// Default Scapegoat effect
- ///
- protected bool DefaultScapegoat()
- {
- if (DefaultSpellWillBeNegated()) return false;
- if (Duel.Player == 0) return false;
- if (Duel.Phase == DuelPhase.End) return true;
- if (DefaultOnBecomeTarget()) return true;
- if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2)
- {
- if (Enemy.HasInMonstersZone(new[]
- {
- _CardId.UltimateConductorTytanno,
- _CardId.InvokedPurgatrio,
- _CardId.ChaosAncientGearGiant,
- _CardId.UltimateAncientGearGolem,
- _CardId.RedDragonArchfiend
- }, true)) return false;
- if (Util.GetTotalAttackingMonsterAttack(1) >= Bot.LifePoints) return true;
- }
- return false;
- }
- ///
- /// Always active in opponent's turn.
- ///
- protected bool DefaultMaxxC()
- {
- return Duel.Player == 1;
- }
- ///
- /// Always disable opponent's effect except some cards like UpstartGoblin
- ///
- protected bool DefaultAshBlossomAndJoyousSpring()
- {
- int[] ignoreList = {
- _CardId.MacroCosmos,
- _CardId.UpstartGoblin,
- _CardId.CyberEmergency
- };
- if (Util.GetLastChainCard().IsCode(ignoreList))
- return false;
- if (Util.GetLastChainCard().HasSetcode(0x11e) && Util.GetLastChainCard().Location == CardLocation.Hand) // Danger! archtype hand effect
- return false;
- return Duel.LastChainPlayer == 1;
- }
- ///
- /// Always activate unless the activating card is disabled
- ///
- protected bool DefaultGhostOgreAndSnowRabbit()
- {
- if (Util.GetLastChainCard() != null && Util.GetLastChainCard().IsDisabled())
- return false;
- return DefaultTrap();
- }
- ///
- /// Always disable opponent's effect
- ///
- protected bool DefaultGhostBelleAndHauntedMansion()
- {
- return DefaultTrap();
- }
- ///
- /// Same as DefaultBreakthroughSkill
- ///
- protected bool DefaultEffectVeiler()
- {
- if (Util.GetLastChainCard() != null && Util.GetLastChainCard().IsCode(_CardId.GalaxySoldier) && Enemy.Hand.Count >= 3) return false;
- if (Util.ChainContainsCard(_CardId.EffectVeiler))
- return false;
- return DefaultBreakthroughSkill();
- }
- ///
- /// Chain common hand traps
- ///
- protected bool DefaultCalledByTheGrave()
- {
- int[] targetList =
- {
- _CardId.MaxxC,
- _CardId.LockBird,
- _CardId.GhostOgreAndSnowRabbit,
- _CardId.AshBlossom,
- _CardId.GhostBelle,
- _CardId.EffectVeiler,
- _CardId.ArtifactLancea
- };
- if (Duel.LastChainPlayer == 1)
- {
- foreach (int id in targetList)
- {
- if (Util.GetLastChainCard().IsCode(id))
- {
- AI.SelectCard(id);
- return UniqueFaceupSpell();
- }
- }
- }
- return false;
- }
- ///
- /// Default InfiniteImpermanence effect
- ///
- protected bool DefaultInfiniteImpermanence()
- {
- // TODO: disable s & t
- if (!DefaultUniqueTrap())
- return false;
- return DefaultDisableMonster();
- }
- ///
- /// Chain the enemy monster, or disable monster like Rescue Rabbit.
- ///
- protected bool DefaultBreakthroughSkill()
- {
- if (!DefaultUniqueTrap())
- return false;
- return DefaultDisableMonster();
- }
- ///
- /// Chain the enemy monster, or disable monster like Rescue Rabbit.
- ///
- protected bool DefaultDisableMonster()
- {
- if (Duel.Player == 1)
- {
- ClientCard target = Enemy.MonsterZone.GetShouldBeDisabledBeforeItUseEffectMonster();
- if (target != null)
- {
- AI.SelectCard(target);
- return true;
- }
- }
-
- ClientCard LastChainCard = Util.GetLastChainCard();
-
- if (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.MonsterZone &&
- !LastChainCard.IsDisabled() && !LastChainCard.IsShouldNotBeTarget() && !LastChainCard.IsShouldNotBeSpellTrapTarget())
- {
- AI.SelectCard(LastChainCard);
- return true;
- }
-
- if (Bot.BattlingMonster != null && Enemy.BattlingMonster != null)
- {
- if (!Enemy.BattlingMonster.IsDisabled() && Enemy.BattlingMonster.IsCode(_CardId.EaterOfMillions))
- {
- AI.SelectCard(Enemy.BattlingMonster);
- return true;
- }
- }
-
- if (Duel.Phase == DuelPhase.BattleStart && Duel.Player == 1 &&
- Enemy.HasInMonstersZone(_CardId.NumberS39UtopiaTheLightning, true))
- {
- AI.SelectCard(_CardId.NumberS39UtopiaTheLightning);
- return true;
- }
-
- return false;
- }
-
- ///
- /// Activate only except this card is the target or we summon monsters.
- ///
- protected bool DefaultSolemnJudgment()
- {
- return !Util.IsChainTargetOnly(Card) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap();
- }
-
- ///
- /// Activate only except we summon monsters.
- ///
- protected bool DefaultSolemnWarning()
- {
- return (Bot.LifePoints > 2000) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap();
- }
-
- ///
- /// Activate only except we summon monsters.
- ///
- protected bool DefaultSolemnStrike()
- {
- return (Bot.LifePoints > 1500) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap();
- }
-
- ///
- /// Activate when all enemy monsters have better ATK.
- ///
- protected bool DefaultTorrentialTribute()
- {
- return !Util.HasChainedTrap(0) && Util.IsAllEnemyBetter(true);
- }
-
- ///
- /// Activate enemy have more S&T.
- ///
- protected bool DefaultHeavyStorm()
- {
- return Bot.GetSpellCount() < Enemy.GetSpellCount();
- }
-
- ///
- /// Activate before other winds, if enemy have more than 2 S&T.
- ///
- protected bool DefaultHarpiesFeatherDusterFirst()
- {
- return Enemy.GetSpellCount() >= 2;
- }
-
- ///
- /// Activate when one enemy monsters have better ATK.
- ///
- protected bool DefaultHammerShot()
- {
- return Util.IsOneEnemyBetter(true);
- }
-
- ///
- /// Activate when one enemy monsters have better ATK or DEF.
- ///
- protected bool DefaultDarkHole()
- {
- return Util.IsOneEnemyBetter();
- }
-
- ///
- /// Activate when one enemy monsters have better ATK or DEF.
- ///
- protected bool DefaultRaigeki()
- {
- return Util.IsOneEnemyBetter();
- }
-
- ///
- /// Activate when one enemy monsters have better ATK or DEF.
- ///
- protected bool DefaultSmashingGround()
- {
- return Util.IsOneEnemyBetter();
- }
-
- ///
- /// Activate when we have more than 15 cards in deck.
- ///
- protected bool DefaultPotOfDesires()
- {
- return Bot.Deck.Count > 15;
- }
-
- ///
- /// Set traps only and avoid block the activation of other cards.
- ///
- protected bool DefaultSpellSet()
- {
- return (Card.IsTrap() || Card.HasType(CardType.QuickPlay)) && Bot.GetSpellCountWithoutField() < 4;
- }
-
- ///
- /// Summon with tributes ATK lower.
- ///
- protected bool DefaultTributeSummon()
- {
- if (!UniqueFaceupMonster())
- return false;
- int tributecount = (int)Math.Ceiling((Card.Level - 4.0d) / 2.0d);
- for (int j = 0; j < 7; ++j)
- {
- ClientCard tributeCard = Bot.MonsterZone[j];
- if (tributeCard == null) continue;
- if (tributeCard.GetDefensePower() < Card.Attack)
- tributecount--;
- }
- return tributecount <= 0;
- }
-
- ///
- /// Activate when we have no field.
- ///
- protected bool DefaultField()
- {
- return Bot.SpellZone[5] == null;
- }
-
- ///
- /// Turn if all enemy is better.
- ///
- protected bool DefaultMonsterRepos()
- {
- if (Card.IsFaceup() && Card.IsDefense() && Card.Attack == 0)
- return false;
-
- if (Enemy.HasInMonstersZone(_CardId.BlueEyesChaosMAXDragon, true) &&
- Card.IsAttack() && (4000 - Card.Defense) * 2 > (4000 - Card.Attack))
- return false;
- if (Enemy.HasInMonstersZone(_CardId.BlueEyesChaosMAXDragon, true) &&
- Card.IsDefense() && Card.IsFaceup() &&
- (4000 - Card.Defense) * 2 > (4000 - Card.Attack))
- return true;
-
- bool enemyBetter = Util.IsAllEnemyBetter(true);
- if (Card.IsAttack() && enemyBetter)
- return true;
- if (Card.IsDefense() && !enemyBetter && Card.Attack >= Card.Defense)
- return true;
-
- return false;
- }
-
- ///
- /// If spell will be negated
- ///
- protected bool DefaultSpellWillBeNegated()
- {
- return Bot.HasInSpellZone(_CardId.ImperialOrder, true, true) || Enemy.HasInSpellZone(_CardId.ImperialOrder, true) || Enemy.HasInMonstersZone(_CardId.NaturiaBeast, true);
- }
-
- ///
- /// If spell must set first to activate
- ///
- protected bool DefaultSpellMustSetFirst()
- {
- ClientCard card = null;
- foreach (ClientCard check in Bot.GetSpells())
- {
- if (check.IsCode(_CardId.AntiSpellFragrance) && !check.IsDisabled())
- card = check;
- }
- if (card != null && card.IsFaceup())
- return true;
- return Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true, true) || Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true);
- }
-
- ///
- /// if spell/trap is the target or enermy activate HarpiesFeatherDuster
- ///
- protected bool DefaultOnBecomeTarget()
- {
- if (Util.IsChainTarget(Card)) return true;
- int[] destroyAllList =
- {
- _CardId.EvilswarmExcitonKnight,
- _CardId.BlackRoseDragon,
- _CardId.JudgmentDragon,
- _CardId.TopologicTrisbaena
- };
- int[] destroyAllOpponentList =
- {
- _CardId.HarpiesFeatherDuster,
- _CardId.DarkMagicAttack
- };
-
- if (Util.ChainContainsCard(destroyAllList)) return true;
- if (Enemy.HasInSpellZone(destroyAllOpponentList, true)) return true;
- // TODO: ChainContainsCard(id, player)
- return false;
- }
- ///
- /// Chain enemy activation or summon.
- ///
- protected bool DefaultTrap()
- {
- return (Duel.LastChainPlayer == -1 && Duel.LastSummonPlayer != 0) || Duel.LastChainPlayer == 1;
- }
-
- ///
- /// Activate when avail and no other our trap card in this chain or face-up.
- ///
- protected bool DefaultUniqueTrap()
- {
- if (Util.HasChainedTrap(0))
- return false;
-
- return UniqueFaceupSpell();
- }
-
- ///
- /// Check no other our spell or trap card with same name face-up.
- ///
- protected bool UniqueFaceupSpell()
- {
- return !Bot.GetSpells().Any(card => card.IsCode(Card.Id) && card.IsFaceup());
- }
-
- ///
- /// Check no other our monster card with same name face-up.
- ///
- protected bool UniqueFaceupMonster()
- {
- return !Bot.GetMonsters().Any(card => card.IsCode(Card.Id) && card.IsFaceup());
- }
-
- ///
- /// Dumb way to avoid the bot chain in mess.
- ///
- protected bool DefaultDontChainMyself()
- {
- if (Executors.Any(exec => exec.Type == Type && exec.CardId == Card.Id))
- return false;
- return Duel.LastChainPlayer != 0;
- }
-
- ///
- /// Draw when we have lower LP, or destroy it. Can be overrided.
- ///
- protected bool DefaultChickenGame()
- {
- if (Executors.Count(exec => exec.Type == Type && exec.CardId == Card.Id) > 1)
- return false;
- if (Bot.LifePoints <= 1000)
- return false;
- if (Bot.LifePoints <= Enemy.LifePoints && ActivateDescription == Util.GetStringId(_CardId.ChickenGame, 0))
- return true;
- if (Bot.LifePoints > Enemy.LifePoints && ActivateDescription == Util.GetStringId(_CardId.ChickenGame, 1))
- return true;
- return false;
- }
-
- ///
- /// Draw when we have Dark monster in hand,and banish random one. Can be overrided.
- ///
- protected bool DefaultAllureofDarkness()
- {
- ClientCard target = Bot.Hand.FirstOrDefault(card => card.HasAttribute(CardAttribute.Dark));
- return target != null;
- }
-
- ///
- /// Clever enough.
- ///
- protected bool DefaultDimensionalBarrier()
- {
- const int RITUAL = 0;
- const int FUSION = 1;
- const int SYNCHRO = 2;
- const int XYZ = 3;
- const int PENDULUM = 4;
- if (Duel.Player != 0)
- {
- List monsters = Enemy.GetMonsters();
- int[] levels = new int[13];
- bool tuner = false;
- bool nontuner = false;
- foreach (ClientCard monster in monsters)
- {
- if (monster.HasType(CardType.Tuner))
- tuner = true;
- else if (!monster.HasType(CardType.Xyz) && !monster.HasType(CardType.Link))
- {
- nontuner = true;
- levels[monster.Level] = levels[monster.Level] + 1;
- }
-
- if (monster.IsOneForXyz())
- {
- AI.SelectOption(XYZ);
- return true;
- }
- }
- if (tuner && nontuner)
- {
- AI.SelectOption(SYNCHRO);
- return true;
- }
- for (int i=1; i<=12; i++)
- {
- if (levels[i]>1)
- {
- AI.SelectOption(XYZ);
- return true;
- }
- }
- ClientCard l = Enemy.SpellZone[6];
- ClientCard r = Enemy.SpellZone[7];
- if (l != null && r != null && l.LScale != r.RScale)
- {
- AI.SelectOption(PENDULUM);
- return true;
- }
- }
- ClientCard lastchaincard = Util.GetLastChainCard();
- if (Duel.LastChainPlayer == 1 && lastchaincard != null && !lastchaincard.IsDisabled())
- {
- if (lastchaincard.HasType(CardType.Ritual))
- {
- AI.SelectOption(RITUAL);
- return true;
- }
- if (lastchaincard.HasType(CardType.Fusion))
- {
- AI.SelectOption(FUSION);
- return true;
- }
- if (lastchaincard.HasType(CardType.Synchro))
- {
- AI.SelectOption(SYNCHRO);
- return true;
- }
- if (lastchaincard.HasType(CardType.Xyz))
- {
- AI.SelectOption(XYZ);
- return true;
- }
- if (lastchaincard.IsFusionSpell())
- {
- AI.SelectOption(FUSION);
- return true;
- }
- }
- if (Util.IsChainTarget(Card))
- {
- AI.SelectOption(XYZ);
- return true;
- }
- return false;
- }
-
- ///
- /// Clever enough
- ///
- protected bool DefaultInterruptedKaijuSlumber()
- {
- if (Card.Location == CardLocation.Grave)
- {
- AI.SelectCard(
- _CardId.GamecieltheSeaTurtleKaiju,
- _CardId.KumongoustheStickyStringKaiju,
- _CardId.GadarlatheMysteryDustKaiju,
- _CardId.RadiantheMultidimensionalKaiju,
- _CardId.DogorantheMadFlameKaiju,
- _CardId.ThunderKingtheLightningstrikeKaiju,
- _CardId.JizukirutheStarDestroyingKaiju
- );
- return true;
- }
-
- if (DefaultDarkHole())
- {
- AI.SelectCard(
- _CardId.JizukirutheStarDestroyingKaiju,
- _CardId.ThunderKingtheLightningstrikeKaiju,
- _CardId.DogorantheMadFlameKaiju,
- _CardId.RadiantheMultidimensionalKaiju,
- _CardId.GadarlatheMysteryDustKaiju,
- _CardId.KumongoustheStickyStringKaiju,
- _CardId.GamecieltheSeaTurtleKaiju
- );
- AI.SelectNextCard(
- _CardId.SuperAntiKaijuWarMachineMechaDogoran,
- _CardId.GamecieltheSeaTurtleKaiju,
- _CardId.KumongoustheStickyStringKaiju,
- _CardId.GadarlatheMysteryDustKaiju,
- _CardId.RadiantheMultidimensionalKaiju,
- _CardId.DogorantheMadFlameKaiju,
- _CardId.ThunderKingtheLightningstrikeKaiju
- );
- return true;
- }
-
- return false;
- }
-
- ///
- /// Clever enough.
- ///
- protected bool DefaultKaijuSpsummon()
- {
- IList kaijus = new[] {
- _CardId.JizukirutheStarDestroyingKaiju,
- _CardId.GadarlatheMysteryDustKaiju,
- _CardId.GamecieltheSeaTurtleKaiju,
- _CardId.RadiantheMultidimensionalKaiju,
- _CardId.KumongoustheStickyStringKaiju,
- _CardId.ThunderKingtheLightningstrikeKaiju,
- _CardId.DogorantheMadFlameKaiju,
- _CardId.SuperAntiKaijuWarMachineMechaDogoran
- };
- foreach (ClientCard monster in Enemy.GetMonsters())
- {
- if (monster.IsCode(kaijus))
- return Card.GetDefensePower() > monster.GetDefensePower();
- }
- ClientCard card = Enemy.MonsterZone.GetFloodgate();
- if (card != null)
- {
- AI.SelectCard(card);
- return true;
- }
- card = Enemy.MonsterZone.GetDangerousMonster();
- if (card != null)
- {
- AI.SelectCard(card);
- return true;
- }
- card = Util.GetOneEnemyBetterThanValue(Card.GetDefensePower());
- if (card != null)
- {
- AI.SelectCard(card);
- return true;
- }
- return false;
- }
-
- ///
- /// Summon when we don't have monster attack higher than enemy's.
- ///
- protected bool DefaultNumberS39UtopiaTheLightningSummon()
- {
- int bestBotAttack = Util.GetBestAttack(Bot);
- return Util.IsOneEnemyBetterThanValue(bestBotAttack, false);
- }
-
- ///
- /// Activate if the card is attack pos, and its attack is below 5000, when the enemy monster is attack pos or not useless faceup defense pos
- ///
- protected bool DefaultNumberS39UtopiaTheLightningEffect()
- {
- return Card.IsAttack() && Card.Attack < 5000 && (Enemy.BattlingMonster.IsAttack() || Enemy.BattlingMonster.IsFacedown() || Enemy.BattlingMonster.GetDefensePower() >= Card.Attack);
- }
-
- ///
- /// Summon when it can and should use effect.
- ///
- protected bool DefaultEvilswarmExcitonKnightSummon()
- {
- int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount() + Bot.GetHandCount();
- int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount() + Enemy.GetHandCount();
- return (selfCount - 1 < oppoCount) && DefaultEvilswarmExcitonKnightEffect();
- }
-
- ///
- /// Activate when we have less cards than enemy's, or the atk sum of we is lower than enemy's.
- ///
- protected bool DefaultEvilswarmExcitonKnightEffect()
- {
- int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount();
- int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount();
-
- if (selfCount < oppoCount)
- return true;
-
- int selfAttack = Bot.GetMonsters().Sum(monster => (int?)monster.GetDefensePower()) ?? 0;
- int oppoAttack = Enemy.GetMonsters().Sum(monster => (int?)monster.GetDefensePower()) ?? 0;
-
- return selfAttack < oppoAttack;
- }
-
- ///
- /// Summon in main2, or when the attack of we is lower than enemy's, but not when enemy have monster higher than 2500.
- ///
- protected bool DefaultStardustDragonSummon()
- {
- int selfBestAttack = Util.GetBestAttack(Bot);
- int oppoBestAttack = Util.GetBestPower(Enemy);
- return (selfBestAttack <= oppoBestAttack && oppoBestAttack <= 2500) || Util.IsTurn1OrMain2();
- }
-
- ///
- /// Negate enemy's destroy effect, and revive from grave.
- ///
- protected bool DefaultStardustDragonEffect()
- {
- return (Card.Location == CardLocation.Grave) || Duel.LastChainPlayer == 1;
- }
-
- ///
- /// Summon when enemy have card which we must solve.
- ///
- protected bool DefaultCastelTheSkyblasterMusketeerSummon()
- {
- return Util.GetProblematicEnemyCard() != null;
- }
-
- ///
- /// Bounce the problematic enemy card. Ignore the 1st effect.
- ///
- protected bool DefaultCastelTheSkyblasterMusketeerEffect()
- {
- if (ActivateDescription == Util.GetStringId(_CardId.CastelTheSkyblasterMusketeer, 0))
- return false;
- ClientCard target = Util.GetProblematicEnemyCard();
- if (target != null)
- {
- AI.SelectCard(0);
- AI.SelectNextCard(target);
- return true;
- }
- return false;
- }
-
- ///
- /// Summon when it should use effect, or when the attack of we is lower than enemy's, but not when enemy have monster higher than 3000.
- ///
- protected bool DefaultScarlightRedDragonArchfiendSummon()
- {
- int selfBestAttack = Util.GetBestAttack(Bot);
- int oppoBestAttack = Util.GetBestPower(Enemy);
- return (selfBestAttack <= oppoBestAttack && oppoBestAttack <= 3000) || DefaultScarlightRedDragonArchfiendEffect();
- }
-
- ///
- /// Activate when we have less monsters than enemy, or when enemy have more than 3 monsters.
- ///
- protected bool DefaultScarlightRedDragonArchfiendEffect()
- {
- int selfCount = Bot.GetMonsters().Count(monster => !monster.Equals(Card) && monster.IsSpecialSummoned && monster.HasType(CardType.Effect) && monster.Attack <= Card.Attack);
- int oppoCount = Enemy.GetMonsters().Count(monster => monster.IsSpecialSummoned && monster.HasType(CardType.Effect) && monster.Attack <= Card.Attack);
- return selfCount <= oppoCount && oppoCount > 0 || oppoCount >= 3;
- }
-
- ///
- /// Clever enough.
- ///
- protected bool DefaultHonestEffect()
- {
- if (Card.Location == CardLocation.Hand)
- {
- return Bot.BattlingMonster.IsAttack() &&
- (((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Attack) || Bot.BattlingMonster.Attack >= Enemy.LifePoints)
- || ((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Defense) && (Bot.BattlingMonster.Attack + Enemy.BattlingMonster.Attack > Enemy.BattlingMonster.Defense)));
- }
-
- if (Util.IsTurn1OrMain2() && HonestEffectCount <= 5)
- {
- HonestEffectCount++;
- return true;
- }
-
- return false;
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using YGOSharp.OCGWrapper.Enums;
+
+namespace WindBot.Game.AI
+{
+ public abstract class DefaultExecutor : Executor
+ {
+ protected class _CardId
+ {
+ public const int JizukirutheStarDestroyingKaiju = 63941210;
+ public const int ThunderKingtheLightningstrikeKaiju = 48770333;
+ public const int DogorantheMadFlameKaiju = 93332803;
+ public const int RadiantheMultidimensionalKaiju = 28674152;
+ public const int GadarlatheMysteryDustKaiju = 36956512;
+ public const int KumongoustheStickyStringKaiju = 29726552;
+ public const int GamecieltheSeaTurtleKaiju = 55063751;
+ public const int SuperAntiKaijuWarMachineMechaDogoran = 84769941;
+
+ public const int UltimateConductorTytanno = 18940556;
+ public const int ElShaddollConstruct = 20366274;
+ public const int AllyOfJusticeCatastor = 26593852;
+
+ public const int DupeFrog = 46239604;
+ public const int MaraudingCaptain = 2460565;
+
+ public const int BlackRoseDragon = 73580471;
+ public const int JudgmentDragon = 57774843;
+ public const int TopologicTrisbaena = 72529749;
+ public const int EvilswarmExcitonKnight = 46772449;
+ public const int HarpiesFeatherDuster = 18144506;
+ public const int DarkMagicAttack = 2314238;
+ public const int MysticalSpaceTyphoon = 5318639;
+ public const int CosmicCyclone = 8267140;
+ public const int ChickenGame = 67616300;
+
+ public const int SantaClaws = 46565218;
+
+ public const int CastelTheSkyblasterMusketeer = 82633039;
+ public const int CrystalWingSynchroDragon = 50954680;
+ public const int NumberS39UtopiaTheLightning = 56832966;
+ public const int Number39Utopia = 84013237;
+ public const int UltimayaTzolkin = 1686814;
+ public const int MekkKnightCrusadiaAstram = 21887175;
+ public const int HamonLordofStrikingThunder = 32491822;
+
+ public const int MoonMirrorShield = 19508728;
+ public const int PhantomKnightsFogBlade = 25542642;
+
+ public const int VampireFraeulein = 6039967;
+ public const int InjectionFairyLily = 79575620;
+
+ public const int BlueEyesChaosMAXDragon = 55410871;
+
+ public const int AshBlossom = 14558127;
+ public const int MaxxC = 23434538;
+ public const int LockBird = 94145021;
+ public const int GhostOgreAndSnowRabbit = 59438930;
+ public const int GhostBelle = 73642296;
+ public const int EffectVeiler = 63845230;
+ public const int ArtifactLancea = 34267821;
+
+ public const int CalledByTheGrave = 24224830;
+ public const int InfiniteImpermanence = 10045474;
+ public const int GalaxySoldier = 46659709;
+ public const int MacroCosmos = 30241314;
+ public const int UpstartGoblin = 70368879;
+ public const int CyberEmergency = 60600126;
+
+ public const int EaterOfMillions = 63845230;
+
+ public const int InvokedPurgatrio = 12307878;
+ public const int ChaosAncientGearGiant = 51788412;
+ public const int UltimateAncientGearGolem = 12652643;
+
+ public const int RedDragonArchfiend = 70902743;
+
+ public const int ImperialOrder = 61740673;
+ public const int NaturiaBeast = 33198837;
+ public const int AntiSpellFragrance = 58921041;
+ }
+
+ int HonestEffectCount = 0;
+
+ protected DefaultExecutor(GameAI ai, Duel duel)
+ : base(ai, duel)
+ {
+ AddExecutor(ExecutorType.Activate, _CardId.ChickenGame, DefaultChickenGame);
+ AddExecutor(ExecutorType.Activate, _CardId.SantaClaws);
+ }
+
+ ///
+ /// Decide which card should the attacker attack.
+ ///
+ /// Card that attack.
+ /// Cards that defend.
+ /// BattlePhaseAction including the target, or null (in this situation, GameAI will check the next attacker)
+ public override BattlePhaseAction OnSelectAttackTarget(ClientCard attacker, IList defenders)
+ {
+ foreach (ClientCard defender in defenders)
+ {
+ attacker.RealPower = attacker.Attack;
+ defender.RealPower = defender.GetDefensePower();
+ if (!OnPreBattleBetween(attacker, defender))
+ continue;
+
+ if (attacker.RealPower > defender.RealPower || (attacker.RealPower >= defender.RealPower && attacker.IsLastAttacker && defender.IsAttack()))
+ return AI.Attack(attacker, defender);
+ }
+
+ if (attacker.CanDirectAttack)
+ return AI.Attack(attacker, null);
+
+ return null;
+ }
+
+ ///
+ /// Decide whether to declare attack between attacker and defender.
+ /// Can be overrided to update the RealPower of attacker for cards like Honest.
+ ///
+ /// Card that attack.
+ /// Card that defend.
+ /// false if the attack shouldn't be done.
+ public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender)
+ {
+ if (attacker.RealPower <= 0)
+ return false;
+
+ if (!attacker.IsMonsterHasPreventActivationEffectInBattle())
+ {
+ if (defender.IsMonsterInvincible() && defender.IsDefense())
+ return false;
+
+ if (defender.IsMonsterDangerous())
+ {
+ bool canIgnoreIt = !attacker.IsDisabled() && (
+ attacker.IsCode(_CardId.UltimateConductorTytanno) && defender.IsDefense() ||
+ attacker.IsCode(_CardId.ElShaddollConstruct) && defender.IsSpecialSummoned ||
+ attacker.IsCode(_CardId.AllyOfJusticeCatastor) && !defender.HasAttribute(CardAttribute.Dark));
+ if (!canIgnoreIt)
+ return false;
+ }
+
+ foreach (ClientCard equip in defender.EquipCards)
+ {
+ if (equip.IsCode(_CardId.MoonMirrorShield) && !equip.IsDisabled())
+ {
+ return false;
+ }
+ }
+
+ if (!defender.IsDisabled())
+ {
+ if (defender.IsCode(_CardId.MekkKnightCrusadiaAstram) && defender.IsAttack() && attacker.IsSpecialSummoned)
+ return false;
+
+ if (defender.IsCode(_CardId.CrystalWingSynchroDragon) && defender.IsAttack() && attacker.Level >= 5)
+ return false;
+
+ if (defender.IsCode(_CardId.AllyOfJusticeCatastor) && !attacker.HasAttribute(CardAttribute.Dark))
+ return false;
+
+ if (defender.IsCode(_CardId.NumberS39UtopiaTheLightning) && defender.IsAttack() && defender.HasXyzMaterial(2, _CardId.Number39Utopia))
+ defender.RealPower = 5000;
+
+ if (defender.IsCode(_CardId.VampireFraeulein))
+ defender.RealPower += (Enemy.LifePoints > 3000) ? 3000 : (Enemy.LifePoints - 100);
+
+ if (defender.IsCode(_CardId.InjectionFairyLily) && Enemy.LifePoints > 2000)
+ defender.RealPower += 3000;
+ }
+ }
+
+ if (!defender.IsMonsterHasPreventActivationEffectInBattle())
+ {
+ if (attacker.IsCode(_CardId.NumberS39UtopiaTheLightning) && !attacker.IsDisabled() && attacker.HasXyzMaterial(2, _CardId.Number39Utopia))
+ attacker.RealPower = 5000;
+
+ foreach (ClientCard equip in attacker.EquipCards)
+ {
+ if (equip.IsCode(_CardId.MoonMirrorShield) && !equip.IsDisabled())
+ {
+ attacker.RealPower = defender.RealPower + 100;
+ }
+ }
+ }
+
+ if (Enemy.HasInMonstersZone(_CardId.MekkKnightCrusadiaAstram, true) && !(defender).IsCode(_CardId.MekkKnightCrusadiaAstram))
+ return false;
+
+ if (Enemy.HasInMonstersZone(_CardId.DupeFrog, true) && !(defender).IsCode(_CardId.DupeFrog))
+ return false;
+
+ if (Enemy.HasInMonstersZone(_CardId.MaraudingCaptain, true) && !defender.IsCode(_CardId.MaraudingCaptain) && defender.Race == (int)CardRace.Warrior)
+ return false;
+
+ if (defender.IsCode(_CardId.UltimayaTzolkin) && !defender.IsDisabled() && Enemy.GetMonsters().Any(monster => !monster.Equals(defender) && monster.HasType(CardType.Synchro)))
+ return false;
+
+ if (Enemy.GetMonsters().Any(monster => !monster.Equals(defender) && monster.IsCode(_CardId.HamonLordofStrikingThunder) && !monster.IsDisabled() && monster.IsDefense()))
+ return false;
+
+ if (defender.OwnTargets.Any(card => card.IsCode(_CardId.PhantomKnightsFogBlade) && !card.IsDisabled()))
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// Called when the AI has to select a card position.
+ ///
+ /// Id of the card to position on the field.
+ /// List of available positions.
+ /// Selected position, or 0 if no position is set for this card.
+ public override CardPosition OnSelectPosition(int cardId, IList positions)
+ {
+ YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId);
+ if (cardData != null)
+ {
+ if (cardData.Attack == 0)
+ return CardPosition.FaceUpDefence;
+ }
+ return 0;
+ }
+
+ public override bool OnSelectBattleReplay()
+ {
+ if (Bot.BattlingMonster == null)
+ return false;
+ List defenders = new List(Duel.Fields[1].GetMonsters());
+ defenders.Sort(CardContainer.CompareDefensePower);
+ defenders.Reverse();
+ BattlePhaseAction result = OnSelectAttackTarget(Bot.BattlingMonster, defenders);
+ if (result != null && result.Action == BattlePhaseAction.BattleAction.Attack)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public override void OnNewTurn()
+ {
+ HonestEffectCount = 0;
+ }
+
+ ///
+ /// Destroy face-down cards first, in our turn.
+ ///
+ protected bool DefaultMysticalSpaceTyphoon()
+ {
+ if (Duel.CurrentChain.Any(card => card.IsCode(_CardId.MysticalSpaceTyphoon)))
+ {
+ return false;
+ }
+
+ List spells = Enemy.GetSpells();
+ if (spells.Count == 0)
+ return false;
+
+ ClientCard selected = Enemy.SpellZone.GetFloodgate();
+
+ if (selected == null)
+ {
+ if (Duel.Player == 0)
+ selected = spells.FirstOrDefault(card => card.IsFacedown());
+ if (Duel.Player == 1)
+ selected = spells.FirstOrDefault(card => card.HasType(CardType.Continuous) || card.HasType(CardType.Equip) || card.HasType(CardType.Field));
+ }
+
+ if (selected == null)
+ return false;
+ AI.SelectCard(selected);
+ return true;
+ }
+
+ ///
+ /// Destroy face-down cards first, in our turn.
+ ///
+ protected bool DefaultCosmicCyclone()
+ {
+ foreach (ClientCard card in Duel.CurrentChain)
+ if (card.IsCode(_CardId.CosmicCyclone))
+ return false;
+ return (Bot.LifePoints > 1000) && DefaultMysticalSpaceTyphoon();
+ }
+
+ ///
+ /// Activate if avail.
+ ///
+ protected bool DefaultGalaxyCyclone()
+ {
+ List spells = Enemy.GetSpells();
+ if (spells.Count == 0)
+ return false;
+
+ ClientCard selected = null;
+
+ if (Card.Location == CardLocation.Grave)
+ {
+ selected = Util.GetBestEnemySpell(true);
+ }
+ else
+ {
+ selected = spells.FirstOrDefault(card => card.IsFacedown());
+ }
+
+ if (selected == null)
+ return false;
+
+ AI.SelectCard(selected);
+ return true;
+ }
+
+ ///
+ /// Set the highest ATK level 4+ effect enemy monster.
+ ///
+ protected bool DefaultBookOfMoon()
+ {
+ if (Util.IsAllEnemyBetter(true))
+ {
+ ClientCard monster = Enemy.GetMonsters().GetHighestAttackMonster(true);
+ if (monster != null && monster.HasType(CardType.Effect) && !monster.HasType(CardType.Link) && (monster.HasType(CardType.Xyz) || monster.Level > 4))
+ {
+ AI.SelectCard(monster);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Return problematic monster, and if this card become target, return any enemy monster.
+ ///
+ protected bool DefaultCompulsoryEvacuationDevice()
+ {
+ ClientCard target = Util.GetProblematicEnemyMonster(0, true);
+ if (target != null)
+ {
+ AI.SelectCard(target);
+ return true;
+ }
+ if (Util.IsChainTarget(Card))
+ {
+ ClientCard monster = Util.GetBestEnemyMonster(false, true);
+ if (monster != null)
+ {
+ AI.SelectCard(monster);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Revive the best monster when we don't have better one in field.
+ ///
+ protected bool DefaultCallOfTheHaunted()
+ {
+ if (!Util.IsAllEnemyBetter(true))
+ return false;
+ ClientCard selected = Bot.Graveyard.GetMatchingCards(card => card.IsCanRevive()).OrderByDescending(card => card.Attack).FirstOrDefault();
+ AI.SelectCard(selected);
+ return true;
+ }
+
+ ///
+ /// Default Scapegoat effect
+ ///
+ protected bool DefaultScapegoat()
+ {
+ if (DefaultSpellWillBeNegated()) return false;
+ if (Duel.Player == 0) return false;
+ if (Duel.Phase == DuelPhase.End) return true;
+ if (DefaultOnBecomeTarget()) return true;
+ if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2)
+ {
+ if (Enemy.HasInMonstersZone(new[]
+ {
+ _CardId.UltimateConductorTytanno,
+ _CardId.InvokedPurgatrio,
+ _CardId.ChaosAncientGearGiant,
+ _CardId.UltimateAncientGearGolem,
+ _CardId.RedDragonArchfiend
+ }, true)) return false;
+ if (Util.GetTotalAttackingMonsterAttack(1) >= Bot.LifePoints) return true;
+ }
+ return false;
+ }
+ ///
+ /// Always active in opponent's turn.
+ ///
+ protected bool DefaultMaxxC()
+ {
+ return Duel.Player == 1;
+ }
+ ///
+ /// Always disable opponent's effect except some cards like UpstartGoblin
+ ///
+ protected bool DefaultAshBlossomAndJoyousSpring()
+ {
+ int[] ignoreList = {
+ _CardId.MacroCosmos,
+ _CardId.UpstartGoblin,
+ _CardId.CyberEmergency
+ };
+ if (Util.GetLastChainCard().IsCode(ignoreList))
+ return false;
+ if (Util.GetLastChainCard().HasSetcode(0x11e) && Util.GetLastChainCard().Location == CardLocation.Hand) // Danger! archtype hand effect
+ return false;
+ return Duel.LastChainPlayer == 1;
+ }
+ ///
+ /// Always activate unless the activating card is disabled
+ ///
+ protected bool DefaultGhostOgreAndSnowRabbit()
+ {
+ if (Util.GetLastChainCard() != null && Util.GetLastChainCard().IsDisabled())
+ return false;
+ return DefaultTrap();
+ }
+ ///
+ /// Always disable opponent's effect
+ ///
+ protected bool DefaultGhostBelleAndHauntedMansion()
+ {
+ return DefaultTrap();
+ }
+ ///
+ /// Same as DefaultBreakthroughSkill
+ ///
+ protected bool DefaultEffectVeiler()
+ {
+ if (Util.GetLastChainCard() != null && Util.GetLastChainCard().IsCode(_CardId.GalaxySoldier) && Enemy.Hand.Count >= 3) return false;
+ if (Util.ChainContainsCard(_CardId.EffectVeiler))
+ return false;
+ return DefaultBreakthroughSkill();
+ }
+ ///
+ /// Chain common hand traps
+ ///
+ protected bool DefaultCalledByTheGrave()
+ {
+ int[] targetList =
+ {
+ _CardId.MaxxC,
+ _CardId.LockBird,
+ _CardId.GhostOgreAndSnowRabbit,
+ _CardId.AshBlossom,
+ _CardId.GhostBelle,
+ _CardId.EffectVeiler,
+ _CardId.ArtifactLancea
+ };
+ if (Duel.LastChainPlayer == 1)
+ {
+ foreach (int id in targetList)
+ {
+ if (Util.GetLastChainCard().IsCode(id))
+ {
+ AI.SelectCard(id);
+ return UniqueFaceupSpell();
+ }
+ }
+ }
+ return false;
+ }
+ ///
+ /// Default InfiniteImpermanence effect
+ ///
+ protected bool DefaultInfiniteImpermanence()
+ {
+ // TODO: disable s & t
+ if (!DefaultUniqueTrap())
+ return false;
+ return DefaultDisableMonster();
+ }
+ ///
+ /// Chain the enemy monster, or disable monster like Rescue Rabbit.
+ ///
+ protected bool DefaultBreakthroughSkill()
+ {
+ if (!DefaultUniqueTrap())
+ return false;
+ return DefaultDisableMonster();
+ }
+ ///
+ /// Chain the enemy monster, or disable monster like Rescue Rabbit.
+ ///
+ protected bool DefaultDisableMonster()
+ {
+ if (Duel.Player == 1)
+ {
+ ClientCard target = Enemy.MonsterZone.GetShouldBeDisabledBeforeItUseEffectMonster();
+ if (target != null)
+ {
+ AI.SelectCard(target);
+ return true;
+ }
+ }
+
+ ClientCard LastChainCard = Util.GetLastChainCard();
+
+ if (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.MonsterZone &&
+ !LastChainCard.IsDisabled() && !LastChainCard.IsShouldNotBeTarget() && !LastChainCard.IsShouldNotBeSpellTrapTarget())
+ {
+ AI.SelectCard(LastChainCard);
+ return true;
+ }
+
+ if (Bot.BattlingMonster != null && Enemy.BattlingMonster != null)
+ {
+ if (!Enemy.BattlingMonster.IsDisabled() && Enemy.BattlingMonster.IsCode(_CardId.EaterOfMillions))
+ {
+ AI.SelectCard(Enemy.BattlingMonster);
+ return true;
+ }
+ }
+
+ if (Duel.Phase == DuelPhase.BattleStart && Duel.Player == 1 &&
+ Enemy.HasInMonstersZone(_CardId.NumberS39UtopiaTheLightning, true))
+ {
+ AI.SelectCard(_CardId.NumberS39UtopiaTheLightning);
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Activate only except this card is the target or we summon monsters.
+ ///
+ protected bool DefaultSolemnJudgment()
+ {
+ return !Util.IsChainTargetOnly(Card) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap();
+ }
+
+ ///
+ /// Activate only except we summon monsters.
+ ///
+ protected bool DefaultSolemnWarning()
+ {
+ return (Bot.LifePoints > 2000) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap();
+ }
+
+ ///
+ /// Activate only except we summon monsters.
+ ///
+ protected bool DefaultSolemnStrike()
+ {
+ return (Bot.LifePoints > 1500) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap();
+ }
+
+ ///
+ /// Activate when all enemy monsters have better ATK.
+ ///
+ protected bool DefaultTorrentialTribute()
+ {
+ return !Util.HasChainedTrap(0) && Util.IsAllEnemyBetter(true);
+ }
+
+ ///
+ /// Activate enemy have more S&T.
+ ///
+ protected bool DefaultHeavyStorm()
+ {
+ return Bot.GetSpellCount() < Enemy.GetSpellCount();
+ }
+
+ ///
+ /// Activate before other winds, if enemy have more than 2 S&T.
+ ///
+ protected bool DefaultHarpiesFeatherDusterFirst()
+ {
+ return Enemy.GetSpellCount() >= 2;
+ }
+
+ ///
+ /// Activate when one enemy monsters have better ATK.
+ ///
+ protected bool DefaultHammerShot()
+ {
+ return Util.IsOneEnemyBetter(true);
+ }
+
+ ///
+ /// Activate when one enemy monsters have better ATK or DEF.
+ ///
+ protected bool DefaultDarkHole()
+ {
+ return Util.IsOneEnemyBetter();
+ }
+
+ ///
+ /// Activate when one enemy monsters have better ATK or DEF.
+ ///
+ protected bool DefaultRaigeki()
+ {
+ return Util.IsOneEnemyBetter();
+ }
+
+ ///
+ /// Activate when one enemy monsters have better ATK or DEF.
+ ///
+ protected bool DefaultSmashingGround()
+ {
+ return Util.IsOneEnemyBetter();
+ }
+
+ ///
+ /// Activate when we have more than 15 cards in deck.
+ ///
+ protected bool DefaultPotOfDesires()
+ {
+ return Bot.Deck.Count > 15;
+ }
+
+ ///
+ /// Set traps only and avoid block the activation of other cards.
+ ///
+ protected bool DefaultSpellSet()
+ {
+ return (Card.IsTrap() || Card.HasType(CardType.QuickPlay)) && Bot.GetSpellCountWithoutField() < 4;
+ }
+
+ ///
+ /// Summon with tributes ATK lower.
+ ///
+ protected bool DefaultTributeSummon()
+ {
+ if (!UniqueFaceupMonster())
+ return false;
+ int tributecount = (int)Math.Ceiling((Card.Level - 4.0d) / 2.0d);
+ for (int j = 0; j < 7; ++j)
+ {
+ ClientCard tributeCard = Bot.MonsterZone[j];
+ if (tributeCard == null) continue;
+ if (tributeCard.GetDefensePower() < Card.Attack)
+ tributecount--;
+ }
+ return tributecount <= 0;
+ }
+
+ ///
+ /// Activate when we have no field.
+ ///
+ protected bool DefaultField()
+ {
+ return Bot.SpellZone[5] == null;
+ }
+
+ ///
+ /// Turn if all enemy is better.
+ ///
+ protected bool DefaultMonsterRepos()
+ {
+ if (Card.IsFaceup() && Card.IsDefense() && Card.Attack == 0)
+ return false;
+
+ if (Enemy.HasInMonstersZone(_CardId.BlueEyesChaosMAXDragon, true) &&
+ Card.IsAttack() && (4000 - Card.Defense) * 2 > (4000 - Card.Attack))
+ return false;
+ if (Enemy.HasInMonstersZone(_CardId.BlueEyesChaosMAXDragon, true) &&
+ Card.IsDefense() && Card.IsFaceup() &&
+ (4000 - Card.Defense) * 2 > (4000 - Card.Attack))
+ return true;
+
+ bool enemyBetter = Util.IsAllEnemyBetter(true);
+ if (Card.IsAttack() && enemyBetter)
+ return true;
+ if (Card.IsDefense() && !enemyBetter && Card.Attack >= Card.Defense)
+ return true;
+
+ return false;
+ }
+
+ ///
+ /// If spell will be negated
+ ///
+ protected bool DefaultSpellWillBeNegated()
+ {
+ return Bot.HasInSpellZone(_CardId.ImperialOrder, true, true) || Enemy.HasInSpellZone(_CardId.ImperialOrder, true) || Enemy.HasInMonstersZone(_CardId.NaturiaBeast, true);
+ }
+
+ ///
+ /// If spell must set first to activate
+ ///
+ protected bool DefaultSpellMustSetFirst()
+ {
+ ClientCard card = null;
+ foreach (ClientCard check in Bot.GetSpells())
+ {
+ if (check.IsCode(_CardId.AntiSpellFragrance) && !check.IsDisabled())
+ card = check;
+ }
+ if (card != null && card.IsFaceup())
+ return true;
+ return Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true, true) || Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true);
+ }
+
+ ///
+ /// if spell/trap is the target or enermy activate HarpiesFeatherDuster
+ ///
+ protected bool DefaultOnBecomeTarget()
+ {
+ if (Util.IsChainTarget(Card)) return true;
+ int[] destroyAllList =
+ {
+ _CardId.EvilswarmExcitonKnight,
+ _CardId.BlackRoseDragon,
+ _CardId.JudgmentDragon,
+ _CardId.TopologicTrisbaena
+ };
+ int[] destroyAllOpponentList =
+ {
+ _CardId.HarpiesFeatherDuster,
+ _CardId.DarkMagicAttack
+ };
+
+ if (Util.ChainContainsCard(destroyAllList)) return true;
+ if (Enemy.HasInSpellZone(destroyAllOpponentList, true)) return true;
+ // TODO: ChainContainsCard(id, player)
+ return false;
+ }
+ ///
+ /// Chain enemy activation or summon.
+ ///
+ protected bool DefaultTrap()
+ {
+ return (Duel.LastChainPlayer == -1 && Duel.LastSummonPlayer != 0) || Duel.LastChainPlayer == 1;
+ }
+
+ ///
+ /// Activate when avail and no other our trap card in this chain or face-up.
+ ///
+ protected bool DefaultUniqueTrap()
+ {
+ if (Util.HasChainedTrap(0))
+ return false;
+
+ return UniqueFaceupSpell();
+ }
+
+ ///
+ /// Check no other our spell or trap card with same name face-up.
+ ///
+ protected bool UniqueFaceupSpell()
+ {
+ return !Bot.GetSpells().Any(card => card.IsCode(Card.Id) && card.IsFaceup());
+ }
+
+ ///
+ /// Check no other our monster card with same name face-up.
+ ///
+ protected bool UniqueFaceupMonster()
+ {
+ return !Bot.GetMonsters().Any(card => card.IsCode(Card.Id) && card.IsFaceup());
+ }
+
+ ///
+ /// Dumb way to avoid the bot chain in mess.
+ ///
+ protected bool DefaultDontChainMyself()
+ {
+ if (Executors.Any(exec => exec.Type == Type && exec.CardId == Card.Id))
+ return false;
+ return Duel.LastChainPlayer != 0;
+ }
+
+ ///
+ /// Draw when we have lower LP, or destroy it. Can be overrided.
+ ///
+ protected bool DefaultChickenGame()
+ {
+ if (Executors.Count(exec => exec.Type == Type && exec.CardId == Card.Id) > 1)
+ return false;
+ if (Bot.LifePoints <= 1000)
+ return false;
+ if (Bot.LifePoints <= Enemy.LifePoints && ActivateDescription == Util.GetStringId(_CardId.ChickenGame, 0))
+ return true;
+ if (Bot.LifePoints > Enemy.LifePoints && ActivateDescription == Util.GetStringId(_CardId.ChickenGame, 1))
+ return true;
+ return false;
+ }
+
+ ///
+ /// Draw when we have Dark monster in hand,and banish random one. Can be overrided.
+ ///
+ protected bool DefaultAllureofDarkness()
+ {
+ ClientCard target = Bot.Hand.FirstOrDefault(card => card.HasAttribute(CardAttribute.Dark));
+ return target != null;
+ }
+
+ ///
+ /// Clever enough.
+ ///
+ protected bool DefaultDimensionalBarrier()
+ {
+ const int RITUAL = 0;
+ const int FUSION = 1;
+ const int SYNCHRO = 2;
+ const int XYZ = 3;
+ const int PENDULUM = 4;
+ if (Duel.Player != 0)
+ {
+ List monsters = Enemy.GetMonsters();
+ int[] levels = new int[13];
+ bool tuner = false;
+ bool nontuner = false;
+ foreach (ClientCard monster in monsters)
+ {
+ if (monster.HasType(CardType.Tuner))
+ tuner = true;
+ else if (!monster.HasType(CardType.Xyz) && !monster.HasType(CardType.Link))
+ {
+ nontuner = true;
+ levels[monster.Level] = levels[monster.Level] + 1;
+ }
+
+ if (monster.IsOneForXyz())
+ {
+ AI.SelectOption(XYZ);
+ return true;
+ }
+ }
+ if (tuner && nontuner)
+ {
+ AI.SelectOption(SYNCHRO);
+ return true;
+ }
+ for (int i=1; i<=12; i++)
+ {
+ if (levels[i]>1)
+ {
+ AI.SelectOption(XYZ);
+ return true;
+ }
+ }
+ ClientCard l = Enemy.SpellZone[6];
+ ClientCard r = Enemy.SpellZone[7];
+ if (l != null && r != null && l.LScale != r.RScale)
+ {
+ AI.SelectOption(PENDULUM);
+ return true;
+ }
+ }
+ ClientCard lastchaincard = Util.GetLastChainCard();
+ if (Duel.LastChainPlayer == 1 && lastchaincard != null && !lastchaincard.IsDisabled())
+ {
+ if (lastchaincard.HasType(CardType.Ritual))
+ {
+ AI.SelectOption(RITUAL);
+ return true;
+ }
+ if (lastchaincard.HasType(CardType.Fusion))
+ {
+ AI.SelectOption(FUSION);
+ return true;
+ }
+ if (lastchaincard.HasType(CardType.Synchro))
+ {
+ AI.SelectOption(SYNCHRO);
+ return true;
+ }
+ if (lastchaincard.HasType(CardType.Xyz))
+ {
+ AI.SelectOption(XYZ);
+ return true;
+ }
+ if (lastchaincard.IsFusionSpell())
+ {
+ AI.SelectOption(FUSION);
+ return true;
+ }
+ }
+ if (Util.IsChainTarget(Card))
+ {
+ AI.SelectOption(XYZ);
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Clever enough
+ ///
+ protected bool DefaultInterruptedKaijuSlumber()
+ {
+ if (Card.Location == CardLocation.Grave)
+ {
+ AI.SelectCard(
+ _CardId.GamecieltheSeaTurtleKaiju,
+ _CardId.KumongoustheStickyStringKaiju,
+ _CardId.GadarlatheMysteryDustKaiju,
+ _CardId.RadiantheMultidimensionalKaiju,
+ _CardId.DogorantheMadFlameKaiju,
+ _CardId.ThunderKingtheLightningstrikeKaiju,
+ _CardId.JizukirutheStarDestroyingKaiju
+ );
+ return true;
+ }
+
+ if (DefaultDarkHole())
+ {
+ AI.SelectCard(
+ _CardId.JizukirutheStarDestroyingKaiju,
+ _CardId.ThunderKingtheLightningstrikeKaiju,
+ _CardId.DogorantheMadFlameKaiju,
+ _CardId.RadiantheMultidimensionalKaiju,
+ _CardId.GadarlatheMysteryDustKaiju,
+ _CardId.KumongoustheStickyStringKaiju,
+ _CardId.GamecieltheSeaTurtleKaiju
+ );
+ AI.SelectNextCard(
+ _CardId.SuperAntiKaijuWarMachineMechaDogoran,
+ _CardId.GamecieltheSeaTurtleKaiju,
+ _CardId.KumongoustheStickyStringKaiju,
+ _CardId.GadarlatheMysteryDustKaiju,
+ _CardId.RadiantheMultidimensionalKaiju,
+ _CardId.DogorantheMadFlameKaiju,
+ _CardId.ThunderKingtheLightningstrikeKaiju
+ );
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Clever enough.
+ ///
+ protected bool DefaultKaijuSpsummon()
+ {
+ IList kaijus = new[] {
+ _CardId.JizukirutheStarDestroyingKaiju,
+ _CardId.GadarlatheMysteryDustKaiju,
+ _CardId.GamecieltheSeaTurtleKaiju,
+ _CardId.RadiantheMultidimensionalKaiju,
+ _CardId.KumongoustheStickyStringKaiju,
+ _CardId.ThunderKingtheLightningstrikeKaiju,
+ _CardId.DogorantheMadFlameKaiju,
+ _CardId.SuperAntiKaijuWarMachineMechaDogoran
+ };
+ foreach (ClientCard monster in Enemy.GetMonsters())
+ {
+ if (monster.IsCode(kaijus))
+ return Card.GetDefensePower() > monster.GetDefensePower();
+ }
+ ClientCard card = Enemy.MonsterZone.GetFloodgate();
+ if (card != null)
+ {
+ AI.SelectCard(card);
+ return true;
+ }
+ card = Enemy.MonsterZone.GetDangerousMonster();
+ if (card != null)
+ {
+ AI.SelectCard(card);
+ return true;
+ }
+ card = Util.GetOneEnemyBetterThanValue(Card.GetDefensePower());
+ if (card != null)
+ {
+ AI.SelectCard(card);
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Summon when we don't have monster attack higher than enemy's.
+ ///
+ protected bool DefaultNumberS39UtopiaTheLightningSummon()
+ {
+ int bestBotAttack = Util.GetBestAttack(Bot);
+ return Util.IsOneEnemyBetterThanValue(bestBotAttack, false);
+ }
+
+ ///
+ /// Activate if the card is attack pos, and its attack is below 5000, when the enemy monster is attack pos or not useless faceup defense pos
+ ///
+ protected bool DefaultNumberS39UtopiaTheLightningEffect()
+ {
+ return Card.IsAttack() && Card.Attack < 5000 && (Enemy.BattlingMonster.IsAttack() || Enemy.BattlingMonster.IsFacedown() || Enemy.BattlingMonster.GetDefensePower() >= Card.Attack);
+ }
+
+ ///
+ /// Summon when it can and should use effect.
+ ///
+ protected bool DefaultEvilswarmExcitonKnightSummon()
+ {
+ int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount() + Bot.GetHandCount();
+ int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount() + Enemy.GetHandCount();
+ return (selfCount - 1 < oppoCount) && DefaultEvilswarmExcitonKnightEffect();
+ }
+
+ ///
+ /// Activate when we have less cards than enemy's, or the atk sum of we is lower than enemy's.
+ ///
+ protected bool DefaultEvilswarmExcitonKnightEffect()
+ {
+ int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount();
+ int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount();
+
+ if (selfCount < oppoCount)
+ return true;
+
+ int selfAttack = Bot.GetMonsters().Sum(monster => (int?)monster.GetDefensePower()) ?? 0;
+ int oppoAttack = Enemy.GetMonsters().Sum(monster => (int?)monster.GetDefensePower()) ?? 0;
+
+ return selfAttack < oppoAttack;
+ }
+
+ ///
+ /// Summon in main2, or when the attack of we is lower than enemy's, but not when enemy have monster higher than 2500.
+ ///
+ protected bool DefaultStardustDragonSummon()
+ {
+ int selfBestAttack = Util.GetBestAttack(Bot);
+ int oppoBestAttack = Util.GetBestPower(Enemy);
+ return (selfBestAttack <= oppoBestAttack && oppoBestAttack <= 2500) || Util.IsTurn1OrMain2();
+ }
+
+ ///
+ /// Negate enemy's destroy effect, and revive from grave.
+ ///
+ protected bool DefaultStardustDragonEffect()
+ {
+ return (Card.Location == CardLocation.Grave) || Duel.LastChainPlayer == 1;
+ }
+
+ ///
+ /// Summon when enemy have card which we must solve.
+ ///
+ protected bool DefaultCastelTheSkyblasterMusketeerSummon()
+ {
+ return Util.GetProblematicEnemyCard() != null;
+ }
+
+ ///
+ /// Bounce the problematic enemy card. Ignore the 1st effect.
+ ///
+ protected bool DefaultCastelTheSkyblasterMusketeerEffect()
+ {
+ if (ActivateDescription == Util.GetStringId(_CardId.CastelTheSkyblasterMusketeer, 0))
+ return false;
+ ClientCard target = Util.GetProblematicEnemyCard();
+ if (target != null)
+ {
+ AI.SelectCard(0);
+ AI.SelectNextCard(target);
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Summon when it should use effect, or when the attack of we is lower than enemy's, but not when enemy have monster higher than 3000.
+ ///
+ protected bool DefaultScarlightRedDragonArchfiendSummon()
+ {
+ int selfBestAttack = Util.GetBestAttack(Bot);
+ int oppoBestAttack = Util.GetBestPower(Enemy);
+ return (selfBestAttack <= oppoBestAttack && oppoBestAttack <= 3000) || DefaultScarlightRedDragonArchfiendEffect();
+ }
+
+ ///
+ /// Activate when we have less monsters than enemy, or when enemy have more than 3 monsters.
+ ///
+ protected bool DefaultScarlightRedDragonArchfiendEffect()
+ {
+ int selfCount = Bot.GetMonsters().Count(monster => !monster.Equals(Card) && monster.IsSpecialSummoned && monster.HasType(CardType.Effect) && monster.Attack <= Card.Attack);
+ int oppoCount = Enemy.GetMonsters().Count(monster => monster.IsSpecialSummoned && monster.HasType(CardType.Effect) && monster.Attack <= Card.Attack);
+ return selfCount <= oppoCount && oppoCount > 0 || oppoCount >= 3;
+ }
+
+ ///
+ /// Clever enough.
+ ///
+ protected bool DefaultHonestEffect()
+ {
+ if (Card.Location == CardLocation.Hand)
+ {
+ return Bot.BattlingMonster.IsAttack() &&
+ (((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Attack) || Bot.BattlingMonster.Attack >= Enemy.LifePoints)
+ || ((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Defense) && (Bot.BattlingMonster.Attack + Enemy.BattlingMonster.Attack > Enemy.BattlingMonster.Defense)));
+ }
+
+ if (Util.IsTurn1OrMain2() && HonestEffectCount <= 5)
+ {
+ HonestEffectCount++;
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Game/AI/Enums/DangerousMonster.cs b/ExecutorBase/Game/AI/Enums/DangerousMonster.cs
similarity index 95%
rename from Game/AI/Enums/DangerousMonster.cs
rename to ExecutorBase/Game/AI/Enums/DangerousMonster.cs
index 996b0062..ffd231c8 100644
--- a/Game/AI/Enums/DangerousMonster.cs
+++ b/ExecutorBase/Game/AI/Enums/DangerousMonster.cs
@@ -1,27 +1,29 @@
-namespace WindBot.Game.AI.Enums
-{
- ///
- /// Cards that are dangerous to attack.
- ///
- public enum DangerousMonster
- {
- LionHeart = 54366836,
- Yubel = 78371393,
- YubelIncarnate = 4779091,
- YubelNightmare = 31764700,
- ZaphionTheTimelord = 28929131,
- SadionTheTimelord = 65314286,
- MetaionTheTimelord = 74530899,
- KamionTheTimelord = 91712985,
- LazionTheTimelord = 92435533,
- MichionTheTimelord = 7733560,
- HailonTheTimelord = 34137269,
- RaphionTheTimelord = 60222213,
- GabrionTheTimelord = 6616912,
- SandaionTheTimelord = 33015627,
- EaterOfMillions = 63845230,
- ElShaddollConstruct = 20366274,
- ZushintheSleepingGiant = 67547370,
- Heart_eartHDragon = 97403510,
- }
-}
+namespace WindBot.Game.AI.Enums
+{
+ ///
+ /// Cards that are dangerous to attack.
+ ///
+ public enum DangerousMonster
+ {
+ LionHeart = 54366836,
+ Yubel = 78371393,
+ YubelIncarnate = 4779091,
+ YubelNightmare = 31764700,
+ ZaphionTheTimelord = 28929131,
+ SadionTheTimelord = 65314286,
+ MetaionTheTimelord = 74530899,
+ KamionTheTimelord = 91712985,
+ LazionTheTimelord = 92435533,
+ MichionTheTimelord = 7733560,
+ HailonTheTimelord = 34137269,
+ RaphionTheTimelord = 60222213,
+ GabrionTheTimelord = 6616912,
+ SandaionTheTimelord = 33015627,
+ EaterOfMillions = 63845230,
+ ElShaddollConstruct = 20366274,
+ ZushintheSleepingGiant = 67547370,
+ Heart_eartHDragon = 97403510,
+ DaigustoSphreeze = 29552709,
+ }
+}
+
diff --git a/Game/AI/Enums/Floodgate.cs b/ExecutorBase/Game/AI/Enums/Floodgate.cs
similarity index 73%
rename from Game/AI/Enums/Floodgate.cs
rename to ExecutorBase/Game/AI/Enums/Floodgate.cs
index fa300a30..9141c503 100644
--- a/Game/AI/Enums/Floodgate.cs
+++ b/ExecutorBase/Game/AI/Enums/Floodgate.cs
@@ -82,6 +82,30 @@ public enum Floodgate
InspectorBoarder = 15397015,
Mashoudou = 76375976,
EternalSoul = 48680970,
- MarincessBattleOcean = 91027843
+ MarincessBattleOcean = 91027843,
+ TopologicZeroboros = 66403530,
+ GladiatorBeastDomitianus = 33652635,
+ SerzielWatcheroftheEvilEye = 82466274,
+ ZerrzielRuleroftheEvilEyed = 17739335,
+ GorgonEmpressoftheEvilEyed = 29357687,
+ UnchainedSoulofRage = 67680512,
+ DracoBerserkeroftheTenyi = 5041348,
+ NidhoggGeneraiderBossofIce = 49275969,
+ UtgardaGeneraiderBossofDelusion = 744887,
+ FrodiGeneraiderBossofSwords = 40998517,
+ HoarrGeneraiderBossofRumbling = 68199168,
+ GodPhoenixGearfried = 22091647,
+ BrotherhoodoftheFireFistEland = 61472381,
+ PredaplantVerteAnaconda = 70369116,
+ RedSupernovaDragon = 99585850,
+ NumberF0UtopicFutureDragon = 26973555,
+ InvokedAugoeides = 97300502,
+ DragonmaidStrahl = 24799107,
+ RavenousCrocodragonArchethys = 87188910,
+ AdamancipatorRisenRaptite = 73079836,
+ AdamancipatorRisenDragite = 9464441,
+ TeardroptheRikkaQueen = 33779875,
+ CeruleanSkyFire = 54828837,
+ SacredBeastAwakening = 53701259
}
}
diff --git a/Game/AI/Enums/FusionSpell.cs b/ExecutorBase/Game/AI/Enums/FusionSpell.cs
similarity index 100%
rename from Game/AI/Enums/FusionSpell.cs
rename to ExecutorBase/Game/AI/Enums/FusionSpell.cs
diff --git a/Game/AI/Enums/InvincibleMonster.cs b/ExecutorBase/Game/AI/Enums/InvincibleMonster.cs
similarity index 95%
rename from Game/AI/Enums/InvincibleMonster.cs
rename to ExecutorBase/Game/AI/Enums/InvincibleMonster.cs
index 11561562..9399125e 100644
--- a/Game/AI/Enums/InvincibleMonster.cs
+++ b/ExecutorBase/Game/AI/Enums/InvincibleMonster.cs
@@ -1,69 +1,71 @@
-namespace WindBot.Game.AI.Enums
-{
- ///
- /// Cards that are invincible to battle.
- ///
- public enum InvincibleMonster
- {
- SpiritReaper = 23205979,
- YubelTheUltimateNightmare = 31764700,
- YubelTerrorIncarnate = 4779091,
- SandaionTheTimelord = 33015627,
- DarknessNeosphere = 60417395,
- GabrionTheTimelord = 6616912,
- MichionTheTimelord = 7733560,
- ZaphionTheTimelord = 28929131,
- HailonTheTimelord = 34137269,
- RaphionTheTimelord = 60222213,
- SadionTheTimelord = 65314286,
- MetaionTheTimelord = 74530899,
- Yubel = 78371393,
- KamionTheTimelord = 91712985,
- LazionTheTimelord = 92435533,
- CloudianEyeofTheTyphoon = 57610714,
- GimmickPuppetShadowFeeler = 34620088,
- TheLegendaryFishermanIII = 44968687,
- CastleGate = 36931229,
- CloudianNimbusman = 20003527,
- ExodiaNecross = 12600382,
- Gellenduo = 11662742,
- CloudianAltus = 79703905,
- CloudianStormDragon = 13474291,
- CloudianCirrostratus = 43318266,
- CloudianTurbulence = 16197610,
- CloudianAcidCloud = 17810268,
- SuperheavySamuraiBlueBrawler = 41628550,
- DinoSewing = 27143874,
- Marshmallon = 31305911,
- ShibaWarriorTaro = 27416701,
- XSaberPashuul = 23093604,
- SuperheavySamuraiBlowtorch = 7864030,
- VijamTheCubicSeed = 15610297,
- ArcanaForce0TheFool = 62892347,
- ReptilianneNaga = 79491903,
- AbyssStungray = 97232518,
- ArmityleTheChaosPhantom = 43378048,
- BlueEyesTwinBurstDragon = 2129638,
- GladiatorBeastNerokius = 29357956,
- MaskedHERODivineWind = 22093873,
- ElementalHEROShiningPhoenixEnforcer = 88820235,
- LunalightCatDancer = 51777272,
- ElementalHEROPhoenixEnforcer = 41436536,
- BloomDivaTheMelodiousChoir = 84988419,
- ReaperonTheNightmare = 85684223,
- BeelzeusofTheDiabolicDragons = 8763963,
- DragocytosCorruptedNethersoulDragon = 21435914,
- BeelzeofTheDiabolicDragons = 34408491,
- BlackwingArmorMaster = 69031175,
- DaigustoSphreeze = 29552709,
- DarkDiviner = 31919988,
- NumberC92HearteartHChaosDragon = 47017574,
- Number92HearteartHDragon = 97403510,
- Number51FinisherTheStrongArm = 56292140,
- NumberC96DarkStorm = 77205367,
- NumberF0UtopicFutureFutureSlash = 43490025,
- NumberF0UtopicFuture = 65305468,
- GoukiTheGiantOgre = 47946130,
- BorrelswordDragon = 85289965
- }
-}
+namespace WindBot.Game.AI.Enums
+{
+ ///
+ /// Cards that are invincible to battle.
+ ///
+ public enum InvincibleMonster
+ {
+ SpiritReaper = 23205979,
+ YubelTheUltimateNightmare = 31764700,
+ YubelTerrorIncarnate = 4779091,
+ SandaionTheTimelord = 33015627,
+ DarknessNeosphere = 60417395,
+ GabrionTheTimelord = 6616912,
+ MichionTheTimelord = 7733560,
+ ZaphionTheTimelord = 28929131,
+ HailonTheTimelord = 34137269,
+ RaphionTheTimelord = 60222213,
+ SadionTheTimelord = 65314286,
+ MetaionTheTimelord = 74530899,
+ Yubel = 78371393,
+ KamionTheTimelord = 91712985,
+ LazionTheTimelord = 92435533,
+ CloudianEyeofTheTyphoon = 57610714,
+ GimmickPuppetShadowFeeler = 34620088,
+ TheLegendaryFishermanIII = 44968687,
+ CastleGate = 36931229,
+ CloudianNimbusman = 20003527,
+ ExodiaNecross = 12600382,
+ Gellenduo = 11662742,
+ CloudianAltus = 79703905,
+ CloudianStormDragon = 13474291,
+ CloudianCirrostratus = 43318266,
+ CloudianTurbulence = 16197610,
+ CloudianAcidCloud = 17810268,
+ SuperheavySamuraiBlueBrawler = 41628550,
+ DinoSewing = 27143874,
+ Marshmallon = 31305911,
+ ShibaWarriorTaro = 27416701,
+ XSaberPashuul = 23093604,
+ SuperheavySamuraiBlowtorch = 7864030,
+ VijamTheCubicSeed = 15610297,
+ ArcanaForce0TheFool = 62892347,
+ ReptilianneNaga = 79491903,
+ AbyssStungray = 97232518,
+ ArmityleTheChaosPhantom = 43378048,
+ BlueEyesTwinBurstDragon = 2129638,
+ GladiatorBeastNerokius = 29357956,
+ MaskedHERODivineWind = 22093873,
+ ElementalHEROShiningPhoenixEnforcer = 88820235,
+ LunalightCatDancer = 51777272,
+ ElementalHEROPhoenixEnforcer = 41436536,
+ BloomDivaTheMelodiousChoir = 84988419,
+ ReaperonTheNightmare = 85684223,
+ BeelzeusofTheDiabolicDragons = 8763963,
+ DragocytosCorruptedNethersoulDragon = 21435914,
+ BeelzeofTheDiabolicDragons = 34408491,
+ BlackwingArmorMaster = 69031175,
+ DaigustoSphreeze = 29552709,
+ DarkDiviner = 31919988,
+ NumberC92HearteartHChaosDragon = 47017574,
+ Number92HearteartHDragon = 97403510,
+ Number51FinisherTheStrongArm = 56292140,
+ NumberC96DarkStorm = 77205367,
+ NumberF0UtopicFutureFutureSlash = 43490025,
+ NumberF0UtopicFuture = 65305468,
+ GoukiTheGiantOgre = 47946130,
+ BorrelswordDragon = 85289965,
+ NumberF0UtopicFutureDragon = 26973555,
+ BorrelendDragon = 98630720
+ }
+}
diff --git a/Game/AI/Enums/OneForXyz.cs b/ExecutorBase/Game/AI/Enums/OneForXyz.cs
similarity index 100%
rename from Game/AI/Enums/OneForXyz.cs
rename to ExecutorBase/Game/AI/Enums/OneForXyz.cs
diff --git a/Game/AI/Enums/PreventActivationEffectInBattle.cs b/ExecutorBase/Game/AI/Enums/PreventActivationEffectInBattle.cs
similarity index 100%
rename from Game/AI/Enums/PreventActivationEffectInBattle.cs
rename to ExecutorBase/Game/AI/Enums/PreventActivationEffectInBattle.cs
diff --git a/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs b/ExecutorBase/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs
similarity index 87%
rename from Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs
rename to ExecutorBase/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs
index b219d850..547e0c16 100644
--- a/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs
+++ b/ExecutorBase/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs
@@ -1,54 +1,61 @@
-namespace WindBot.Game.AI.Enums
-{
- ///
- /// Monsters that release or banish itself to use effect. So them should be disabled (with Breakthrough Skill) before it use effect.
- ///
- public enum ShouldBeDisabledBeforeItUseEffectMonster
- {
- MachinaMegaform = 51617185,
- DarkSummoningBeast = 87917187,
- GemKnightAlexandrite = 90019393,
- RedEyesRetroDragon = 53485634,
- DeepSweeper = 8649148,
- BeastWarriorPuma = 16796157,
- ZefrasaberSwordmasteroftheNekroz = 84388461,
- CipherWing = 81974607,
- MadolcheAnjelly = 34680482,
- PlanetPathfinder = 97526666,
- RescueCat = 14878871,
- RescueHamster = 50485594,
- RescueFerret = 56343672,
- RescueRabbit = 85138716,
- GalaxyWizard = 98555327,
- Backlinker = 71172240,
- Merlin = 3580032,
- CrystalVanguard = 87475570,
- TemperanceofProphecy = 87608852,
- Kuribandit = 16404809,
- PhotonLizard = 38973775,
- SuperheavySamuraiFlutist = 27978707,
- ConstellarRasalhague = 70624184,
- CardcarD = 45812361,
- UnifloraMysticalBeastoftheForest = 36318200,
- BusterWhelpoftheDestructionSwordsman = 49823708,
- GalaxyEyesCloudragon = 9260791,
- SylvanPrincessprout = 20579538,
- AltergeistPixiel = 57769391,
- AbyssActorExtras = 88412339,
- PerformapalTrumpWitch = 91584698,
- RaidraptorLastStrix = 97219708,
- MythicalBeastJackal = 91182675,
- TimeMaiden = 27107590,
- SuperQuantalFairyAlphan = 58753372,
- TheBlackStoneofLegend = 66574418,
- PaladinofDarkDragon = 71408082,
- PaladinofPhotonDragon = 85346853,
- TwinPhotonLizard = 29455728,
-
- CosmoBrain = 85679527,
- ShiranuiSolitaire = 94801854,
- Mixeroid = 71340250,
- LonefireBlossom = 48686504,
- BrotherhoodoftheFireFist_Leopard = 39699564
- }
-}
+namespace WindBot.Game.AI.Enums
+{
+ ///
+ /// Monsters that release or banish itself to use effect. So them should be disabled (with Breakthrough Skill) before it use effect.
+ ///
+ public enum ShouldBeDisabledBeforeItUseEffectMonster
+ {
+ MachinaMegaform = 51617185,
+ DarkSummoningBeast = 87917187,
+ GemKnightAlexandrite = 90019393,
+ RedEyesRetroDragon = 53485634,
+ DeepSweeper = 8649148,
+ BeastWarriorPuma = 16796157,
+ ZefrasaberSwordmasteroftheNekroz = 84388461,
+ CipherWing = 81974607,
+ MadolcheAnjelly = 34680482,
+ PlanetPathfinder = 97526666,
+ RescueCat = 14878871,
+ RescueHamster = 50485594,
+ RescueFerret = 56343672,
+ RescueRabbit = 85138716,
+ GalaxyWizard = 98555327,
+ Backlinker = 71172240,
+ Merlin = 3580032,
+ CrystalVanguard = 87475570,
+ TemperanceofProphecy = 87608852,
+ Kuribandit = 16404809,
+ PhotonLizard = 38973775,
+ SuperheavySamuraiFlutist = 27978707,
+ ConstellarRasalhague = 70624184,
+ CardcarD = 45812361,
+ UnifloraMysticalBeastoftheForest = 36318200,
+ BusterWhelpoftheDestructionSwordsman = 49823708,
+ GalaxyEyesCloudragon = 9260791,
+ SylvanPrincessprout = 20579538,
+ AltergeistPixiel = 57769391,
+ AbyssActorExtras = 88412339,
+ PerformapalTrumpWitch = 91584698,
+ RaidraptorLastStrix = 97219708,
+ MythicalBeastJackal = 91182675,
+ TimeMaiden = 27107590,
+ SuperQuantalFairyAlphan = 58753372,
+ TheBlackStoneofLegend = 66574418,
+ PaladinofDarkDragon = 71408082,
+ PaladinofPhotonDragon = 85346853,
+ TwinPhotonLizard = 29455728,
+ TimeThiefRegulator = 19891131,
+ MathmechNabla = 53577438,
+ NidhoggGeneraiderBossofIce = 49275969,
+ HoarrGeneraiderBossofRumbling = 68199168,
+ RedFamiliar = 8372133,
+ AccesscodeTalker = 86066372,
+ ChaosSummoningBeast = 27439792,
+
+ CosmoBrain = 85679527,
+ ShiranuiSolitaire = 94801854,
+ Mixeroid = 71340250,
+ LonefireBlossom = 48686504,
+ BrotherhoodoftheFireFist_Leopard = 39699564
+ }
+}
diff --git a/Game/AI/Enums/ShouldNotBeMonsterTarget.cs b/ExecutorBase/Game/AI/Enums/ShouldNotBeMonsterTarget.cs
similarity index 89%
rename from Game/AI/Enums/ShouldNotBeMonsterTarget.cs
rename to ExecutorBase/Game/AI/Enums/ShouldNotBeMonsterTarget.cs
index 3a9b33d4..855824e2 100644
--- a/Game/AI/Enums/ShouldNotBeMonsterTarget.cs
+++ b/ExecutorBase/Game/AI/Enums/ShouldNotBeMonsterTarget.cs
@@ -1,18 +1,19 @@
-namespace WindBot.Game.AI.Enums
-{
- ///
- /// Cards that are can't be selected as target of monster's effect, or immuned to monster's effect.
- /// So them shouldn't be tried to be selected as target of monster at most times.
- ///
- public enum ShouldNotBeMonsterTarget
- {
- TheLegendaryFishermanII = 19801646,
- GaiaDraketheUniversalForce = 58601383,
- FirstoftheDragons = 10817524,
- Tatsunoko = 55863245,
- CXyzSimontheGreatMoralLeader = 41147577,
- PaleozoicAnomalocaris = 61307542,
- PaleozoicOpabinia = 37649320,
- BorreloadDragon = 31833038
- }
-}
+namespace WindBot.Game.AI.Enums
+{
+ ///
+ /// Cards that are can't be selected as target of monster's effect, or immuned to monster's effect.
+ /// So them shouldn't be tried to be selected as target of monster at most times.
+ ///
+ public enum ShouldNotBeMonsterTarget
+ {
+ TheLegendaryFishermanII = 19801646,
+ GaiaDraketheUniversalForce = 58601383,
+ FirstoftheDragons = 10817524,
+ Tatsunoko = 55863245,
+ CXyzSimontheGreatMoralLeader = 41147577,
+ PaleozoicAnomalocaris = 61307542,
+ PaleozoicOpabinia = 37649320,
+ BorreloadDragon = 31833038,
+ BorrelendDragon = 98630720
+ }
+}
diff --git a/Game/AI/Enums/ShouldNotBeSpellTarget.cs b/ExecutorBase/Game/AI/Enums/ShouldNotBeSpellTarget.cs
similarity index 97%
rename from Game/AI/Enums/ShouldNotBeSpellTarget.cs
rename to ExecutorBase/Game/AI/Enums/ShouldNotBeSpellTarget.cs
index ba8e69c6..fa78eaf9 100644
--- a/Game/AI/Enums/ShouldNotBeSpellTarget.cs
+++ b/ExecutorBase/Game/AI/Enums/ShouldNotBeSpellTarget.cs
@@ -1,14 +1,14 @@
-namespace WindBot.Game.AI.Enums
-{
- ///
- /// Cards that are can't be selected as target of spell&trap's effect, or immuned to spell&trap's effect.
- /// So them shouldn't be tried to be selected as target of spell&trap at most times.
- ///
- public enum ShouldNotBeSpellTrapTarget
- {
- ApoqliphortTowers = 27279764,
- ApoqliphortSkybase = 40061558,
- TheLegendaryFishermanIII = 44968687,
- ChaosAncientGearGiant = 51788412
- }
-}
+namespace WindBot.Game.AI.Enums
+{
+ ///
+ /// Cards that are can't be selected as target of spell&trap's effect, or immuned to spell&trap's effect.
+ /// So them shouldn't be tried to be selected as target of spell&trap at most times.
+ ///
+ public enum ShouldNotBeSpellTrapTarget
+ {
+ ApoqliphortTowers = 27279764,
+ ApoqliphortSkybase = 40061558,
+ TheLegendaryFishermanIII = 44968687,
+ ChaosAncientGearGiant = 51788412
+ }
+}
diff --git a/Game/AI/Enums/ShouldNotBeTarget.cs b/ExecutorBase/Game/AI/Enums/ShouldNotBeTarget.cs
similarity index 97%
rename from Game/AI/Enums/ShouldNotBeTarget.cs
rename to ExecutorBase/Game/AI/Enums/ShouldNotBeTarget.cs
index 58fc6167..c3e066d7 100644
--- a/Game/AI/Enums/ShouldNotBeTarget.cs
+++ b/ExecutorBase/Game/AI/Enums/ShouldNotBeTarget.cs
@@ -1,53 +1,54 @@
-namespace WindBot.Game.AI.Enums
-{
- ///
- /// Cards that are can't be selected as target, or immuned to most effect.
- /// So them shouldn't be tried to be selected as target at most times.
- ///
- public enum ShouldNotBeTarget
- {
- DivineSerpentGeh = 82103466,
- ObelisktheTormentor = 10000000,
- TheWingedDragonofRaSphereMode = 10000080,
- TheWingedDragonofRaImmortalPhoenix = 10000090,
- KozmoDarkPlanet = 85991529,
- ZushintheSleepingGiant = 67547370,
- TheLegendaryExodiaIncarnate = 58604027,
- KozmoDarkEclipser = 64063868,
- KozmoDarkDestroyer = 55885348,
- KozmoForerunner = 20849090,
- MajespecterUnicornKirin = 31178212,
- WorldLegacyWorldShield = 55787576,
- KiwiMagicianGirl = 82627406,
- MajespecterFoxKyubi = 94784213,
- MajespecterToadOgama = 645794,
- MajespecterCrowYata = 68395509,
- MajespecterRaccoonBunbuku = 31991800,
- MajespecterCatNekomata = 5506791,
- HazyFlameHydra = 8696773,
- HazyFlameMantikor = 96051150,
- HazyFlameHyppogrif = 31303283,
- HazyFlameCerbereus = 38525760,
- HazyFlameSphynx = 1409474,
- HazyFlamePeryton = 37803172,
- HazyFlameGriffin = 74010769,
- BlueEyesChaosMAXDragon = 55410871,
- BlueEyesChaosDragon = 20654247,
- SupremeKingZARC = 13331639,
- CrimsonNovaTrinitytheDarkCubicLord = 72664875,
- LunalightLeoDancer = 24550676,
- TimaeustheKnightofDestiny = 53315891,
- DantePilgrimoftheBurningAbyss = 18386170,
- AncientGearHowitzer = 87182127,
- InvokedCocytus = 85908279,
- LyriluscIndependentNightingale = 76815942,
- FlowerCardianLightshower = 42291297,
- YaziEviloftheYangZing = 43202238,
- RaidraptorUltimateFalcon = 86221741,
- DisdainfulBirdofParadise = 27240101,
- DarkestDiabolosLordOfTheLair = 50383626,
- Blackwing_FullArmoredWing = 54082269,
- DragunofRedEyes = 37818794,
- RedEyesBDragon = 74677422, // sometimes the name of DragunofRedEyes will be changed to RedEyesBDragon
- }
-}
+namespace WindBot.Game.AI.Enums
+{
+ ///
+ /// Cards that are can't be selected as target, or immuned to most effect.
+ /// So them shouldn't be tried to be selected as target at most times.
+ ///
+ public enum ShouldNotBeTarget
+ {
+ DivineSerpentGeh = 82103466,
+ ObelisktheTormentor = 10000000,
+ TheWingedDragonofRaSphereMode = 10000080,
+ TheWingedDragonofRaImmortalPhoenix = 10000090,
+ KozmoDarkPlanet = 85991529,
+ ZushintheSleepingGiant = 67547370,
+ TheLegendaryExodiaIncarnate = 58604027,
+ KozmoDarkEclipser = 64063868,
+ KozmoDarkDestroyer = 55885348,
+ KozmoForerunner = 20849090,
+ MajespecterUnicornKirin = 31178212,
+ WorldLegacyWorldShield = 55787576,
+ KiwiMagicianGirl = 82627406,
+ MajespecterFoxKyubi = 94784213,
+ MajespecterToadOgama = 645794,
+ MajespecterCrowYata = 68395509,
+ MajespecterRaccoonBunbuku = 31991800,
+ MajespecterCatNekomata = 5506791,
+ HazyFlameHydra = 8696773,
+ HazyFlameMantikor = 96051150,
+ HazyFlameHyppogrif = 31303283,
+ HazyFlameCerbereus = 38525760,
+ HazyFlameSphynx = 1409474,
+ HazyFlamePeryton = 37803172,
+ HazyFlameGriffin = 74010769,
+ BlueEyesChaosMAXDragon = 55410871,
+ BlueEyesChaosDragon = 20654247,
+ SupremeKingZARC = 13331639,
+ CrimsonNovaTrinitytheDarkCubicLord = 72664875,
+ LunalightLeoDancer = 24550676,
+ TimaeustheKnightofDestiny = 53315891,
+ DantePilgrimoftheBurningAbyss = 18386170,
+ AncientGearHowitzer = 87182127,
+ InvokedCocytus = 85908279,
+ LyriluscIndependentNightingale = 76815942,
+ FlowerCardianLightshower = 42291297,
+ YaziEviloftheYangZing = 43202238,
+ RaidraptorUltimateFalcon = 86221741,
+ DisdainfulBirdofParadise = 27240101,
+ DarkestDiabolosLordOfTheLair = 50383626,
+ Blackwing_FullArmoredWing = 54082269,
+ DragunofRedEyes = 37818794,
+ RedEyesBDragon = 74677422, // sometimes the name of DragunofRedEyes will be changed to RedEyesBDragon
+ TheArrivalCyberseIgnister = 11738489
+ }
+}
diff --git a/Game/AI/Executor.cs b/ExecutorBase/Game/AI/Executor.cs
similarity index 92%
rename from Game/AI/Executor.cs
rename to ExecutorBase/Game/AI/Executor.cs
index b4c5bc4c..6265438f 100644
--- a/Game/AI/Executor.cs
+++ b/ExecutorBase/Game/AI/Executor.cs
@@ -1,248 +1,251 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using YGOSharp.OCGWrapper.Enums;
-using WindBot;
-using WindBot.Game;
-using WindBot.Game.AI;
-
-namespace WindBot.Game.AI
-{
- public abstract class Executor
- {
- public string Deck { get; set; }
- public Duel Duel { get; private set; }
- public IList Executors { get; private set; }
- public GameAI AI { get; private set; }
- public AIUtil Util { get; private set; }
-
- protected MainPhase Main { get; private set; }
- protected BattlePhase Battle { get; private set; }
-
- protected ExecutorType Type { get; private set; }
- protected ClientCard Card { get; private set; }
- protected long ActivateDescription { get; private set; }
-
- protected ClientField Bot { get; private set; }
- protected ClientField Enemy { get; private set; }
-
- protected Executor(GameAI ai, Duel duel)
- {
- Duel = duel;
- AI = ai;
- Util = new AIUtil(duel);
- Executors = new List();
-
- Bot = Duel.Fields[0];
- Enemy = Duel.Fields[1];
- }
-
- public virtual int OnRockPaperScissors()
- {
- return Program.Rand.Next(1, 4);
- }
-
- public virtual bool OnSelectHand()
- {
- return Program.Rand.Next(2) > 0;
- }
-
- ///
- /// Called when the AI has to decide if it should attack
- ///
- /// List of monsters that can attcack.
- /// List of monsters of enemy.
- /// A new BattlePhaseAction containing the action to do.
- public virtual BattlePhaseAction OnBattle(IList attackers, IList defenders)
- {
- // For overriding
- return null;
- }
-
- ///
- /// Called when the AI has to decide which card to attack first
- ///
- /// List of monsters that can attcack.
- /// List of monsters of enemy.
- /// The card to attack first.
- public virtual ClientCard OnSelectAttacker(IList attackers, IList defenders)
- {
- // For overriding
- return null;
- }
-
- public virtual BattlePhaseAction OnSelectAttackTarget(ClientCard attacker, IList defenders)
- {
- // Overrided in DefalultExecutor
- return null;
- }
-
- public virtual bool OnPreBattleBetween(ClientCard attacker, ClientCard defender)
- {
- // Overrided in DefalultExecutor
- return true;
- }
-
- public virtual void OnChaining(int player, ClientCard card)
- {
- // For overriding
- }
-
- public virtual void OnChainEnd()
- {
- // For overriding
- }
- public virtual void OnNewPhase()
- {
- // Some AI need do something on new phase
- }
- public virtual void OnNewTurn()
- {
- // Some AI need do something on new turn
- }
-
- public virtual void OnDraw(int player)
- {
- // Some AI need do something on draw
- }
-
- public virtual IList OnSelectCard(IList cards, int min, int max, int hint, bool cancelable)
- {
- // For overriding
- return null;
- }
-
- public virtual IList OnSelectSum(IList cards, int sum, int min, int max, int hint, bool mode)
- {
- // For overriding
- return null;
- }
-
- public virtual IList OnSelectFusionMaterial(IList cards, int min, int max)
- {
- // For overriding
- return null;
- }
-
- public virtual IList OnSelectSynchroMaterial(IList cards, int sum, int min, int max)
- {
- // For overriding
- return null;
- }
-
- public virtual IList OnSelectXyzMaterial(IList cards, int min, int max)
- {
- // For overriding
- return null;
- }
-
- public virtual IList OnSelectLinkMaterial(IList cards, int min, int max)
- {
- // For overriding
- return null;
- }
-
- public virtual IList OnSelectRitualTribute(IList cards, int sum, int min, int max)
- {
- // For overriding
- return null;
- }
-
- public virtual IList OnSelectPendulumSummon(IList cards, int max)
- {
- // For overriding
- return null;
- }
-
- public virtual IList OnCardSorting(IList cards)
- {
- // For overriding
- return null;
- }
-
- public virtual bool OnSelectYesNo(long desc)
- {
- return true;
- }
-
- public virtual int OnSelectOption(IList options)
- {
- return -1;
- }
-
- public virtual int OnSelectPlace(int cardId, int player, CardLocation location, int available)
- {
- // For overriding
- return 0;
- }
-
- public virtual CardPosition OnSelectPosition(int cardId, IList positions)
- {
- // Overrided in DefalultExecutor
- return 0;
- }
-
- public virtual bool OnSelectBattleReplay()
- {
- // Overrided in DefalultExecutor
- return false;
- }
-
- public void SetMain(MainPhase main)
- {
- Main = main;
- }
-
- public void SetBattle(BattlePhase battle)
- {
- Battle = battle;
- }
-
- ///
- /// Set global variables Type, Card, ActivateDescription for Executor
- ///
- public void SetCard(ExecutorType type, ClientCard card, long description)
- {
- Type = type;
- Card = card;
- ActivateDescription = description;
- }
-
- ///
- /// Do the action for the card if func return true.
- ///
- public void AddExecutor(ExecutorType type, int cardId, Func func)
- {
- Executors.Add(new CardExecutor(type, cardId, func));
- }
-
- ///
- /// Do the action for the card if available.
- ///
- public void AddExecutor(ExecutorType type, int cardId)
- {
- Executors.Add(new CardExecutor(type, cardId, null));
- }
-
- ///
- /// Do the action for every card if func return true.
- ///
- public void AddExecutor(ExecutorType type, Func func)
- {
- Executors.Add(new CardExecutor(type, -1, func));
- }
-
- ///
- /// Do the action for every card if no other Executor is added to it.
- ///
- public void AddExecutor(ExecutorType type)
- {
- Executors.Add(new CardExecutor(type, -1, DefaultNoExecutor));
- }
-
- private bool DefaultNoExecutor()
- {
- return Executors.All(exec => exec.Type != Type || exec.CardId != Card.Id);
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using YGOSharp.OCGWrapper.Enums;
+using WindBot;
+using WindBot.Game;
+using WindBot.Game.AI;
+
+namespace WindBot.Game.AI
+{
+ public abstract class Executor
+ {
+ public string Deck { get; set; }
+ public Duel Duel { get; private set; }
+ public IList Executors { get; private set; }
+ public GameAI AI { get; private set; }
+ public AIUtil Util { get; private set; }
+
+ protected MainPhase Main { get; private set; }
+ protected BattlePhase Battle { get; private set; }
+
+ protected ExecutorType Type { get; private set; }
+ protected ClientCard Card { get; private set; }
+ protected long ActivateDescription { get; private set; }
+
+ protected ClientField Bot { get; private set; }
+ protected ClientField Enemy { get; private set; }
+
+ public Random Rand;
+
+ protected Executor(GameAI ai, Duel duel)
+ {
+ Rand = new Random();
+ Duel = duel;
+ AI = ai;
+ Util = new AIUtil(duel);
+ Executors = new List();
+
+ Bot = Duel.Fields[0];
+ Enemy = Duel.Fields[1];
+ }
+
+ public virtual int OnRockPaperScissors()
+ {
+ return Rand.Next(1, 4);
+ }
+
+ public virtual bool OnSelectHand()
+ {
+ return Rand.Next(2) > 0;
+ }
+
+ ///
+ /// Called when the AI has to decide if it should attack
+ ///
+ /// List of monsters that can attcack.
+ /// List of monsters of enemy.
+ /// A new BattlePhaseAction containing the action to do.
+ public virtual BattlePhaseAction OnBattle(IList attackers, IList defenders)
+ {
+ // For overriding
+ return null;
+ }
+
+ ///
+ /// Called when the AI has to decide which card to attack first
+ ///
+ /// List of monsters that can attcack.
+ /// List of monsters of enemy.
+ /// The card to attack first.
+ public virtual ClientCard OnSelectAttacker(IList attackers, IList defenders)
+ {
+ // For overriding
+ return null;
+ }
+
+ public virtual BattlePhaseAction OnSelectAttackTarget(ClientCard attacker, IList defenders)
+ {
+ // Overrided in DefalultExecutor
+ return null;
+ }
+
+ public virtual bool OnPreBattleBetween(ClientCard attacker, ClientCard defender)
+ {
+ // Overrided in DefalultExecutor
+ return true;
+ }
+
+ public virtual void OnChaining(int player, ClientCard card)
+ {
+ // For overriding
+ }
+
+ public virtual void OnChainEnd()
+ {
+ // For overriding
+ }
+ public virtual void OnNewPhase()
+ {
+ // Some AI need do something on new phase
+ }
+ public virtual void OnNewTurn()
+ {
+ // Some AI need do something on new turn
+ }
+
+ public virtual void OnDraw(int player)
+ {
+ // Some AI need do something on draw
+ }
+
+ public virtual IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable)
+ {
+ // For overriding
+ return null;
+ }
+
+ public virtual IList OnSelectSum(IList cards, int sum, int min, int max, long hint, bool mode)
+ {
+ // For overriding
+ return null;
+ }
+
+ public virtual IList OnSelectFusionMaterial(IList cards, int min, int max)
+ {
+ // For overriding
+ return null;
+ }
+
+ public virtual IList OnSelectSynchroMaterial(IList cards, int sum, int min, int max)
+ {
+ // For overriding
+ return null;
+ }
+
+ public virtual IList OnSelectXyzMaterial(IList cards, int min, int max)
+ {
+ // For overriding
+ return null;
+ }
+
+ public virtual IList OnSelectLinkMaterial(IList cards, int min, int max)
+ {
+ // For overriding
+ return null;
+ }
+
+ public virtual IList OnSelectRitualTribute(IList cards, int sum, int min, int max)
+ {
+ // For overriding
+ return null;
+ }
+
+ public virtual IList OnSelectPendulumSummon(IList cards, int max)
+ {
+ // For overriding
+ return null;
+ }
+
+ public virtual IList OnCardSorting(IList cards)
+ {
+ // For overriding
+ return null;
+ }
+
+ public virtual bool OnSelectYesNo(long desc)
+ {
+ return true;
+ }
+
+ public virtual int OnSelectOption(IList options)
+ {
+ return -1;
+ }
+
+ public virtual int OnSelectPlace(long cardId, int player, CardLocation location, int available)
+ {
+ // For overriding
+ return 0;
+ }
+
+ public virtual CardPosition OnSelectPosition(int cardId, IList positions)
+ {
+ // Overrided in DefalultExecutor
+ return 0;
+ }
+
+ public virtual bool OnSelectBattleReplay()
+ {
+ // Overrided in DefalultExecutor
+ return false;
+ }
+
+ public void SetMain(MainPhase main)
+ {
+ Main = main;
+ }
+
+ public void SetBattle(BattlePhase battle)
+ {
+ Battle = battle;
+ }
+
+ ///
+ /// Set global variables Type, Card, ActivateDescription for Executor
+ ///
+ public void SetCard(ExecutorType type, ClientCard card, long description)
+ {
+ Type = type;
+ Card = card;
+ ActivateDescription = description;
+ }
+
+ ///
+ /// Do the action for the card if func return true.
+ ///
+ public void AddExecutor(ExecutorType type, int cardId, Func func)
+ {
+ Executors.Add(new CardExecutor(type, cardId, func));
+ }
+
+ ///
+ /// Do the action for the card if available.
+ ///
+ public void AddExecutor(ExecutorType type, int cardId)
+ {
+ Executors.Add(new CardExecutor(type, cardId, null));
+ }
+
+ ///
+ /// Do the action for every card if func return true.
+ ///
+ public void AddExecutor(ExecutorType type, Func func)
+ {
+ Executors.Add(new CardExecutor(type, -1, func));
+ }
+
+ ///
+ /// Do the action for every card if no other Executor is added to it.
+ ///
+ public void AddExecutor(ExecutorType type)
+ {
+ Executors.Add(new CardExecutor(type, -1, DefaultNoExecutor));
+ }
+
+ private bool DefaultNoExecutor()
+ {
+ return Executors.All(exec => exec.Type != Type || exec.CardId != Card.Id);
+ }
+ }
}
\ No newline at end of file
diff --git a/Game/AI/ExecutorType.cs b/ExecutorBase/Game/AI/ExecutorType.cs
similarity index 94%
rename from Game/AI/ExecutorType.cs
rename to ExecutorBase/Game/AI/ExecutorType.cs
index 47a557d3..dc4b8083 100644
--- a/Game/AI/ExecutorType.cs
+++ b/ExecutorBase/Game/AI/ExecutorType.cs
@@ -1,16 +1,16 @@
-namespace WindBot.Game.AI
-{
- public enum ExecutorType
- {
- Summon,
- SpSummon,
- Repos,
- MonsterSet,
- SpellSet,
- Activate,
- SummonOrSet,
- GoToBattlePhase,
- GoToMainPhase2,
- GoToEndPhase
- }
+namespace WindBot.Game.AI
+{
+ public enum ExecutorType
+ {
+ Summon,
+ SpSummon,
+ Repos,
+ MonsterSet,
+ SpellSet,
+ Activate,
+ SummonOrSet,
+ GoToBattlePhase,
+ GoToMainPhase2,
+ GoToEndPhase
+ }
}
\ No newline at end of file
diff --git a/Game/AI/Zones.cs b/ExecutorBase/Game/AI/Zones.cs
similarity index 95%
rename from Game/AI/Zones.cs
rename to ExecutorBase/Game/AI/Zones.cs
index 05944de4..f4721c0b 100644
--- a/Game/AI/Zones.cs
+++ b/ExecutorBase/Game/AI/Zones.cs
@@ -1,24 +1,24 @@
-namespace WindBot.Game.AI
-{
- public static class Zones
- {
- public const int z0 = 0x1,
- z1 = 0x2,
- z2 = 0x4,
- z3 = 0x8,
- z4 = 0x10,
- z5 = 0x20,
- z6 = 0x40,
-
- MonsterZones = 0x7f,
- MainMonsterZones = 0x1f,
- ExtraMonsterZones = 0x60,
-
- SpellZones = 0x1f,
-
- PendulumZones = 0x3,
-
- LinkedZones = 0x10000,
- NotLinkedZones = 0x20000;
- }
+namespace WindBot.Game.AI
+{
+ public static class Zones
+ {
+ public const int z0 = 0x1,
+ z1 = 0x2,
+ z2 = 0x4,
+ z3 = 0x8,
+ z4 = 0x10,
+ z5 = 0x20,
+ z6 = 0x40,
+
+ MonsterZones = 0x7f,
+ MainMonsterZones = 0x1f,
+ ExtraMonsterZones = 0x60,
+
+ SpellZones = 0x1f,
+
+ PendulumZones = 0x3,
+
+ LinkedZones = 0x10000,
+ NotLinkedZones = 0x20000;
+ }
}
\ No newline at end of file
diff --git a/Game/BattlePhase.cs b/ExecutorBase/Game/BattlePhase.cs
similarity index 96%
rename from Game/BattlePhase.cs
rename to ExecutorBase/Game/BattlePhase.cs
index 38e72d44..a7b102ab 100644
--- a/Game/BattlePhase.cs
+++ b/ExecutorBase/Game/BattlePhase.cs
@@ -1,20 +1,20 @@
-using System.Collections.Generic;
-
-namespace WindBot.Game
-{
- public class BattlePhase
- {
- public IList AttackableCards { get; private set; }
- public IList ActivableCards { get; private set; }
- public IList ActivableDescs { get; private set; }
- public bool CanMainPhaseTwo { get; set; }
- public bool CanEndPhase { get; set; }
-
- public BattlePhase()
- {
- AttackableCards = new List();
- ActivableCards = new List();
- ActivableDescs = new List();
- }
- }
+using System.Collections.Generic;
+
+namespace WindBot.Game
+{
+ public class BattlePhase
+ {
+ public IList AttackableCards { get; private set; }
+ public IList ActivableCards { get; private set; }
+ public IList ActivableDescs { get; private set; }
+ public bool CanMainPhaseTwo { get; set; }
+ public bool CanEndPhase { get; set; }
+
+ public BattlePhase()
+ {
+ AttackableCards = new List();
+ ActivableCards = new List();
+ ActivableDescs = new List();
+ }
+ }
}
\ No newline at end of file
diff --git a/Game/BattlePhaseAction.cs b/ExecutorBase/Game/BattlePhaseAction.cs
similarity index 95%
rename from Game/BattlePhaseAction.cs
rename to ExecutorBase/Game/BattlePhaseAction.cs
index 3238a22b..f11d148b 100644
--- a/Game/BattlePhaseAction.cs
+++ b/ExecutorBase/Game/BattlePhaseAction.cs
@@ -1,33 +1,33 @@
-namespace WindBot.Game
-{
- public class BattlePhaseAction
- {
- public enum BattleAction
- {
- Activate = 0,
- Attack = 1,
- ToMainPhaseTwo = 2,
- ToEndPhase = 3
- }
-
- public BattleAction Action { get; private set; }
- public int Index { get; private set; }
-
- public BattlePhaseAction(BattleAction action)
- {
- Action = action;
- Index = 0;
- }
-
- public BattlePhaseAction(BattleAction action, int[] indexes)
- {
- Action = action;
- Index = indexes[(int)action];
- }
-
- public int ToValue()
- {
- return (Index << 16) + (int)Action;
- }
- }
+namespace WindBot.Game
+{
+ public class BattlePhaseAction
+ {
+ public enum BattleAction
+ {
+ Activate = 0,
+ Attack = 1,
+ ToMainPhaseTwo = 2,
+ ToEndPhase = 3
+ }
+
+ public BattleAction Action { get; private set; }
+ public int Index { get; private set; }
+
+ public BattlePhaseAction(BattleAction action)
+ {
+ Action = action;
+ Index = 0;
+ }
+
+ public BattlePhaseAction(BattleAction action, int[] indexes)
+ {
+ Action = action;
+ Index = indexes[(int)action];
+ }
+
+ public int ToValue()
+ {
+ return (Index << 16) + (int)Action;
+ }
+ }
}
\ No newline at end of file
diff --git a/Game/ClientCard.cs b/ExecutorBase/Game/ClientCard.cs
similarity index 95%
rename from Game/ClientCard.cs
rename to ExecutorBase/Game/ClientCard.cs
index 78707f38..98207ba0 100644
--- a/Game/ClientCard.cs
+++ b/ExecutorBase/Game/ClientCard.cs
@@ -1,412 +1,418 @@
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using YGOSharp.OCGWrapper;
-using YGOSharp.OCGWrapper.Enums;
-
-namespace WindBot.Game
-{
- public class ClientCard
- {
- public int Id { get; private set; }
- public NamedCard Data { get; private set; }
- public string Name { get; private set; }
-
- public int Position { get; set; }
- public int Sequence { get; set; }
- public CardLocation Location { get; set; }
- public int Alias { get; private set; }
- public int Level { get; private set; }
- public int Rank { get; private set; }
- public int Type { get; private set; }
- public int Attribute { get; private set; }
- public int Race { get; private set; }
- public int Attack { get; private set; }
- public int Defense { get; private set; }
- public int LScale { get; private set; }
- public int RScale { get; private set; }
- public int LinkCount { get; private set; }
- public int LinkMarker { get; private set; }
- public int BaseAttack { get; private set; }
- public int BaseDefense { get; private set; }
- public int RealPower { get; set; }
- public List Overlays { get; private set; }
- public int Owner { get; private set; }
- public int Controller { get; private set; }
- public int Disabled { get; private set; }
- public int ProcCompleted { get; private set; }
- public int SelectSeq { get; set; }
- public int OpParam1 { get; set; }
- public int OpParam2 { get; set; }
-
- public List EquipCards { get; set; }
- public ClientCard EquipTarget;
- public List OwnTargets { get; set; }
- public List TargetCards { get; set; }
-
- public bool CanDirectAttack { get; set; }
- public bool ShouldDirectAttack { get; set; }
- public bool Attacked { get; set; }
- public bool IsLastAttacker { get; set; }
- public bool IsSpecialSummoned { get; set; }
-
- public int[] ActionIndex { get; set; }
- public IDictionary ActionActivateIndex { get; private set; }
-
- public ClientCard(int id, CardLocation loc, int sequence)
- : this(id, loc, -1 , 0)
- {
- }
-
- public ClientCard(int id, CardLocation loc, int sequence, int position)
- {
- SetId(id);
- Sequence = sequence;
- Position = position;
- Overlays = new List();
- EquipCards = new List();
- OwnTargets = new List();
- TargetCards = new List();
- ActionIndex = new int[16];
- ActionActivateIndex = new Dictionary();
- Location = loc;
- }
-
- public void SetId(int id)
- {
- if (Id == id) return;
- Id = id;
- Data = NamedCard.Get(Id);
- if (Data != null)
- {
- Name = Data.Name;
- if (Data.Alias != 0)
- Alias = Data.Alias;
- }
- }
-
- public long Update(BinaryReader packet, Duel duel)
- {
- long pos = packet.BaseStream.Position;
- while (true)
- {
- int size = packet.ReadInt16();
- if (size == 0)
- return packet.BaseStream.Position - pos;
- uint flag = packet.ReadUInt32();
- switch (flag)
- {
- case (uint)Query.Code:
- {
- SetId(packet.ReadInt32());
- break;
- }
- case (uint)Query.Position:
- {
- Position = packet.ReadInt32();
- break;
- }
- case (uint)Query.Alias:
- {
- Alias = packet.ReadInt32();
- break;
- }
- case (uint)Query.Type:
- {
- Type = packet.ReadInt32();
- break;
- }
- case (uint)Query.Level:
- {
- Level = packet.ReadInt32();
- break;
- }
- case (uint)Query.Rank:
- {
- Rank = packet.ReadInt32();
- break;
- }
- case (uint)Query.Attribute:
- {
- Attribute = packet.ReadInt32();
- break;
- }
- case (uint)Query.Race:
- {
- Race = packet.ReadInt32();
- break;
- }
- case (uint)Query.Attack:
- {
- Attack = packet.ReadInt32();
- break;
- }
- case (uint)Query.Defence:
- {
- Defense = packet.ReadInt32();
- break;
- }
- case (uint)Query.BaseAttack:
- {
- BaseAttack = packet.ReadInt32();
- break;
- }
- case (uint)Query.BaseDefence:
- {
- BaseDefense = packet.ReadInt32();
- break;
- }
- case (uint)Query.OverlayCard:
- {
- Overlays.Clear();
- int count = packet.ReadInt32();
- for (int i = 0; i < count; ++i)
- Overlays.Add(packet.ReadInt32());
- }
- break;
- case (uint)Query.Owner:
- {
- Owner = duel.GetLocalPlayer(packet.ReadByte());
- break;
- }
- case (uint)Query.Status:
- {
- int status = packet.ReadInt32();
- const int STATUS_DISABLED = 0x0001;
- const int STATUS_PROC_COMPLETE = 0x0008;
- Disabled = status & STATUS_DISABLED;
- ProcCompleted = status & STATUS_PROC_COMPLETE;
- break;
- }
- case (uint)Query.LScale:
- {
- LScale = packet.ReadInt32();
- break;
- }
- case (uint)Query.RScale:
- {
- RScale = packet.ReadInt32();
- break;
- }
- case (uint)Query.Link:
- {
- LinkCount = packet.ReadInt32();
- LinkMarker = packet.ReadInt32();
- break;
- }
- case 0x80000000: //Query.End
- return packet.BaseStream.Position - pos;
- default:
- {
- packet.ReadChars(size - sizeof(uint));
- break;
- }
- }
- }
- }
-
- public void ClearCardTargets()
- {
- foreach (ClientCard card in TargetCards)
- {
- card.OwnTargets.Remove(this);
- }
- foreach (ClientCard card in OwnTargets)
- {
- card.TargetCards.Remove(this);
- }
- OwnTargets.Clear();
- TargetCards.Clear();
- }
-
- public bool HasLinkMarker(int dir)
- {
- return (LinkMarker & dir) != 0;
- }
-
- public bool HasLinkMarker(CardLinkMarker dir)
- {
- return (LinkMarker & (int)dir) != 0;
- }
-
- public int GetLinkedZones()
- {
- if (!HasType(CardType.Link) || Location != CardLocation.MonsterZone)
- return 0;
- int zones = 0;
- if (Sequence > 0 && Sequence <= 4 && HasLinkMarker(CardLinkMarker.Left))
- zones |= 1 << (Sequence - 1);
- if (Sequence <= 3 && HasLinkMarker(CardLinkMarker.Right))
- zones |= 1 << (Sequence + 1);
- if (Sequence == 0 && HasLinkMarker(CardLinkMarker.TopRight)
- || Sequence == 1 && HasLinkMarker(CardLinkMarker.Top)
- || Sequence == 2 && HasLinkMarker(CardLinkMarker.TopLeft))
- zones |= (1 << 5) | (1 << (16 + 6));
- if (Sequence == 2 && HasLinkMarker(CardLinkMarker.TopRight)
- || Sequence == 3 && HasLinkMarker(CardLinkMarker.Top)
- || Sequence == 4 && HasLinkMarker(CardLinkMarker.TopLeft))
- zones |= (1 << 6) | (1 << (16 + 5));
- if (Sequence == 5)
- {
- if (HasLinkMarker(CardLinkMarker.BottomLeft))
- zones |= 1 << 0;
- if (HasLinkMarker(CardLinkMarker.Bottom))
- zones |= 1 << 1;
- if (HasLinkMarker(CardLinkMarker.BottomRight))
- zones |= 1 << 2;
- if (HasLinkMarker(CardLinkMarker.TopLeft))
- zones |= 1 << (16 + 4);
- if (HasLinkMarker(CardLinkMarker.Top))
- zones |= 1 << (16 + 3);
- if (HasLinkMarker(CardLinkMarker.TopRight))
- zones |= 1 << (16 + 2);
- }
- if (Sequence == 6)
- {
- if (HasLinkMarker(CardLinkMarker.BottomLeft))
- zones |= 1 << 2;
- if (HasLinkMarker(CardLinkMarker.Bottom))
- zones |= 1 << 3;
- if (HasLinkMarker(CardLinkMarker.BottomRight))
- zones |= 1 << 4;
- if (HasLinkMarker(CardLinkMarker.TopLeft))
- zones |= 1 << (16 + 2);
- if (HasLinkMarker(CardLinkMarker.Top))
- zones |= 1 << (16 + 1);
- if (HasLinkMarker(CardLinkMarker.TopRight))
- zones |= 1 << (16 + 0);
- }
- return zones;
- }
-
- public bool HasType(CardType type)
- {
- return (Type & (int)type) != 0;
- }
-
- public bool HasPosition(CardPosition position)
- {
- return (Position & (int)position) != 0;
- }
-
- public bool HasAttribute(CardAttribute attribute)
- {
- return (Attribute & (int)attribute) != 0;
- }
-
- public bool HasSetcode(int setcode)
- {
- if (Data == null) return false;
- long setcodes = Data.Setcode;
- int settype = setcode & 0xfff;
- int setsubtype = setcode & 0xf000;
- while (setcodes > 0)
- {
- long check_setcode = setcodes & 0xffff;
- setcodes >>= 16;
- if ((check_setcode & 0xfff) == settype && (check_setcode & 0xf000 & setsubtype) == setsubtype) return true;
- }
- return false;
- }
-
- public bool IsMonster()
- {
- return HasType(CardType.Monster);
- }
-
- public bool IsTuner()
- {
- return HasType(CardType.Tuner);
- }
-
- public bool IsSpell()
- {
- return HasType(CardType.Spell);
- }
-
- public bool IsTrap()
- {
- return HasType(CardType.Trap);
- }
-
- public bool IsExtraCard()
- {
- return HasType(CardType.Fusion) || HasType(CardType.Synchro) || HasType(CardType.Xyz) || HasType(CardType.Link);
- }
-
- public bool IsFaceup()
- {
- return HasPosition(CardPosition.FaceUp);
- }
-
- public bool IsFacedown()
- {
- return HasPosition(CardPosition.FaceDown);
- }
-
- public bool IsAttack()
- {
- return HasPosition(CardPosition.Attack);
- }
-
- public bool IsDefense()
- {
- return HasPosition(CardPosition.Defence);
- }
-
- public bool IsDisabled()
- {
- return Disabled != 0;
- }
-
- public bool IsCanRevive()
- {
- return ProcCompleted != 0 || !(IsExtraCard() || HasType(CardType.Ritual) || HasType(CardType.SpSummon));
- }
-
- public bool IsCode(int id)
- {
- return Id == id || Alias != 0 && Alias == id;
- }
-
- public bool IsCode(IList ids)
- {
- return ids.Contains(Id) || Alias != 0 && ids.Contains(Alias);
- }
-
- public bool IsCode(params int[] ids)
- {
- return ids.Contains(Id) || Alias != 0 && ids.Contains(Alias);
- }
-
- public bool IsOriginalCode(int id)
- {
- return Id == id || Alias - Id < 10 && Alias == id;
- }
-
- public bool HasXyzMaterial()
- {
- return Overlays.Count > 0;
- }
-
- public bool HasXyzMaterial(int count)
- {
- return Overlays.Count >= count;
- }
-
- public bool HasXyzMaterial(int count, int cardid)
- {
- return Overlays.Count >= count && Overlays.Contains(cardid);
- }
-
- public int GetDefensePower()
- {
- return IsAttack() ? Attack : Defense;
- }
-
- public bool Equals(ClientCard card)
- {
- return ReferenceEquals(this, card);
- }
- }
-}
\ No newline at end of file
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using YGOSharp.OCGWrapper;
+using YGOSharp.OCGWrapper.Enums;
+
+namespace WindBot.Game
+{
+ public class ClientCard
+ {
+ public int Id { get; private set; }
+ public NamedCard Data { get; private set; }
+ public string Name { get; private set; }
+
+ public int Position { get; set; }
+ public int Sequence { get; set; }
+ public CardLocation Location { get; set; }
+ public int Alias { get; private set; }
+ public int Level { get; private set; }
+ public int Rank { get; private set; }
+ public int Type { get; private set; }
+ public int Attribute { get; private set; }
+ public int Race { get; private set; }
+ public int Attack { get; private set; }
+ public int Defense { get; private set; }
+ public int LScale { get; private set; }
+ public int RScale { get; private set; }
+ public int LinkCount { get; private set; }
+ public int LinkMarker { get; private set; }
+ public int BaseAttack { get; private set; }
+ public int BaseDefense { get; private set; }
+ public int RealPower { get; set; }
+ public List Overlays { get; private set; }
+ public int Owner { get; private set; }
+ public int Controller { get; set; }
+ public int Disabled { get; private set; }
+ public int ProcCompleted { get; private set; }
+ public int SelectSeq { get; set; }
+ public int OpParam1 { get; set; }
+ public int OpParam2 { get; set; }
+
+ public List EquipCards { get; set; }
+ public ClientCard EquipTarget;
+ public List OwnTargets { get; set; }
+ public List TargetCards { get; set; }
+
+ public bool CanDirectAttack { get; set; }
+ public bool ShouldDirectAttack { get; set; }
+ public bool Attacked { get; set; }
+ public bool IsLastAttacker { get; set; }
+ public bool IsSpecialSummoned { get; set; }
+
+ public int[] ActionIndex { get; set; }
+ public IDictionary ActionActivateIndex { get; private set; }
+
+ public ClientCard(int id, CardLocation loc, int sequence, int controller)
+ : this(id, loc, -1, 0, controller)
+ {
+ }
+
+ public ClientCard(int id, CardLocation loc, int sequence, int position, int controller)
+ {
+ SetId(id);
+ Sequence = sequence;
+ Position = position;
+ Controller = controller;
+ Overlays = new List();
+ EquipCards = new List();
+ OwnTargets = new List();
+ TargetCards = new List();
+ ActionIndex = new int[16];
+ ActionActivateIndex = new Dictionary();
+ Location = loc;
+ }
+
+ public void SetId(int id)
+ {
+ if (Id == id) return;
+ Id = id;
+ Data = NamedCard.Get(Id);
+ if (Data != null)
+ {
+ Name = Data.Name;
+ if (Data.Alias != 0)
+ Alias = Data.Alias;
+ }
+ }
+
+ public long Update(BinaryReader packet, Duel duel)
+ {
+ long pos = packet.BaseStream.Position;
+ while (true)
+ {
+ int size = packet.ReadInt16();
+ if (size == 0)
+ return packet.BaseStream.Position - pos;
+ uint flag = packet.ReadUInt32();
+ switch (flag)
+ {
+ case (uint)Query.Code:
+ {
+ SetId(packet.ReadInt32());
+ break;
+ }
+ case (uint)Query.Position:
+ {
+ Position = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.Alias:
+ {
+ Alias = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.Type:
+ {
+ Type = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.Level:
+ {
+ Level = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.Rank:
+ {
+ Rank = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.Attribute:
+ {
+ Attribute = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.Race:
+ {
+ Race = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.Attack:
+ {
+ Attack = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.Defence:
+ {
+ Defense = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.BaseAttack:
+ {
+ BaseAttack = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.BaseDefence:
+ {
+ BaseDefense = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.OverlayCard:
+ {
+ Overlays.Clear();
+ int count = packet.ReadInt32();
+ for (int i = 0; i < count; ++i)
+ Overlays.Add(packet.ReadInt32());
+ }
+ break;
+ case (uint)Query.Owner:
+ {
+ Owner = duel.GetLocalPlayer(packet.ReadByte());
+ break;
+ }
+ case (uint)Query.Status:
+ {
+ int status = packet.ReadInt32();
+ const int STATUS_DISABLED = 0x0001;
+ const int STATUS_PROC_COMPLETE = 0x0008;
+ Disabled = status & STATUS_DISABLED;
+ ProcCompleted = status & STATUS_PROC_COMPLETE;
+ break;
+ }
+ case (uint)Query.LScale:
+ {
+ LScale = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.RScale:
+ {
+ RScale = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.Link:
+ {
+ LinkCount = packet.ReadInt32();
+ LinkMarker = packet.ReadInt32();
+ break;
+ }
+ case (uint)Query.End: //Query.End
+ return packet.BaseStream.Position - pos;
+ default:
+ {
+ packet.ReadChars(size - sizeof(uint));
+ break;
+ }
+ }
+ }
+ }
+
+ public void ClearCardTargets()
+ {
+ foreach (ClientCard card in TargetCards)
+ {
+ card.OwnTargets.Remove(this);
+ }
+ foreach (ClientCard card in OwnTargets)
+ {
+ card.TargetCards.Remove(this);
+ }
+ OwnTargets.Clear();
+ TargetCards.Clear();
+ }
+
+ public bool HasLinkMarker(int dir)
+ {
+ return (LinkMarker & dir) != 0;
+ }
+
+ public bool HasLinkMarker(CardLinkMarker dir)
+ {
+ return (LinkMarker & (int)dir) != 0;
+ }
+
+ public int GetLinkedZones()
+ {
+ if (!HasType(CardType.Link) || Location != CardLocation.MonsterZone)
+ return 0;
+ int zones = 0;
+ if (Sequence > 0 && Sequence <= 4 && HasLinkMarker(CardLinkMarker.Left))
+ zones |= 1 << (Sequence - 1);
+ if (Sequence <= 3 && HasLinkMarker(CardLinkMarker.Right))
+ zones |= 1 << (Sequence + 1);
+ if (Sequence == 0 && HasLinkMarker(CardLinkMarker.TopRight)
+ || Sequence == 1 && HasLinkMarker(CardLinkMarker.Top)
+ || Sequence == 2 && HasLinkMarker(CardLinkMarker.TopLeft))
+ zones |= (1 << 5) | (1 << (16 + 6));
+ if (Sequence == 2 && HasLinkMarker(CardLinkMarker.TopRight)
+ || Sequence == 3 && HasLinkMarker(CardLinkMarker.Top)
+ || Sequence == 4 && HasLinkMarker(CardLinkMarker.TopLeft))
+ zones |= (1 << 6) | (1 << (16 + 5));
+ if (Sequence == 5)
+ {
+ if (HasLinkMarker(CardLinkMarker.BottomLeft))
+ zones |= 1 << 0;
+ if (HasLinkMarker(CardLinkMarker.Bottom))
+ zones |= 1 << 1;
+ if (HasLinkMarker(CardLinkMarker.BottomRight))
+ zones |= 1 << 2;
+ if (HasLinkMarker(CardLinkMarker.TopLeft))
+ zones |= 1 << (16 + 4);
+ if (HasLinkMarker(CardLinkMarker.Top))
+ zones |= 1 << (16 + 3);
+ if (HasLinkMarker(CardLinkMarker.TopRight))
+ zones |= 1 << (16 + 2);
+ }
+ if (Sequence == 6)
+ {
+ if (HasLinkMarker(CardLinkMarker.BottomLeft))
+ zones |= 1 << 2;
+ if (HasLinkMarker(CardLinkMarker.Bottom))
+ zones |= 1 << 3;
+ if (HasLinkMarker(CardLinkMarker.BottomRight))
+ zones |= 1 << 4;
+ if (HasLinkMarker(CardLinkMarker.TopLeft))
+ zones |= 1 << (16 + 2);
+ if (HasLinkMarker(CardLinkMarker.Top))
+ zones |= 1 << (16 + 1);
+ if (HasLinkMarker(CardLinkMarker.TopRight))
+ zones |= 1 << (16 + 0);
+ }
+ return zones;
+ }
+
+ public bool HasType(CardType type)
+ {
+ return (Type & (int)type) != 0;
+ }
+
+ public bool HasPosition(CardPosition position)
+ {
+ return (Position & (int)position) != 0;
+ }
+
+ public bool HasAttribute(CardAttribute attribute)
+ {
+ return (Attribute & (int)attribute) != 0;
+ }
+
+ public bool HasRace(CardRace race)
+ {
+ return (Race & (int)race) != 0;
+ }
+
+ public bool HasSetcode(int setcode)
+ {
+ if (Data == null) return false;
+ long setcodes = Data.Setcode;
+ int settype = setcode & 0xfff;
+ int setsubtype = setcode & 0xf000;
+ while (setcodes > 0)
+ {
+ long check_setcode = setcodes & 0xffff;
+ setcodes >>= 16;
+ if ((check_setcode & 0xfff) == settype && (check_setcode & 0xf000 & setsubtype) == setsubtype) return true;
+ }
+ return false;
+ }
+
+ public bool IsMonster()
+ {
+ return HasType(CardType.Monster);
+ }
+
+ public bool IsTuner()
+ {
+ return HasType(CardType.Tuner);
+ }
+
+ public bool IsSpell()
+ {
+ return HasType(CardType.Spell);
+ }
+
+ public bool IsTrap()
+ {
+ return HasType(CardType.Trap);
+ }
+
+ public bool IsExtraCard()
+ {
+ return HasType(CardType.Fusion) || HasType(CardType.Synchro) || HasType(CardType.Xyz) || HasType(CardType.Link);
+ }
+
+ public bool IsFaceup()
+ {
+ return HasPosition(CardPosition.FaceUp);
+ }
+
+ public bool IsFacedown()
+ {
+ return HasPosition(CardPosition.FaceDown);
+ }
+
+ public bool IsAttack()
+ {
+ return HasPosition(CardPosition.Attack);
+ }
+
+ public bool IsDefense()
+ {
+ return HasPosition(CardPosition.Defence);
+ }
+
+ public bool IsDisabled()
+ {
+ return Disabled != 0;
+ }
+
+ public bool IsCanRevive()
+ {
+ return ProcCompleted != 0 || !(IsExtraCard() || HasType(CardType.Ritual) || HasType(CardType.SpSummon));
+ }
+
+ public bool IsCode(int id)
+ {
+ return Id == id || Alias != 0 && Alias == id;
+ }
+
+ public bool IsCode(IList ids)
+ {
+ return ids.Contains(Id) || Alias != 0 && ids.Contains(Alias);
+ }
+
+ public bool IsCode(params int[] ids)
+ {
+ return ids.Contains(Id) || Alias != 0 && ids.Contains(Alias);
+ }
+
+ public bool IsOriginalCode(int id)
+ {
+ return Id == id || Alias - Id < 10 && Alias == id;
+ }
+
+ public bool HasXyzMaterial()
+ {
+ return Overlays.Count > 0;
+ }
+
+ public bool HasXyzMaterial(int count)
+ {
+ return Overlays.Count >= count;
+ }
+
+ public bool HasXyzMaterial(int count, int cardid)
+ {
+ return Overlays.Count >= count && Overlays.Contains(cardid);
+ }
+
+ public int GetDefensePower()
+ {
+ return IsAttack() ? Attack : Defense;
+ }
+
+ public bool Equals(ClientCard card)
+ {
+ return ReferenceEquals(this, card);
+ }
+ }
+}
diff --git a/Game/ClientField.cs b/ExecutorBase/Game/ClientField.cs
similarity index 96%
rename from Game/ClientField.cs
rename to ExecutorBase/Game/ClientField.cs
index 6b4eff5e..871c9d5c 100644
--- a/Game/ClientField.cs
+++ b/ExecutorBase/Game/ClientField.cs
@@ -1,352 +1,352 @@
-using System.Collections.Generic;
-using System.Linq;
-using WindBot.Game.AI;
-using YGOSharp.OCGWrapper.Enums;
-
-namespace WindBot.Game
-{
- public class ClientField
- {
- public IList Hand { get; private set; }
- public ClientCard[] MonsterZone { get; private set; }
- public ClientCard[] SpellZone { get; private set; }
- public IList Graveyard { get; private set; }
- public IList Banished { get; private set; }
- public IList Deck { get; private set; }
- public IList ExtraDeck { get; private set; }
-
- public int LifePoints;
- public ClientCard BattlingMonster;
- public bool UnderAttack;
-
- public ClientField()
- {
- }
-
- public void Init(int deck, int extra)
- {
- Hand = new List();
- MonsterZone = new ClientCard[7];
- SpellZone = new ClientCard[8];
- Graveyard = new List();
- Banished = new List();
- Deck = new List();
- ExtraDeck = new List();
-
- for (int i = 0; i < deck; ++i)
- Deck.Add(new ClientCard(0, CardLocation.Deck, -1));
- for (int i = 0; i < extra; ++i)
- ExtraDeck.Add(new ClientCard(0, CardLocation.Extra, -1));
- }
-
- public int GetMonstersExtraZoneCount()
- {
- int count = 0;
- if (MonsterZone[5] != null)
- count++;
- if (MonsterZone[6] != null)
- count++;
- return count;
- }
- public int GetMonsterCount()
- {
- return GetCount(MonsterZone);
- }
-
- public int GetSpellCount()
- {
- return GetCount(SpellZone);
- }
-
- public int GetHandCount()
- {
- return GetCount(Hand);
- }
-
- public int GetSpellCountWithoutField()
- {
- int count = 0;
- for (int i = 0; i < 5; ++i)
- {
- if (SpellZone[i] != null)
- ++count;
- }
- return count;
- }
-
- ///
- /// Count Column
- ///
- /// range of zone 0-4
- public int GetColumnCount(int zone, bool IncludeExtraMonsterZone = true)
- {
- int count = 0;
- if (SpellZone[zone] != null)
- count++;
- if (MonsterZone[zone] != null)
- count++;
- if(zone == 1 && IncludeExtraMonsterZone)
- {
- if (MonsterZone[5] != null)
- count++;
- }
- if (zone == 3 && IncludeExtraMonsterZone)
- {
- if (MonsterZone[6] != null)
- count++;
- }
- return count;
- }
-
- public int GetFieldCount()
- {
- return GetSpellCount() + GetMonsterCount();
- }
-
- public int GetFieldHandCount()
- {
- return GetSpellCount() + GetMonsterCount() + GetHandCount();
- }
-
- public bool IsFieldEmpty()
- {
- return GetMonsters().Count == 0 && GetSpells().Count == 0;
- }
-
- public int GetLinkedZones()
- {
- int zones = 0;
- for (int i = 0; i < 7; i++)
- {
- zones |= MonsterZone[i]?.GetLinkedZones() ?? 0;
- }
- return zones;
- }
-
- public List GetMonsters()
- {
- return GetCards(MonsterZone);
- }
-
- public List GetGraveyardMonsters()
- {
- return GetCards(Graveyard, CardType.Monster);
- }
-
- public List GetGraveyardSpells()
- {
- return GetCards(Graveyard, CardType.Spell);
- }
-
- public List GetGraveyardTraps()
- {
- return GetCards(Graveyard, CardType.Trap);
- }
-
- public List