基于bfs的五子棋AI v1.2

基于bfs的五子棋AI v1.2(含完整代码)

网上大部分五子棋引擎,都是基于dfs进行ab剪枝,但我要给大家介绍另一种方法,基于bfs扩展节点。

1.思路

思路很简单:一开始的局面作为根节点,然后一层一层地不断向下扩展。每个节点评分后,向上minmax,直到根节点为止。

2.优化

[ 重要 ] \color{red}\small[重要] [重要] 节点储存优化:在每个节点储存整个棋盘会浪费大量内存。其实,只储存了一个落子位置就够了。因为思考层数不深,bfs时,只需要从叶子节点向上遍历到根节点,就可以还原当时的棋盘状态了。

[ 重要 ] \color{red}\small[重要] [重要] 搜索顺序优化:一层一层地暴力搜索,会浪费大量时间在无用的节点上。因此,我们可以优先搜索评分最高的(min节点先搜最低的),合理分配时间。实现:权值减去访问量,得到优先级。(为了给后面的节点一些机会)

评分常数优化:扩展节点时,不需要评估整个棋盘。只需要评估落子位置一圈的棋子即可。还可以通过搜索预处理一些权重信息,进一步提高速度。

上代码

v1.2

  • 把结构体内的 bestvaldep 变量删除了,因为用处不大。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)
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值