基于bfs的五子棋AI v1.2(含完整代码)
网上大部分五子棋引擎,都是基于dfs进行ab剪枝,但我要给大家介绍另一种方法,基于bfs扩展节点。
1.思路
思路很简单:一开始的局面作为根节点,然后一层一层地不断向下扩展。每个节点评分后,向上minmax,直到根节点为止。
2.优化
[ 重要 ] \color{red}\small[重要] [重要] 节点储存优化:在每个节点储存整个棋盘会浪费大量内存。其实,只储存了一个落子位置就够了。因为思考层数不深,bfs时,只需要从叶子节点向上遍历到根节点,就可以还原当时的棋盘状态了。
[ 重要 ] \color{red}\small[重要] [重要] 搜索顺序优化:一层一层地暴力搜索,会浪费大量时间在无用的节点上。因此,我们可以优先搜索评分最高的(min节点先搜最低的),合理分配时间。实现:权值减去访问量,得到优先级。(为了给后面的节点一些机会)
评分常数优化:扩展节点时,不需要评估整个棋盘。只需要评估落子位置一圈的棋子即可。还可以通过搜索预处理一些权重信息,进一步提高速度。
上代码
v1.2
-
把结构体内的
bestval
和dep
变量删除了,因为用处不大。dep
在扩展的时候顺便记一下就行了。bestval
也可以递归得到 -
把
fa
(父节点)列表也删除了,因为只需要向下扩展,记了父节点也没什么用。
#include<bits/stdc++.h>
#include<windows.h>
#pragma GCC optimize(1,2,3,"Ofast","inline")
#pragma G++ optimize(1,2,3,"Ofast","inline")
using namespace std;
// 权重
const short W[15][3] = {
{
0,0,0},{
8,4,0},{
24,12,0},{
80,40,0},{
999,999,999},{
999,999,999},{
999,999,999},{
999,999,999},{
999,999,999},{
999,999,999},{
999,999,999},{
999,999,999},{
999,999,999},{
999,999,999},{
999,999,999}};
short weight1[65536], weight2[65536]; // 1黑 2白
namespace Winit {
int b[9];
int get(int x,int color) {
int con=0, stop=0;
for(int i=x+1 ;; i++) {
if(i<0 || i>8) {
stop++;
break;
}
if(b[i]==color) con++;
else {
if(b[i]==3-color || b[i]==3) stop++;
break;
}
}
for(int i=x-1 ;; i--) {
if(i<0 || i>8) {
stop++;
break;
}
if(b[i]==color) con++;
else {
if(b[i]==3-color || b[i]==3) stop++;
break;
}
}
return W[con][stop];
}
short sc() {
short rv=0;
for(int i=0; i<9; i++) {
if(b[i] == 1) rv -= get(i,1)<<1;
else if(b[i] ==2) rv += get(i,2);
}
return rv;
}
void dfs(int dep) {
if(dep == 8) {
int tmp=sc();
b[4]=1;
weight1[b[0]<<14 | b[1]<<12 | b[2]<<10 | b[3]<<8 | b[5]<<6 | b[6]<<4 | b[7]<<2 | b[8]] = sc()-tmp;
b[4]=2;
weight2[b[0]<<14 | b[1]<<12 | b[2]<<10 | b[3]<<8 | b[5]<<6 | b[6]<<4 | b[7]<<2 | b[8]] = sc()-tmp;
b[4]=0;
return;
}
for(int i=0; i<=3; i++) b[dep+(dep>=4)]=i, dfs(dep+1); // 0 空 1 黑 2 白 3 墙
}
}
int board[32][32], Time;
void UI() {
//输出
cout << "-------------------------------" << endl;
for(int i=1; i<=15; i++) {
cout << "|";
for(int j=1; j<=15; j++) {
if(board[i][j] == 0) cout << " |";
else if(board[i][j] == 1) cout << "●|";
else cout << "○|";
}
cout << endl << "-------------------------------" << endl;
}
}
int get(int &x,int &y,int xx,int yy,int color) {
//得到x,y往xx,yy方向的评分
int con = 0,stop = 0;
for(int i=x+xx,j=y+yy ;; i+=xx,j+=yy) {
if(i<1 || i>15 || j<1 || j>15) {
stop++;
break;
}
if(board[i][j] == color) con++;
else {
if(board[i][j] == 3-color) stop++;
break;
}
}
for(int i=x-xx,j=y-yy ;; i-=xx,j-=yy) {
if(i<1 || i>15 || j<1 || j>15) {
stop++;
break;
}
if(board[i][j] == color) con++;
else {
if(board[i][j] == 3-color) stop++;
break;
}
}
return W[min(4,con)][stop];
}
short calc() {
//计算整个棋盘的权重
short a=0, b=0;
for(int i=1; i<=15; i++) {
for(int j=1; j<=15; j++) {
if(board[i][j] == 2) a+=get(i,j,-1,-1,2)+get(i,j,-1,0,2)+get(i,j,-1,1,2)+get(i,j,0,-1,2);
else if(board[i][j] == 1) b+=get(i,j,-1,-1,1)+get(i,j,-1,0,1)+get(i,j,-1,1,1)+get(i,j,0,-1,1);
}
}
return a - (b<<1);
}
const int F[8][2]= {
{
-1,-1},{
-1,0},{
-1,1},{
0,-1},{
0,1},{
1,-1},{
1,0},{
1,1}};
inline short minicalc(int &x, int &y, int color) {
//计算某点周围的权重
short rv=0;
#define g(p) ((x+F[i][0]*p<1||x+F[i][0]*p>15||y+F[i][1]*p<1||y+F[i][1]*p>15)?3:board[x+F[i][0]*p][y+F[i][1]*p])
if(color == 1)
for(int i=0; i<4; i++)
rv += weight1[g(-4)<<14 | g(-3)<<12 | g(-2)<<10 | g(-1)<<8 | g(1)<<6 | g(2)<<4 | g(3)<<2 | g(4)];
else
for(int i=0; i<4; i++)
rv += weight2[g(-4)<<14 | g(-3)<<12 | g(-2)<<10 | g(-1)<<8 | g(1)<<6 | g(2)<<4 | g(3)<<2 | g(4)];
#undef g
return rv;
}
inline int find(int &x,int &y) {
#define ck(xx,yy) (x+xx>0 && y+yy>0 && x+xx<=15 && y+yy<=15 && board[x+xx][y+yy]!=0)
return ck(-1,-1)||ck(-1,0)||ck(-1,1)||ck(0,-1)||ck(0,1)||ck(1,-1)||ck(1,0)||ck(1,1)||ck(-2,0)||ck(2,0)||ck(0,-2)||ck(0,2)