写这篇博客的目的呢,是因为做(嫖)了一个黑白棋的课设,就对这玩意产生了点兴趣,还请个位指教!
Minimax算法
Minimax算法大多被用于棋类游戏中,是一种找出失败的最大可能性中的最小值的算法,即最小化对手的最大得益的算法,就是说想将自己的分数尽量高,而对手尽量选取小的值的算法。
首先先来了解一下这几个概念
局面估价函数:我们给每个局面(state)规定一个估价函数值 f,评价它对于己方的有利程度。胜利的局面的估价函数值为 +oo,而失败的局面的估价函数值为–oo。
Max 局面:假设这个局面轮到己方走,有多种决策可以选择,其中每种决策都导致一种子局面(sub-state)。由于决策权在我们手中,当然是选择估价函数值 f 最大的子局面,因此该局面的估价函数值等于子局面 f 值的最大值,把这样的局面称为 max 局面。
Min 局面:假设这个局面轮到对方走,它也有多种决策可以选择,其中每种决策都导致一种子局面(sub-state)。但由于决策权在对方手中,在最坏的情况下,对方当然是选择估价函数值 f 最小的子局面,因此该局面的估价函数值等于子局面 f 值的最小值,把这样的局面称为 max 局面。
现在看这么一个博弈树
画的好像有点烂,不过没关系的。
现在我和你进行博弈下棋,我先手,我在下棋的时候,当然会想着取到最好的结果,这个结果我们用之前提到的估值来表示,在那个博弈树里面,最后一层代表着估值,我先手,我当然希望我得到最大的估值,那我的那一步也就是那个节点的估值要从下面的节点开始,我们是max局面,就是取得最好值,你对我来说应该取到min局面,为什么我说那你取得是min局面呢??因为你看那个博弈搜索树,我下一步,你下一步,你的min值决定了我的max值,就说第三层和第四层,1 和 -4,你在第五层取-4的时候,也可以取3,但你要是取了3,对我的max局面来说就会取到3,而不是1了。
那我们在设计棋类算法的时候,就要考虑到每个点的估值。
黑白棋中的估值表
多玩几次黑白棋,那就会发现,边角上的棋子很重要,黑白棋的游戏规则就不说了,总之就是边角上的 棋子很重要,在边角旁边的棋子又很危险,因为放置在这里,最后可能会被一波转化掉,根据经验,我们可以得到一个估值表。
这样在AI决策的时候就会方便许多。
对于AI的设定,我们能走边角就尽量走边角,不到万不得已的情况下,不走邻角点,对于其他情况,我们采用极大极小算法。
α-β剪枝
经过查阅可以得知,六层的搜索就接近是二十亿,而十层的搜索就超过两千万亿,so??
就有了所谓的α-β剪枝。
怎么理解这个α-β剪枝呢?
就还拿我上面画的图举例子。我们看第二层吧,1和9,第二层的那个9的选取,第三层的第二个节点是10,第三个节点是9,假如说我们已经已知了9,对第三层10选取的时候,我只知道第四层的第四个是10,第四层的第三个节点假设不清楚,那么第三层的那个节点值它肯定是>=10的吧,因为这里为max决策,然后上一层为min决策,那再10下面的哪一片就可以剪枝掉,这种剪枝方式局叫做α-β剪枝。
简单难度AI设计
这个简单难度的AI就简单设计,AI的算法为贪心算法,寻找每次可以转换最多点。说实话,这个简单AI属实拉跨,我用贪心思想跟AI的贪心算法下,AI有的时候下不过我,哈哈哈哈哈!
POINT2 Easy() //人机对战简单AI
{
POINT2 MAX; //定义以及初始化最优解
MAX.INIT(0, 0);
int maxx = 0;
for (int i = 0; i < SIZE; ++i)
for (int j = 0; j < SIZE; ++j)
{
if (expect[i][j] >= maxx) //寻找可以转化棋子最多的点作为最优解
{
maxx = expect[i][j];
MAX.INIT(i, j);
}
}
if (ESCEXIT)gameStart();
Sleep(800); //间歇
return MAX; //返回最优解
}
纯贪心思想,使用之前给到的每个点的估值进行判断的。
中等难度AI的设计
POINT2 MIDDLE() //人机对战中等AI
{
POINT2 MAX;
int maxx = -10005;
MAX.INIT(0, 0);
for (int i = 0; i < SIZE; i++)
for (int j = 0; j < SIZE; j++)
{
if (expect[i][j])
{
if ((i == 0 && j == 0) || (i == 0 && j == SIZE - 1) || (i == SIZE - 1 && j == SIZE - 1) || (i == SIZE - 1 && j == 0))
{
MAX.INIT(i, j);
return MAX; //如果在角,返回角坐标
}
int k = difai(i, j, mapp, expect, 0, 1); //递归搜索 搜索一层
if (k >= maxx)
{
maxx = k;
MAX.INIT(i, j);
}
}
}
return MAX;
}
困难难度AI
困难难度和中等难度的区别就是搜索的深度不同,通俗点讲的话就是我们下棋可以一眼看几步
POINT2 Difficult() //人机对战困难AI
{
POINT2 MAX;
int maxx = -10005;
MAX.INIT(0, 0);
for (int i = 0; i < SIZE; i++)
for (int j = 0; j < SIZE; j++)
{
if (expect[i][j])
{
if ((i == 0 && j == 0) || (i == 0 && j == SIZE - 1) || (i == SIZE - 1 && j == SIZE - 1) || (i == SIZE - 1 && j == 0))
{
MAX.INIT(i, j);
return MAX; //如果在角,返回角坐标
}
int k = difai(i,j,mapp,expect,0,3); //递归搜索 搜索三层
if (k >= maxx)
{
maxx = k;
MAX.INIT(i, j);
}
}
}
return MAX;
}
最大最小搜索搜索算法
还是递归的运用,至于剪枝,是有一点点啦
int difai(int x,int y,int mapnow[SIZE][SIZE],int expectnow[SIZE][SIZE],int depin,int depinmax) //极大极小搜索
{
if (depin >= depinmax)return 0; //递归出口
int maxx = -10005; //最大权值
POINT2 NOW;
int expectnow2[SIZE][SIZE] , mapnow2[SIZE][SIZE],mapnext[SIZE][SIZE],expectlast[SIZE][SIZE]; //定义临时数组
copymap(mapnow2, mapnow); //复制当前棋盘
mapnow2[x][y] = NOWCOLOR ? 1 : -1; //模拟在当前棋盘上下棋
int ME = MAPPOINTCOUNT[x][y] + expectnow[x][y]; //当前棋子权
NOW.INIT(x,y);
Change(NOW, mapnow2, false); //改变棋盘AI结束
int MAXEXPECT = 0, LINEEXPECT = 0, COUNT = 0;
for (int i = 0; i < SIZE; ++i)
for (int j = 0; j < SIZE; ++j)
{
expectnow2[i][j] = Judge(i, j, !NOWCOLOR, mapnow2); //预判对方是否可以走棋
if (expectnow2[i][j])
{
++MAXEXPECT;
if ((i == 0 && j == 0) || (i == 0 && j == SIZE - 1) || (i == SIZE - 1 && j == SIZE - 1) || (i == SIZE - 1 && j == 0))return -1800; //如果对方有占角的可能
if ((i < 2 && j < 2) || (i < 2 && SIZE - j - 1 < 2) || (SIZE - 1 - i < 2 && j < 2) || (SIZE - 1 - i < 2 && SIZE - 1 - j < 2))++LINEEXPECT;
}
}
if (LINEEXPECT * 10 > MAXEXPECT * 7)return 1400; //如果对方走到坏点状态较多 剪枝
for (int i = 0; i < SIZE; i++)
for (int j = 0; j < SIZE; j++)
if (expectnow2[i][j]) //如果对方可以走棋
{
int YOU = MAPPOINTCOUNT[i][j] + expectnow2[i][j]; //当前权值
copymap(mapnext, mapnow2); //拷贝地图
mapnext[i][j] = (!NOWCOLOR) ? 1 : -1; //模拟对方走棋
NOW.INIT(i, j);
Change(NOW, mapnext, false); //改变棋盘
for (int k = 0; k < SIZE; k++)
for (int l = 0; l < SIZE; l++)
expectlast[k][l] = Judge(k, l, NOWCOLOR, mapnext); //寻找AI可行点
for (int k = 0; k < SIZE; k++)
for (int l = 0; l < SIZE;l++)
if (expectlast[k][l])
{
int nowm = ME - YOU + difai(k, l, mapnext, expectlast, depin + 1, depinmax);
maxx = maxx < nowm ? nowm : maxx;
}
}
return maxx;
}
本课设以征求开源作者同意使用,本来博主是拒绝白嫖的,ue4不香吗,垃圾玩意崩溃打不开项目了,也就有了这篇博文.