一、AI原理
当我们人在下棋的时候,我们根据当前棋局的变化,做出防守和攻击的策略。如果不用堵对手的棋子,我们都想下在那个能我方快速连成5子的地方。我们肉眼看见,三子连珠我们肯定会去防,看见两子连珠却无动于衷,那是因为我们脑海里面装了评分表,不同棋型对应不同的分数,取最大的分数。人类有时会失误,因为不是所有人都有比较强的抽象能力,下棋可以看很多步的。
那我们就是要用代码告诉电脑当前的棋型是怎么样的,如果电脑来下,最佳下在那里,然后让电脑去评分表里面找对应的权值,查字典,这就攻击值;然后找站在对手的角度来看,对手最会下的位置在哪,去评分表找出权值,这个叫防守值。比较两值大小,取值最大的点着子
如果只看一步,那就止于当前了。我们肯定是想让AI更聪明,下起来才有意思。那AI就要这样来思考了,像人一样,如果我把棋下在这里,那对手肯定也是想下在对他自己最有利对我最不利的地方,那他下了以后,我再下对自己最有利对他最不利的地方,如此类推,递归下去。
五子棋是15x15,也就是说最多也就是7层,对整个棋盘进行遍历,除了已经落子的地方,假设下下去,并且假设对手做出最佳的策略,然后比较每条路得到的权值,取权值最大的
二、评分表
五子棋棋型参考:https://www.cnblogs.com/songdechiu/p/5768999.html
protected Dictionary<string, float> toScore = new Dictionary<string, float>();
就是一个字符串对应一个值
比如“aa_” 代表两子连珠,左边堵死,右边空
“_aa” 代表两子连珠,右边堵死,左边空
“_aa” 代表两子连珠,左右边空
“aa” 代表两子连珠,左右边非空,这个没有意义,不必计入表中
“a_a” 代表两子中间空
三、AI难度选择
如果一个人知道的棋型越多,水平肯定越高。
如果一个人下棋看到的深度越深,水平也是越高
1、决定因素
那电脑的难度,取决于评分表全面度
那电脑的看棋深度,取决于扫棋方式,
2、简单模式
他继承于之前那个玩家下棋的类
public class AILevelOne : Player {
比如简单模式的扫棋,不考虑棋子中间存在未落子的情况,
只考虑这种:
评分表:
toScore.Add("aa_", 50);
toScore.Add("_aa", 50);
toScore.Add("_aa_", 100);
toScore.Add("_aaa_", 1000);
toScore.Add("aaa_", 500);
toScore.Add("_aaa", 500);
toScore.Add("_aaaa_", 10000);
toScore.Add("aaaa_", 5000);
toScore.Add("_aaaa", 5000);
toScore.Add("aaaaa", float.MaxValue);
toScore.Add("aaaaa_", float.MaxValue);
toScore.Add("_aaaaa", float.MaxValue);
toScore.Add("_aaaaa_", float.MaxValue);
扫棋方法
public virtual void CheckOneLine(int[] pos, int[] offset,int chess)
{
string str = "a";
//右边
for (int i = offset[0], j = offset[1]; (pos[0] + i >= 0 && pos[0] + i < 15) &&
pos[1] + j >= 0 && pos[1] + j < 15; i += offset[0], j += offset[1])
{
if (ChessBoard.Instacne.grid[pos[0] + i, pos[1] + j] ==chess)
{
str += "a";
}
else if (ChessBoard.Instacne.grid[pos[0] + i, pos[1] + j] == 0)
{
str += "_";
break;
}
else
{
break;
}
}
//左边
for (int i = -offset[0], j = -offset[1]; (pos[0] + i >= 0 && pos[0] + i < 15) &&
pos[1] + j >= 0 && pos[1] + j < 15; i -= offset[0], j -= offset[1])
{
if (ChessBoard.Instacne.grid[pos[0] + i, pos[1] + j] == chess)
{
str = "a" + str;
}
else if (ChessBoard.Instacne.grid[pos[0] + i, pos[1] + j] == 0)
{
str = "_" +str;
break;
}
else
{
break;
}
}
if (toScore.ContainsKey(str))
{
score[pos[0], pos[1]] += toScore[str];
}
}
3、困难模式
这个C#文件的类继承于简单模式,然后对评分表和扫棋模式进行重写
public class AILevelTwo : AILevelOne
评分表:
toScore.Add("aa___", 100); //眠二
toScore.Add("a_a__", 100);
toScore.Add("___aa", 100);
toScore.Add("__a_a", 100);
toScore.Add("a__a_", 100);
toScore.Add("_a__a", 100);
toScore.Add("a___a", 100);
toScore.Add("__aa__", 500); //活二 "_aa___"
toScore.Add("_a_a_", 500);
toScore.Add("_a__a_", 500);
toScore.Add("_aa__", 500);
toScore.Add("__aa_", 500);
toScore.Add("a_a_a", 1000); // bool lfirst = true, lstop,rstop = false int AllNum = 1
toScore.Add("aa__a", 1000);
toScore.Add("_aa_a", 1000);
toScore.Add("a_aa_", 1000);
toScore.Add("_a_aa", 1000);
toScore.Add("aa_a_", 1000);
toScore.Add("aaa__", 1000); //眠三
toScore.Add("_aa_a_", 9000); //跳活三
toScore.Add("_a_aa_", 9000);
toScore.Add("_aaa_", 10000); //活三
toScore.Add("a_aaa", 15000); //冲四
toScore.Add("aaa_a", 15000); //冲四
toScore.Add("_aaaa", 15000); //冲四
toScore.Add("aaaa_", 15000); //冲四
toScore.Add("aa_aa", 15000); //冲四
toScore.Add("_aaaa_", 1000000); //活四
toScore.Add("aaaaa", float.MaxValue); //连五
扫棋方法
public override void CheckOneLine(int[] pos, int[] offset, int chess)
{
bool lfirst = true, lstop= false, rstop = false;
int AllNum = 1;
string str = "a";
int ri = offset[0], rj = offset[1];
int li = -offset[0], lj = -offset[1];
while (AllNum<7 && (!lstop||!rstop))
{
if (lfirst)
{
//左边
if ((pos[0] + li >= 0 && pos[0] + li < 15) &&
pos[1] + lj >= 0 && pos[1] + lj < 15 && !lstop)
{
if (ChessBoard.Instacne.grid[pos[0] + li, pos[1] + lj] == chess)
{
AllNum++;
str = "a" + str;
}
else if(ChessBoard.Instacne.grid[pos[0] + li, pos[1] + lj] == 0)
{
AllNum++;
str = "_" + str;
if(!rstop) lfirst = false;
}
else
{
lstop = true;
if (!rstop) lfirst = false;
}
li -= offset[0]; lj -= offset[1];
}
else
{
lstop = true;
if (!rstop) lfirst = false;
}
}
else
{
if ((pos[0] + ri >= 0 && pos[0] + ri < 15) &&
pos[1] + rj >= 0 && pos[1] + rj < 15 && !lfirst && !rstop)
{
if (ChessBoard.Instacne.grid[pos[0] + ri, pos[1] + rj] == chess)
{
AllNum++;
str += "a" ;
}
else if (ChessBoard.Instacne.grid[pos[0] + ri, pos[1] + rj] == 0)
{
AllNum++;
str += "_" ;
if (!lstop) lfirst = true;
}
else
{
rstop = true;
if (!lstop) lfirst = true;
}
ri += offset[0]; rj += offset[1];
}
else
{
rstop = true;
if (!lstop) lfirst = true;
}
}
}
string cmpStr = "";
foreach (var keyInfo in toScore)
{
if (str.Contains(keyInfo.Key))
{
if(cmpStr != "")
{
if(toScore[keyInfo.Key] > toScore[cmpStr])
{
cmpStr = keyInfo.Key;
}
}
else
{
cmpStr = keyInfo.Key;
}
}
}
if(cmpStr!= "")
{
score[pos[0], pos[1]] += toScore[cmpStr];
}
}
活二可能包含眠二,这些棋型不是完全唯一的,所以我们要搞一个记录值记录一下,
4、炼狱模式
这个比两个都复杂很多,单独写博客说
要涉及到极小值极大值算法,Alpha-Beta算法等
目前就已经实现了双人对战,单机模式下的简单和困难。
接下来先把游戏开始结束动画,模式选择整一下,看起来像一个正常的游戏,后面可以再加入炼狱模式,局域网下棋,联网模式等等