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 YGOSharp.OCGWrapper.Enums;
namespace WindBot.Game.AI
{
public class AIFunctions
{
public Duel Duel { get; private set; }
public ClientField Bot { get; private set; }
public ClientField Enemy { get; private set; }
public AIFunctions(Duel duel)
{
Duel = duel;
Bot = Duel.Fields[0];
Enemy = Duel.Fields[1];
}
public static int CompareCardAttack(ClientCard cardA, ClientCard cardB)
......@@ -37,52 +42,259 @@ namespace WindBot.Game.AI
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)
return false;
List<ClientCard> monsters = Duel.Fields[0].GetMonsters();
monsters.Sort(CompareCardAttack);
int bestAtk = -1;
if (monsters.Count > 0)
bestAtk = monsters[monsters.Count - 1].Attack;
if (all)
return IsAllEnnemyBetterThanValue(bestAtk, onlyatk);
return IsOneEnnemyBetterThanValue(bestAtk, onlyatk);
int bestPower = -1;
for (int i = 0; i < 7; ++i)
{
ClientCard card = field.MonsterZone[i];
if (card == null || card.Data == null) continue;
if (onlyATK && card.IsDefense()) continue;
int newPower = card.GetDefensePower();
if (newPower > bestPower)
bestPower = newPower;
}
return bestPower;
}
public bool IsOneEnnemyBetterThanValue(int value, bool onlyatk)
public int GetBestAttack(ClientField field)
{
return GetBestPower(field, true);
}
public bool IsOneEnemyBetterThanValue(int value, bool onlyATK)
{
int bestValue = -1;
bool nomonster = true;
for (int i = 0; i < 5; ++i)
for (int i = 0; i < 7; ++i)
{
ClientCard card = Duel.Fields[1].MonsterZone[i];
if (card == null) continue;
if (onlyatk && card.IsDefense()) continue;
ClientCard card = Enemy.MonsterZone[i];
if (card == null || card.Data == null) continue;
if (onlyATK && card.IsDefense()) continue;
nomonster = false;
int ennemyValue = card.GetDefensePower();
if (ennemyValue > bestValue)
bestValue = ennemyValue;
int enemyValue = card.GetDefensePower();
if (enemyValue > bestValue)
bestValue = enemyValue;
}
if (nomonster) return false;
return bestValue > value;
}
public bool IsAllEnnemyBetterThanValue(int value, bool onlyatk)
public bool IsAllEnemyBetterThanValue(int value, bool onlyATK)
{
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 (onlyatk && card.IsDefense()) continue;
if (onlyATK && card.IsDefense()) continue;
nomonster = false;
int ennemyValue = card.GetDefensePower();
if (ennemyValue <= value)
int enemyValue = card.GetDefensePower();
if (enemyValue <= value)
return false;
}
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 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;
......
using System;
using System.Collections.Generic;
using YGOSharp.OCGWrapper.Enums;
using WindBot;
using WindBot.Game;
using WindBot.Game.AI;
namespace WindBot.Game.AI
{
public abstract class DefaultExecutor : Executor
{
protected class _CardId
{
public const int JizukirutheStarDestroyingKaiju = 63941210;
public const int 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)
: base(ai, duel)
{
AddExecutor(ExecutorType.Activate, _CardId.ChickenGame, DefaultChickenGame);
}
private enum CardId
/// <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)
{
MysticalSpaceTyphoon = 5318639
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;
}
if (!defender.IsMonsterHasPreventActivationEffectInBattle())
{
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()
{
foreach (ClientCard card in CurrentChain)
if (card.Id == (int)CardId.MysticalSpaceTyphoon)
if (card.Id == _CardId.MysticalSpaceTyphoon)
return false;
return DefaultStampingDestruction();
List<ClientCard> spells = Enemy.GetSpells();
if (spells.Count == 0)
return false;
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)
return false;
ClientCard selected = null;
foreach (ClientCard card in spells)
if (Card.Location == CardLocation.Grave)
{
if (card.IsSpellNegateAttack())
selected = Enemy.SpellZone.GetFloodgate();
if (selected == null)
{
selected = card;
break;
foreach (ClientCard card in spells)
{
if (!card.IsFacedown())
{
selected = card;
break;
}
}
}
}
if (selected == null)
else
{
foreach (ClientCard card in spells)
{
if (Duel.Player == 1 && !card.HasType(CardType.Continuous))
continue;
selected = card;
if (Duel.Player == 0 && card.IsFacedown())
if (card.IsFacedown())
{
selected = card;
break;
}
}
}
if (selected == null)
return false;
AI.SelectCard(selected);
return true;
}
/// <summary>
/// Set the highest ATK level 4+ effect enemy monster.
/// </summary>
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))
{
AI.SelectCard(monster);
......@@ -73,84 +196,561 @@ namespace WindBot.Game.AI
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()
{
return (AI.Utils.IsEnnemyBetter(true, true));
return !HasChainedTrap(0) && AI.Utils.IsAllEnemyBetter(true);
}
/// <summary>
/// Activate enemy have more S&T.
/// </summary>
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()
{
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()
{
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()
{
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()
{
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()
{
if (!UniqueFaceupMonster())
return false;
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.Attack < Card.Attack)
if (tributeCard.GetDefensePower() < Card.Attack)
tributecount--;
}
return tributecount <= 0;
}
/// <summary>
/// Activate when we have no field.
/// </summary>
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()
{
bool ennemyBetter = AI.Utils.IsEnnemyBetter(true, true);
bool enemyBetter = AI.Utils.IsAllEnemyBetter(true);
if (Card.IsAttack() && ennemyBetter)
if (Card.IsAttack() && enemyBetter)
return true;
if (Card.IsDefense() && !ennemyBetter && Card.Attack >= Card.Defense)
if (Card.IsDefense() && !enemyBetter && Card.Attack >= Card.Defense)
return true;
return false;
}
/// <summary>
/// Chain enemy activation or summon.
/// </summary>
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()
{
if (HasChainedTrap(0))
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()
{
foreach (ClientCard card in Bot.GetSpells())
{
if (card != null &&
card.Id == Card.Id &&
card.HasPosition(CardPosition.FaceUp))
if (card.Id == Card.Id && card.IsFaceup())
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;
}
/// <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
{
[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)
......
using System.Collections.Generic;
using System.Linq;
using System.Collections.Generic;
using WindBot.Game.AI;
using YGOSharp.OCGWrapper.Enums;
......@@ -22,6 +23,27 @@ namespace WindBot.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>
/// Called when the duel starts.
/// </summary>
......@@ -30,10 +52,19 @@ namespace WindBot.Game
_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>
/// Called when the AI won the rock-paper-scissors.
/// </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()
{
return Executor.OnSelectHand();
......@@ -44,6 +75,7 @@ namespace WindBot.Game
/// </summary>
public void OnNewTurn()
{
Executor.OnNewTurn();
}
/// <summary>
......@@ -54,9 +86,21 @@ namespace WindBot.Game
m_selector = null;
m_nextSelector = null;
m_option = -1;
m_yesno = -1;
m_position = CardPosition.FaceUpAttack;
Duel.LastSummonPlayer = -1;
if (Duel.Player == 0 && Duel.Phase == DuelPhase.Draw)
{
_dialogs.SendNewTurn();
}
}
/// <summary>
/// Called when the AI got attack directly.
/// </summary>
public void OnDirectAttack(ClientCard card)
{
_dialogs.SendOnDirectAttack(card.Name);
}
/// <summary>
......@@ -66,6 +110,7 @@ namespace WindBot.Game
/// <param name="player">Player who is currently chaining.</param>
public void OnChaining(ClientCard card, int player)
{
Duel.LastSummonPlayer = -1;
Executor.OnChaining(player,card);
}
......@@ -118,7 +163,7 @@ namespace WindBot.Game
public IList<ClientCard> OnSelectCard(IList<ClientCard> cards, int min, int max, bool cancelable)
{
// 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)
return result;
......@@ -198,7 +243,7 @@ namespace WindBot.Game
/// </summary>
/// <param name="card">Card to activate.</param>
/// <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)
{
......@@ -245,6 +290,7 @@ namespace WindBot.Game
if (ShouldExecute(exec, card, ExecutorType.SpSummon))
{
_dialogs.SendSummon(card.Name);
Duel.LastSummonPlayer = 0;
return new MainPhaseAction(MainPhaseAction.MainAction.SpSummon, card.ActionIndex);
}
}
......@@ -253,17 +299,19 @@ namespace WindBot.Game
if (ShouldExecute(exec, card, ExecutorType.Summon))
{
_dialogs.SendSummon(card.Name);
Duel.LastSummonPlayer = 0;
return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex);
}
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))
{
_dialogs.SendSetMonster();
return new MainPhaseAction(MainPhaseAction.MainAction.SetMonster, card.ActionIndex);
}
_dialogs.SendSummon(card.Name);
Duel.LastSummonPlayer = 0;
return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex);
}
}
......@@ -288,8 +336,13 @@ namespace WindBot.Game
/// <returns>Index of the selected option.</returns>
public int OnSelectOption(IList<int> options)
{
if (m_option != -1)
if (m_option != -1 && m_option < options.Count)
return m_option;
int result = Executor.OnSelectOption(options);
if (result != -1)
return result;
return 0; // Always select the first option.
}
......@@ -312,23 +365,130 @@ namespace WindBot.Game
}
/// <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>
/// <param name="cards">Available cards.</param>
/// <param name="sum">Result of the operation.</param>
/// <param name="min">Minimum cards.</param>
/// <param name="max">Maximum cards.</param>
/// <param name="mode">True for exact equal.</param>
/// <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)
{
// Always return one card. The first available.
foreach (ClientCard card in cards)
IList<ClientCard> selected = Executor.OnSelectSum(cards, sum, min, max, mode);
if (selected != null)
{
if (card.Level == sum)
return new[] { card };
return selected;
}
// However return everything, that may work.
return cards;
if (mode)
{
// equal
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
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;
}
// 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>
......@@ -361,6 +521,8 @@ namespace WindBot.Game
/// <returns>True for yes, false for no.</returns>
public bool OnSelectYesNo(int desc)
{
if (m_yesno != -1)
return m_yesno > 0;
return Executor.OnSelectYesNo(desc);
}
......@@ -380,10 +542,12 @@ namespace WindBot.Game
private CardSelector m_selector;
private CardSelector m_nextSelector;
private CardSelector m_thirdSelector;
private CardPosition m_position = CardPosition.FaceUpAttack;
private int m_option;
private int m_number;
private int m_announce;
private int m_yesno;
private IList<CardAttribute> m_attributes = new List<CardAttribute>();
private IList<CardRace> m_races = new List<CardRace>();
......@@ -437,17 +601,45 @@ namespace WindBot.Game
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()
{
CardSelector selected = m_selector;
m_selector = null;
if (m_nextSelector != null)
{
m_selector = m_nextSelector;
m_nextSelector = null;
if (m_thirdSelector != null)
{
m_nextSelector = m_thirdSelector;
m_thirdSelector = null;
}
}
else
m_selector = null;
return selected;
}
......@@ -497,6 +689,11 @@ namespace WindBot.Game
m_announce = id;
}
public void SelectYesNo(bool opt)
{
m_yesno = opt ? 1 : 0;
}
/// <summary>
/// Called when the AI has to declare a number.
/// </summary>
......@@ -552,14 +749,19 @@ namespace WindBot.Game
public BattlePhaseAction Attack(ClientCard attacker, ClientCard defender)
{
Executor.SetCard(0, attacker, -1);
if (defender != null)
{
string cardName = defender.Name ?? "monster";
attacker.ShouldDirectAttack = false;
_dialogs.SendAttack(attacker.Name, cardName);
SelectCard(defender);
}
else
{
attacker.ShouldDirectAttack = true;
_dialogs.SendDirectAttack(attacker.Name);
}
return new BattlePhaseAction(BattlePhaseAction.BattleAction.Attack, attacker.ActionIndex);
}
......
......@@ -24,11 +24,13 @@ namespace WindBot.Game
private Room _room;
private Duel _duel;
private int _hand;
public GameBehavior(GameClient game)
{
Game = game;
Connection = game.Connection;
_hand = game.Hand;
_packets = new Dictionary<StocMessage, Action<BinaryReader>>();
_messages = new Dictionary<GameMessage, Action<BinaryReader>>();
......@@ -73,12 +75,16 @@ namespace WindBot.Game
_packets.Add(StocMessage.Replay, OnReplay);
_packets.Add(StocMessage.DuelEnd, OnDuelEnd);
_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.Win, OnWin);
_messages.Add(GameMessage.Draw, OnDraw);
_messages.Add(GameMessage.ShuffleDeck, OnShuffleDeck);
_messages.Add(GameMessage.ShuffleHand, OnShuffleHand);
_messages.Add(GameMessage.TagSwap, OnTagSwap);
_messages.Add(GameMessage.NewTurn, OnNewTurn);
_messages.Add(GameMessage.NewPhase, OnNewPhase);
_messages.Add(GameMessage.Damage, OnDamage);
......@@ -86,13 +92,15 @@ namespace WindBot.Game
_messages.Add(GameMessage.Recover, OnRecover);
_messages.Add(GameMessage.LpUpdate, OnLpUpdate);
_messages.Add(GameMessage.Move, OnMove);
_messages.Add(GameMessage.Attack, OnAttack);
_messages.Add(GameMessage.PosChange, OnPosChange);
_messages.Add(GameMessage.Chaining, OnChaining);
_messages.Add(GameMessage.ChainEnd, OnChainEnd);
_messages.Add(GameMessage.SortCard, OnCardSorting);
_messages.Add(GameMessage.SortChain, OnChainSorting);
_messages.Add(GameMessage.UpdateCard, OnUpdateCard);
_messages.Add(GameMessage.UpdateData, OnUpdateData);
_messages.Add(GameMessage.BecomeTarget, OnBecomeTarget);
_messages.Add(GameMessage.SelectBattleCmd, OnSelectBattleCmd);
_messages.Add(GameMessage.SelectCard, OnSelectCard);
_messages.Add(GameMessage.SelectChain, OnSelectChain);
......@@ -110,10 +118,17 @@ namespace WindBot.Game
_messages.Add(GameMessage.AnnounceCard, OnAnnounceCard);
_messages.Add(GameMessage.AnnounceNumber, OnAnnounceNumber);
_messages.Add(GameMessage.AnnounceRace, OnAnnounceRace);
_messages.Add(GameMessage.AnnounceCardFilter, OnAnnounceCard);
_messages.Add(GameMessage.RockPaperScissors, OnRockPaperScissors);
}
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);
......@@ -124,13 +139,29 @@ namespace WindBot.Game
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);
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 OnTypeChange(BinaryReader packet)
{
int type = packet.ReadByte();
int pos = type & 0xF;
if (pos != 0 && pos != 1)
if (pos < 0 || pos > 3)
{
Connection.Close();
return;
......@@ -143,7 +174,7 @@ namespace WindBot.Game
private void OnPlayerEnter(BinaryReader packet)
{
string name = packet.ReadUnicode(Program.PlayerNameSize);
string name = packet.ReadUnicode(20);
int pos = packet.ReadByte();
if (pos < 8)
_room.Names[pos] = name;
......@@ -180,7 +211,12 @@ namespace WindBot.Game
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)
......@@ -198,8 +234,9 @@ namespace WindBot.Game
private void OnReplay(BinaryReader packet)
{
byte[] replay = packet.ReadToEnd();
/*byte[] replay =*/ packet.ReadToEnd();
/*
const string directory = "Replays";
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
......@@ -210,8 +247,9 @@ namespace WindBot.Game
if (Regex.IsMatch(file, @"^[\w\-. ]+$"))
File.WriteAllBytes(fullname, replay);
*/
Connection.Close();
//Connection.Close();
}
private void OnDuelEnd(BinaryReader packet)
......@@ -221,8 +259,40 @@ namespace WindBot.Game
private void OnChat(BinaryReader packet)
{
packet.ReadInt16(); // player
packet.ReadUnicode(256); // message
int player = packet.ReadInt16();
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)
......@@ -238,7 +308,7 @@ namespace WindBot.Game
extra = packet.ReadInt16();
_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();
}
......@@ -248,7 +318,7 @@ namespace WindBot.Game
string otherName = _room.Position == 0 ? _room.Names[1] : _room.Names[0];
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)
......@@ -278,6 +348,33 @@ namespace WindBot.Game
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)
{
_duel.Turn++;
......@@ -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)
{
packet.ReadInt32(); // card id
......@@ -378,16 +493,24 @@ namespace WindBot.Game
ClientCard card = _duel.GetCard(pcc, pcl, pcs, subs);
int cc = GetLocalPlayer(packet.ReadByte());
_ai.OnChaining(card, cc);
_duel.ChainTargets.Clear();
}
private void OnChainEnd(BinaryReader packet)
{
_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)
{
BinaryWriter writer = GamePacketFactory.Create(CtosMessage.Response);
/*BinaryWriter writer =*/ GamePacketFactory.Create(CtosMessage.Response);
Connection.Send(CtosMessage.Response, -1);
}
......@@ -409,7 +532,6 @@ namespace WindBot.Game
{
int player = GetLocalPlayer(packet.ReadByte());
CardLocation loc = (CardLocation)packet.ReadByte();
IList<ClientCard> cards = null;
switch (loc)
{
......@@ -440,14 +562,29 @@ namespace WindBot.Game
foreach (ClientCard card in cards)
{
int len = packet.ReadInt32();
if (len == 4) continue;
long pos = packet.BaseStream.Position;
card.Update(packet, _duel);
if (len > 8)
card.Update(packet, _duel);
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)
{
packet.ReadByte(); // player
......@@ -479,15 +616,26 @@ namespace WindBot.Game
int con = GetLocalPlayer(packet.ReadByte());
CardLocation loc = (CardLocation)packet.ReadByte();
int seq = packet.ReadByte();
packet.ReadByte(); // diratt
int diratt = packet.ReadByte();
ClientCard card = _duel.GetCard(con, loc, seq);
if (card != null)
{
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.CanEndPhase = packet.ReadByte() != 0;
......@@ -576,7 +724,7 @@ namespace WindBot.Game
int con = GetLocalPlayer(packet.ReadByte());
int loc = packet.ReadByte();
int seq = packet.ReadByte();
int sseq = 0; //packet.ReadByte();
int sseq = packet.ReadByte();
int desc = packet.ReadInt32();
cards.Add(_duel.GetCard(con, loc, seq, sseq));
......@@ -619,10 +767,14 @@ namespace WindBot.Game
}
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);
for (int i = 0; i < quantity; ++i)
reply.Write((short)used[i]);
reply.Write(result);
Connection.Send(reply);
}
......@@ -640,6 +792,7 @@ namespace WindBot.Game
CardLocation loc = (CardLocation)packet.ReadByte();
int seq = packet.ReadByte();
packet.ReadByte();
int desc = packet.ReadInt32();
ClientCard card = _duel.GetCard(player, loc, seq);
if (card == null)
......@@ -647,10 +800,10 @@ namespace WindBot.Game
Connection.Send(CtosMessage.Response, 0);
return;
}
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);
}
......@@ -740,11 +893,11 @@ namespace WindBot.Game
bool pendulumZone = false;
int filter;
if ((field & 0x1f) != 0)
if ((field & 0x7f) != 0)
{
resp[0] = (byte)GetLocalPlayer(0);
resp[1] = 0x4;
filter = field & 0x1f;
filter = field & 0x7f;
}
else if ((field & 0x1f00) != 0)
{
......@@ -759,11 +912,11 @@ namespace WindBot.Game
filter = (field >> 14) & 0x3;
pendulumZone = true;
}
else if ((field & 0x1f0000) != 0)
else if ((field & 0x7f0000) != 0)
{
resp[0] = (byte)GetLocalPlayer(1);
resp[1] = 0x4;
filter = (field >> 16) & 0x1f;
filter = (field >> 16) & 0x7f;
}
else if ((field & 0x1f000000) != 0)
{
......@@ -781,7 +934,9 @@ namespace WindBot.Game
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 & 0x8) != 0) resp[2] = 3;
else if ((filter & 0x1) != 0) resp[2] = 0;
......@@ -822,11 +977,14 @@ namespace WindBot.Game
private void OnSelectSum(BinaryReader packet)
{
packet.ReadByte(); // mode
bool mode = packet.ReadByte() == 0;
packet.ReadByte(); // player
int sumval = packet.ReadInt32();
int min = packet.ReadByte();
int max = packet.ReadByte();
if (max <= 0)
max = 99;
IList<ClientCard> mandatoryCards = new List<ClientCard>();
IList<ClientCard> cards = new List<ClientCard>();
......@@ -841,42 +999,47 @@ namespace WindBot.Game
CardLocation loc = (CardLocation)packet.ReadByte();
int seq = packet.ReadByte();
ClientCard card = _duel.GetCard(player, loc, seq);
if (card != null)
if (cardId != 0 && card.Id != 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
{
if (cardId != 0 && card.Id != cardId)
card.SetId(cardId);
card.OpParam1 = OpParam1;
card.OpParam2 = OpParam2;
}
if (j == 0)
mandatoryCards.Add(card);
else
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];
int index = 0;
result[index++] = (byte)(mandatoryCards.Count + selected.Count);
while (index < mandatoryCards.Count)
while (index <= mandatoryCards.Count)
{
result[index++] = 0;
}
for (int i = 0; i < selected.Count; ++i)
{
int id = 0;
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;
result[index++] = (byte)selected[i].SelectSeq;
}
BinaryWriter reply = GamePacketFactory.Create(CtosMessage.Response);
......@@ -918,6 +1081,7 @@ namespace WindBot.Game
private void OnAnnounceCard(BinaryReader packet)
{
// not fully implemented
Connection.Send(CtosMessage.Response, _ai.OnAnnounceCard());
}
......@@ -950,5 +1114,16 @@ namespace WindBot.Game
reply += (int)races[i];
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
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