您的位置:首页 > 移动开发 > Cocos引擎

cocos 2dx 基于C++的 五子棋AI算法思想

2015-10-31 17:37 513 查看
今天我想要分享一下我做五子棋AI的思路。因为在做这个之前,我没有接触过任何像这种类似的东西。通过这一次,我也算是有所了解,我的思路也是来自很多网络上的博客,看了很多,最终总结出了自己的这样一个。

那我的五子棋是15*15的大小(一般也就是这样的一个大小)。我的AI算法要求每一次落子之后都要去计算每一个空暇的位置的“分值”,简单的说,我们需要一个存放棋子的数组,表示是否存放了棋子,还要一个计算每一个空格的数组来记录“分数”,这个分数是后期AI用来运算的基础,也是你AI难度控制的点。

我现有的思路就是分两部分。首先是如果是玩家先落子,那么要求电脑AI随即在你落子的地方的任意一个方向,随机落子,这是第一步。接下来以后就正式进入到算法中去。

首先初始化你的分数数组,让他们全部为零。然后在每一次落子之后进行全盘的遍历,如果发现该处为空白,于是检查其四周八个方向(当然如果是边缘位置就相对修改,判断是否出了边界)。若在空白处,且发现在某一对角线方向发现有一个其他颜色的棋子,那么相对的给这个空白区域的分数数组加上一定的分值,然后继续往这个方向检测是否还有连续的同一颜色的棋子,若没有则检查其他方向或者检测下一个空白位置。若是还在同一方向上面找到了相同颜色的棋子,那么第二个棋子的出现,你可以给改空白处加上双倍的分值,表明这个空白位置更加重要。一次类推,继续检测。(PS:因为最终AI棋子落在什么地方,依靠的是最后遍历整个分数数组,然后根据分数的高低来进行判断落子落在哪里的,在下面讲)。

经过上一遍的遍历,每一次落子都会使得分数数组得到一些变化,每一次都会导致AI判断的变化。在这个基础上,每一次落子还要进行一次对自己本身棋子颜色的一个遍历,判断自己的情况,同时加分加在分数数组之中,这样一来,电脑就会根据自己的棋子的情况以及玩家的落子情况进行判断,哪一个地方更加适合落子。

因为我是第一次做AI,网络上搜到的一些思想一般也是这种类似的遍历思想。理解了以后写代码就比较方便。最后可能会有一些点的分数是相同的,所以还有设置一下随机落子。把分数相同的地点随机落子。

个人感觉AI的强弱是根据你每一次给他增加分数的多少来确定的。这个我的AI有时候也会抽风,不过一般情况比较正常,可能运气也占了一部分,当初设计加分的时候其实没想那么多,现在却发现好像还不错。

大家要多去实践练习,多改改分数可能就会出来不错的AI了,o(^▽^)o。

下面贴上我的代码!

void GameScene::Robot(int *x, int *y, int *Sum)
{
ExWhile1 = true;
if (*Sum == 1)
{
while (ExWhile1)
{
ChessOne(*x, *y);
if (ch[*x][*y] == 2){ ExWhile1 = false; }
}
ch[*x][*y] = tp;													//记录这个点
printpart(*x, *y, tp);												//打印出电脑AI第一次落子
isTouch = true;
tp++;
tp = tp % 2;
}
else																    //从第2步开始,使用评分系统
{
Findscore(*x, *y);
}
}

void GameScene::Findscore(int &x, int &y)									//查找评分最高的坐标
{
srand((unsigned)time(NULL));
int i, j, x1, x2, y1, y2, lx;
int Max = 0;
ChessScore();															//调用评分函数
for (i = 0; i<15; i++)
{
for (j = 0; j<15; j++)
{
if (Score[i][j]>Max)
{
Max = Score[i][j];											//获取所有点中,评分最高的
x1 = i;
y1 = j;
}
}
}
x2 = x1; y2 = y1;
for (i = 0; i<15; i++)													//可能的话,有评分相同的多个点
{
for (j = 0; j<15; j++)
{
if (Score[i][j] == Max&&i != x2&&j != y2)						//在这么多个相同分数的点中,随机找一个
{
lx = rand() % 10;
if (lx<5)
{
x2 = i, y2 = j;
break;
}
}
}
}
if (x2 != x1 || y2 != y1)												//棋盘上有2个最高分
{
lx = rand() % 10;													//随机一个
if (lx>6)
{
x = x1, y = y1;
}
else
{
x = x2, y = y2;
}
}
else																	//棋盘上只有一个最高分
{
x = x1, y = y1;
}
Max = 0;																//清空最大值
ch[x][y] = tp;															//记录这个点
printpart(x, y, tp);													//打印出电脑AI落子
if (winerValue==2)
{
isTouch = true;
}

tp++;
tp = tp % 2;
}

inline void GameScene::ChessOne(int &x, int &y)								//玩家走第1步时的落子
{
int i, j;
srand((unsigned)time(NULL));											//随机数随着时间的改变而改变
for (i = 0; i<15; i++)
{
for (j = 0; j<15; j++)
{
if (ch[i][j] == 0)												//如果找到了玩家的棋子,在它的8个方的任意一点落子
{
int lx = rand() % 7;
if (lx == 0)
{
x = i + 1; y = j + 1;
if (ch[x][y] == 2){ break; }
}
else if (lx == 1)
{
x = i + 1; y = j - 1;
if (ch[x][y] == 2){ break; }
}
else if (lx == 2)
{
x = i - 1; y = j - 1;
if (ch[x][y] == 2){ break; }
}
else if (lx == 3)
{
x = i - 1; y = j + 1;
if (ch[x][y] == 2){ break; }
}
else if (lx == 4)
{
x = i - 1; y = j;										//上
if (ch[x][y] == 2){ break; }
}
else if (lx == 5)
{
x = i; y = j - 1;										//左
if (ch[x][y] == 2){ break; }
}
else if (lx == 6)
{
x = i; y = j + 1;										//右
if (ch[x][y] == 2){ break; }
}
else
{
x = i + 1; y = j;										//下
if (ch[x][y] == 2){ break; }
}
}
}
}
}

void GameScene::ChessScore()
{
int x, y, i, j, k;														//循环变量
int number1 = 0, number2 = 0;											//number用来统计玩家或电脑棋子连成个数
int empty = 0;															//empty用来统计空点个数
memset(Score, 0, sizeof(Score));										//把评分数组先清零
for (x = 0; x<15; x++)
{
for (y = 0; y<15; y++)
{
if (ch[x][y] == 2)												//如果这个点为空
{
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)								//判断8个方向
{
if (i != 0 || j != 0)								//若是都为0的话,那不就是原坐标嘛
{
//对玩家落点评分
for (k = 1; i <= 4; k++)						//循环4次
{												//这点没越界   且这点存在黑子(玩家)
if (x + k*i >= 0 && x + k*i <= 14 &&
y + k*j >= 0 && y + k*j <= 14 &&
ch[x + k*i][y + k*j] == 0)
{
number1++;
}
else if (ch[x + k*i][y + k*j] == 2)			//这点是个空点,+1后退出
{
empty++;
break;
}
else										//否则是墙或者对方的棋子了
{
break;
}
}
for (k = -1; k >= -4; k--)						//向它的相反方向判断
{												//这点没越界   且这点存在黑子(玩家)
if (x + k*i >= 0 && x + k*i <= 14 &&
y + k*j >= 0 && y + k*j <= 14 &&
ch[x + k*i][y + k*j] == 0)
{
number1++;
}
else if (ch[x + k*i][y + k*j] == 2)			//这点是个空点,+1后退出
{
empty++;
break;
}
else
{
break;
}
}
if (number2 == 1)								//2个棋子
{
Score[x][y] += 1;
}
else if (number1 == 2)							//3个棋子
{
if (empty == 1)
{
Score[x][y] += 5;						//有一个空点+5分  死3
}
else if (empty == 2)
{
Score[x][y] += 10;						//有两个空点+10分 活3
}
}
else if (number1 == 3)							//4个棋子
{
if (empty == 1)
{
Score[x][y] += 20;						//有一个空点+20分  死4
}
else if (empty == 2)
{
Score[x][y] += 100;						//有2个空点+100分  活4
}
}
else if (number1 >= 4)
{
Score[x][y] += 1000;						//对方有5个棋子,分数要高点,先堵
}

empty = 0;										//统计空点个数的变量清零

//对电脑落点评分
for (k = 1; i <= 4; k++)						//循环4次
{												//这点没越界   且这点存在白子(电脑)
if (x + k*i >= 0 && x + k*i <= 14 &&
y + k*j >= 0 && y + k*j <= 14 &&
ch[x + k*i][y + k*j] == 1)
{
number2++;
}
else if (ch[x + k*i][y + k*j] == 2)
{
empty++; break;							//空点
}
else
{
break;
}
}
for (k = -1; k >= -4; k--)						//向它的相反方向判断
{
if (x + k*i >= 0 && x + k*i <= 14 &&
y + k*j >= 0 && y + k*j <= 14 &&
ch[x + k*i][y + k*j] == 1)
{
number2++;
}
else if (ch[x + k*i][y + k*j] == 2)
{
empty++; break;
}
else
{
break;									//注释与上面玩家版相同
}
}
if (number2 == 0)
{
Score[x][y] += 1;							//1个棋子
}
else if (number2 == 1)
{
Score[x][y] += 2;							//2个棋子
}
else if (number2 == 2)							//3个棋子
{
if (empty == 1)
{
Score[x][y] += 8;						//死3
}
else if (empty == 2)
{
Score[x][y] += 30;						//活3
}
}
else if (number2 == 3)							//4个棋子
{
if (empty == 1)
{
Score[x][y] += 50;						//死4
}
else if (empty == 2)
{
Score[x][y] += 200;						//活4
}
}
else if (number2 >= 4)
{
Score[x][y] += 10000;						//自己落在这点能形成5个,也就能胜利了,分数最高
}

number1 = 0;									//清零,以便下次重新统计
number2 = 0;
empty = 0;
}
}
}
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: