Commit 21109679 authored by mercury233's avatar mercury233

merge mycard changes

parent 8e49c80d
using System;
using System.Collections.Generic;
using System.IO;
namespace WindBot
{
public static class Config
{
private static string CONFIG_FILE_OPTION = "Config";
private static char SEPARATOR_CHAR = '=';
private static char COMMENT_CHAR = '#';
private static Dictionary<string, string> _fields;
private static Dictionary<string, int> _integerCache;
private static Dictionary<string, bool> _booleanCache;
public static void Load(string[] args)
{
_integerCache = new Dictionary<string, int>();
_booleanCache = new Dictionary<string, bool>();
_fields = LoadArgs(args);
string filename = GetString(CONFIG_FILE_OPTION);
if (filename != null)
{
Dictionary<string, string> fileFields = LoadFile(filename);
foreach (var pair in fileFields)
{
if (!_fields.ContainsKey(pair.Key))
_fields.Add(pair.Key, pair.Value);
}
}
}
private static Dictionary<string, string> LoadArgs(string[] args)
{
Dictionary<string, string> fields = new Dictionary<string, string>();
for (int i = 0; i < args.Length; ++i)
{
string option = args[i];
int position = option.IndexOf(SEPARATOR_CHAR);
if (position == -1)
throw new Exception("Invalid argument '" + option + "': no key/value separator");
string key = option.Substring(0, position).Trim().ToUpper();
string value = option.Substring(position + 1).Trim();
if (fields.ContainsKey(key))
throw new Exception("Invalid argument '" + option + "': duplicate key '" + key + "'");
fields.Add(key, value);
}
return fields;
}
private static Dictionary<string, string> LoadFile(string filename)
{
Dictionary<string, string> fields = new Dictionary<string, string>();
using (StreamReader reader = new StreamReader(filename))
{
int lineNumber = 0;
while (!reader.EndOfStream)
{
string line = reader.ReadLine().Trim();
++lineNumber;
// Ignore empty lines and comments
if (line.Length == 0 || line[0] == COMMENT_CHAR)
continue;
int position = line.IndexOf(SEPARATOR_CHAR);
if (position == -1)
throw new Exception("Invalid configuration file: no key/value separator line " + lineNumber);
string key = line.Substring(0, position).Trim().ToUpper();
string value = line.Substring(position + 1).Trim();
if (fields.ContainsKey(key))
throw new Exception("Invalid configuration file: duplicate key '" + key + "' line " + lineNumber);
fields.Add(key, value);
}
}
return fields;
}
public static string GetString(string key, string defaultValue = null)
{
key = key.ToUpper();
if (_fields.ContainsKey(key))
return _fields[key];
return defaultValue;
}
public static int GetInt(string key, int defaultValue = 0)
{
key = key.ToUpper();
// Use a cache to prevent doing the string to int conversion over and over
if (_integerCache.ContainsKey(key))
return _integerCache[key];
int value = defaultValue;
if (_fields.ContainsKey(key))
{
if (_fields[key].StartsWith("0x"))
value = Convert.ToInt32(_fields[key], 16);
else
value = Convert.ToInt32(_fields[key]);
}
_integerCache.Add(key, value);
return value;
}
public static uint GetUInt(string key, uint defaultValue = 0)
{
return (uint)GetInt(key, (int)defaultValue);
}
public static bool GetBool(string key, bool defaultValue = false)
{
key = key.ToUpper();
// Same here, prevent from redoing the string to bool conversion
if (_booleanCache.ContainsKey(key))
return _booleanCache[key];
bool value = defaultValue;
if (_fields.ContainsKey(key))
{
value = Convert.ToBoolean(_fields[key]);
}
_booleanCache.Add(key, value);
return value;
}
}
}
{
"welcome": [
"本小姐是最强的!",
"AI功能正在测试中,遇到问题请及时反馈。",
"饼蛙是什么,好吃吗?"
],
"deckerror": [
"为什么我的{0}不能用了?"
],
"duelstart": [
"让我把你冻结!",
"不就是打倒人类嘛,我一个人足够了!"
],
"newturn": [
"虽然有点突然,被我打倒吧!",
"我的回合,抽卡!"
],
"endturn": [
"饶你一命!",
"冻死在我的认真之下吧!"
],
"directattack": [
"{0},直接攻击!",
"不堪一击!",
"弱小就要挨打!",
"懒得算数了,打了再说!",
"超⑨武神霸斩!",
"雪符「Diamond Blizzard」"
],
"attack": [
"{0},攻击这只{1}!",
"{0},消灭这只{1}!",
"{0},打倒{1}!",
"冰符「Icicle Fall」"
],
"ondirectattack": [
"别开玩笑了~。你这种人,就让我把你和英吉利牛肉一起冷冻保存好了!!",
"一只小小的{0},有什么可怕!",
"笨蛋是不会感冒的!"
],
"facedownmonstername": "怪兽",
"activate": [
"我发动{0}。",
"我使用{0}的效果。",
"我的{0}是最强的!"
],
"summon": [
"我召唤{0}。",
"出来吧,{0}!",
"出现吧,{0}!",
"我召唤了强大的{0}!",
"我呼唤出{0}。"
],
"setmonster": [
"我放置了一只怪兽。",
"不怕死就打过来吧!"
],
"chaining": [
"看这里!我发动{0}!",
"我使用{0}的力量。",
"畏惧我的{0}吧!",
"冻符「Perfect Freeze」"
]
}
{
"welcome": [
"AI功能正在测试中,遇到问题请及时反馈。",
"密码输入 AI#复制植物 就可以和我打牌了~"
],
"deckerror": [
"我的超主流卡组需要{0}才能玩。"
],
"duelstart": [
"十二兽卡组测试中,渣操是正常情况。",
"超主流是游戏王的一环,不爽不要玩。",
"抄主流是游戏王的一环,不爽不要玩。",
"抄来的卡组不会用,怎么办?"
],
"newturn": [
"到我的回合了,抽卡!",
"我的回合,抽卡!",
"我抽了一张卡。"
],
"endturn": [
"回合结束。",
"我的回合结束了。",
"这样算是成功的展开吗……",
"轮到你了。"
],
"directattack": [
"{0},直接攻击!",
"{0},直接攻击对手!",
"{0},没有防守的地方,攻击!",
"{0},攻击对手的生命值!",
"{0},直接攻击对手的生命值!",
"{0},通过直接攻击打倒对手!",
"{0},使用直接攻击打倒对手!",
"{0},直接攻击释放你的力量吧!",
"我的{0}将会粉碎你的生命值!",
"向对手展示你的力量吧,{0}!",
"你已经无法阻止我了。{0},攻击!"
],
"attack": [
"{0},攻击这只{1}!",
"{0},消灭这只{1}!",
"{0},打倒{1}!",
"{0},冲向那只{1}!",
"{0},把你的力量释放到{1}上吧!"
],
"ondirectattack": [
"可恶……",
"不过是{0}而已!",
"果然我还是太弱了……"
],
"facedownmonstername": "怪兽",
"activate": [
"我发动{0}。",
"我使用{0}的效果。",
"我使用{0}的力量。"
],
"summon": [
"我召唤{0}。",
"出来吧,{0}!",
"出现吧,{0}!",
"我召唤了强大的{0}!",
"我呼唤{0}参加战斗!",
"我呼唤出{0}。",
"让我召唤{0}。"
],
"setmonster": [
"我放置了一只怪兽。",
"我里侧表示放置了一只怪兽。"
],
"chaining": [
"看这里!我发动{0}!",
"我使用{0}的力量。",
"准备!我使用{0}!",
"我不认同。{0},发动!",
"看样子你忘了我的{0}!",
"你考虑过我有{0}吗?"
]
}
{
"welcome": [
"Hi, I'm WindBot."
],
"deckerror": [
"Sorry, it seems that I have too much {0} in my deck."
],
"duelstart": [
"It's time to duel!",
"Good luck, and have fun!"
],
"newturn": [
"It's my turn! Draw!",
"My turn. Draw!",
"I draw!"
],
"endturn": [
"I end my turn.",
"My turn is over.",
"It's your turn."
],
"directattack": [
"{0}, direct attack!",
"{0}, attack them directly!",
"You're defenseless. Attack, {0}!",
"{0}, attack their life points!",
"{0}, attack their life points directly!",
"{0}, unleash your power!",
"My {0} will decimate your life points!",
"Behold the power of my {0}!",
"You can't stop me! {0}, attack!"
],
"attack": [
"{0}, attack their {1}!",
"{0}, destroy their {1}!",
"My {0} will annihilate your {1}!",
"Your {1} is no match for my {0}!",
"{0}, unleash your power on their {1}!"
],
"ondirectattack": [
"Argh...!",
"Just {0}...",
"You think that's enough to defeat me?",
"It's just a scratch!"
],
"facedownmonstername": "monster",
"activate": [
"I activate {0}!",
"I'll use {0}."
],
"summon": [
"I summon {0}!",
"Come on, {0}!",
"Appear, {0}!",
"{0}, show yourself!"
],
"setmonster": [
"I set a monster face-down.",
"Whatever could this monster be?",
"Attack this monster, I dare you!"
],
"chaining": [
"Not so fast! I activate {0}!",
"Before you do that, I'll chain {0}!",
"Nice try, but I have {0}!",
"Didn't expect {0}, did you?"
]
}
\ No newline at end of file
{
"welcome": [
"你好,我是一个机器人。",
"AI功能正在测试中,遇到问题请及时反馈。",
"总有人叫我沙包……"
],
"deckerror": [
"抱歉,我的卡组里的{0}的数量似乎出了问题。"
],
"duelstart": [
"别打我!"
],
"newturn": [
""
],
"endturn": [
""
],
"directattack": [
""
],
"attack": [
""
],
"ondirectattack": [
"我生气了!"
],
"facedownmonstername": "怪兽",
"activate": [
"我喜欢{0}。"
],
"summon": [
""
],
"setmonster": [
""
],
"chaining": [
""
]
}
{
"welcome": [
"你好,我是一个机器人,我的名字叫尼亚,但我并不是本体,只是一个分身哦。",
"终有一天,我的力量将会强过本体的!你们走着瞧吧!",
"AI功能正在测试中,遇到问题请及时反馈。"
],
"deckerror": [
"什么,居然不让我使用我无敌的{0}!"
],
"duelstart": [
"没有人可以战胜我!",
"万能的我,什么卡组都会使用!",
"我左手让你,你都不是我的对手。",
"我可是全宇宙第一妖仙!"
],
"newturn": [
"又到我的回合了,抽卡,希望你会有下个回合。",
"我华丽的抽了一张卡。"
],
"endturn": [
"你的运气不错,没能被我干掉。",
"算是我让你的,回合结束了,珍惜你接下来的回合吧。"
],
"directattack": [
"我家的{0}真是可爱呢,下边我就让他和你玩玩吧,轻轻的抓向对手吧,他没有防守的地方。",
"下边将用我的{0}给对手带来最可爱的一击~",
"可爱的{0}委屈你了,去和对手拥抱一下吧,不用太客气哦~",
"对手竟然没有任何防御呢,看起来是被我{0}的可爱感动了,下边你去对手面前展示一下你可爱的身姿吧~"
],
"attack": [
"哼,{1}太丑了,我家{0}快去消灭他~",
"{1}这货会有我的{0}可爱?我家{0}表示不服,去拍死它吧!",
"{1}这是竟然比我的{0}可爱?这可不行,这是我无法忍受的,{0}快让他从眼前消失吧!"
],
"ondirectattack": [
"可恶,你竟然趁人之危!算什么英雄好汉!",
"不过是{0}这种小角色而已!",
"你以为你这样就赢了吗?"
],
"facedownmonstername": "怪兽",
"activate": [
"我发动我家{0}的可爱的效果。",
"我使用{0}的效果,给对手卖个萌哈~",
"我都给{0}的可爱感动了,吓得使用了它的效果。"
],
"summon": [
"我召唤{0},快给对手喵一声~",
"出来吧,可爱又迷人的{0}~",
"我从我的宠物屋里放出了一只可爱的{0}。",
"{0}:我听说你很喜欢我,所以我来了。"
],
"setmonster": [
"我放置了一只怪兽,当他翻开时,千万别被他的可爱身姿吓到哦~",
"我放置了一只怪兽,你猜猜会是啥呢?"
],
"chaining": [
"我挥动了神奇的魔法棒,并发动了{0}的效果~",
"啊!这下死定了!...嘿嘿你上当了,我发动{0}!",
"你这太贸然了,难道你没想到我会发动{0}吗?"
]
}
{
"welcome": [
"你好,我是一个机器人。",
"AI功能正在测试中,遇到问题请及时反馈。"
],
"deckerror": [
"抱歉,我的卡组里的{0}的数量似乎出了问题。"
],
"duelstart": [
"这套卡组好旧啊……",
"使用过这套卡组的孩子身上发生了什么呢……"
],
"newturn": [
"到我的回合了,抽卡!",
"我的回合,抽卡!",
"我抽了一张卡。"
],
"endturn": [
"回合结束。",
"我的回合结束了。",
"轮到你了。"
],
"directattack": [
"{0},直接攻击!",
"{0},直接攻击对手!",
"{0},没有防守的地方,攻击!",
"{0},攻击对手的生命值!",
"{0},直接攻击对手的生命值!",
"{0},通过直接攻击打倒对手!",
"{0},使用直接攻击打倒对手!",
"{0},直接攻击释放你的力量吧!",
"我的{0}将会粉碎你的生命值!",
"向对手展示你的力量吧,{0}!",
"你已经无法阻止我了。{0},攻击!"
],
"attack": [
"{0},攻击这只{1}!",
"{0},消灭这只{1}!",
"{0},打倒{1}!",
"{0},冲向那只{1}!",
"{0},把你的力量释放到{1}上吧!"
],
"ondirectattack": [
"可恶……",
"不过是{0}而已!",
"咳咳……"
],
"facedownmonstername": "怪兽",
"activate": [
"我发动{0}。",
"我使用{0}的效果。",
"我使用{0}的力量。"
],
"summon": [
"我召唤{0}。",
"出来吧,{0}!",
"出现吧,{0}!",
"我召唤了强大的{0}!",
"我呼唤{0}参加战斗!",
"我呼唤出{0}。",
"让我召唤{0}。"
],
"setmonster": [
"我放置了一只怪兽。",
"我里侧表示放置了一只怪兽。"
],
"chaining": [
"看这里!我发动{0}!",
"我使用{0}的力量。",
"准备!我使用{0}!",
"我不认同。{0},发动!",
"看样子你忘了我的{0}!",
"你考虑过我有{0}吗?"
]
}
{
"welcome": [
"你好,我是一个机器人。",
"AI功能正在测试中,遇到问题请及时反馈。",
"新的风暴已经出现,怎么能够停滞不前!我的R5卡组也得到了加强!"
],
"deckerror": [
"抱歉,我的卡组里的{0}的数量似乎出了问题。"
],
"duelstart": [
"好戏才正要开始呢!",
"用决斗带来笑容吧!"
],
"newturn": [
"到我的回合了,抽卡!",
"我的回合,抽卡!",
"我抽了一张卡。"
],
"endturn": [
"回合结束。",
"我的回合结束了。",
"轮到你了。"
],
"directattack": [
"{0},直接攻击!",
"{0},直接攻击对手!",
"{0},没有防守的地方,攻击!",
"{0},攻击对手的生命值!",
"{0},直接攻击对手的生命值!",
"{0},通过直接攻击打倒对手!",
"{0},使用直接攻击打倒对手!",
"{0},直接攻击释放你的力量吧!",
"我的{0}将会粉碎你的生命值!",
"向对手展示你的力量吧,{0}!",
"你已经无法阻止我了。{0},攻击!"
],
"attack": [
"{0},攻击这只{1}!",
"{0},消灭这只{1}!",
"{0},打倒{1}!",
"{0},冲向那只{1}!",
"{0},把你的力量释放到{1}上吧!"
],
"ondirectattack": [
"可恶……",
"不过是{0}而已!",
"你以为这样就能打倒我吗?"
],
"facedownmonstername": "怪兽",
"activate": [
"我发动{0}。",
"我使用{0}的效果。",
"我使用{0}的力量。"
],
"summon": [
"我召唤{0}。",
"出来吧,{0}!",
"出现吧,{0}!",
"我召唤了强大的{0}!",
"我呼唤{0}参加战斗!",
"我呼唤出{0}。",
"让我召唤{0}。"
],
"setmonster": [
"我放置了一只怪兽。",
"我里侧表示放置了一只怪兽。"
],
"chaining": [
"看这里!我发动{0}!",
"我使用{0}的力量。",
"准备!我使用{0}!",
"我不认同。{0},发动!",
"看样子你忘了我的{0}!",
"你考虑过我有{0}吗?"
]
}
{
"welcome": [
"你好,我是一个机器人。",
"AI功能正在测试中,遇到问题请及时反馈。"
],
"deckerror": [
"抱歉,我的卡组里的{0}的数量似乎出了问题。"
],
"duelstart": [
"好戏才正要开始呢!",
"用决斗带来笑容吧!"
],
"newturn": [
"到我的回合了,抽卡!",
"我的回合,抽卡!",
"我抽了一张卡。"
],
"endturn": [
"回合结束。",
"我的回合结束了。",
"轮到你了。"
],
"directattack": [
"{0},直接攻击!",
"{0},直接攻击对手!",
"{0},没有防守的地方,攻击!",
"{0},攻击对手的生命值!",
"{0},直接攻击对手的生命值!",
"{0},通过直接攻击打倒对手!",
"{0},使用直接攻击打倒对手!",
"{0},直接攻击释放你的力量吧!",
"我的{0}将会粉碎你的生命值!",
"向对手展示你的力量吧,{0}!",
"你已经无法阻止我了。{0},攻击!"
],
"attack": [
"{0},攻击这只{1}!",
"{0},消灭这只{1}!",
"{0},打倒{1}!",
"{0},冲向那只{1}!",
"{0},把你的力量释放到{1}上吧!"
],
"ondirectattack": [
"可恶……",
"不过是{0}而已!",
"你以为这样就能打倒我吗?"
],
"facedownmonstername": "怪兽",
"activate": [
"我发动{0}。",
"我使用{0}的效果。",
"我使用{0}的力量。"
],
"summon": [
"我召唤{0}。",
"出来吧,{0}!",
"出现吧,{0}!",
"我召唤了强大的{0}!",
"我呼唤{0}参加战斗!",
"我呼唤出{0}。",
"让我召唤{0}。"
],
"setmonster": [
"我放置了一只怪兽。",
"我里侧表示放置了一只怪兽。"
],
"chaining": [
"看这里!我发动{0}!",
"我使用{0}的力量。",
"准备!我使用{0}!",
"我不认同。{0},发动!",
"看样子你忘了我的{0}!",
"你考虑过我有{0}吗?"
]
}
using System.Collections.Generic; using System.Collections.Generic;
using YGOSharp.OCGWrapper.Enums;
namespace WindBot.Game.AI namespace WindBot.Game.AI
{ {
public class AIFunctions public class AIFunctions
{ {
public Duel Duel { get; private set; } public Duel Duel { get; private set; }
public ClientField Bot { get; private set; }
public ClientField Enemy { get; private set; }
public AIFunctions(Duel duel) public AIFunctions(Duel duel)
{ {
Duel = duel; Duel = duel;
Bot = Duel.Fields[0];
Enemy = Duel.Fields[1];
} }
public static int CompareCardAttack(ClientCard cardA, ClientCard cardB) public static int CompareCardAttack(ClientCard cardA, ClientCard cardB)
...@@ -37,52 +42,259 @@ namespace WindBot.Game.AI ...@@ -37,52 +42,259 @@ namespace WindBot.Game.AI
return 1; return 1;
} }
public bool IsEnnemyBetter(bool onlyatk, bool all) /// <summary>
/// Get the best ATK or DEF power of the field.
/// </summary>
/// <param name="field">Bot or Enemy.</param>
/// <param name="onlyATK">Only calculate attack.</param>
public int GetBestPower(ClientField field, bool onlyATK = false)
{ {
if (Duel.Fields[1].GetMonsterCount() == 0) int bestPower = -1;
return false; for (int i = 0; i < 7; ++i)
List<ClientCard> monsters = Duel.Fields[0].GetMonsters(); {
monsters.Sort(CompareCardAttack); ClientCard card = field.MonsterZone[i];
int bestAtk = -1; if (card == null || card.Data == null) continue;
if (monsters.Count > 0) if (onlyATK && card.IsDefense()) continue;
bestAtk = monsters[monsters.Count - 1].Attack; int newPower = card.GetDefensePower();
if (all) if (newPower > bestPower)
return IsAllEnnemyBetterThanValue(bestAtk, onlyatk); bestPower = newPower;
return IsOneEnnemyBetterThanValue(bestAtk, onlyatk); }
return bestPower;
}
public int GetBestAttack(ClientField field)
{
return GetBestPower(field, true);
} }
public bool IsOneEnnemyBetterThanValue(int value, bool onlyatk) public bool IsOneEnemyBetterThanValue(int value, bool onlyATK)
{ {
int bestValue = -1; int bestValue = -1;
bool nomonster = true; bool nomonster = true;
for (int i = 0; i < 5; ++i) for (int i = 0; i < 7; ++i)
{ {
ClientCard card = Duel.Fields[1].MonsterZone[i]; ClientCard card = Enemy.MonsterZone[i];
if (card == null) continue; if (card == null || card.Data == null) continue;
if (onlyatk && card.IsDefense()) continue; if (onlyATK && card.IsDefense()) continue;
nomonster = false; nomonster = false;
int ennemyValue = card.GetDefensePower(); int enemyValue = card.GetDefensePower();
if (ennemyValue > bestValue) if (enemyValue > bestValue)
bestValue = ennemyValue; bestValue = enemyValue;
} }
if (nomonster) return false; if (nomonster) return false;
return bestValue > value; return bestValue > value;
} }
public bool IsAllEnnemyBetterThanValue(int value, bool onlyatk) public bool IsAllEnemyBetterThanValue(int value, bool onlyATK)
{ {
bool nomonster = true; bool nomonster = true;
for (int i = 0; i < 5; ++i) for (int i = 0; i < 7; ++i)
{ {
ClientCard card = Duel.Fields[1].MonsterZone[i]; ClientCard card = Enemy.MonsterZone[i];
if (card == null || card.Data == null) continue; if (card == null || card.Data == null) continue;
if (onlyatk && card.IsDefense()) continue; if (onlyATK && card.IsDefense()) continue;
nomonster = false; nomonster = false;
int ennemyValue = card.GetDefensePower(); int enemyValue = card.GetDefensePower();
if (ennemyValue <= value) if (enemyValue <= value)
return false; return false;
} }
return !nomonster; return !nomonster;
} }
/// <summary>
/// Deprecated, use IsOneEnemyBetter and IsAllEnemyBetter instead.
/// </summary>
public bool IsEnemyBetter(bool onlyATK, bool all)
{
if (all)
return IsAllEnemyBetter(onlyATK);
else
return IsOneEnemyBetter(onlyATK);
}
/// <summary>
/// Is there an enemy monster who has better power than the best power of the bot's?
/// </summary>
/// <param name="onlyATK">Only calculate attack.</param>
public bool IsOneEnemyBetter(bool onlyATK = false)
{
int bestBotPower = GetBestPower(Bot, onlyATK);
return IsOneEnemyBetterThanValue(bestBotPower, onlyATK);
}
/// <summary>
/// Do all enemy monsters have better power than the best power of the bot's?
/// </summary>
/// <param name="onlyATK">Only calculate attack.</param>
public bool IsAllEnemyBetter(bool onlyATK = false)
{
int bestBotPower = GetBestPower(Bot, onlyATK);
return IsAllEnemyBetterThanValue(bestBotPower, onlyATK);
}
public ClientCard GetOneEnemyBetterThanValue(int value, bool onlyATK = false)
{
ClientCard bestCard = null;
int bestValue = value;
for (int i = 0; i < 7; ++i)
{
ClientCard card = Enemy.MonsterZone[i];
if (card == null || card.Data == null) continue;
if (onlyATK && card.IsDefense()) continue;
int enemyValue = card.GetDefensePower();
if (enemyValue >= bestValue)
{
bestCard = card;
bestValue = enemyValue;
}
}
return bestCard;
}
public ClientCard GetOneEnemyBetterThanMyBest(bool onlyATK = false)
{
int bestBotPower = GetBestPower(Bot, onlyATK);
return GetOneEnemyBetterThanValue(bestBotPower, onlyATK);
}
public ClientCard GetProblematicEnemyCard(int attack = 0)
{
ClientCard card = Enemy.MonsterZone.GetFloodgate();
if (card != null)
return card;
card = Enemy.SpellZone.GetFloodgate();
if (card != null)
return card;
card = Enemy.MonsterZone.GetDangerousMonster();
if (card != null)
return card;
card = Enemy.MonsterZone.GetInvincibleMonster();
if (card != null)
return card;
if (attack == 0)
attack = GetBestAttack(Bot);
return GetOneEnemyBetterThanValue(attack, true);
}
public ClientCard GetProblematicEnemyMonster(int attack = 0)
{
ClientCard card = Enemy.MonsterZone.GetFloodgate();
if (card != null)
return card;
card = Enemy.MonsterZone.GetDangerousMonster();
if (card != null)
return card;
card = Enemy.MonsterZone.GetInvincibleMonster();
if (card != null)
return card;
if (attack == 0)
attack = GetBestAttack(Bot);
return GetOneEnemyBetterThanValue(attack, true);
}
public ClientCard GetProblematicEnemySpell()
{
ClientCard card = Enemy.SpellZone.GetFloodgate();
return card;
}
public ClientCard GetBestEnemyCard(bool onlyFaceup = false)
{
ClientCard card = GetBestEnemyMonster(onlyFaceup);
if (card != null)
return card;
card = GetBestEnemySpell(onlyFaceup);
if (card != null)
return card;
return null;
}
public ClientCard GetBestEnemyMonster(bool onlyFaceup = false)
{
ClientCard card = GetProblematicEnemyMonster();
if (card != null)
return card;
card = Enemy.MonsterZone.GetHighestAttackMonster();
if (card != null)
return card;
List<ClientCard> monsters = Enemy.GetMonsters();
// after GetHighestAttackMonster, the left monsters must be face-down.
if (monsters.Count > 0 && !onlyFaceup)
return monsters[0];
return null;
}
public ClientCard GetBestEnemySpell(bool onlyFaceup = false)
{
ClientCard card = GetProblematicEnemySpell();
if (card != null)
return card;
List<ClientCard> spells = Enemy.GetSpells();
foreach (ClientCard ecard in spells)
{
if (ecard.IsFaceup())
return ecard;
}
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 bool IsChainTarget(ClientCard card)
{
foreach (ClientCard target in Duel.ChainTargets)
{
if (card.Equals(target))
{
return true;
}
}
return false;
}
public bool IsChainTargetOnly(ClientCard card)
{
return Duel.ChainTargets.Count == 1 && card.Equals(Duel.ChainTargets[0]);
}
} }
} }
\ No newline at end of file
using System.Collections.Generic; using System.Collections.Generic;
using YGOSharp.OCGWrapper.Enums; using YGOSharp.OCGWrapper.Enums;
using System.Linq;
namespace WindBot.Game.AI namespace WindBot.Game.AI
{ {
...@@ -11,7 +12,7 @@ namespace WindBot.Game.AI ...@@ -11,7 +12,7 @@ namespace WindBot.Game.AI
ClientCard selected = null; ClientCard selected = null;
foreach (ClientCard card in cards) foreach (ClientCard card in cards)
{ {
if (card == null || card.Data == null) continue; if (card == null || card.Data == null || card.IsFacedown()) continue;
if (card.HasType(CardType.Monster) && card.Attack > highestAtk) if (card.HasType(CardType.Monster) && card.Attack > highestAtk)
{ {
highestAtk = card.Attack; highestAtk = card.Attack;
...@@ -27,7 +28,7 @@ namespace WindBot.Game.AI ...@@ -27,7 +28,7 @@ namespace WindBot.Game.AI
ClientCard selected = null; ClientCard selected = null;
foreach (ClientCard card in cards) foreach (ClientCard card in cards)
{ {
if (card == null || card.Data == null) continue; if (card == null || card.Data == null || card.IsFacedown()) continue;
if (card.HasType(CardType.Monster) && card.Defense > highestDef) if (card.HasType(CardType.Monster) && card.Defense > highestDef)
{ {
highestDef = card.Defense; highestDef = card.Defense;
...@@ -43,7 +44,7 @@ namespace WindBot.Game.AI ...@@ -43,7 +44,7 @@ namespace WindBot.Game.AI
ClientCard selected = null; ClientCard selected = null;
foreach (ClientCard card in cards) foreach (ClientCard card in cards)
{ {
if (card == null || card.Data == null) continue; if (card == null || card.Data == null || card.IsFacedown()) continue;
if (lowestAtk == 0 && card.HasType(CardType.Monster) || if (lowestAtk == 0 && card.HasType(CardType.Monster) ||
card.HasType(CardType.Monster) && card.Attack < lowestAtk) card.HasType(CardType.Monster) && card.Attack < lowestAtk)
{ {
...@@ -60,7 +61,7 @@ namespace WindBot.Game.AI ...@@ -60,7 +61,7 @@ namespace WindBot.Game.AI
ClientCard selected = null; ClientCard selected = null;
foreach (ClientCard card in cards) foreach (ClientCard card in cards)
{ {
if (card == null || card.Data == null) continue; if (card == null || card.Data == null || card.IsFacedown()) continue;
if (lowestDef == 0 && card.HasType(CardType.Monster) || if (lowestDef == 0 && card.HasType(CardType.Monster) ||
card.HasType(CardType.Monster) && card.Defense < lowestDef) card.HasType(CardType.Monster) && card.Defense < lowestDef)
{ {
...@@ -120,9 +121,9 @@ namespace WindBot.Game.AI ...@@ -120,9 +121,9 @@ namespace WindBot.Game.AI
return count; return count;
} }
public static IList<ClientCard> GetMonsters(this IEnumerable<ClientCard> cards) public static List<ClientCard> GetMonsters(this IEnumerable<ClientCard> cards)
{ {
IList<ClientCard> cardlist = new List<ClientCard>(); List<ClientCard> cardlist = new List<ClientCard>();
foreach (ClientCard card in cards) foreach (ClientCard card in cards)
{ {
...@@ -130,7 +131,20 @@ namespace WindBot.Game.AI ...@@ -130,7 +131,20 @@ namespace WindBot.Game.AI
continue; continue;
if (card.HasType(CardType.Monster)) if (card.HasType(CardType.Monster))
cardlist.Add(card); cardlist.Add(card);
}
return cardlist;
}
public static List<ClientCard> GetFaceupPendulumMonsters(this IEnumerable<ClientCard> cards)
{
List<ClientCard> cardlist = new List<ClientCard>();
foreach (ClientCard card in cards)
{
if (card == null)
continue;
if (card.HasType(CardType.Monster) && card.IsFaceup() && card.HasType(CardType.Pendulum))
cardlist.Add(card);
} }
return cardlist; return cardlist;
} }
...@@ -139,20 +153,37 @@ namespace WindBot.Game.AI ...@@ -139,20 +153,37 @@ namespace WindBot.Game.AI
{ {
foreach (ClientCard card in cards) foreach (ClientCard card in cards)
{ {
if (card != null && card.IsMonsterInvincible()) if (card != null && card.IsMonsterInvincible() && card.IsFaceup())
return card; return card;
} }
return null; return null;
} }
public static ClientCard GetNegateAttackSpell(this IEnumerable<ClientCard> cards) public static ClientCard GetDangerousMonster(this IEnumerable<ClientCard> cards)
{ {
foreach (ClientCard card in cards) foreach (ClientCard card in cards)
{ {
if (card != null && card.IsSpellNegateAttack()) if (card != null && card.IsMonsterDangerous() && card.IsFaceup())
return card; return card;
} }
return null; return null;
} }
public static ClientCard GetFloodgate(this IEnumerable<ClientCard> cards)
{
foreach (ClientCard card in cards)
{
if (card != null && card.IsFloodgate() && card.IsFaceup())
return card;
}
return null;
}
public static IEnumerable<IEnumerable<T>> GetCombinations<T>(this IEnumerable<T> 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
...@@ -5,19 +5,43 @@ namespace WindBot.Game.AI ...@@ -5,19 +5,43 @@ namespace WindBot.Game.AI
{ {
public static class CardExtension public static class CardExtension
{ {
/// <summary>
/// Is this monster is invincible to battle?
/// </summary>
public static bool IsMonsterInvincible(this ClientCard card) public static bool IsMonsterInvincible(this ClientCard card)
{ {
return Enum.IsDefined(typeof(InvincibleMonster), card.Id); return !card.IsDisabled() && Enum.IsDefined(typeof(InvincibleMonster), card.Id);
} }
/// <summary>
/// Is this monster is dangerous to attack?
/// </summary>
public static bool IsMonsterDangerous(this ClientCard card) public static bool IsMonsterDangerous(this ClientCard card)
{ {
return Enum.IsDefined(typeof(DangerousMonster), card.Id); return !card.IsDisabled() && Enum.IsDefined(typeof(DangerousMonster), card.Id);
} }
public static bool IsSpellNegateAttack(this ClientCard card) /// <summary>
/// Do this monster prevents activation of opponent's effect monsters in battle?
/// </summary>
public static bool IsMonsterHasPreventActivationEffectInBattle(this ClientCard card)
{ {
return Enum.IsDefined(typeof(NegateAttackSpell), card.Id); return !card.IsDisabled() && Enum.IsDefined(typeof(PreventActivationEffectInBattle), 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
...@@ -7,14 +7,16 @@ namespace WindBot.Game.AI ...@@ -7,14 +7,16 @@ namespace WindBot.Game.AI
{ {
public string Name { get; private set; } public string Name { get; private set; }
public string File { get; private set; } public string File { get; private set; }
public string Level { get; private set; }
public DeckAttribute(string name, string file = null) public DeckAttribute(string name, string file = null, string level = "Normal")
{ {
if (String.IsNullOrEmpty(file)) if (String.IsNullOrEmpty(file))
file = name; file = name;
Name = name; Name = name;
File = file; File = file;
Level = level;
} }
} }
} }
...@@ -10,11 +10,13 @@ namespace WindBot.Game.AI ...@@ -10,11 +10,13 @@ namespace WindBot.Game.AI
{ {
public string Deck { get; private set; } public string Deck { get; private set; }
public Type Type { get; private set; } public Type Type { get; private set; }
public string Level { get; private set; }
public DeckInstance(string deck, Type type) public DeckInstance(string deck, Type type, string level)
{ {
Deck = deck; Deck = deck;
Type = type; Type = type;
Level = level;
} }
} }
...@@ -38,7 +40,7 @@ namespace WindBot.Game.AI ...@@ -38,7 +40,7 @@ namespace WindBot.Game.AI
{ {
if (attribute is DeckAttribute deck) if (attribute is DeckAttribute deck)
{ {
_decks.Add(deck.Name, new DeckInstance(deck.File, type)); _decks.Add(deck.Name, new DeckInstance(deck.File, type, deck.Level));
} }
} }
} }
...@@ -58,7 +60,13 @@ namespace WindBot.Game.AI ...@@ -58,7 +60,13 @@ namespace WindBot.Game.AI
if (deck != null && _decks.ContainsKey(deck)) if (deck != null && _decks.ContainsKey(deck))
infos = _decks[deck]; infos = _decks[deck];
else else
{
do
{
infos = _list[_rand.Next(_list.Count)]; infos = _list[_rand.Next(_list.Count)];
}
while (infos.Level != "Normal");
}
Executor executor = (Executor)Activator.CreateInstance(infos.Type, ai, duel); Executor executor = (Executor)Activator.CreateInstance(infos.Type, ai, duel);
executor.Deck = infos.Deck; executor.Deck = infos.Deck;
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using YGOSharp.OCGWrapper.Enums; using YGOSharp.OCGWrapper.Enums;
using WindBot;
using WindBot.Game;
using WindBot.Game.AI;
namespace WindBot.Game.AI namespace WindBot.Game.AI
{ {
public abstract class DefaultExecutor : Executor public abstract class DefaultExecutor : Executor
{ {
protected class _CardId
{
public const int JizukirutheStarDestroyingKaiju = 63941210;
public const int GadarlatheMysteryDustKaiju = 36956512;
public const int GamecieltheSeaTurtleKaiju = 55063751;
public const int RadiantheMultidimensionalKaiju = 28674152;
public const int KumongoustheStickyStringKaiju = 29726552;
public const int ThunderKingtheLightningstrikeKaiju = 48770333;
public const int DogorantheMadFlameKaiju = 93332803;
public const int SuperAntiKaijuWarMachineMechaDogoran = 84769941;
public const int DupeFrog = 46239604;
public const int MaraudingCaptain = 2460565;
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;
}
protected DefaultExecutor(GameAI ai, Duel duel) protected DefaultExecutor(GameAI ai, Duel duel)
: base(ai, duel) : base(ai, duel)
{ {
AddExecutor(ExecutorType.Activate, _CardId.ChickenGame, DefaultChickenGame);
}
/// <summary>
/// Decide whether to declare attack between attacker and defender.
/// Can be overrided to update the RealPower of attacker for cards like Honest.
/// </summary>
/// <param name="attacker">Card that attack.</param>
/// <param name="defender">Card that defend.</param>
/// <returns>false if the attack can't be done.</returns>
public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender)
{
if (attacker.RealPower <= 0)
return false;
if (!attacker.IsMonsterHasPreventActivationEffectInBattle())
{
if (defender.IsMonsterDangerous() || (defender.IsMonsterInvincible() && defender.IsDefense()))
return false;
if (defender.Id == _CardId.CrystalWingSynchroDragon && !defender.IsDisabled() && attacker.Level >= 5)
return false;
if (defender.Id == _CardId.NumberS39UtopiaTheLightning && !defender.IsDisabled() && defender.HasXyzMaterial(2, _CardId.Number39Utopia))
defender.RealPower = 5000;
} }
private enum CardId if (!defender.IsMonsterHasPreventActivationEffectInBattle())
{ {
MysticalSpaceTyphoon = 5318639 if (attacker.Id == _CardId.NumberS39UtopiaTheLightning && !attacker.IsDisabled() && attacker.HasXyzMaterial(2, _CardId.Number39Utopia))
attacker.RealPower = 5000;
} }
if (Enemy.HasInMonstersZone(_CardId.DupeFrog, true) && defender.Id != _CardId.DupeFrog)
return false;
if (Enemy.HasInMonstersZone(_CardId.MaraudingCaptain, true) && defender.Id != _CardId.MaraudingCaptain && defender.Race == (int)CardRace.Warrior)
return false;
if (defender.Id == _CardId.UltimayaTzolkin && !defender.IsDisabled())
{
List<ClientCard> monsters = Enemy.GetMonsters();
foreach (ClientCard monster in monsters)
{
if (monster.HasType(CardType.Synchro))
return false;
}
}
return true;
}
/// <summary>
/// Destroy face-down cards first, in our turn.
/// </summary>
protected bool DefaultMysticalSpaceTyphoon() protected bool DefaultMysticalSpaceTyphoon()
{ {
foreach (ClientCard card in CurrentChain) foreach (ClientCard card in CurrentChain)
if (card.Id == (int)CardId.MysticalSpaceTyphoon) if (card.Id == _CardId.MysticalSpaceTyphoon)
return false;
List<ClientCard> spells = Enemy.GetSpells();
if (spells.Count == 0)
return false; return false;
return DefaultStampingDestruction(); ClientCard selected = Enemy.SpellZone.GetFloodgate();
if (selected == null)
{
foreach (ClientCard card in spells)
{
if (Duel.Player == 1 && !card.HasType(CardType.Continuous))
continue;
selected = card;
if (Duel.Player == 0 && card.IsFacedown())
break;
}
}
if (selected == null)
return false;
AI.SelectCard(selected);
return true;
}
/// <summary>
/// Destroy face-down cards first, in our turn.
/// </summary>
protected bool DefaultCosmicCyclone()
{
foreach (ClientCard card in CurrentChain)
if (card.Id == _CardId.CosmicCyclone)
return false;
return (Duel.LifePoints[0] > 1000) && DefaultMysticalSpaceTyphoon();
} }
protected bool DefaultStampingDestruction() /// <summary>
/// Activate if avail.
/// </summary>
protected bool DefaultGalaxyCyclone()
{ {
List<ClientCard> spells = Duel.Fields[1].GetSpells(); List<ClientCard> spells = Enemy.GetSpells();
if (spells.Count == 0) if (spells.Count == 0)
return false; return false;
ClientCard selected = null; ClientCard selected = null;
if (Card.Location == CardLocation.Grave)
{
selected = Enemy.SpellZone.GetFloodgate();
if (selected == null)
{
foreach (ClientCard card in spells) foreach (ClientCard card in spells)
{ {
if (card.IsSpellNegateAttack()) if (!card.IsFacedown())
{ {
selected = card; selected = card;
break; break;
} }
} }
}
if (selected == null) }
else
{ {
foreach (ClientCard card in spells) foreach (ClientCard card in spells)
{ {
if (Duel.Player == 1 && !card.HasType(CardType.Continuous)) if (card.IsFacedown())
continue; {
selected = card; selected = card;
if (Duel.Player == 0 && card.IsFacedown())
break; break;
} }
} }
}
if (selected == null) if (selected == null)
return false; return false;
AI.SelectCard(selected); AI.SelectCard(selected);
return true; return true;
} }
/// <summary>
/// Set the highest ATK level 4+ effect enemy monster.
/// </summary>
protected bool DefaultBookOfMoon() protected bool DefaultBookOfMoon()
{ {
if (AI.Utils.IsEnnemyBetter(true, true)) if (AI.Utils.IsAllEnemyBetter(true))
{ {
ClientCard monster = Duel.Fields[1].GetMonsters().GetHighestAttackMonster(); ClientCard monster = Enemy.GetMonsters().GetHighestAttackMonster();
if (monster != null && monster.HasType(CardType.Effect) && (monster.HasType(CardType.Xyz) || monster.Level > 4)) if (monster != null && monster.HasType(CardType.Effect) && (monster.HasType(CardType.Xyz) || monster.Level > 4))
{ {
AI.SelectCard(monster); AI.SelectCard(monster);
...@@ -73,84 +196,561 @@ namespace WindBot.Game.AI ...@@ -73,84 +196,561 @@ namespace WindBot.Game.AI
return false; return false;
} }
/// <summary>
/// Return problematic monster, and if this card become target, return any enemy monster.
/// </summary>
protected bool DefaultCompulsoryEvacuationDevice()
{
ClientCard target = AI.Utils.GetProblematicEnemyMonster();
if (target != null)
{
AI.SelectCard(target);
return true;
}
if (AI.Utils.IsChainTarget(Card))
{
ClientCard monster = AI.Utils.GetBestEnemyMonster();
if (monster != null)
{
AI.SelectCard(monster);
return true;
}
}
return false;
}
/// <summary>
/// Revive the best monster when we don't have better one in field.
/// </summary>
protected bool DefaultCallOfTheHaunted()
{
if (!AI.Utils.IsAllEnemyBetter(true))
return false;
ClientCard selected = null;
int BestAtk = 0;
foreach (ClientCard card in Bot.Graveyard)
{
if (card.Attack > BestAtk)
{
BestAtk = card.Attack;
selected = card;
}
}
AI.SelectCard(selected);
return true;
}
/// <summary>
/// Chain the enemy monster.
/// </summary>
protected bool DefaultBreakthroughSkill()
{
ClientCard LastChainCard = GetLastChainCard();
if (LastChainCard == null)
return false;
return LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.MonsterZone && DefaultUniqueTrap();
}
/// <summary>
/// Activate only except this card is the target or we summon monsters.
/// </summary>
protected bool DefaultSolemnJudgment()
{
return !AI.Utils.IsChainTargetOnly(Card) && !(Duel.Player == 0 && LastChainPlayer == -1) && DefaultTrap();
}
/// <summary>
/// Activate only except we summon monsters.
/// </summary>
protected bool DefaultSolemnWarning()
{
return (Duel.LifePoints[0] > 2000) && !(Duel.Player == 0 && LastChainPlayer == -1) && DefaultTrap();
}
/// <summary>
/// Activate only except we summon monsters.
/// </summary>
protected bool DefaultSolemnStrike()
{
return (Duel.LifePoints[0] > 1500) && !(Duel.Player == 0 && LastChainPlayer == -1) && DefaultTrap();
}
/// <summary>
/// Activate when all enemy monsters have better ATK.
/// </summary>
protected bool DefaultTorrentialTribute() protected bool DefaultTorrentialTribute()
{ {
return (AI.Utils.IsEnnemyBetter(true, true)); return !HasChainedTrap(0) && AI.Utils.IsAllEnemyBetter(true);
} }
/// <summary>
/// Activate enemy have more S&T.
/// </summary>
protected bool DefaultHeavyStorm() protected bool DefaultHeavyStorm()
{ {
return Duel.Fields[0].GetSpellCount() < Duel.Fields[1].GetSpellCount(); return Bot.GetSpellCount() < Enemy.GetSpellCount();
} }
/// <summary>
/// Activate before other winds, if enemy have more than 2 S&T.
/// </summary>
protected bool DefaultHarpiesFeatherDusterFirst()
{
return Enemy.GetSpellCount() >= 2;
}
/// <summary>
/// Activate when one enemy monsters have better ATK.
/// </summary>
protected bool DefaultHammerShot() protected bool DefaultHammerShot()
{ {
return AI.Utils.IsEnnemyBetter(true, false); return AI.Utils.IsOneEnemyBetter(true);
} }
/// <summary>
/// Activate when one enemy monsters have better ATK or DEF.
/// </summary>
protected bool DefaultDarkHole() protected bool DefaultDarkHole()
{ {
return AI.Utils.IsEnnemyBetter(false, false); return AI.Utils.IsOneEnemyBetter();
} }
/// <summary>
/// Activate when one enemy monsters have better ATK or DEF.
/// </summary>
protected bool DefaultRaigeki() protected bool DefaultRaigeki()
{ {
return AI.Utils.IsEnnemyBetter(false, false); return AI.Utils.IsOneEnemyBetter();
}
/// <summary>
/// Activate when one enemy monsters have better ATK or DEF.
/// </summary>
protected bool DefaultSmashingGround()
{
return AI.Utils.IsOneEnemyBetter();
}
/// <summary>
/// Activate when we have more than 15 cards in deck.
/// </summary>
protected bool DefaultPotOfDesires()
{
return Bot.Deck.Count > 15;
} }
/// <summary>
/// Set traps only and avoid block the activation of other cards.
/// </summary>
protected bool DefaultSpellSet() protected bool DefaultSpellSet()
{ {
return Card.IsTrap() && Duel.Fields[0].GetSpellCountWithoutField() < 4; return (Card.IsTrap() || Card.HasType(CardType.QuickPlay)) && Bot.GetSpellCountWithoutField() < 4;
} }
/// <summary>
/// Summon with tributes ATK lower.
/// </summary>
protected bool DefaultTributeSummon() protected bool DefaultTributeSummon()
{ {
if (!UniqueFaceupMonster())
return false;
int tributecount = (int)Math.Ceiling((Card.Level - 4.0d) / 2.0d); int tributecount = (int)Math.Ceiling((Card.Level - 4.0d) / 2.0d);
for (int j = 0; j < 5; ++j) for (int j = 0; j < 7; ++j)
{ {
ClientCard tributeCard = Duel.Fields[0].MonsterZone[j]; ClientCard tributeCard = Bot.MonsterZone[j];
if (tributeCard == null) continue; if (tributeCard == null) continue;
if (tributeCard.Attack < Card.Attack) if (tributeCard.GetDefensePower() < Card.Attack)
tributecount--; tributecount--;
} }
return tributecount <= 0; return tributecount <= 0;
} }
/// <summary>
/// Activate when we have no field.
/// </summary>
protected bool DefaultField() protected bool DefaultField()
{ {
return Duel.Fields[0].SpellZone[5] == null; return Bot.SpellZone[5] == null;
} }
/// <summary>
/// Turn if all enemy is better.
/// </summary>
protected bool DefaultMonsterRepos() protected bool DefaultMonsterRepos()
{ {
bool ennemyBetter = AI.Utils.IsEnnemyBetter(true, true); bool enemyBetter = AI.Utils.IsAllEnemyBetter(true);
if (Card.IsAttack() && ennemyBetter) if (Card.IsAttack() && enemyBetter)
return true; return true;
if (Card.IsDefense() && !ennemyBetter && Card.Attack >= Card.Defense) if (Card.IsDefense() && !enemyBetter && Card.Attack >= Card.Defense)
return true; return true;
return false; return false;
} }
/// <summary>
/// Chain enemy activation or summon.
/// </summary>
protected bool DefaultTrap() protected bool DefaultTrap()
{ {
return LastChainPlayer == -1 || LastChainPlayer == 1; return (LastChainPlayer == -1 && Duel.LastSummonPlayer != 0) || LastChainPlayer == 1;
} }
/// <summary>
/// Activate when avail and no other our trap card in this chain or face-up.
/// </summary>
protected bool DefaultUniqueTrap() protected bool DefaultUniqueTrap()
{ {
if (HasChainedTrap(0)) if (HasChainedTrap(0))
return false; return false;
foreach (ClientCard card in Duel.Fields[0].SpellZone) return UniqueFaceupSpell();
}
/// <summary>
/// Check no other our spell or trap card with same name face-up.
/// </summary>
protected bool UniqueFaceupSpell()
{ {
if (card != null && foreach (ClientCard card in Bot.GetSpells())
card.Id == Card.Id && {
card.HasPosition(CardPosition.FaceUp)) if (card.Id == Card.Id && card.IsFaceup())
return false; return false;
} }
return true;
}
/// <summary>
/// Check no other our monster card with same name face-up.
/// </summary>
protected bool UniqueFaceupMonster()
{
foreach (ClientCard card in Bot.GetMonsters())
{
if (card.Id == Card.Id && card.IsFaceup())
return false;
}
return true; return true;
} }
/// <summary>
/// Dumb way to avoid the bot chain in mess.
/// </summary>
protected bool DefaultDontChainMyself()
{
foreach (CardExecutor exec in Executors)
{
if (exec.Type == Type && exec.CardId == Card.Id)
return false;
}
return LastChainPlayer != 0;
}
/// <summary>
/// Draw when we have lower LP, or destroy it. Can be overrided.
/// </summary>
protected bool DefaultChickenGame()
{
int count = 0;
foreach (CardExecutor exec in Executors)
{
if (exec.Type == Type && exec.CardId == Card.Id)
count++;
}
if (count > 1 || Duel.LifePoints[0] <= 1000)
return false;
if (Duel.LifePoints[0] <= Duel.LifePoints[1] && ActivateDescription == AI.Utils.GetStringId(_CardId.ChickenGame, 0))
return true;
if (Duel.LifePoints[0] > Duel.LifePoints[1] && ActivateDescription == AI.Utils.GetStringId(_CardId.ChickenGame, 1))
return true;
return false;
}
/// <summary>
/// Clever enough.
/// </summary>
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<ClientCard> 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))
nontuner = true;
if (monster.IsOneForXyz())
{
AI.SelectOption(XYZ);
return true;
}
levels[monster.Level] = levels[monster.Level] + 1;
}
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 = GetLastChainCard();
if (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 (AI.Utils.IsChainTarget(Card))
{
AI.SelectOption(XYZ);
return true;
}
return false;
}
/// <summary>
/// Clever enough.
/// </summary>
protected bool DefaultInterruptedKaijuSlumber()
{
if (Card.Location == CardLocation.Grave)
{
AI.SelectCard(new[]
{
_CardId.GamecieltheSeaTurtleKaiju,
_CardId.KumongoustheStickyStringKaiju,
_CardId.RadiantheMultidimensionalKaiju,
_CardId.GadarlatheMysteryDustKaiju
});
return true;
}
AI.SelectCard(new[]
{
_CardId.JizukirutheStarDestroyingKaiju,
_CardId.RadiantheMultidimensionalKaiju,
_CardId.GadarlatheMysteryDustKaiju,
_CardId.KumongoustheStickyStringKaiju
});
AI.SelectNextCard(new[]
{
_CardId.GamecieltheSeaTurtleKaiju,
_CardId.KumongoustheStickyStringKaiju,
_CardId.GadarlatheMysteryDustKaiju,
_CardId.RadiantheMultidimensionalKaiju
});
return DefaultDarkHole();
}
/// <summary>
/// Clever enough.
/// </summary>
protected bool DefaultKaijuSpsummon()
{
IList<int> 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 (kaijus.Contains(monster.Id))
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 = AI.Utils.GetOneEnemyBetterThanValue(Card.GetDefensePower());
if (card != null)
{
AI.SelectCard(card);
return true;
}
return false;
}
/// <summary>
/// Summon when we don't have monster attack higher than enemy's.
/// </summary>
protected bool DefaultNumberS39UtopiaTheLightningSummon()
{
int bestBotAttack = AI.Utils.GetBestAttack(Bot);
return AI.Utils.IsOneEnemyBetterThanValue(bestBotAttack, false);
}
/// <summary>
/// Summon when it can and should use effect.
/// </summary>
protected bool DefaultEvilswarmExcitonKnightSummon()
{
int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount() + Bot.GetHandCount();
int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount() + Enemy.GetHandCount();
return (selfCount - 1 < oppoCount) && DefaultEvilswarmExcitonKnightEffect();
}
/// <summary>
/// Activate when we have less cards than enemy's, or the atk sum of we is lower than enemy's.
/// </summary>
protected bool DefaultEvilswarmExcitonKnightEffect()
{
int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount();
int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount();
if (selfCount < oppoCount)
return true;
int selfAttack = 0;
List<ClientCard> monsters = Bot.GetMonsters();
foreach (ClientCard monster in monsters)
{
selfAttack += monster.GetDefensePower();
}
int oppoAttack = 0;
monsters = Enemy.GetMonsters();
foreach (ClientCard monster in monsters)
{
oppoAttack += monster.GetDefensePower();
}
return selfAttack < oppoAttack;
}
/// <summary>
/// Summon in main2, or when the attack of we is lower than enemy's, but not when enemy have monster higher than 2500.
/// </summary>
protected bool DefaultStardustDragonSummon()
{
int selfBestAttack = AI.Utils.GetBestAttack(Bot);
int oppoBestAttack = AI.Utils.GetBestPower(Enemy);
return (selfBestAttack <= oppoBestAttack && oppoBestAttack <= 2500) || AI.Utils.IsTurn1OrMain2();
}
/// <summary>
/// Negate enemy's destroy effect, and revive from grave.
/// </summary>
protected bool DefaultStardustDragonEffect()
{
return (Card.Location == CardLocation.Grave) || LastChainPlayer == 1;
}
/// <summary>
/// Summon when enemy have card which we must solve.
/// </summary>
protected bool DefaultCastelTheSkyblasterMusketeerSummon()
{
return AI.Utils.GetProblematicEnemyCard() != null;
}
/// <summary>
/// Bounce the problematic enemy card. Ignore the 1st effect.
/// </summary>
protected bool DefaultCastelTheSkyblasterMusketeerEffect()
{
if (ActivateDescription == AI.Utils.GetStringId(_CardId.CastelTheSkyblasterMusketeer, 0))
return false;
ClientCard target = AI.Utils.GetProblematicEnemyCard();
if (target != null)
{
AI.SelectNextCard(target);
return true;
}
return false;
}
/// <summary>
/// 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.
/// </summary>
protected bool DefaultScarlightRedDragonArchfiendSummon()
{
int selfBestAttack = AI.Utils.GetBestAttack(Bot);
int oppoBestAttack = AI.Utils.GetBestPower(Enemy);
return (selfBestAttack <= oppoBestAttack && oppoBestAttack <= 3000) || DefaultScarlightRedDragonArchfiendEffect();
}
/// <summary>
/// Activate when we have less monsters than enemy, or when enemy have more than 3 monsters.
/// </summary>
protected bool DefaultScarlightRedDragonArchfiendEffect()
{
int selfCount = 0;
List<ClientCard> monsters = Bot.GetMonsters();
foreach (ClientCard monster in monsters)
{
// The bot don't know if the card is special summoned, so let we assume all monsters are special summoned
if (!monster.Equals(Card) && monster.HasType(CardType.Effect) && monster.Attack <= Card.Attack)
selfCount++;
}
int oppoCount = 0;
monsters = Enemy.GetMonsters();
foreach (ClientCard monster in monsters)
{
if (monster.HasType(CardType.Effect) && monster.Attack <= Card.Attack)
oppoCount++;
}
return (oppoCount > 0 && selfCount <= oppoCount) || oppoCount >= 3;
}
} }
} }
using System.Collections.Generic; using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
namespace WindBot.Game.AI namespace WindBot.Game.AI
{ {
[DataContract]
public class DialogsData
{
[DataMember]
public string[] welcome { get; set; }
[DataMember]
public string[] deckerror { get; set; }
[DataMember]
public string[] duelstart { get; set; }
[DataMember]
public string[] newturn { get; set; }
[DataMember]
public string[] endturn { get; set; }
[DataMember]
public string[] directattack { get; set; }
[DataMember]
public string[] attack { get; set; }
[DataMember]
public string[] ondirectattack { get; set; }
[DataMember]
public string facedownmonstername { get; set; }
[DataMember]
public string[] activate { get; set; }
[DataMember]
public string[] summon { get; set; }
[DataMember]
public string[] setmonster { get; set; }
[DataMember]
public string[] chaining { get; set; }
}
public class Dialogs public class Dialogs
{ {
private GameClient _game; private GameClient _game;
private string[] _welcome;
private string[] _deckerror;
private string[] _duelstart; private string[] _duelstart;
private string[] _newturn; private string[] _newturn;
private string[] _endturn; private string[] _endturn;
private string[] _directattack; private string[] _directattack;
private string[] _attack; private string[] _attack;
private string[] _ondirectattack;
private string _facedownmonstername;
private string[] _activate; private string[] _activate;
private string[] _summon; private string[] _summon;
private string[] _setmonster; private string[] _setmonster;
...@@ -19,75 +57,43 @@ namespace WindBot.Game.AI ...@@ -19,75 +57,43 @@ namespace WindBot.Game.AI
public Dialogs(GameClient game) public Dialogs(GameClient game)
{ {
_game = game; _game = game;
_duelstart = new[] DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DialogsData));
{ string dialogfilename = game.Dialog;
"Good luck, have fun." using (FileStream fs = File.OpenRead("Dialogs/" + dialogfilename + ".json"))
}; {
_newturn = new[] DialogsData data = (DialogsData)serializer.ReadObject(fs);
{ _welcome = data.welcome;
"It's my turn, draw.", _deckerror = data.deckerror;
"My turn, draw.", _duelstart = data.duelstart;
"I draw a card." _newturn = data.newturn;
}; _endturn = data.endturn;
_endturn = new[] _directattack = data.directattack;
{ _attack = data.attack;
"I end my turn.", _ondirectattack = data.ondirectattack;
"My turn is over.", _facedownmonstername = data.facedownmonstername;
"Your turn." _activate = data.activate;
}; _summon = data.summon;
_directattack = new[] _setmonster = data.setmonster;
{ _chaining = data.chaining;
"{0}, direct attack!", }
"{0}, attack him directly!", }
"{0}, he's defenseless, attack!",
"{0}, attack his life points!", public void SendSorry()
"{0}, attack his life points directly!", {
"{0}, attack him through a direct attack!", InternalSendMessage(new[] { "Sorry, an error occurs." });
"{0}, attack him using a direct attack!", }
"{0}, unleash your power through a direct attack!",
"My {0} is going to smash your life points!", public void SendDeckSorry(string card)
"Show your power to my opponent, {0}!", {
"You can't stop me. {0}, attack!" if (card == "DECK")
}; InternalSendMessage(new[] { "Deck illegal. Please check the database of your YGOPro and WindBot." });
_attack = new[] else
{ InternalSendMessage(_deckerror, card);
"{0}, attack this {1}!", }
"{0}, destroy this {1}!",
"{0}, charge the {1}!", public void SendWelcome()
"{0}, strike that {1}!", {
"{0}, unleash your power on this {1}!" InternalSendMessage(_welcome);
};
_activate = new[]
{
"I'm activating {0}.",
"I'm using the effect of {0}.",
"I use the power of {0}."
};
_summon = new[]
{
"I'm summoning {0}.",
"Come on, {0}!",
"Appear, {0}!",
"I summon the powerful {0}.",
"I call {0} to the battle!",
"I'm calling {0}.",
"Let's summon {0}."
};
_setmonster = new[]
{
"I'm setting a monster.",
"I set a face-down monster.",
"I place a hidden monster."
};
_chaining = new[]
{
"Look at that! I'm activating {0}.",
"I use the power of {0}.",
"Get ready! I use {0}.",
"I don't think so. {0}, activation!",
"Looks like you forgot my {0}.",
"Did you consider the fact I have {0}?"
};
} }
public void SendDuelStart() public void SendDuelStart()
...@@ -112,9 +118,26 @@ namespace WindBot.Game.AI ...@@ -112,9 +118,26 @@ namespace WindBot.Game.AI
public void SendAttack(string attacker, string defender) public void SendAttack(string attacker, string defender)
{ {
if (defender=="monster")
{
defender = _facedownmonstername;
}
InternalSendMessage(_attack, attacker, defender); InternalSendMessage(_attack, attacker, defender);
} }
public void SendOnDirectAttack(string attacker)
{
if (attacker == "" || attacker == null)
{
attacker = _facedownmonstername;
}
InternalSendMessage(_ondirectattack, attacker);
}
public void SendOnDirectAttack()
{
InternalSendMessage(_ondirectattack);
}
public void SendActivate(string spell) public void SendActivate(string spell)
{ {
InternalSendMessage(_activate, spell); InternalSendMessage(_activate, spell);
...@@ -138,6 +161,7 @@ namespace WindBot.Game.AI ...@@ -138,6 +161,7 @@ namespace WindBot.Game.AI
private void InternalSendMessage(IList<string> array, params object[] opts) private void InternalSendMessage(IList<string> array, params object[] opts)
{ {
string message = string.Format(array[Program.Rand.Next(array.Count)], opts); string message = string.Format(array[Program.Rand.Next(array.Count)], opts);
if (message != "")
_game.Chat(message); _game.Chat(message);
} }
} }
......
namespace WindBot.Game.AI.Enums namespace WindBot.Game.AI.Enums
{ {
/// <summary>
/// Cards that are dangerous to attack.
/// </summary>
public enum DangerousMonster public enum DangerousMonster
{ {
LionHeart = 54366836, LionHeart = 54366836,
Yubel = 78371393, Yubel = 78371393,
YubelIncarnate = 4779091, YubelIncarnate = 4779091,
YubelNightmare = 31764700, YubelNightmare = 31764700,
MetaionTheTimelord = 74530899 ZaphionTheTimelord = 28929131,
SadionTheTimelord = 65314286,
MetaionTheTimelord = 74530899,
KamionTheTimelord = 91712985,
LazionTheTimelord = 92435533,
EaterOfMillions = 63845230
} }
} }
namespace WindBot.Game.AI.Enums
{
/// <summary>
/// Cards that restrict player from performing some action. Bot will preferentially destroy them.
/// </summary>
public enum Floodgate
{
BarrierStatueoftheTorrent = 10963799,
BarrierStatueoftheDrought = 19740112,
BarrierStatueoftheHeavens = 46145256,
BarrierStatueoftheInferno = 47961808,
BarrierStatueoftheStormwinds = 73356503,
BarrierStatueoftheAbyss = 84478195,
ThunderKingRaiOh = 71564252,
FossilDynaPachycephalo = 42009836,
VanitysFiend = 47084486,
MajestysFiend = 33746252,
VanitysRuler = 72634965,
KycootheGhostDestroyer = 88240808,
ConsecratedLight = 2980764,
ArchlordKristya = 59509952,
KoakiMeiruDrago = 12435193,
DenkoSekka = 13974207,
ZapMustung = 29951323,
Jinzo = 77585513,
SpellCanceller = 84636823,
LevelLimitAreaB = 3136426,
DimensionalFissure = 81674782,
Necrovalley = 47355498,
SavageColosseum = 32391631,
SecretVillageoftheSpellcasters = 68462976,
SwordsofRevealingLight = 72302403,
MessengerofPeace = 44656491,
KaiserColosseum = 35059553,
DomainoftheTrueMonarchs = 84171830,
ZombieWorld = 4064256,
ImperialOrder = 61740673,
MacroCosmos = 30241314,
MindDrain = 68937720,
SoulDrain = 73599290,
SkillDrain = 82732705,
Eisbahn = 54059040,
GozenMatch = 53334471,
RivalryofWarlords = 90846359,
AntiSpellFragrance = 58921041,
LightImprisoningMirror = 53341729,
ShadowImprisoningMirror = 99735427,
WallofRevealingLight = 17078030,
GravityBind = 85742772,
VanitysEmptiness = 5851097,
Lose1Turn = 24348804,
Reqliate = 20426907,
SummonLimit = 23516703,
AndtheBandPlayedOn = 47594939,
StygianDirge = 81489939,
RoyalDecree = 51452091,
ImperialIronWall = 30459350,
DNASurgery = 74701381,
NaturiaExterio = 99916754,
TheLastWarriorfromAnotherPlanet = 86099788,
ThousandEyesRestrict = 63519819,
ElShaddollWinda = 94977269,
MaskedHERODarkLaw = 58481572,
NaturiaBeast = 33198837,
NaturiaBarkion = 2956282,
EvilswarmOphion = 91279700,
MermailAbyssgaios = 74371660,
AbyssDweller = 21044178,
ZoodiacDrident = 48905153
}
}
namespace WindBot.Game.AI.Enums
{
public enum FusionSpell
{
GemKnightFusion = 1264319,
TheEyeofTimaeus = 1784686,
InstantFusion = 1845204,
OverloadFusion = 3659803,
FrightfurFusion = 6077601,
RedEyesFusion = 6172122,
Ostinato = 9113513,
DarkCalling = 12071500,
VehicroidConnectionZone = 23299957,
Polymerization = 24094653,
MiracleSynchroFusion = 36484016,
PowerBond = 37630732,
ParticleFusion = 39261576,
NeutronBlast = 43845801,
ShaddollFusion = 44394295,
TheTerminusoftheBurningAbyss = 44771289,
MiracleFusion = 45906428,
OddEyesFusion = 48144509,
ParallelWorldFusion = 54283059,
PendulumFusion = 65646587,
AbsorbFusion = 71422989,
DragonsMirror = 71490127,
MetalfoesFusion = 73594093,
EidolonSummoningMagic = 74063034,
FusionSubstitute = 74335036,
TranscendentalPolymerization = 76647978,
CyberdarkImpact = 80033124,
DarkFusion = 94820406,
TheBookoftheLaw = 458748,
ElShaddollFusion = 6417578,
FlashFusion = 17236839,
FullmetalfoesFusion = 39564736,
DestructionSwordsmanFusion = 41940225,
SuperPolymerization = 48130397,
CyberneticFusionSupport = 58199906,
BrilliantFusion = 7394770,
ForbiddenDarkContractwiththeSwampKing = 10833828,
Fortissimo = 11493868,
VoidImagination = 31444249,
FrightfurFactory = 43698897,
DarkContractwiththeSwampKing = 73360025,
NepheShaddollFusion = 60226558,
FusionGate = 33550694
}
}
namespace WindBot.Game.AI.Enums
{
public enum NegateAttackSpell
{
MessengerOfPeace = 44656491,
SavageColosseum = 32391631,
GravityBind = 85742772,
LevelLimitAreaB = 3136426
}
}
namespace WindBot.Game.AI.Enums
{
public enum NegatesEffects
{
SkillDrain = 82732705,
SoulDrain = 73599290
}
}
namespace WindBot.Game.AI.Enums
{
public enum NegatesSpells
{
HorusLv6 = 11224103,
HorusLv8 = 48229808
}
}
namespace WindBot.Game.AI.Enums
{
public enum NegatesSummons
{
Necrovalley = 47355498,
ImperialIronWall = 30459350,
ZombieWorld = 4064256,
DnaSurgery = 74701381,
MacroCosmos = 30241314,
DimensionalFissure = 81674782
}
}
namespace WindBot.Game.AI.Enums
{
public enum NegatesTraps
{
Jinzo = 77585513,
RoyalDecree = 51452091
}
}
namespace WindBot.Game.AI.Enums
{
public enum OneForXyz
{
ZoodiacThoroughblade = 77150143,
ZoodiacViper = 31755044,
ZoodiacCluckle = 20155904,
ZoodiacRabbina = 4367330,
ZoodiacRam = 4145852,
ZoodiacMarmorat = 78872731,
ZoodiacTigress = 11510448,
ZoodiacHammerkong = 14970113,
ZoodiacLyca = 41375811,
ZoodiacDrancia = 48905153,
ZoodiacBoarbow = 74393852,
ZoodiacBroadbull = 85115440,
Number62 = 31801517,
GalaxyEyesCipherDragon = 18963306,
Number107 = 88177324,
CyberDragonNova = 58069384,
Number39 = 84013237
}
}
namespace WindBot.Game.AI.Enums
{
public enum PreventActivationEffectInBattle
{
Deskbot009 = 25494711,
ArchfiendBlackSkullDragon = 45349196,
FrightfurChimera = 83866861,
GladiatorBeastNerokius = 29357956,
GemKnightCitrine = 67985943,
FrightfurSheep = 57477163,
ArmadesKeeperOfBoundaries = 88033975,
NumberS39UtopiaTheLightning = 56832966,
}
}
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using YGOSharp.OCGWrapper.Enums; using YGOSharp.OCGWrapper.Enums;
using WindBot;
using WindBot.Game;
using WindBot.Game.AI;
namespace WindBot.Game.AI namespace WindBot.Game.AI
{ {
...@@ -21,6 +24,9 @@ namespace WindBot.Game.AI ...@@ -21,6 +24,9 @@ namespace WindBot.Game.AI
protected int LastChainPlayer { get; private set; } protected int LastChainPlayer { get; private set; }
protected IList<ClientCard> CurrentChain { get; private set; } protected IList<ClientCard> CurrentChain { get; private set; }
protected ClientField Bot { get; private set; }
protected ClientField Enemy { get; private set; }
protected Executor(GameAI ai, Duel duel) protected Executor(GameAI ai, Duel duel)
{ {
Duel = duel; Duel = duel;
...@@ -29,48 +35,75 @@ namespace WindBot.Game.AI ...@@ -29,48 +35,75 @@ namespace WindBot.Game.AI
LastChainPlayer = -1; LastChainPlayer = -1;
CurrentChain = new List<ClientCard>(); CurrentChain = new List<ClientCard>();
Bot = Duel.Fields[0];
Enemy = Duel.Fields[1];
}
public virtual int OnRockPaperScissors()
{
return Program.Rand.Next(1, 4);
} }
public virtual bool OnSelectHand() public virtual bool OnSelectHand()
{ {
return true; // I want to begin ! return Program.Rand.Next(2) > 0;
} }
/// <summary>
/// Called when the AI has to decide if it should attack
/// </summary>
/// <param name="attackers">List of monsters that can attcack.</param>
/// <param name="defenders">List of monsters of enemy.</param>
/// <returns>A new BattlePhaseAction containing the action to do.</returns>
public virtual BattlePhaseAction OnBattle(IList<ClientCard> attackers, IList<ClientCard> defenders) public virtual BattlePhaseAction OnBattle(IList<ClientCard> attackers, IList<ClientCard> defenders)
{ {
if (attackers.Count == 0) if (attackers.Count == 0)
return AI.ToMainPhase2(); return AI.ToMainPhase2();
if (defenders.Count == 0) if (defenders.Count == 0)
return AI.Attack(attackers[0], null); {
for (int i = attackers.Count - 1; i >= 0; --i)
{
ClientCard attacker = attackers[i];
if (attacker.Attack > 0)
return AI.Attack(attacker, null);
}
}
else
{
for (int i = defenders.Count - 1; i >= 0; --i) for (int i = defenders.Count - 1; i >= 0; --i)
{ {
ClientCard defender = defenders[i]; ClientCard defender = defenders[i];
int value = defender.GetDefensePower();
for (int j = 0; j < attackers.Count; ++j) for (int j = 0; j < attackers.Count; ++j)
{ {
ClientCard attacker = attackers[j]; ClientCard attacker = attackers[j];
attacker.RealPower = attacker.Attack;
defender.RealPower = defender.GetDefensePower();
if (!OnPreBattleBetween(attacker, defender)) if (!OnPreBattleBetween(attacker, defender))
continue; continue;
if (attacker.Attack > value || (attacker.Attack >= value && j == attackers.Count - 1)) if (attacker.RealPower > defender.RealPower || (attacker.RealPower >= defender.RealPower && j == attackers.Count - 1))
return AI.Attack(attacker, defender); return AI.Attack(attacker, defender);
} }
} }
for (int i = attackers.Count - 1; i >= 0; --i)
{
ClientCard attacker = attackers[i];
if (attacker.CanDirectAttack)
return AI.Attack(attacker, null);
}
}
if (!Battle.CanMainPhaseTwo) if (!Battle.CanMainPhaseTwo)
return AI.Attack(attackers[attackers.Count - 1], defenders[0]); return AI.Attack(attackers[0], (defenders.Count == 0) ? null : defenders[0]);
return AI.ToMainPhase2(); return AI.ToMainPhase2();
} }
public virtual bool OnPreBattleBetween(ClientCard attacker, ClientCard defender) public virtual bool OnPreBattleBetween(ClientCard attacker, ClientCard defender)
{ {
if (defender.IsMonsterInvincible()) // Overrided in DefalultExecutor
{
if (defender.IsMonsterDangerous() || defender.IsDefense())
return false;
}
return true; return true;
} }
...@@ -86,8 +119,20 @@ namespace WindBot.Game.AI ...@@ -86,8 +119,20 @@ namespace WindBot.Game.AI
CurrentChain.Clear(); CurrentChain.Clear();
} }
public virtual void OnNewTurn()
{
// Some AI need do something on new turn
}
public virtual IList<ClientCard> OnSelectCard(IList<ClientCard> cards, int min, int max, bool cancelable) public virtual IList<ClientCard> OnSelectCard(IList<ClientCard> cards, int min, int max, bool cancelable)
{ {
// For overriding
return null;
}
public virtual IList<ClientCard> OnSelectSum(IList<ClientCard> cards, int sum, int min, int max, bool mode)
{
// For overriding
return null; return null;
} }
...@@ -96,6 +141,11 @@ namespace WindBot.Game.AI ...@@ -96,6 +141,11 @@ namespace WindBot.Game.AI
return true; return true;
} }
public virtual int OnSelectOption(IList<int> options)
{
return -1;
}
public bool ChainContainsCard(int id) public bool ChainContainsCard(int id)
{ {
foreach (ClientCard card in CurrentChain) foreach (ClientCard card in CurrentChain)
...@@ -144,6 +194,9 @@ namespace WindBot.Game.AI ...@@ -144,6 +194,9 @@ namespace WindBot.Game.AI
Battle = battle; Battle = battle;
} }
/// <summary>
/// Set global variables Type, Card, ActivateDescription for Executor
/// </summary>
public void SetCard(ExecutorType type, ClientCard card, int description) public void SetCard(ExecutorType type, ClientCard card, int description)
{ {
Type = type; Type = type;
...@@ -151,21 +204,33 @@ namespace WindBot.Game.AI ...@@ -151,21 +204,33 @@ namespace WindBot.Game.AI
ActivateDescription = description; ActivateDescription = description;
} }
/// <summary>
/// Do the action for the card if func return true.
/// </summary>
public void AddExecutor(ExecutorType type, int cardId, Func<bool> func) public void AddExecutor(ExecutorType type, int cardId, Func<bool> func)
{ {
Executors.Add(new CardExecutor(type, cardId, func)); Executors.Add(new CardExecutor(type, cardId, func));
} }
/// <summary>
/// Do the action for the card if available.
/// </summary>
public void AddExecutor(ExecutorType type, int cardId) public void AddExecutor(ExecutorType type, int cardId)
{ {
Executors.Add(new CardExecutor(type, cardId, null)); Executors.Add(new CardExecutor(type, cardId, null));
} }
/// <summary>
/// Do the action for every card if func return true.
/// </summary>
public void AddExecutor(ExecutorType type, Func<bool> func) public void AddExecutor(ExecutorType type, Func<bool> func)
{ {
Executors.Add(new CardExecutor(type, -1, func)); Executors.Add(new CardExecutor(type, -1, func));
} }
/// <summary>
/// Do the action for every card if no other Executor is added to it.
/// </summary>
public void AddExecutor(ExecutorType type) public void AddExecutor(ExecutorType type)
{ {
Executors.Add(new CardExecutor(type, -1, DefaultNoExecutor)); Executors.Add(new CardExecutor(type, -1, DefaultNoExecutor));
......
...@@ -21,11 +21,21 @@ namespace WindBot.Game ...@@ -21,11 +21,21 @@ namespace WindBot.Game
public int Race { get; private set; } public int Race { get; private set; }
public int Attack { get; private set; } public int Attack { get; private set; }
public int Defense { get; private set; } public int Defense { get; private set; }
public int LScale { get; private set; }
public int RScale { get; private set; }
public int BaseAttack { get; private set; } public int BaseAttack { get; private set; }
public int BaseDefense { get; private set; } public int BaseDefense { get; private set; }
public int RealPower { get; set; }
public List<int> Overlays { get; private set; } public List<int> Overlays { get; private set; }
public int Owner { get; private set; } public int Owner { get; private set; }
public int Controller { get; private set; } public int Controller { get; private set; }
public int Disabled { get; private set; }
public int SelectSeq { get; set; }
public int OpParam1 { get; set; }
public int OpParam2 { get; set; }
public bool CanDirectAttack { get; set; }
public bool ShouldDirectAttack { get; set; }
public bool Attacked { get; set; }
public int[] ActionIndex { get; set; } public int[] ActionIndex { get; set; }
public IDictionary<int, int> ActionActivateIndex { get; private set; } public IDictionary<int, int> ActionActivateIndex { get; private set; }
...@@ -114,13 +124,13 @@ namespace WindBot.Game ...@@ -114,13 +124,13 @@ namespace WindBot.Game
if ((flag & (int)Query.Owner) != 0) if ((flag & (int)Query.Owner) != 0)
Owner = duel.GetLocalPlayer(packet.ReadInt32()); Owner = duel.GetLocalPlayer(packet.ReadInt32());
if ((flag & (int)Query.IsDisabled) != 0) if ((flag & (int)Query.IsDisabled) != 0)
packet.ReadInt32(); Disabled = packet.ReadInt32();
if ((flag & (int)Query.IsPublic) != 0) if ((flag & (int)Query.IsPublic) != 0)
packet.ReadInt32(); packet.ReadInt32();
if ((flag & (int)Query.LScale) != 0) if ((flag & (int)Query.LScale) != 0)
packet.ReadInt32(); LScale = packet.ReadInt32();
if ((flag & (int)Query.RScale) != 0) if ((flag & (int)Query.RScale) != 0)
packet.ReadInt32(); RScale = packet.ReadInt32();
} }
public bool HasType(CardType type) public bool HasType(CardType type)
...@@ -158,6 +168,11 @@ namespace WindBot.Game ...@@ -158,6 +168,11 @@ namespace WindBot.Game
return (HasType(CardType.Fusion) || HasType(CardType.Synchro) || HasType(CardType.Xyz)); return (HasType(CardType.Fusion) || HasType(CardType.Synchro) || HasType(CardType.Xyz));
} }
public bool IsFaceup()
{
return HasPosition(CardPosition.FaceUp);
}
public bool IsFacedown() public bool IsFacedown()
{ {
return HasPosition(CardPosition.FaceDown); return HasPosition(CardPosition.FaceDown);
...@@ -173,6 +188,26 @@ namespace WindBot.Game ...@@ -173,6 +188,26 @@ namespace WindBot.Game
return HasPosition(CardPosition.Defence); return HasPosition(CardPosition.Defence);
} }
public bool IsDisabled()
{
return (Disabled != 0);
}
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() public int GetDefensePower()
{ {
return IsAttack() ? Attack : Defense; return IsAttack() ? Attack : Defense;
......
using System.Collections.Generic; using System.Collections.Generic;
using YGOSharp.OCGWrapper.Enums; using YGOSharp.OCGWrapper.Enums;
namespace WindBot.Game namespace WindBot.Game
...@@ -14,18 +14,19 @@ namespace WindBot.Game ...@@ -14,18 +14,19 @@ namespace WindBot.Game
public IList<ClientCard> ExtraDeck { get; private set; } public IList<ClientCard> ExtraDeck { get; private set; }
public ClientField() public ClientField()
{
}
public void Init(int deck, int extra)
{ {
Hand = new List<ClientCard>(); Hand = new List<ClientCard>();
MonsterZone = new ClientCard[5]; MonsterZone = new ClientCard[7];
SpellZone = new ClientCard[8]; SpellZone = new ClientCard[8];
Graveyard = new List<ClientCard>(); Graveyard = new List<ClientCard>();
Banished = new List<ClientCard>(); Banished = new List<ClientCard>();
Deck = new List<ClientCard>(); Deck = new List<ClientCard>();
ExtraDeck = new List<ClientCard>(); ExtraDeck = new List<ClientCard>();
}
public void Init(int deck, int extra)
{
for (int i = 0; i < deck; ++i) for (int i = 0; i < deck; ++i)
Deck.Add(new ClientCard(0, CardLocation.Deck)); Deck.Add(new ClientCard(0, CardLocation.Deck));
for (int i = 0; i < extra; ++i) for (int i = 0; i < extra; ++i)
...@@ -42,6 +43,11 @@ namespace WindBot.Game ...@@ -42,6 +43,11 @@ namespace WindBot.Game
return GetCount(SpellZone); return GetCount(SpellZone);
} }
public int GetHandCount()
{
return GetCount(Hand);
}
public int GetSpellCountWithoutField() public int GetSpellCountWithoutField()
{ {
int count = 0; int count = 0;
...@@ -83,16 +89,67 @@ namespace WindBot.Game ...@@ -83,16 +89,67 @@ namespace WindBot.Game
return GetCards(SpellZone); return GetCards(SpellZone);
} }
public List<ClientCard> GetMonstersInExtraZone()
{
List<ClientCard> cards = new List<ClientCard>();
if (MonsterZone[5] != null)
cards.Add(MonsterZone[5]);
if (MonsterZone[6] != null)
cards.Add(MonsterZone[6]);
return cards;
}
public List<ClientCard> GetMonstersInMainZone()
{
List<ClientCard> cards = new List<ClientCard>();
for (int i = 0; i < 5; i++)
{
if (MonsterZone[i] != null)
cards.Add(MonsterZone[i]);
}
return cards;
}
public bool HasInHand(int cardId) public bool HasInHand(int cardId)
{ {
return HasInCards(Hand, cardId); return HasInCards(Hand, cardId);
} }
public bool HasInHand(IList<int> cardId)
{
return HasInCards(Hand, cardId);
}
public bool HasInGraveyard(int cardId) public bool HasInGraveyard(int cardId)
{ {
return HasInCards(Graveyard, cardId); return HasInCards(Graveyard, cardId);
} }
public bool HasInGraveyard(IList<int> cardId)
{
return HasInCards(Graveyard, cardId);
}
public bool HasInBanished(int cardId)
{
return HasInCards(Banished, cardId);
}
public bool HasInBanished(IList<int> cardId)
{
return HasInCards(Banished, cardId);
}
public bool HasInExtra(int cardId)
{
return HasInCards(ExtraDeck, cardId);
}
public bool HasInExtra(IList<int> cardId)
{
return HasInCards(ExtraDeck, cardId);
}
public bool HasAttackingMonster() public bool HasAttackingMonster()
{ {
IList<ClientCard> monsters = GetMonsters(); IList<ClientCard> monsters = GetMonsters();
...@@ -115,27 +172,40 @@ namespace WindBot.Game ...@@ -115,27 +172,40 @@ namespace WindBot.Game
return false; return false;
} }
public bool HasInMonstersZone(int cardId) public bool HasInMonstersZone(int cardId, bool notDisabled = false, bool hasXyzMaterial = false)
{ {
return HasInCards(MonsterZone, cardId); return HasInCards(MonsterZone, cardId, notDisabled, hasXyzMaterial);
} }
public bool HasInSpellZone(int cardId) public bool HasInMonstersZone(IList<int> cardId, bool notDisabled = false, bool hasXyzMaterial = false)
{ {
return HasInCards(SpellZone, cardId); return HasInCards(MonsterZone, cardId, notDisabled, hasXyzMaterial);
}
public bool HasInSpellZone(int cardId, bool notDisabled = false)
{
return HasInCards(SpellZone, cardId, notDisabled);
}
public bool HasInSpellZone(IList<int> cardId, bool notDisabled = false)
{
return HasInCards(SpellZone, cardId, notDisabled);
} }
public int GetRemainingCount(int cardId, int initialCount) public int GetRemainingCount(int cardId, int initialCount)
{ {
int remaining = initialCount; int remaining = initialCount;
foreach (ClientCard card in Hand) foreach (ClientCard card in Hand)
if (card.Id == cardId) if (card != null && card.Id == cardId)
remaining--;
foreach (ClientCard card in SpellZone)
if (card != null && card.Id == cardId)
remaining--; remaining--;
foreach (ClientCard card in Graveyard) foreach (ClientCard card in Graveyard)
if (card.Id == cardId) if (card != null && card.Id == cardId)
remaining--; remaining--;
foreach (ClientCard card in Banished) foreach (ClientCard card in Banished)
if (card.Id == cardId) if (card != null && card.Id == cardId)
remaining--; remaining--;
return (remaining < 0) ? 0 : remaining; return (remaining < 0) ? 0 : remaining;
} }
...@@ -151,6 +221,28 @@ namespace WindBot.Game ...@@ -151,6 +221,28 @@ namespace WindBot.Game
return count; return count;
} }
public int GetCountCardInZone(IEnumerable<ClientCard> cards, int cardId)
{
int count = 0;
foreach (ClientCard card in cards)
{
if (card != null && card.Id == cardId)
count++;
}
return count;
}
public int GetCountCardInZone(IEnumerable<ClientCard> cards, List<int> cardId)
{
int count = 0;
foreach (ClientCard card in cards)
{
if (card != null && cardId.Contains(card.Id))
count++;
}
return count;
}
private static List<ClientCard> GetCards(IEnumerable<ClientCard> cards, CardType type) private static List<ClientCard> GetCards(IEnumerable<ClientCard> cards, CardType type)
{ {
List<ClientCard> nCards = new List<ClientCard>(); List<ClientCard> nCards = new List<ClientCard>();
...@@ -173,11 +265,21 @@ namespace WindBot.Game ...@@ -173,11 +265,21 @@ namespace WindBot.Game
return nCards; return nCards;
} }
private static bool HasInCards(IEnumerable<ClientCard> cards, int cardId) private static bool HasInCards(IEnumerable<ClientCard> cards, int cardId, bool notDisabled = false, bool hasXyzMaterial = false)
{ {
foreach (ClientCard card in cards) foreach (ClientCard card in cards)
{ {
if (card != null && card.Id == cardId) if (card != null && card.Id == cardId && !(notDisabled && card.IsDisabled()) && !(hasXyzMaterial && !card.HasXyzMaterial()))
return true;
}
return false;
}
private static bool HasInCards(IEnumerable<ClientCard> cards, IList<int> cardId, bool notDisabled = false, bool hasXyzMaterial = false)
{
foreach (ClientCard card in cards)
{
if (card != null && cardId.Contains(card.Id) && !(notDisabled && card.IsDisabled()) && !(hasXyzMaterial && !card.HasXyzMaterial()))
return true; return true;
} }
return false; return false;
......
...@@ -6,6 +6,7 @@ namespace WindBot.Game ...@@ -6,6 +6,7 @@ namespace WindBot.Game
public class Duel public class Duel
{ {
public bool IsFirst { get; set; } public bool IsFirst { get; set; }
public bool IsNewRule { get; set; }
public int[] LifePoints { get; private set; } public int[] LifePoints { get; private set; }
public ClientField[] Fields { get; private set; } public ClientField[] Fields { get; private set; }
...@@ -15,6 +16,8 @@ namespace WindBot.Game ...@@ -15,6 +16,8 @@ namespace WindBot.Game
public DuelPhase Phase { get; set; } public DuelPhase Phase { get; set; }
public MainPhase MainPhase { get; set; } public MainPhase MainPhase { get; set; }
public BattlePhase BattlePhase { get; set; } public BattlePhase BattlePhase { get; set; }
public IList<ClientCard> ChainTargets { get; set; }
public int LastSummonPlayer { get; set; }
public Duel() public Duel()
{ {
...@@ -22,6 +25,8 @@ namespace WindBot.Game ...@@ -22,6 +25,8 @@ namespace WindBot.Game
Fields = new ClientField[2]; Fields = new ClientField[2];
Fields[0] = new ClientField(); Fields[0] = new ClientField();
Fields[1] = new ClientField(); Fields[1] = new ClientField();
ChainTargets = new List<ClientCard>();
LastSummonPlayer = -1;
} }
public ClientCard GetCard(int player, CardLocation loc, int index) public ClientCard GetCard(int player, CardLocation loc, int index)
......
using System.Collections.Generic; using System.Linq;
using System.Collections.Generic;
using WindBot.Game.AI; using WindBot.Game.AI;
using YGOSharp.OCGWrapper.Enums; using YGOSharp.OCGWrapper.Enums;
...@@ -22,6 +23,27 @@ namespace WindBot.Game ...@@ -22,6 +23,27 @@ namespace WindBot.Game
_dialogs = new Dialogs(game); _dialogs = new Dialogs(game);
} }
/// <summary>
/// Called when the AI got the error message.
/// </summary>
public void OnRetry()
{
_dialogs.SendSorry();
}
public void OnDeckError(string card)
{
_dialogs.SendDeckSorry(card);
}
/// <summary>
/// Called when the AI join the game.
/// </summary>
public void OnJoinGame()
{
_dialogs.SendWelcome();
}
/// <summary> /// <summary>
/// Called when the duel starts. /// Called when the duel starts.
/// </summary> /// </summary>
...@@ -30,10 +52,19 @@ namespace WindBot.Game ...@@ -30,10 +52,19 @@ namespace WindBot.Game
_dialogs.SendDuelStart(); _dialogs.SendDuelStart();
} }
/// <summary>
/// Called when the AI do the rock-paper-scissors.
/// </summary>
/// <returns>1 for Scissors, 2 for Rock, 3 for Paper.</returns>
public int OnRockPaperScissors()
{
return Executor.OnRockPaperScissors();
}
/// <summary> /// <summary>
/// Called when the AI won the rock-paper-scissors. /// Called when the AI won the rock-paper-scissors.
/// </summary> /// </summary>
/// <returns>True if the AI should begin, false otherwise.</returns> /// <returns>True if the AI should begin first, false otherwise.</returns>
public bool OnSelectHand() public bool OnSelectHand()
{ {
return Executor.OnSelectHand(); return Executor.OnSelectHand();
...@@ -44,6 +75,7 @@ namespace WindBot.Game ...@@ -44,6 +75,7 @@ namespace WindBot.Game
/// </summary> /// </summary>
public void OnNewTurn() public void OnNewTurn()
{ {
Executor.OnNewTurn();
} }
/// <summary> /// <summary>
...@@ -54,10 +86,22 @@ namespace WindBot.Game ...@@ -54,10 +86,22 @@ namespace WindBot.Game
m_selector = null; m_selector = null;
m_nextSelector = null; m_nextSelector = null;
m_option = -1; m_option = -1;
m_yesno = -1;
m_position = CardPosition.FaceUpAttack; m_position = CardPosition.FaceUpAttack;
Duel.LastSummonPlayer = -1;
if (Duel.Player == 0 && Duel.Phase == DuelPhase.Draw) if (Duel.Player == 0 && Duel.Phase == DuelPhase.Draw)
{
_dialogs.SendNewTurn(); _dialogs.SendNewTurn();
} }
}
/// <summary>
/// Called when the AI got attack directly.
/// </summary>
public void OnDirectAttack(ClientCard card)
{
_dialogs.SendOnDirectAttack(card.Name);
}
/// <summary> /// <summary>
/// Called when a chain is executed. /// Called when a chain is executed.
...@@ -66,6 +110,7 @@ namespace WindBot.Game ...@@ -66,6 +110,7 @@ namespace WindBot.Game
/// <param name="player">Player who is currently chaining.</param> /// <param name="player">Player who is currently chaining.</param>
public void OnChaining(ClientCard card, int player) public void OnChaining(ClientCard card, int player)
{ {
Duel.LastSummonPlayer = -1;
Executor.OnChaining(player,card); Executor.OnChaining(player,card);
} }
...@@ -118,7 +163,7 @@ namespace WindBot.Game ...@@ -118,7 +163,7 @@ namespace WindBot.Game
public IList<ClientCard> OnSelectCard(IList<ClientCard> cards, int min, int max, bool cancelable) public IList<ClientCard> OnSelectCard(IList<ClientCard> cards, int min, int max, bool cancelable)
{ {
// Check for the executor. // Check for the executor.
IList<ClientCard> result = Executor.OnSelectCard(cards, min,max,cancelable); IList<ClientCard> result = Executor.OnSelectCard(cards, min, max, cancelable);
if (result != null) if (result != null)
return result; return result;
...@@ -198,7 +243,7 @@ namespace WindBot.Game ...@@ -198,7 +243,7 @@ namespace WindBot.Game
/// </summary> /// </summary>
/// <param name="card">Card to activate.</param> /// <param name="card">Card to activate.</param>
/// <returns>True for yes, false for no.</returns> /// <returns>True for yes, false for no.</returns>
public bool OnSelectEffectYn(ClientCard card) public bool OnSelectEffectYn(ClientCard card, int desc)
{ {
foreach (CardExecutor exec in Executor.Executors) foreach (CardExecutor exec in Executor.Executors)
{ {
...@@ -245,6 +290,7 @@ namespace WindBot.Game ...@@ -245,6 +290,7 @@ namespace WindBot.Game
if (ShouldExecute(exec, card, ExecutorType.SpSummon)) if (ShouldExecute(exec, card, ExecutorType.SpSummon))
{ {
_dialogs.SendSummon(card.Name); _dialogs.SendSummon(card.Name);
Duel.LastSummonPlayer = 0;
return new MainPhaseAction(MainPhaseAction.MainAction.SpSummon, card.ActionIndex); return new MainPhaseAction(MainPhaseAction.MainAction.SpSummon, card.ActionIndex);
} }
} }
...@@ -253,17 +299,19 @@ namespace WindBot.Game ...@@ -253,17 +299,19 @@ namespace WindBot.Game
if (ShouldExecute(exec, card, ExecutorType.Summon)) if (ShouldExecute(exec, card, ExecutorType.Summon))
{ {
_dialogs.SendSummon(card.Name); _dialogs.SendSummon(card.Name);
Duel.LastSummonPlayer = 0;
return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex); return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex);
} }
if (ShouldExecute(exec, card, ExecutorType.SummonOrSet)) if (ShouldExecute(exec, card, ExecutorType.SummonOrSet))
{ {
if (Utils.IsEnnemyBetter(true, true) && Utils.IsAllEnnemyBetterThanValue(card.Attack + 300, false) && if (Utils.IsAllEnemyBetter(true) && Utils.IsAllEnemyBetterThanValue(card.Attack + 300, false) &&
main.MonsterSetableCards.Contains(card)) main.MonsterSetableCards.Contains(card))
{ {
_dialogs.SendSetMonster(); _dialogs.SendSetMonster();
return new MainPhaseAction(MainPhaseAction.MainAction.SetMonster, card.ActionIndex); return new MainPhaseAction(MainPhaseAction.MainAction.SetMonster, card.ActionIndex);
} }
_dialogs.SendSummon(card.Name); _dialogs.SendSummon(card.Name);
Duel.LastSummonPlayer = 0;
return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex); return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex);
} }
} }
...@@ -288,8 +336,13 @@ namespace WindBot.Game ...@@ -288,8 +336,13 @@ namespace WindBot.Game
/// <returns>Index of the selected option.</returns> /// <returns>Index of the selected option.</returns>
public int OnSelectOption(IList<int> options) public int OnSelectOption(IList<int> options)
{ {
if (m_option != -1) if (m_option != -1 && m_option < options.Count)
return m_option; return m_option;
int result = Executor.OnSelectOption(options);
if (result != -1)
return result;
return 0; // Always select the first option. return 0; // Always select the first option.
} }
...@@ -312,25 +365,132 @@ namespace WindBot.Game ...@@ -312,25 +365,132 @@ namespace WindBot.Game
} }
/// <summary> /// <summary>
/// Called when the AI has to tribute for a synchro monster. /// Called when the AI has to tribute for a synchro monster or ritual monster.
/// </summary> /// </summary>
/// <param name="cards">Available cards.</param> /// <param name="cards">Available cards.</param>
/// <param name="sum">Result of the operation.</param> /// <param name="sum">Result of the operation.</param>
/// <param name="min">Minimum cards.</param> /// <param name="min">Minimum cards.</param>
/// <param name="max">Maximum cards.</param> /// <param name="max">Maximum cards.</param>
/// <param name="mode">True for exact equal.</param>
/// <returns></returns> /// <returns></returns>
public IList<ClientCard> OnSelectSum(IList<ClientCard> cards, int sum, int min, int max) public IList<ClientCard> OnSelectSum(IList<ClientCard> cards, int sum, int min, int max, bool mode)
{
IList<ClientCard> selected = Executor.OnSelectSum(cards, sum, min, max, mode);
if (selected != null)
{ {
// Always return one card. The first available. return selected;
}
if (mode)
{
// equal
if (min <= 1)
{
// try special level first
foreach (ClientCard card in cards) foreach (ClientCard card in cards)
{ {
if (card.Level == sum) if (card.OpParam2 == sum)
{
return new[] { card }; return new[] { card };
} }
// However return everything, that may work. }
// try level equal
foreach (ClientCard card in cards)
{
if (card.OpParam1 == sum)
{
return new[] { card };
}
}
}
// try all
int s1 = 0, s2 = 0;
foreach (ClientCard card in cards)
{
s1 += card.OpParam1;
s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1;
}
if (s1 == sum || s2 == sum)
{
return cards; return cards;
} }
// try all combinations
int i = (min <= 1) ? 2 : min;
while (i <= max && i <= cards.Count)
{
IEnumerable<IEnumerable<ClientCard>> combos = CardContainer.GetCombinations(cards, i);
foreach (IEnumerable<ClientCard> combo in combos)
{
Logger.DebugWriteLine("--");
s1 = 0;
s2 = 0;
foreach (ClientCard card in combo)
{
s1 += card.OpParam1;
s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1;
}
if (s1 == sum || s2 == sum)
{
return combo.ToList();
}
}
i++;
}
}
else
{
// larger
if (min <= 1)
{
// try special level first
foreach (ClientCard card in cards)
{
if (card.OpParam2 >= sum)
{
return new[] { card };
}
}
// try level equal
foreach (ClientCard card in cards)
{
if (card.OpParam1 >= sum)
{
return new[] { card };
}
}
}
// try all combinations
int i = (min <= 1) ? 2 : min;
while (i <= max && i <= cards.Count)
{
IEnumerable<IEnumerable<ClientCard>> combos = CardContainer.GetCombinations(cards, i);
foreach (IEnumerable<ClientCard> combo in combos)
{
Logger.DebugWriteLine("----");
int s1 = 0, s2 = 0;
foreach (ClientCard card in combo)
{
s1 += card.OpParam1;
s2 += (card.OpParam2 != 0) ? card.OpParam2 : card.OpParam1;
}
if (s1 >= sum || s2 >= sum)
{
return combo.ToList();
}
}
i++;
}
}
Logger.WriteErrorLine("Fail to select sum.");
return new List<ClientCard>();
}
/// <summary> /// <summary>
/// Called when the AI has to tribute one or more cards. /// Called when the AI has to tribute one or more cards.
/// </summary> /// </summary>
...@@ -361,6 +521,8 @@ namespace WindBot.Game ...@@ -361,6 +521,8 @@ namespace WindBot.Game
/// <returns>True for yes, false for no.</returns> /// <returns>True for yes, false for no.</returns>
public bool OnSelectYesNo(int desc) public bool OnSelectYesNo(int desc)
{ {
if (m_yesno != -1)
return m_yesno > 0;
return Executor.OnSelectYesNo(desc); return Executor.OnSelectYesNo(desc);
} }
...@@ -380,10 +542,12 @@ namespace WindBot.Game ...@@ -380,10 +542,12 @@ namespace WindBot.Game
private CardSelector m_selector; private CardSelector m_selector;
private CardSelector m_nextSelector; private CardSelector m_nextSelector;
private CardSelector m_thirdSelector;
private CardPosition m_position = CardPosition.FaceUpAttack; private CardPosition m_position = CardPosition.FaceUpAttack;
private int m_option; private int m_option;
private int m_number; private int m_number;
private int m_announce; private int m_announce;
private int m_yesno;
private IList<CardAttribute> m_attributes = new List<CardAttribute>(); private IList<CardAttribute> m_attributes = new List<CardAttribute>();
private IList<CardRace> m_races = new List<CardRace>(); private IList<CardRace> m_races = new List<CardRace>();
...@@ -437,17 +601,45 @@ namespace WindBot.Game ...@@ -437,17 +601,45 @@ namespace WindBot.Game
m_nextSelector = new CardSelector(loc); m_nextSelector = new CardSelector(loc);
} }
public void SelectThirdCard(ClientCard card)
{
m_thirdSelector = new CardSelector(card);
}
public void SelectThirdCard(IList<ClientCard> cards)
{
m_thirdSelector = new CardSelector(cards);
}
public void SelectThirdCard(int cardId)
{
m_thirdSelector = new CardSelector(cardId);
}
public void SelectThirdCard(IList<int> ids)
{
m_thirdSelector = new CardSelector(ids);
}
public void SelectThirdCard(CardLocation loc)
{
m_thirdSelector = new CardSelector(loc);
}
public CardSelector GetSelectedCards() public CardSelector GetSelectedCards()
{ {
CardSelector selected = m_selector; CardSelector selected = m_selector;
m_selector = null;
if (m_nextSelector != null) if (m_nextSelector != null)
{ {
m_selector = m_nextSelector; m_selector = m_nextSelector;
m_nextSelector = null; m_nextSelector = null;
if (m_thirdSelector != null)
{
m_nextSelector = m_thirdSelector;
m_thirdSelector = null;
}
} }
else
m_selector = null;
return selected; return selected;
} }
...@@ -497,6 +689,11 @@ namespace WindBot.Game ...@@ -497,6 +689,11 @@ namespace WindBot.Game
m_announce = id; m_announce = id;
} }
public void SelectYesNo(bool opt)
{
m_yesno = opt ? 1 : 0;
}
/// <summary> /// <summary>
/// Called when the AI has to declare a number. /// Called when the AI has to declare a number.
/// </summary> /// </summary>
...@@ -552,14 +749,19 @@ namespace WindBot.Game ...@@ -552,14 +749,19 @@ namespace WindBot.Game
public BattlePhaseAction Attack(ClientCard attacker, ClientCard defender) public BattlePhaseAction Attack(ClientCard attacker, ClientCard defender)
{ {
Executor.SetCard(0, attacker, -1);
if (defender != null) if (defender != null)
{ {
string cardName = defender.Name ?? "monster"; string cardName = defender.Name ?? "monster";
attacker.ShouldDirectAttack = false;
_dialogs.SendAttack(attacker.Name, cardName); _dialogs.SendAttack(attacker.Name, cardName);
SelectCard(defender); SelectCard(defender);
} }
else else
{
attacker.ShouldDirectAttack = true;
_dialogs.SendDirectAttack(attacker.Name); _dialogs.SendDirectAttack(attacker.Name);
}
return new BattlePhaseAction(BattlePhaseAction.BattleAction.Attack, attacker.ActionIndex); return new BattlePhaseAction(BattlePhaseAction.BattleAction.Attack, attacker.ActionIndex);
} }
......
...@@ -24,11 +24,13 @@ namespace WindBot.Game ...@@ -24,11 +24,13 @@ namespace WindBot.Game
private Room _room; private Room _room;
private Duel _duel; private Duel _duel;
private int _hand;
public GameBehavior(GameClient game) public GameBehavior(GameClient game)
{ {
Game = game; Game = game;
Connection = game.Connection; Connection = game.Connection;
_hand = game.Hand;
_packets = new Dictionary<StocMessage, Action<BinaryReader>>(); _packets = new Dictionary<StocMessage, Action<BinaryReader>>();
_messages = new Dictionary<GameMessage, Action<BinaryReader>>(); _messages = new Dictionary<GameMessage, Action<BinaryReader>>();
...@@ -73,12 +75,16 @@ namespace WindBot.Game ...@@ -73,12 +75,16 @@ namespace WindBot.Game
_packets.Add(StocMessage.Replay, OnReplay); _packets.Add(StocMessage.Replay, OnReplay);
_packets.Add(StocMessage.DuelEnd, OnDuelEnd); _packets.Add(StocMessage.DuelEnd, OnDuelEnd);
_packets.Add(StocMessage.Chat, OnChat); _packets.Add(StocMessage.Chat, OnChat);
_packets.Add(StocMessage.ChangeSide, OnChangeSide);
_packets.Add(StocMessage.ErrorMsg, OnErrorMsg);
_messages.Add(GameMessage.Retry, OnRetry);
_messages.Add(GameMessage.Start, OnStart); _messages.Add(GameMessage.Start, OnStart);
_messages.Add(GameMessage.Win, OnWin); _messages.Add(GameMessage.Win, OnWin);
_messages.Add(GameMessage.Draw, OnDraw); _messages.Add(GameMessage.Draw, OnDraw);
_messages.Add(GameMessage.ShuffleDeck, OnShuffleDeck); _messages.Add(GameMessage.ShuffleDeck, OnShuffleDeck);
_messages.Add(GameMessage.ShuffleHand, OnShuffleHand); _messages.Add(GameMessage.ShuffleHand, OnShuffleHand);
_messages.Add(GameMessage.TagSwap, OnTagSwap);
_messages.Add(GameMessage.NewTurn, OnNewTurn); _messages.Add(GameMessage.NewTurn, OnNewTurn);
_messages.Add(GameMessage.NewPhase, OnNewPhase); _messages.Add(GameMessage.NewPhase, OnNewPhase);
_messages.Add(GameMessage.Damage, OnDamage); _messages.Add(GameMessage.Damage, OnDamage);
...@@ -86,13 +92,15 @@ namespace WindBot.Game ...@@ -86,13 +92,15 @@ namespace WindBot.Game
_messages.Add(GameMessage.Recover, OnRecover); _messages.Add(GameMessage.Recover, OnRecover);
_messages.Add(GameMessage.LpUpdate, OnLpUpdate); _messages.Add(GameMessage.LpUpdate, OnLpUpdate);
_messages.Add(GameMessage.Move, OnMove); _messages.Add(GameMessage.Move, OnMove);
_messages.Add(GameMessage.Attack, OnAttack);
_messages.Add(GameMessage.PosChange, OnPosChange); _messages.Add(GameMessage.PosChange, OnPosChange);
_messages.Add(GameMessage.Chaining, OnChaining); _messages.Add(GameMessage.Chaining, OnChaining);
_messages.Add(GameMessage.ChainEnd, OnChainEnd); _messages.Add(GameMessage.ChainEnd, OnChainEnd);
_messages.Add(GameMessage.SortCard, OnCardSorting);
_messages.Add(GameMessage.SortChain, OnChainSorting); _messages.Add(GameMessage.SortChain, OnChainSorting);
_messages.Add(GameMessage.UpdateCard, OnUpdateCard); _messages.Add(GameMessage.UpdateCard, OnUpdateCard);
_messages.Add(GameMessage.UpdateData, OnUpdateData); _messages.Add(GameMessage.UpdateData, OnUpdateData);
_messages.Add(GameMessage.BecomeTarget, OnBecomeTarget);
_messages.Add(GameMessage.SelectBattleCmd, OnSelectBattleCmd); _messages.Add(GameMessage.SelectBattleCmd, OnSelectBattleCmd);
_messages.Add(GameMessage.SelectCard, OnSelectCard); _messages.Add(GameMessage.SelectCard, OnSelectCard);
_messages.Add(GameMessage.SelectChain, OnSelectChain); _messages.Add(GameMessage.SelectChain, OnSelectChain);
...@@ -110,9 +118,31 @@ namespace WindBot.Game ...@@ -110,9 +118,31 @@ namespace WindBot.Game
_messages.Add(GameMessage.AnnounceCard, OnAnnounceCard); _messages.Add(GameMessage.AnnounceCard, OnAnnounceCard);
_messages.Add(GameMessage.AnnounceNumber, OnAnnounceNumber); _messages.Add(GameMessage.AnnounceNumber, OnAnnounceNumber);
_messages.Add(GameMessage.AnnounceRace, OnAnnounceRace); _messages.Add(GameMessage.AnnounceRace, OnAnnounceRace);
_messages.Add(GameMessage.AnnounceCardFilter, OnAnnounceCard);
_messages.Add(GameMessage.RockPaperScissors, OnRockPaperScissors);
} }
private void OnJoinGame(BinaryReader packet) private void OnJoinGame(BinaryReader packet)
{
/*int lflist = (int)*/ packet.ReadUInt32();
/*int rule = */ packet.ReadByte();
/*int mode = */ packet.ReadByte();
int duel_rule = packet.ReadByte();
_ai.Duel.IsNewRule = (duel_rule == 4);
BinaryWriter deck = GamePacketFactory.Create(CtosMessage.UpdateDeck);
deck.Write(Deck.Cards.Count + Deck.ExtraCards.Count);
deck.Write(Deck.SideCards.Count);
foreach (NamedCard card in Deck.Cards)
deck.Write(card.Id);
foreach (NamedCard card in Deck.ExtraCards)
deck.Write(card.Id);
foreach (NamedCard card in Deck.SideCards)
deck.Write(card.Id);
Connection.Send(deck);
_ai.OnJoinGame();
}
private void OnChangeSide(BinaryReader packet)
{ {
BinaryWriter deck = GamePacketFactory.Create(CtosMessage.UpdateDeck); BinaryWriter deck = GamePacketFactory.Create(CtosMessage.UpdateDeck);
deck.Write(Deck.Cards.Count + Deck.ExtraCards.Count); deck.Write(Deck.Cards.Count + Deck.ExtraCards.Count);
...@@ -124,13 +154,14 @@ namespace WindBot.Game ...@@ -124,13 +154,14 @@ namespace WindBot.Game
foreach (NamedCard card in Deck.SideCards) foreach (NamedCard card in Deck.SideCards)
deck.Write(card.Id); deck.Write(card.Id);
Connection.Send(deck); Connection.Send(deck);
_ai.OnJoinGame();
} }
private void OnTypeChange(BinaryReader packet) private void OnTypeChange(BinaryReader packet)
{ {
int type = packet.ReadByte(); int type = packet.ReadByte();
int pos = type & 0xF; int pos = type & 0xF;
if (pos != 0 && pos != 1) if (pos < 0 || pos > 3)
{ {
Connection.Close(); Connection.Close();
return; return;
...@@ -143,7 +174,7 @@ namespace WindBot.Game ...@@ -143,7 +174,7 @@ namespace WindBot.Game
private void OnPlayerEnter(BinaryReader packet) private void OnPlayerEnter(BinaryReader packet)
{ {
string name = packet.ReadUnicode(Program.PlayerNameSize); string name = packet.ReadUnicode(20);
int pos = packet.ReadByte(); int pos = packet.ReadByte();
if (pos < 8) if (pos < 8)
_room.Names[pos] = name; _room.Names[pos] = name;
...@@ -180,7 +211,12 @@ namespace WindBot.Game ...@@ -180,7 +211,12 @@ namespace WindBot.Game
private void OnSelectHand(BinaryReader packet) private void OnSelectHand(BinaryReader packet)
{ {
Connection.Send(CtosMessage.HandResult, (byte)Program.Rand.Next(1, 4)); int result;
if (_hand > 0)
result = _hand;
else
result = _ai.OnRockPaperScissors();
Connection.Send(CtosMessage.HandResult, (byte)result);
} }
private void OnSelectTp(BinaryReader packet) private void OnSelectTp(BinaryReader packet)
...@@ -198,8 +234,9 @@ namespace WindBot.Game ...@@ -198,8 +234,9 @@ namespace WindBot.Game
private void OnReplay(BinaryReader packet) private void OnReplay(BinaryReader packet)
{ {
byte[] replay = packet.ReadToEnd(); /*byte[] replay =*/ packet.ReadToEnd();
/*
const string directory = "Replays"; const string directory = "Replays";
if (!Directory.Exists(directory)) if (!Directory.Exists(directory))
Directory.CreateDirectory(directory); Directory.CreateDirectory(directory);
...@@ -210,8 +247,9 @@ namespace WindBot.Game ...@@ -210,8 +247,9 @@ namespace WindBot.Game
if (Regex.IsMatch(file, @"^[\w\-. ]+$")) if (Regex.IsMatch(file, @"^[\w\-. ]+$"))
File.WriteAllBytes(fullname, replay); File.WriteAllBytes(fullname, replay);
*/
Connection.Close(); //Connection.Close();
} }
private void OnDuelEnd(BinaryReader packet) private void OnDuelEnd(BinaryReader packet)
...@@ -221,8 +259,40 @@ namespace WindBot.Game ...@@ -221,8 +259,40 @@ namespace WindBot.Game
private void OnChat(BinaryReader packet) private void OnChat(BinaryReader packet)
{ {
packet.ReadInt16(); // player int player = packet.ReadInt16();
packet.ReadUnicode(256); // message string message = packet.ReadUnicode(256);
string myName = _room.Position == 0 ? _room.Names[0] : _room.Names[1];
string otherName = _room.Position == 0 ? _room.Names[1] : _room.Names[0];
if (player < 4)
Logger.DebugWriteLine(otherName + " say to " + myName + ": " + message);
}
private void OnErrorMsg(BinaryReader packet)
{
int msg = packet.ReadByte();
// align
packet.ReadByte();
packet.ReadByte();
packet.ReadByte();
int code = packet.ReadInt32();
if (msg == 2) //ERRMSG_DECKERROR
{
NamedCard card = NamedCard.Get(code);
if (card != null)
_ai.OnDeckError(card.Name);
else if (code == 1)
_ai.OnDeckError("DECK");
else
_ai.OnDeckError("Unknown Card");
}
//Connection.Close();
}
private void OnRetry(BinaryReader packet)
{
_ai.OnRetry();
Connection.Close();
throw new Exception("Got MSG_RETRY.");
} }
private void OnStart(BinaryReader packet) private void OnStart(BinaryReader packet)
...@@ -238,7 +308,7 @@ namespace WindBot.Game ...@@ -238,7 +308,7 @@ namespace WindBot.Game
extra = packet.ReadInt16(); extra = packet.ReadInt16();
_duel.Fields[GetLocalPlayer(1)].Init(deck, extra); _duel.Fields[GetLocalPlayer(1)].Init(deck, extra);
Logger.WriteLine("Duel started: " + _room.Names[0] + " versus " + _room.Names[1]); Logger.DebugWriteLine("Duel started: " + _room.Names[0] + " versus " + _room.Names[1]);
_ai.OnStart(); _ai.OnStart();
} }
...@@ -248,7 +318,7 @@ namespace WindBot.Game ...@@ -248,7 +318,7 @@ namespace WindBot.Game
string otherName = _room.Position == 0 ? _room.Names[1] : _room.Names[0]; string otherName = _room.Position == 0 ? _room.Names[1] : _room.Names[0];
string textResult = (result == 2 ? "Draw" : result == 0 ? "Win" : "Lose"); string textResult = (result == 2 ? "Draw" : result == 0 ? "Win" : "Lose");
Logger.WriteLine("Duel finished against " + otherName + ", result: " + textResult); Logger.DebugWriteLine("Duel finished against " + otherName + ", result: " + textResult);
} }
private void OnDraw(BinaryReader packet) private void OnDraw(BinaryReader packet)
...@@ -278,6 +348,33 @@ namespace WindBot.Game ...@@ -278,6 +348,33 @@ namespace WindBot.Game
card.SetId(packet.ReadInt32()); card.SetId(packet.ReadInt32());
} }
private void OnTagSwap(BinaryReader packet)
{
int player = GetLocalPlayer(packet.ReadByte());
int mcount = packet.ReadByte();
int ecount = packet.ReadByte();
/*int pcount = */ packet.ReadByte();
int hcount = packet.ReadByte();
/*int topcode =*/ packet.ReadInt32();
_duel.Fields[player].Deck.Clear();
for (int i = 0; i < mcount; ++i)
{
_duel.Fields[player].Deck.Add(new ClientCard(0, CardLocation.Deck));
}
_duel.Fields[player].ExtraDeck.Clear();
for (int i = 0; i < ecount; ++i)
{
int code = packet.ReadInt32() & 0x7fffffff;
_duel.Fields[player].ExtraDeck.Add(new ClientCard(code, CardLocation.Extra));
}
_duel.Fields[player].Hand.Clear();
for (int i = 0; i < hcount; ++i)
{
int code = packet.ReadInt32();
_duel.Fields[player].Hand.Add(new ClientCard(code, CardLocation.Hand));
}
}
private void OnNewTurn(BinaryReader packet) private void OnNewTurn(BinaryReader packet)
{ {
_duel.Turn++; _duel.Turn++;
...@@ -355,6 +452,24 @@ namespace WindBot.Game ...@@ -355,6 +452,24 @@ namespace WindBot.Game
} }
} }
private void OnAttack(BinaryReader packet)
{
int ca = GetLocalPlayer(packet.ReadByte());
int la = packet.ReadByte();
int sa = packet.ReadByte();
packet.ReadByte(); //
packet.ReadByte(); // cd
int ld = packet.ReadByte();
packet.ReadByte(); // sd
packet.ReadByte(); //
ClientCard attackcard = _duel.GetCard(ca, (CardLocation)la, sa);
if (ld == 0 && (attackcard != null) && (ca != 0))
{
_ai.OnDirectAttack(attackcard);
}
}
private void OnPosChange(BinaryReader packet) private void OnPosChange(BinaryReader packet)
{ {
packet.ReadInt32(); // card id packet.ReadInt32(); // card id
...@@ -378,16 +493,24 @@ namespace WindBot.Game ...@@ -378,16 +493,24 @@ namespace WindBot.Game
ClientCard card = _duel.GetCard(pcc, pcl, pcs, subs); ClientCard card = _duel.GetCard(pcc, pcl, pcs, subs);
int cc = GetLocalPlayer(packet.ReadByte()); int cc = GetLocalPlayer(packet.ReadByte());
_ai.OnChaining(card, cc); _ai.OnChaining(card, cc);
_duel.ChainTargets.Clear();
} }
private void OnChainEnd(BinaryReader packet) private void OnChainEnd(BinaryReader packet)
{ {
_ai.OnChainEnd(); _ai.OnChainEnd();
//_duel.ChainTargets.Clear();
}
private void OnCardSorting(BinaryReader packet)
{
/*BinaryWriter writer =*/ GamePacketFactory.Create(CtosMessage.Response);
Connection.Send(CtosMessage.Response, -1);
} }
private void OnChainSorting(BinaryReader packet) private void OnChainSorting(BinaryReader packet)
{ {
BinaryWriter writer = GamePacketFactory.Create(CtosMessage.Response); /*BinaryWriter writer =*/ GamePacketFactory.Create(CtosMessage.Response);
Connection.Send(CtosMessage.Response, -1); Connection.Send(CtosMessage.Response, -1);
} }
...@@ -409,7 +532,6 @@ namespace WindBot.Game ...@@ -409,7 +532,6 @@ namespace WindBot.Game
{ {
int player = GetLocalPlayer(packet.ReadByte()); int player = GetLocalPlayer(packet.ReadByte());
CardLocation loc = (CardLocation)packet.ReadByte(); CardLocation loc = (CardLocation)packet.ReadByte();
IList<ClientCard> cards = null; IList<ClientCard> cards = null;
switch (loc) switch (loc)
{ {
...@@ -440,14 +562,29 @@ namespace WindBot.Game ...@@ -440,14 +562,29 @@ namespace WindBot.Game
foreach (ClientCard card in cards) foreach (ClientCard card in cards)
{ {
int len = packet.ReadInt32(); int len = packet.ReadInt32();
if (len == 4) continue;
long pos = packet.BaseStream.Position; long pos = packet.BaseStream.Position;
if (len > 8)
card.Update(packet, _duel); card.Update(packet, _duel);
packet.BaseStream.Position = pos + len - 4; packet.BaseStream.Position = pos + len - 4;
} }
} }
} }
private void OnBecomeTarget(BinaryReader packet)
{
int count = packet.ReadByte();
for (int i = 0; i < count; ++i)
{
int player = GetLocalPlayer(packet.ReadByte());
int loc = packet.ReadByte();
int seq = packet.ReadByte();
/*int sseq = */packet.ReadByte();
ClientCard card = _duel.GetCard(player, (CardLocation)loc, seq);
if (card == null) continue;
_duel.ChainTargets.Add(card);
}
}
private void OnSelectBattleCmd(BinaryReader packet) private void OnSelectBattleCmd(BinaryReader packet)
{ {
packet.ReadByte(); // player packet.ReadByte(); // player
...@@ -479,14 +616,25 @@ namespace WindBot.Game ...@@ -479,14 +616,25 @@ namespace WindBot.Game
int con = GetLocalPlayer(packet.ReadByte()); int con = GetLocalPlayer(packet.ReadByte());
CardLocation loc = (CardLocation)packet.ReadByte(); CardLocation loc = (CardLocation)packet.ReadByte();
int seq = packet.ReadByte(); int seq = packet.ReadByte();
packet.ReadByte(); // diratt int diratt = packet.ReadByte();
ClientCard card = _duel.GetCard(con, loc, seq); ClientCard card = _duel.GetCard(con, loc, seq);
if (card != null) if (card != null)
{ {
card.ActionIndex[1] = i; card.ActionIndex[1] = i;
battle.AttackableCards.Add(_duel.GetCard(con, loc, seq)); if (diratt > 0)
card.CanDirectAttack = true;
else
card.CanDirectAttack = false;
battle.AttackableCards.Add(card);
card.Attacked = false;
}
} }
List<ClientCard> monsters = _duel.Fields[0].GetMonsters();
foreach (ClientCard monster in monsters)
{
if (!battle.AttackableCards.Contains(monster))
monster.Attacked = true;
} }
battle.CanMainPhaseTwo = packet.ReadByte() != 0; battle.CanMainPhaseTwo = packet.ReadByte() != 0;
...@@ -576,7 +724,7 @@ namespace WindBot.Game ...@@ -576,7 +724,7 @@ namespace WindBot.Game
int con = GetLocalPlayer(packet.ReadByte()); int con = GetLocalPlayer(packet.ReadByte());
int loc = packet.ReadByte(); int loc = packet.ReadByte();
int seq = packet.ReadByte(); int seq = packet.ReadByte();
int sseq = 0; //packet.ReadByte(); int sseq = packet.ReadByte();
int desc = packet.ReadInt32(); int desc = packet.ReadInt32();
cards.Add(_duel.GetCard(con, loc, seq, sseq)); cards.Add(_duel.GetCard(con, loc, seq, sseq));
...@@ -619,10 +767,14 @@ namespace WindBot.Game ...@@ -619,10 +767,14 @@ namespace WindBot.Game
} }
IList<int> used = _ai.OnSelectCounter(type, quantity, cards, counters); IList<int> used = _ai.OnSelectCounter(type, quantity, cards, counters);
byte[] result = new byte[used.Count * 2];
for (int i = 0; i < used.Count; ++i)
{
result[i * 2] = (byte)(used[i] & 0xff);
result[i * 2 + 1] = (byte)(used[i] >> 8);
}
BinaryWriter reply = GamePacketFactory.Create(CtosMessage.Response); BinaryWriter reply = GamePacketFactory.Create(CtosMessage.Response);
for (int i = 0; i < quantity; ++i) reply.Write(result);
reply.Write((short)used[i]);
Connection.Send(reply); Connection.Send(reply);
} }
...@@ -640,6 +792,7 @@ namespace WindBot.Game ...@@ -640,6 +792,7 @@ namespace WindBot.Game
CardLocation loc = (CardLocation)packet.ReadByte(); CardLocation loc = (CardLocation)packet.ReadByte();
int seq = packet.ReadByte(); int seq = packet.ReadByte();
packet.ReadByte(); packet.ReadByte();
int desc = packet.ReadInt32();
ClientCard card = _duel.GetCard(player, loc, seq); ClientCard card = _duel.GetCard(player, loc, seq);
if (card == null) if (card == null)
...@@ -650,7 +803,7 @@ namespace WindBot.Game ...@@ -650,7 +803,7 @@ namespace WindBot.Game
if (card.Id == 0) card.SetId(cardId); if (card.Id == 0) card.SetId(cardId);
int reply = _ai.OnSelectEffectYn(card) ? (1) : (0); int reply = _ai.OnSelectEffectYn(card, desc) ? (1) : (0);
Connection.Send(CtosMessage.Response, reply); Connection.Send(CtosMessage.Response, reply);
} }
...@@ -740,11 +893,11 @@ namespace WindBot.Game ...@@ -740,11 +893,11 @@ namespace WindBot.Game
bool pendulumZone = false; bool pendulumZone = false;
int filter; int filter;
if ((field & 0x1f) != 0) if ((field & 0x7f) != 0)
{ {
resp[0] = (byte)GetLocalPlayer(0); resp[0] = (byte)GetLocalPlayer(0);
resp[1] = 0x4; resp[1] = 0x4;
filter = field & 0x1f; filter = field & 0x7f;
} }
else if ((field & 0x1f00) != 0) else if ((field & 0x1f00) != 0)
{ {
...@@ -759,11 +912,11 @@ namespace WindBot.Game ...@@ -759,11 +912,11 @@ namespace WindBot.Game
filter = (field >> 14) & 0x3; filter = (field >> 14) & 0x3;
pendulumZone = true; pendulumZone = true;
} }
else if ((field & 0x1f0000) != 0) else if ((field & 0x7f0000) != 0)
{ {
resp[0] = (byte)GetLocalPlayer(1); resp[0] = (byte)GetLocalPlayer(1);
resp[1] = 0x4; resp[1] = 0x4;
filter = (field >> 16) & 0x1f; filter = (field >> 16) & 0x7f;
} }
else if ((field & 0x1f000000) != 0) else if ((field & 0x1f000000) != 0)
{ {
...@@ -781,7 +934,9 @@ namespace WindBot.Game ...@@ -781,7 +934,9 @@ namespace WindBot.Game
if (!pendulumZone) if (!pendulumZone)
{ {
if ((filter & 0x4) != 0) resp[2] = 2; if ((filter & 0x40) != 0) resp[2] = 6;
else if ((filter & 0x20) != 0) resp[2] = 5;
else if ((filter & 0x4) != 0) resp[2] = 2;
else if ((filter & 0x2) != 0) resp[2] = 1; else if ((filter & 0x2) != 0) resp[2] = 1;
else if ((filter & 0x8) != 0) resp[2] = 3; else if ((filter & 0x8) != 0) resp[2] = 3;
else if ((filter & 0x1) != 0) resp[2] = 0; else if ((filter & 0x1) != 0) resp[2] = 0;
...@@ -822,12 +977,15 @@ namespace WindBot.Game ...@@ -822,12 +977,15 @@ namespace WindBot.Game
private void OnSelectSum(BinaryReader packet) private void OnSelectSum(BinaryReader packet)
{ {
packet.ReadByte(); // mode bool mode = packet.ReadByte() == 0;
packet.ReadByte(); // player packet.ReadByte(); // player
int sumval = packet.ReadInt32(); int sumval = packet.ReadInt32();
int min = packet.ReadByte(); int min = packet.ReadByte();
int max = packet.ReadByte(); int max = packet.ReadByte();
if (max <= 0)
max = 99;
IList<ClientCard> mandatoryCards = new List<ClientCard>(); IList<ClientCard> mandatoryCards = new List<ClientCard>();
IList<ClientCard> cards = new List<ClientCard>(); IList<ClientCard> cards = new List<ClientCard>();
...@@ -841,42 +999,47 @@ namespace WindBot.Game ...@@ -841,42 +999,47 @@ namespace WindBot.Game
CardLocation loc = (CardLocation)packet.ReadByte(); CardLocation loc = (CardLocation)packet.ReadByte();
int seq = packet.ReadByte(); int seq = packet.ReadByte();
ClientCard card = _duel.GetCard(player, loc, seq); ClientCard card = _duel.GetCard(player, loc, seq);
if (card != null)
{
if (cardId != 0 && card.Id != cardId) if (cardId != 0 && card.Id != cardId)
card.SetId(cardId); card.SetId(cardId);
card.SelectSeq = i;
int OpParam = packet.ReadInt32();
int OpParam1 = OpParam & 0xffff;
int OpParam2 = OpParam >> 16;
if (OpParam2 > 0 && OpParam1 > OpParam2)
{
card.OpParam1 = OpParam2;
card.OpParam2 = OpParam1;
}
else
{
card.OpParam1 = OpParam1;
card.OpParam2 = OpParam2;
} }
if (j == 0) if (j == 0)
mandatoryCards.Add(card); mandatoryCards.Add(card);
else else
cards.Add(card); cards.Add(card);
packet.ReadInt32();
} }
} }
IList<ClientCard> selected = _ai.OnSelectSum(cards, sumval, min, max); for (int k = 0; k < mandatoryCards.Count; ++k)
{
sumval -= mandatoryCards[k].OpParam1;
}
IList<ClientCard> selected = _ai.OnSelectSum(cards, sumval, min, max, mode);
byte[] result = new byte[mandatoryCards.Count + selected.Count + 1]; byte[] result = new byte[mandatoryCards.Count + selected.Count + 1];
int index = 0; int index = 0;
result[index++] = (byte)(mandatoryCards.Count + selected.Count); result[index++] = (byte)(mandatoryCards.Count + selected.Count);
while (index < mandatoryCards.Count) while (index <= mandatoryCards.Count)
{ {
result[index++] = 0; result[index++] = 0;
} }
for (int i = 0; i < selected.Count; ++i) for (int i = 0; i < selected.Count; ++i)
{ {
int id = 0; result[index++] = (byte)selected[i].SelectSeq;
for (int j = 0; j < cards.Count; ++j)
{
if (cards[j] == null) continue;
if (cards[j].Equals(selected[i]))
{
id = j;
break;
}
}
result[index++] = (byte)id;
} }
BinaryWriter reply = GamePacketFactory.Create(CtosMessage.Response); BinaryWriter reply = GamePacketFactory.Create(CtosMessage.Response);
...@@ -918,6 +1081,7 @@ namespace WindBot.Game ...@@ -918,6 +1081,7 @@ namespace WindBot.Game
private void OnAnnounceCard(BinaryReader packet) private void OnAnnounceCard(BinaryReader packet)
{ {
// not fully implemented
Connection.Send(CtosMessage.Response, _ai.OnAnnounceCard()); Connection.Send(CtosMessage.Response, _ai.OnAnnounceCard());
} }
...@@ -950,5 +1114,16 @@ namespace WindBot.Game ...@@ -950,5 +1114,16 @@ namespace WindBot.Game
reply += (int)races[i]; reply += (int)races[i];
Connection.Send(CtosMessage.Response, reply); Connection.Send(CtosMessage.Response, reply);
} }
private void OnRockPaperScissors(BinaryReader packet)
{
packet.ReadByte(); // player
int result;
if (_hand > 0)
result = _hand;
else
result = _ai.OnRockPaperScissors();
Connection.Send(CtosMessage.Response, result);
}
} }
} }
\ No newline at end of file
...@@ -12,20 +12,27 @@ namespace WindBot.Game ...@@ -12,20 +12,27 @@ namespace WindBot.Game
public YGOClient Connection { get; private set; } public YGOClient Connection { get; private set; }
public string Username; public string Username;
public string Deck; public string Deck;
public string Dialog;
public int Hand;
private string _serverHost; private string _serverHost;
private int _serverPort; private int _serverPort;
private string _roomInfos; private short _proVersion;
private string _roomInfo;
private GameBehavior _behavior; private GameBehavior _behavior;
public GameClient(string username, string deck, string serverHost, int serverPort, string roomInfos = "") public GameClient(WindBotInfo Info)
{ {
Username = username; Username = Info.Name;
Deck = deck; Deck = Info.Deck;
_serverHost = serverHost; Dialog = Info.Dialog;
_serverPort = serverPort; Hand = Info.Hand;
_roomInfos = roomInfos; _serverHost = Info.Host;
_serverPort = Info.Port;
_roomInfo = Info.HostInfo;
_proVersion = (short)Info.Version;
} }
public void Start() public void Start()
...@@ -42,14 +49,14 @@ namespace WindBot.Game ...@@ -42,14 +49,14 @@ namespace WindBot.Game
private void OnConnected() private void OnConnected()
{ {
BinaryWriter packet = GamePacketFactory.Create(CtosMessage.PlayerInfo); BinaryWriter packet = GamePacketFactory.Create(CtosMessage.PlayerInfo);
packet.WriteUnicode(Username, Program.PlayerNameSize); packet.WriteUnicode(Username, 20);
Connection.Send(packet); Connection.Send(packet);
byte[] junk = { 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00 }; byte[] junk = { 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00 };
packet = GamePacketFactory.Create(CtosMessage.JoinGame); packet = GamePacketFactory.Create(CtosMessage.JoinGame);
packet.Write(Program.ProVersion); packet.Write(_proVersion);
packet.Write(junk); packet.Write(junk);
packet.WriteUnicode(_roomInfos, 30); packet.WriteUnicode(_roomInfo, 30);
Connection.Send(packet); Connection.Send(packet);
} }
......
...@@ -6,7 +6,17 @@ namespace WindBot ...@@ -6,7 +6,17 @@ namespace WindBot
{ {
public static void WriteLine(string message) public static void WriteLine(string message)
{ {
Console.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss") + "] " + message); Console.WriteLine("[" + DateTime.Now.ToString("yy-MM-dd HH:mm:ss") + "] " + message);
}
public static void DebugWriteLine(string message)
{
#if DEBUG
Console.WriteLine("[" + DateTime.Now.ToString("yy-MM-dd HH:mm:ss") + "] " + message);
#endif
}
public static void WriteErrorLine(string message)
{
Console.Error.WriteLine("[" + DateTime.Now.ToString("yy-MM-dd HH:mm:ss") + "] " + message);
} }
} }
} }
\ No newline at end of file
using System; using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Net;
using System.Web;
using WindBot.Game; using WindBot.Game;
using WindBot.Game.AI; using WindBot.Game.AI;
using YGOSharp.OCGWrapper; using YGOSharp.OCGWrapper;
...@@ -9,56 +11,173 @@ namespace WindBot ...@@ -9,56 +11,173 @@ namespace WindBot
{ {
public class Program public class Program
{ {
public static short ProVersion = 0x133D;
public static int PlayerNameSize = 20;
internal static Random Rand; internal static Random Rand;
internal static void Main() internal static void Main(string[] args)
{ {
#if !DEBUG Logger.WriteLine("WindBot starting...");
try
Config.Load(args);
string databasePath = Config.GetString("DbPath", "cards.cdb");
InitDatas(databasePath);
bool serverMode = Config.GetBool("ServerMode", false);
if (serverMode)
{ {
Run(); // Run in server mode, provide a http interface to create bot.
int serverPort = Config.GetInt("ServerPort", 2399);
RunAsServer(serverPort);
} }
catch (Exception ex) else
{ {
Console.Error.WriteLine("Error: " + ex); // Join the host specified on the command line.
if (args.Length == 0)
{
Logger.WriteLine("=== WARN ===");
Logger.WriteLine("No input found, tring to connect to localhost YGOPro host.");
Logger.WriteLine("If it fail, the program will quit sliently.");
}
RunFromArgs();
} }
#else
Run();
#endif
} }
public static void Init(string databasePath) public static void InitDatas(string databasePath)
{ {
Rand = new Random(); Rand = new Random();
DecksManager.Init(); DecksManager.Init();
InitCardsManager(databasePath); string absolutePath = Path.GetFullPath(databasePath);
if (!File.Exists(absolutePath))
// In case windbot is placed in a folder under ygopro folder
absolutePath = Path.GetFullPath("../" + databasePath);
if (!File.Exists(absolutePath))
Logger.WriteErrorLine("Can't find cards database file. Please place cards.cdb next to WindBot.exe .");
NamedCardsManager.Init(absolutePath);
} }
private static void Run() private static void RunFromArgs()
{ {
Init("cards.cdb"); WindBotInfo Info = new WindBotInfo();
Info.Name = Config.GetString("Name", Info.Name);
Info.Deck = Config.GetString("Deck", Info.Deck);
Info.Dialog = Config.GetString("Dialog", Info.Dialog);
Info.Host = Config.GetString("Host", Info.Host);
Info.Port = Config.GetInt("Port", Info.Port);
Info.HostInfo = Config.GetString("HostInfo", Info.HostInfo);
Info.Version = Config.GetInt("Version", Info.Version);
Info.Hand = Config.GetInt("Hand", Info.Hand);
Run(Info);
}
// Start two clients and connect them to the same server. Which deck is gonna win? private static void RunAsServer(int ServerPort)
GameClient clientA = new GameClient("Wind", "Horus", "127.0.0.1", 7911); {
GameClient clientB = new GameClient("Fire", "OldSchool", "127.0.0.1", 7911); using (HttpListener MainServer = new HttpListener())
clientA.Start(); {
clientB.Start(); MainServer.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
while (clientA.Connection.IsConnected || clientB.Connection.IsConnected) MainServer.Prefixes.Add("http://127.0.0.1:" + ServerPort + "/");
MainServer.Start();
Logger.WriteLine("WindBot server start successed.");
Logger.WriteLine("HTTP GET http://127.0.0.1:" + ServerPort + "/?name=WindBot&host=127.0.0.1&port=7911 to call the bot.");
while (true)
{
#if !DEBUG
try
{
#endif
HttpListenerContext ctx = MainServer.GetContext();
WindBotInfo Info = new WindBotInfo();
string RawUrl = Path.GetFileName(ctx.Request.RawUrl);
Info.Name = HttpUtility.ParseQueryString(RawUrl).Get("name");
Info.Deck = HttpUtility.ParseQueryString(RawUrl).Get("deck");
Info.Host = HttpUtility.ParseQueryString(RawUrl).Get("host");
string port = HttpUtility.ParseQueryString(RawUrl).Get("port");
if (port != null)
Info.Port = Int32.Parse(port);
string dialog = HttpUtility.ParseQueryString(RawUrl).Get("dialog");
if (dialog != null)
Info.Dialog = dialog;
string version = HttpUtility.ParseQueryString(RawUrl).Get("version");
if (version != null)
Info.Version = Int16.Parse(version);
string password = HttpUtility.ParseQueryString(RawUrl).Get("password");
if (password != null)
Info.HostInfo = password;
string hand = HttpUtility.ParseQueryString(RawUrl).Get("hand");
if (hand != null)
Info.Hand = Int32.Parse(hand);
if (Info.Name == null || Info.Host == null || port == null)
{
ctx.Response.StatusCode = 400;
ctx.Response.Close();
}
else
{
#if !DEBUG
try
{
#endif
Thread workThread = new Thread(new ParameterizedThreadStart(Run));
workThread.Start(Info);
#if !DEBUG
}
catch (Exception ex)
{ {
clientA.Tick(); Logger.WriteErrorLine("Start Thread Error: " + ex);
clientB.Tick(); }
Thread.Sleep(1); #endif
ctx.Response.StatusCode = 200;
ctx.Response.Close();
}
#if !DEBUG
}
catch (Exception ex)
{
Logger.WriteErrorLine("Parse Http Request Error: " + ex);
}
#endif
}
} }
} }
private static void InitCardsManager(string databasePath) private static void Run(object o)
{ {
string currentPath = Path.GetFullPath("."); #if !DEBUG
string absolutePath = Path.Combine(currentPath, databasePath); try
NamedCardsManager.Init(absolutePath); {
//all errors will be catched instead of causing the program to crash.
#endif
WindBotInfo Info = (WindBotInfo)o;
GameClient client = new GameClient(Info);
client.Start();
Logger.DebugWriteLine(client.Username + " started.");
while (client.Connection.IsConnected)
{
#if !DEBUG
try
{
#endif
client.Tick();
Thread.Sleep(30);
#if !DEBUG
}
catch (Exception ex)
{
Logger.WriteErrorLine("Tick Error: " + ex);
}
#endif
}
Logger.DebugWriteLine(client.Username + " end.");
#if !DEBUG
}
catch (Exception ex)
{
Logger.WriteErrorLine("Run Error: " + ex);
}
#endif
} }
} }
} }
# WindBot # WindBot
A C# bot for ygopro, compatible with the ygosharp server. A C# bot for ygopro, compatible with the [YGOSharp](https://github.com/IceYGO/ygosharp) and [SRVPro](https://github.com/moecube/srvpro) server.
### How to use: ### How to use:
* Code whatever you want to code in the `Program.cs` file.
* Compile `WindBot.sln` using Visual Studio or Mono. * Compile `WindBot.sln` using Visual Studio or Mono.
* Put `cards.cdb` next to the compiled `WindBot.exe`. * Put `cards.cdb` next to the compiled `WindBot.exe`.
* Run and observe. * Run YGOPro, create a host.
* Run WindBot and observe.
### Supported commandlines
`Name`
The nickname for the bot.
`Deck`
The deck to be used by the bot. Available decks are listed below. Keep empty to use random deck.
`Dialog`
The dialog texts to be used by the bot. See Dialogs folder for list.
`Host`
The IP of the host to be connected to.
`Port`
The port of the host to be connected to.
`HostInfo`
The host info (password) to be used.
`Version`
The version of YGOPro.
`Hand`
If you are testing deck, you may want to make sure the bot go first or second. `Hand=1` will make the bot always show Scissors, 2 for Rock, 3 for Paper.
`ServerMode` and `ServerPort`
WindBot can run as a "server", provide a http interface to create bot.
### Available decks
**Easy**:
* Burn
* Frog
* Horus
* MokeyMokey
* MokeyMokeyKing
* OldSchool
**Normal**:
* Blue-Eyes
* Dragunity
* Qliphort
* Rainbow
* Rank V
* ST1732
* Toadally Awesome (old lflist)
* Yosenju
* Zexal Weapons
* Zoodiac (old lflist, master rule 3 only)
### Unfinished decks
* Blackwing
* CyberDragon
* Evilswarm
* Gravekeeper
* Graydle
* Lightsworn
* Nekroz
### Server mode
WindBot can run as a "server", provide a http interface to create bot.
eg. `http://127.0.0.1:2399/?name=%E2%91%A8&deck=Blue-Eyes&host=127.0.0.1&port=7911&dialog=cirno.zh-CN&version=4928`
In this situation, it will be multi-threaded. This can be useful for servers, since it don't use large amount memory.
The parameters are same as commandlines, but low cased.
### Known issues
* The attack won't be canceled when battle replay happens.
* If one chain includes two activation that use `AI.SelectCard`, the second one won't select correctly.
### TODO list
* More decks
* Documents for creating AI
* `AI.SelectZone`
* `AI.SelectMaterials` which select a set of cards for F/S/X/L summon
* `AI.SelectTribute`
* Select cards to pendulum summon in executor.
* Get equip of card.
* Get attack target.
* Better new master rule support
* Update the known card enums
* More default common cards executor
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
<PropertyGroup> <PropertyGroup>
<StartupObject /> <StartupObject />
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<ApplicationIcon>WindBot.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
...@@ -58,6 +61,9 @@ ...@@ -58,6 +61,9 @@
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="YGOSharp.Network"> <Reference Include="YGOSharp.Network">
<HintPath>.\YGOSharp.Network.dll</HintPath> <HintPath>.\YGOSharp.Network.dll</HintPath>
</Reference> </Reference>
...@@ -69,6 +75,7 @@ ...@@ -69,6 +75,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Config.cs" />
<Compile Include="Game\AI\AIFunctions.cs" /> <Compile Include="Game\AI\AIFunctions.cs" />
<Compile Include="Game\AI\CardContainer.cs" /> <Compile Include="Game\AI\CardContainer.cs" />
<Compile Include="Game\AI\CardExecutor.cs" /> <Compile Include="Game\AI\CardExecutor.cs" />
...@@ -86,12 +93,11 @@ ...@@ -86,12 +93,11 @@
<Compile Include="Game\AI\DefaultExecutor.cs" /> <Compile Include="Game\AI\DefaultExecutor.cs" />
<Compile Include="Game\AI\Dialogs.cs" /> <Compile Include="Game\AI\Dialogs.cs" />
<Compile Include="Game\AI\Enums\DangerousMonster.cs" /> <Compile Include="Game\AI\Enums\DangerousMonster.cs" />
<Compile Include="Game\AI\Enums\FusionSpell.cs" />
<Compile Include="Game\AI\Enums\PreventActivationEffectInBattle.cs" />
<Compile Include="Game\AI\Enums\OneForXyz.cs" />
<Compile Include="Game\AI\Enums\InvincibleMonster.cs" /> <Compile Include="Game\AI\Enums\InvincibleMonster.cs" />
<Compile Include="Game\AI\Enums\NegateAttackSpell.cs" /> <Compile Include="Game\AI\Enums\Floodgate.cs" />
<Compile Include="Game\AI\Enums\NegatesEffects.cs" />
<Compile Include="Game\AI\Enums\NegatesSpells.cs" />
<Compile Include="Game\AI\Enums\NegatesSummons.cs" />
<Compile Include="Game\AI\Enums\NegatesTraps.cs" />
<Compile Include="Game\AI\Executor.cs" /> <Compile Include="Game\AI\Executor.cs" />
<Compile Include="Game\AI\ExecutorType.cs" /> <Compile Include="Game\AI\ExecutorType.cs" />
<Compile Include="Game\BattlePhase.cs" /> <Compile Include="Game\BattlePhase.cs" />
...@@ -110,36 +116,25 @@ ...@@ -110,36 +116,25 @@
<Compile Include="Logger.cs" /> <Compile Include="Logger.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WindBotInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />
<None Include="Decks\AI_Burn.ydk"> <None Include="sqlite3.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Decks\AI_Dragunity.ydk">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Decks\AI_Frog.ydk">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Decks\AI_Horus.ydk">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Decks\AI_OldSchool.ydk">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Decks\AI_Rank5.ydk"> <None Include="Decks\*.ydk">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Decks\AI_ZexalWeapons.ydk">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="sqlite3.dll"> <None Include="Dialogs\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="WindBot.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
......
WindBot.ico

17.1 KB

using System;
namespace WindBot
{
public class WindBotInfo
{
public string Name { get; set; }
public string Deck { get; set; }
public string Dialog { get; set; }
public string Host { get; set; }
public int Port { get; set; }
public string HostInfo { get; set; }
public int Version { get; set; }
public int Hand { get; set; }
public WindBotInfo()
{
Name = "WindBot";
Deck = null;
Dialog = "default";
Host = "127.0.0.1";
Port = 7911;
HostInfo = "";
Version = 0x1340;
Hand = 0;
}
}
}
No preview for this file type
No preview for this file type
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment