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}吗?"
]
}
This diff is collapsed.
using System.Collections.Generic;
using YGOSharp.OCGWrapper.Enums;
using System.Linq;
namespace WindBot.Game.AI
{
......@@ -11,7 +12,7 @@ namespace WindBot.Game.AI
ClientCard selected = null;
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)
{
highestAtk = card.Attack;
......@@ -27,7 +28,7 @@ namespace WindBot.Game.AI
ClientCard selected = null;
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)
{
highestDef = card.Defense;
......@@ -43,7 +44,7 @@ namespace WindBot.Game.AI
ClientCard selected = null;
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) ||
card.HasType(CardType.Monster) && card.Attack < lowestAtk)
{
......@@ -60,7 +61,7 @@ namespace WindBot.Game.AI
ClientCard selected = null;
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) ||
card.HasType(CardType.Monster) && card.Defense < lowestDef)
{
......@@ -120,9 +121,9 @@ namespace WindBot.Game.AI
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)
{
......@@ -130,7 +131,20 @@ namespace WindBot.Game.AI
continue;
if (card.HasType(CardType.Monster))
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;
}
......@@ -139,20 +153,37 @@ namespace WindBot.Game.AI
{
foreach (ClientCard card in cards)
{
if (card != null && card.IsMonsterInvincible())
if (card != null && card.IsMonsterInvincible() && card.IsFaceup())
return card;
}
return null;
}
public static ClientCard GetNegateAttackSpell(this IEnumerable<ClientCard> cards)
public static ClientCard GetDangerousMonster(this IEnumerable<ClientCard> cards)
{
foreach (ClientCard card in cards)
{
if (card != null && card.IsSpellNegateAttack())
if (card != null && card.IsMonsterDangerous() && card.IsFaceup())
return card;
}
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
{
public static class CardExtension
{
/// <summary>
/// Is this monster is invincible to battle?
/// </summary>
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)
{
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
{
public string Name { get; private set; }
public string File { get; private set; }
public string Level { get; private set; }
public DeckAttribute(string name, string file = null)
public DeckAttribute(string name, string file = null, string level = "Normal")
{
if (String.IsNullOrEmpty(file))
file = name;
Name = name;
File = file;
Level = level;
}
}
}
......@@ -10,11 +10,13 @@ namespace WindBot.Game.AI
{
public string Deck { 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;
Type = type;
Level = level;
}
}
......@@ -38,7 +40,7 @@ namespace WindBot.Game.AI
{
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
if (deck != null && _decks.ContainsKey(deck))
infos = _decks[deck];
else
infos = _list[_rand.Next(_list.Count)];
{
do
{
infos = _list[_rand.Next(_list.Count)];
}
while (infos.Level != "Normal");
}
Executor executor = (Executor)Activator.CreateInstance(infos.Type, ai, duel);
executor.Deck = infos.Deck;
......
This diff is collapsed.
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
{
[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
{
private GameClient _game;
private string[] _welcome;
private string[] _deckerror;
private string[] _duelstart;
private string[] _newturn;
private string[] _endturn;
private string[] _directattack;
private string[] _attack;
private string[] _ondirectattack;
private string _facedownmonstername;
private string[] _activate;
private string[] _summon;
private string[] _setmonster;
......@@ -19,75 +57,43 @@ namespace WindBot.Game.AI
public Dialogs(GameClient game)
{
_game = game;
_duelstart = new[]
{
"Good luck, have fun."
};
_newturn = new[]
{
"It's my turn, draw.",
"My turn, draw.",
"I draw a card."
};
_endturn = new[]
{
"I end my turn.",
"My turn is over.",
"Your turn."
};
_directattack = new[]
{
"{0}, direct attack!",
"{0}, attack him directly!",
"{0}, he's defenseless, attack!",
"{0}, attack his life points!",
"{0}, attack his life points directly!",
"{0}, attack him through a direct attack!",
"{0}, attack him using a direct attack!",
"{0}, unleash your power through a direct attack!",
"My {0} is going to smash your life points!",
"Show your power to my opponent, {0}!",
"You can't stop me. {0}, attack!"
};
_attack = new[]
{
"{0}, attack this {1}!",
"{0}, destroy this {1}!",
"{0}, charge the {1}!",
"{0}, strike that {1}!",
"{0}, unleash your power on this {1}!"
};
_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}?"
};
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DialogsData));
string dialogfilename = game.Dialog;
using (FileStream fs = File.OpenRead("Dialogs/" + dialogfilename + ".json"))
{
DialogsData data = (DialogsData)serializer.ReadObject(fs);
_welcome = data.welcome;
_deckerror = data.deckerror;
_duelstart = data.duelstart;
_newturn = data.newturn;
_endturn = data.endturn;
_directattack = data.directattack;
_attack = data.attack;
_ondirectattack = data.ondirectattack;
_facedownmonstername = data.facedownmonstername;
_activate = data.activate;
_summon = data.summon;
_setmonster = data.setmonster;
_chaining = data.chaining;
}
}
public void SendSorry()
{
InternalSendMessage(new[] { "Sorry, an error occurs." });
}
public void SendDeckSorry(string card)
{
if (card == "DECK")
InternalSendMessage(new[] { "Deck illegal. Please check the database of your YGOPro and WindBot." });
else
InternalSendMessage(_deckerror, card);
}
public void SendWelcome()
{
InternalSendMessage(_welcome);
}
public void SendDuelStart()
......@@ -112,9 +118,26 @@ namespace WindBot.Game.AI
public void SendAttack(string attacker, string defender)
{
if (defender=="monster")
{
defender = _facedownmonstername;
}
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)
{
InternalSendMessage(_activate, spell);
......@@ -138,7 +161,8 @@ namespace WindBot.Game.AI
private void InternalSendMessage(IList<string> array, params object[] opts)
{
string message = string.Format(array[Program.Rand.Next(array.Count)], opts);
_game.Chat(message);
if (message != "")
_game.Chat(message);
}
}
}
\ No newline at end of file
}
namespace WindBot.Game.AI.Enums
{
/// <summary>
/// Cards that are dangerous to attack.
/// </summary>
public enum DangerousMonster
{
LionHeart = 54366836,
Yubel = 78371393,
YubelIncarnate = 4779091,
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.Collections.Generic;
using YGOSharp.OCGWrapper.Enums;
using WindBot;
using WindBot.Game;
using WindBot.Game.AI;
namespace WindBot.Game.AI
{
......@@ -19,7 +22,10 @@ namespace WindBot.Game.AI
protected int ActivateDescription { 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)
{
......@@ -29,48 +35,75 @@ namespace WindBot.Game.AI
LastChainPlayer = -1;
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()
{
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)
{
if (attackers.Count == 0)
return AI.ToMainPhase2();
if (defenders.Count == 0)
return AI.Attack(attackers[0], null);
for (int i = defenders.Count - 1; i >= 0; --i)
{
ClientCard defender = defenders[i];
int value = defender.GetDefensePower();
for (int j = 0; j < attackers.Count; ++j)
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)
{
ClientCard defender = defenders[i];
for (int j = 0; j < attackers.Count; ++j)
{
ClientCard attacker = attackers[j];
attacker.RealPower = attacker.Attack;
defender.RealPower = defender.GetDefensePower();
if (!OnPreBattleBetween(attacker, defender))
continue;
if (attacker.RealPower > defender.RealPower || (attacker.RealPower >= defender.RealPower && j == attackers.Count - 1))
return AI.Attack(attacker, defender);
}
}
for (int i = attackers.Count - 1; i >= 0; --i)
{
ClientCard attacker = attackers[j];
if (!OnPreBattleBetween(attacker, defender))
continue;
if (attacker.Attack > value || (attacker.Attack >= value && j == attackers.Count - 1))
return AI.Attack(attacker, defender);
ClientCard attacker = attackers[i];
if (attacker.CanDirectAttack)
return AI.Attack(attacker, null);
}
}
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();
}
public virtual bool OnPreBattleBetween(ClientCard attacker, ClientCard defender)
{
if (defender.IsMonsterInvincible())
{
if (defender.IsMonsterDangerous() || defender.IsDefense())
return false;
}
// Overrided in DefalultExecutor
return true;
}
......@@ -86,8 +119,20 @@ namespace WindBot.Game.AI
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)
{
// For overriding
return null;
}
public virtual IList<ClientCard> OnSelectSum(IList<ClientCard> cards, int sum, int min, int max, bool mode)
{
// For overriding
return null;
}
......@@ -96,6 +141,11 @@ namespace WindBot.Game.AI
return true;
}
public virtual int OnSelectOption(IList<int> options)
{
return -1;
}
public bool ChainContainsCard(int id)
{
foreach (ClientCard card in CurrentChain)
......@@ -144,6 +194,9 @@ namespace WindBot.Game.AI
Battle = battle;
}
/// <summary>
/// Set global variables Type, Card, ActivateDescription for Executor
/// </summary>
public void SetCard(ExecutorType type, ClientCard card, int description)
{
Type = type;
......@@ -151,21 +204,33 @@ namespace WindBot.Game.AI
ActivateDescription = description;
}
/// <summary>
/// Do the action for the card if func return true.
/// </summary>
public void AddExecutor(ExecutorType type, int cardId, Func<bool> 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)
{
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)
{
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)
{
Executors.Add(new CardExecutor(type, -1, DefaultNoExecutor));
......
......@@ -21,11 +21,21 @@ namespace WindBot.Game
public int Race { get; private set; }
public int Attack { get; private set; }
public int Defense { get; private set; }
public int LScale { get; private set; }
public int RScale { get; private set; }
public int BaseAttack { get; private set; }
public int BaseDefense { get; private set; }
public int RealPower { get; set; }
public List<int> Overlays { get; private set; }
public int Owner { 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 IDictionary<int, int> ActionActivateIndex { get; private set; }
......@@ -114,13 +124,13 @@ namespace WindBot.Game
if ((flag & (int)Query.Owner) != 0)
Owner = duel.GetLocalPlayer(packet.ReadInt32());
if ((flag & (int)Query.IsDisabled) != 0)
packet.ReadInt32();
Disabled = packet.ReadInt32();
if ((flag & (int)Query.IsPublic) != 0)
packet.ReadInt32();
if ((flag & (int)Query.LScale) != 0)
packet.ReadInt32();
LScale = packet.ReadInt32();
if ((flag & (int)Query.RScale) != 0)
packet.ReadInt32();
RScale = packet.ReadInt32();
}
public bool HasType(CardType type)
......@@ -158,6 +168,11 @@ namespace WindBot.Game
return (HasType(CardType.Fusion) || HasType(CardType.Synchro) || HasType(CardType.Xyz));
}
public bool IsFaceup()
{
return HasPosition(CardPosition.FaceUp);
}
public bool IsFacedown()
{
return HasPosition(CardPosition.FaceDown);
......@@ -173,6 +188,26 @@ namespace WindBot.Game
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()
{
return IsAttack() ? Attack : Defense;
......
using System.Collections.Generic;
using System.Collections.Generic;
using YGOSharp.OCGWrapper.Enums;
namespace WindBot.Game
......@@ -14,18 +14,19 @@ namespace WindBot.Game
public IList<ClientCard> ExtraDeck { get; private set; }
public ClientField()
{
}
public void Init(int deck, int extra)
{
Hand = new List<ClientCard>();
MonsterZone = new ClientCard[5];
MonsterZone = new ClientCard[7];
SpellZone = new ClientCard[8];
Graveyard = new List<ClientCard>();
Banished = new List<ClientCard>();
Deck = new List<ClientCard>();
ExtraDeck = new List<ClientCard>();
}
public void Init(int deck, int extra)
{
for (int i = 0; i < deck; ++i)
Deck.Add(new ClientCard(0, CardLocation.Deck));
for (int i = 0; i < extra; ++i)
......@@ -42,6 +43,11 @@ namespace WindBot.Game
return GetCount(SpellZone);
}
public int GetHandCount()
{
return GetCount(Hand);
}
public int GetSpellCountWithoutField()
{
int count = 0;
......@@ -83,15 +89,66 @@ namespace WindBot.Game
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)
{
return HasInCards(Hand, cardId);
}
public bool HasInHand(IList<int> cardId)
{
return HasInCards(Hand, cardId);
}
public bool HasInGraveyard(int 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()
{
......@@ -115,27 +172,40 @@ namespace WindBot.Game
return false;
}
public bool HasInMonstersZone(int cardId)
public bool HasInMonstersZone(int cardId, bool notDisabled = false, bool hasXyzMaterial = false)
{
return HasInCards(MonsterZone, cardId, notDisabled, hasXyzMaterial);
}
public bool HasInMonstersZone(IList<int> cardId, bool notDisabled = false, bool hasXyzMaterial = false)
{
return HasInCards(MonsterZone, cardId, notDisabled, hasXyzMaterial);
}
public bool HasInSpellZone(int cardId, bool notDisabled = false)
{
return HasInCards(MonsterZone, cardId);
return HasInCards(SpellZone, cardId, notDisabled);
}
public bool HasInSpellZone(int cardId)
public bool HasInSpellZone(IList<int> cardId, bool notDisabled = false)
{
return HasInCards(SpellZone, cardId);
return HasInCards(SpellZone, cardId, notDisabled);
}
public int GetRemainingCount(int cardId, int initialCount)
{
int remaining = initialCount;
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--;
foreach (ClientCard card in Graveyard)
if (card.Id == cardId)
if (card != null && card.Id == cardId)
remaining--;
foreach (ClientCard card in Banished)
if (card.Id == cardId)
if (card != null && card.Id == cardId)
remaining--;
return (remaining < 0) ? 0 : remaining;
}
......@@ -151,6 +221,28 @@ namespace WindBot.Game
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)
{
List<ClientCard> nCards = new List<ClientCard>();
......@@ -173,11 +265,21 @@ namespace WindBot.Game
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)
{
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 false;
......
......@@ -6,6 +6,7 @@ namespace WindBot.Game
public class Duel
{
public bool IsFirst { get; set; }
public bool IsNewRule { get; set; }
public int[] LifePoints { get; private set; }
public ClientField[] Fields { get; private set; }
......@@ -15,6 +16,8 @@ namespace WindBot.Game
public DuelPhase Phase { get; set; }
public MainPhase MainPhase { get; set; }
public BattlePhase BattlePhase { get; set; }
public IList<ClientCard> ChainTargets { get; set; }
public int LastSummonPlayer { get; set; }
public Duel()
{
......@@ -22,6 +25,8 @@ namespace WindBot.Game
Fields = new ClientField[2];
Fields[0] = new ClientField();
Fields[1] = new ClientField();
ChainTargets = new List<ClientCard>();
LastSummonPlayer = -1;
}
public ClientCard GetCard(int player, CardLocation loc, int index)
......
This diff is collapsed.
This diff is collapsed.
......@@ -12,20 +12,27 @@ namespace WindBot.Game
public YGOClient Connection { get; private set; }
public string Username;
public string Deck;
public string Dialog;
public int Hand;
private string _serverHost;
private int _serverPort;
private string _roomInfos;
private short _proVersion;
private string _roomInfo;
private GameBehavior _behavior;
public GameClient(string username, string deck, string serverHost, int serverPort, string roomInfos = "")
public GameClient(WindBotInfo Info)
{
Username = username;
Deck = deck;
_serverHost = serverHost;
_serverPort = serverPort;
_roomInfos = roomInfos;
Username = Info.Name;
Deck = Info.Deck;
Dialog = Info.Dialog;
Hand = Info.Hand;
_serverHost = Info.Host;
_serverPort = Info.Port;
_roomInfo = Info.HostInfo;
_proVersion = (short)Info.Version;
}
public void Start()
......@@ -42,14 +49,14 @@ namespace WindBot.Game
private void OnConnected()
{
BinaryWriter packet = GamePacketFactory.Create(CtosMessage.PlayerInfo);
packet.WriteUnicode(Username, Program.PlayerNameSize);
packet.WriteUnicode(Username, 20);
Connection.Send(packet);
byte[] junk = { 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00 };
packet = GamePacketFactory.Create(CtosMessage.JoinGame);
packet.Write(Program.ProVersion);
packet.Write(_proVersion);
packet.Write(junk);
packet.WriteUnicode(_roomInfos, 30);
packet.WriteUnicode(_roomInfo, 30);
Connection.Send(packet);
}
......
......@@ -6,7 +6,17 @@ namespace WindBot
{
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.IO;
using System.Threading;
using System.Net;
using System.Web;
using WindBot.Game;
using WindBot.Game.AI;
using YGOSharp.OCGWrapper;
......@@ -9,56 +11,173 @@ namespace WindBot
{
public class Program
{
public static short ProVersion = 0x133D;
public static int PlayerNameSize = 20;
internal static Random Rand;
internal static void Main()
internal static void Main(string[] args)
{
#if !DEBUG
try
Logger.WriteLine("WindBot starting...");
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();
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?
GameClient clientA = new GameClient("Wind", "Horus", "127.0.0.1", 7911);
GameClient clientB = new GameClient("Fire", "OldSchool", "127.0.0.1", 7911);
clientA.Start();
clientB.Start();
while (clientA.Connection.IsConnected || clientB.Connection.IsConnected)
private static void RunAsServer(int ServerPort)
{
using (HttpListener MainServer = new HttpListener())
{
clientA.Tick();
clientB.Tick();
Thread.Sleep(1);
MainServer.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
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)
{
Logger.WriteErrorLine("Start Thread Error: " + ex);
}
#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(".");
string absolutePath = Path.Combine(currentPath, databasePath);
NamedCardsManager.Init(absolutePath);
#if !DEBUG
try
{
//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
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:
* Code whatever you want to code in the `Program.cs` file.
* Compile `WindBot.sln` using Visual Studio or Mono.
* 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 @@
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>WindBot.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath>
......@@ -58,6 +61,9 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="YGOSharp.Network">
<HintPath>.\YGOSharp.Network.dll</HintPath>
</Reference>
......@@ -69,6 +75,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Config.cs" />
<Compile Include="Game\AI\AIFunctions.cs" />
<Compile Include="Game\AI\CardContainer.cs" />
<Compile Include="Game\AI\CardExecutor.cs" />
......@@ -86,12 +93,11 @@
<Compile Include="Game\AI\DefaultExecutor.cs" />
<Compile Include="Game\AI\Dialogs.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\NegateAttackSpell.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\Enums\Floodgate.cs" />
<Compile Include="Game\AI\Executor.cs" />
<Compile Include="Game\AI\ExecutorType.cs" />
<Compile Include="Game\BattlePhase.cs" />
......@@ -110,36 +116,25 @@
<Compile Include="Logger.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WindBotInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="Decks\AI_Burn.ydk">
<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">
<None Include="sqlite3.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Include="Decks\AI_Rank5.ydk">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Decks\AI_ZexalWeapons.ydk">
<None Include="Decks\*.ydk">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="sqlite3.dll">
<None Include="Dialogs\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="WindBot.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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.
......
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