下五子棋的bot----五子棋算法

使用的是北大信科开发的AI平台,百度搜素botzone,或者直接输入https://www.botzone.org.cn/
代码已经在平台上参与排行,成绩还行,现分享给大家。

#include<math.h>

#include<stdlib.h>

#include<time.h>

#include<iostream>

#include<cstdio>

#include "jsoncpp/json.h" // C++编译时默认包含此库  

#define N 7 //每个节点的分支数

//以下为各棋型的识别码  //权重

#define WIN 1 //4000

#define LOSE 2 //-4000

#define FLEX4 3 //2000

#define flex4 4 //-2000

#define BLOCK4 5 //1000

#define block4 6 //-1000 

#define FLEX3 7 //1000 

#define flex3 8 //-1000

#define BLOCK3 9 //400 

#define block3 10 //-600  

#define FLEX2 11 //400 

#define flex2 12 //-600 

#define BLOCK2 13 //100 

#define block2 14 //-150 

#define FLEX1 15 //100 

#define flex1 16 //-150 

using namespace std;
using namespace Json;

char board[15][15]; //当前棋盘

//给棋盘board[15][15]赋值
void placeAt(int x,int y,int d) {
	if(x>=0&&y>=0)
		board[x][y]=d;
}

//复制棋盘A到B 
int copy_board(char A[15][15],char B[15][15])
{
	int i,j;
	
	for(i=0;i<15;i++)
	{
		for(j=0;j<15;j++)
		{
			B[i][j]=A[i][j];
		}
	}
	return 0;
}

//复制取反后的棋盘A到B
int inv_board(char A[15][15],char B[15][15])
{
	int i,j;
	
	int INV[3]={0,2,1};
	
	for(i=0;i<15;i++)
	{
		for(j=0;j<15;j++)
		{
			B[i][j]=INV[A[i][j]];
		}
	}
	return 0;
}

//棋型辨识数组  
int score[3][3][3][3][3][3]; 

//给棋型辨识数组赋值 
int init_score()
{
	//黑五连 AI胜 (这里的黑不是指先手,而是指AI)(棋型 1 //4000) 
	score[1][1][1][1][1][1]=WIN;
	score[1][1][1][1][1][0]=WIN;
	score[0][1][1][1][1][1]=WIN;
	score[1][1][1][1][1][2]=WIN;
	score[2][1][1][1][1][1]=WIN;
	
	//白五连 AI负  (棋型 2 // -4000) 
	score[2][2][2][2][2][2]=LOSE;
	score[2][2][2][2][2][0]=LOSE;
	score[0][2][2][2][2][2]=LOSE;
	score[2][2][2][2][2][1]=LOSE;
	score[1][2][2][2][2][2]=LOSE;
	
	
	//黑活四   (棋型 3 // 2000) 
	score[0][1][1][1][1][0]=FLEX4;
	
	//白活四	(棋型 4 // -2000) 
	score[0][2][2][2][2][0]=flex4;
	
	
	//黑活三	(棋型 7 // 1000) 
	score[0][1][1][1][0][0]=FLEX3;
	score[0][1][1][0][1][0]=FLEX3;
	score[0][1][0][1][1][0]=FLEX3;
	score[0][0][1][1][1][0]=FLEX3;
	
	//白活三	(棋型 8 // -1000) 
	score[0][2][2][2][0][0]=flex3;
	score[0][2][2][0][2][0]=flex3;
	score[0][2][0][2][2][0]=flex3;
	score[0][0][2][2][2][0]=flex3;
	
	
	//黑活二	(棋型 11 // 400) 
	score[0][1][1][0][0][0]=FLEX2;
	score[0][1][0][1][0][0]=FLEX2;
	score[0][1][0][0][1][0]=FLEX2;
	score[0][0][1][1][0][0]=FLEX2;
	score[0][0][1][0][1][0]=FLEX2; 
	score[0][0][0][1][1][0]=FLEX2;
	
	//白活二	(棋型 12 // -600) 
	score[0][2][2][0][0][0]=flex2;
	score[0][2][0][2][0][0]=flex2;
	score[0][2][0][0][2][0]=flex2;
	score[0][0][2][2][0][0]=flex2;
	score[0][0][2][0][2][0]=flex2; 
	score[0][0][0][2][2][0]=flex2;
	
	
	//黑活一	(棋型 15 // 100) 
	score[0][1][0][0][0][0]=FLEX1;
	score[0][0][1][0][0][0]=FLEX1;
	score[0][0][0][1][0][0]=FLEX1;
	score[0][0][0][0][1][0]=FLEX1;
	
	//白活一	(棋型 16 // -150) 
	score[0][2][0][0][0][0]=flex1;
	score[0][0][2][0][0][0]=flex1;
	score[0][0][0][2][0][0]=flex1;
	score[0][0][0][0][2][0]=flex1;
	
	
	int p1,p2,p3,p4,p5,p6,x,y,ix,iy;
	
	for(p1=0;p1<3;p1++)
	{
		for(p2=0;p2<3;p2++)
		{
			for(p3=0;p3<3;p3++)
			{
				for(p4=0;p4<3;p4++)
				{
					for(p5=0;p5<3;p5++)
					{
						for(p6=0;p6<3;p6++)
						{
							x=0;
							y=0;
							ix=0;
							iy=0;
							if(p1==1) x++;
							else if(p1==2) y++;
							
							if(p2==1) x++,ix++;
							else if(p2==2) y++,iy++;
							
							if(p3==1) x++,ix++;
							else if(p3==2) y++,iy++;
							
							if(p4==1) x++,ix++;
							else if(p4==2) y++,iy++;
							
							if(p5==1) x++,ix++;
							else if(p5==2) y++,iy++;
							
							if(p6==1) ix++;
							else if(p6==2) iy++;
							
							//黑冲四 
							if(x==4&&y==0||ix==4&&iy==0)
							{
								if(score[p1][p2][p3][p4][p5][p6]==0)
								{
									score[p1][p2][p3][p4][p5][p6]=BLOCK4;  //(棋型 5 // 1000)  
								}
							}
							
							//白冲四 
							else if(x==0&&y==4||ix==0&&iy==4)
							{
								if(score[p1][p2][p3][p4][p5][p6]==0)
								{
									score[p1][p2][p3][p4][p5][p6]=block4;  //(棋型 6 // -1000) 
								}
							}
							
							//黑眠三 
							else if(x==3&&y==0||ix==3&&iy==0)
							{
								if(score[p1][p2][p3][p4][p5][p6]==0)
								{
									score[p1][p2][p3][p4][p5][p6]=BLOCK3;  //(棋型 9 // 400) 
								}
							}
							
							//白眠三 
							else if(x==0&&y==3||ix==0&&iy==3)
							{
								if(score[p1][p2][p3][p4][p5][p6]==0)
								{
									score[p1][p2][p3][p4][p5][p6]=block3;  //(棋型 10 // -600) 
								}
							}
							
							//黑眠二
							else if(x==2&&y==0||ix==2&&iy==0)
							{
								if(score[p1][p2][p3][p4][p5][p6]==0)
								{
									score[p1][p2][p3][p4][p5][p6]=BLOCK2; //(棋型 13 // 100) 
								}
							}
							
							//白眠二
							else if(x==0&&y==2||ix==0&&iy==2)
							{
								if(score[p1][p2][p3][p4][p5][p6]==0)
								{
									score[p1][p2][p3][p4][p5][p6]=block2;  //(棋型 14 // -150) 
								}
							}
							
						}
					}
				}
			}
		}
	}
	
	return 0;
}

struct evaluation
{
	int score;
	int result;
};

//对棋盘A的局势进行评分 
struct evaluation evaluate(char A[][15])
{
	//各棋型权重 
	int weight[17]={0,4000,-4000,2000,-2000,1000,-1000,1000,-1000,400,-600,400,-600,100,-150,100,-150};
	
	int i,j,s;
	
	int stat[4][17]={0};

	int STAT[17];
	
	struct evaluation EVALUATION;
	
	//棋型统计
	/*
	0 1 2 ... 14
	1
	2
	.
	.
	.
	14 
	*/
	for(i=0;i<15;i++)
	{
		for(j=0;j<10;j++)
		{
			s=score[A[i][j]][A[i][j+1]][A[i][j+2]][A[i][j+3]][A[i][j+4]][A[i][j+5]];
			/*
			横向五子的情况 
			i=0,j=0 a[0][0] a[0][1] a[0][2] a[0][3] a[0][4] a[0][5]
				j=1 a[0][1,2,3,4,5,6]
				...
				j=9 a[0][9,10,11,12,13,14,15]
			...
			i=14,j=0 a[14][0,1,2,3,4,5]
				j=1 a[14][1,2,3,4,5,6]
				...
				j=9 a[14][9,10,11,12,13,14,15]				
			*/ 
			stat[1][s]++;
		}
	}
	
	for(j=0;j<15;j++)
	{
		for(i=0;i<10;i++)
		{
			s=score[A[i][j]][A[i+1][j]][A[i+2][j]][A[i+3][j]][A[i+4][j]][A[i+5][j]];
			/*
			竖向五子的情况 
			j=0,i=0 a[0][0]
					a[1][0]
					a[2][0]
					a[3][0]
					a[4][0]
					a[5][0]
				
			*/ 
			stat[2][s]++;
		}
	}
	
	for(i=0;i<10;i++)
	{
		for(j=0;j<10;j++)
		{
			s=score[A[i][j]][A[i+1][j+1]][A[i+2][j+2]][A[i+3][j+3]][A[i+4][j+4]][A[i+5][j+5]];
			/*
			斜向五子的情况1 
			i=0,j=0 a[0][0]
						a[1][1]
							a[2][2]
							...
								a[5][5]
			*/ 
			stat[3][s]++;
		}
	}
	
	for(i=5;i<15;i++)
	{
		for(j=0;j<10;j++)
		{
			s=score[A[i][j]][A[i-1][j+1]][A[i-2][j+2]][A[i-3][j+3]][A[i-4][j+4]][A[i-5][j+5]];
			/*
			斜向五子的情况2
			i=5,j=0 a[5][0]
				a[4][1]
			a[3][2]
		a[2][3]
	a[1][4]
a[0][5]
			*/
			stat[0][s]++;
		}
	}
	
	s=0;
	
	//初步评分累加 
	for(i=1;i<17;i++)
	{
		s+=(stat[1][i]+stat[2][i]+stat[3][i]+stat[0][i])*weight[i]; //当前棋局的得分 
		
		STAT[i]=(stat[1][i]>0)+(stat[2][i]>0)+(stat[3][i]>0)+(stat[0][i]>0);  //存在这种棋型的方向的个数 
	}
	
	EVALUATION.result=0;  //记录当前棋局的结果,没有人 胜出则为 0. 
	
	//胜 
	if(STAT[1]>0)
	{
		s+=100000;   //在计算了各种棋型得分值情况下,这种棋局能使我方胜,则得分加 100000. 
		EVALUATION.result=1;
	}
	
	//负 
	else if(STAT[2]>0)
	{
		s-=100000;  //在计算了各种棋型得分值情况下,这种棋局能使对方胜,则得分减 100000.  
		EVALUATION.result=2;
	}
	
	//对手冲四、活四 
	else if(STAT[4]>0||STAT[6]>0)
	{
		s-=30000;
	}
	
	//对手无冲四、活四 
	else if(STAT[4]==0&&STAT[6]==0)
	{
		int k=0;
		
		//检验 冲四活三
		for(i=0;i<4;i++)
		{
			for(j=0;j<4;j++)
			{
				if(i!=j)
				{
					k+=stat[i][5]*stat[j][7];
				}
			}
		}
		
		//活四
		if(STAT[3]>0)
		{
			s+=20000;
		} 
		
		//双冲四
		else if(STAT[5]>=2)
		{
			s+=20000;
		}
		
		//冲四活三
		else if(k>0)
		{
			s+=20000;
		} 
		
		//对手有活三 
		else if(STAT[8]>0&&STAT[5]==0)
		{
			s-=20000;
		}
		
		//双活三 
		else if(STAT[7]>=2&&STAT[8]==0)
		{
			s+=10000;
		}
	}
	EVALUATION.score=s;
		
	return EVALUATION;
}

struct points
{
	//N=7, 每一个子周围8个,0-7 
	int coo_x[N]; //横坐标 
	
	int coo_y[N]; //纵坐标 
	
	int eva[N]; //评估值 
	
	int exi[N]; //存在性 
};

//评估出最佳的N个待定落子点 
struct points seek_points(char A[][15])
{
	struct points best_points;
	
	struct evaluation EVA;
	
	int i,j,k;
	
	int worth[15][15];
	
	int B[15][15]={0};
	 
	for(k=0;k<N;k++)
	{
		best_points.exi[k]=0;
	}
	/*
	评估当前棋盘 
	*/ 
	EVA=evaluate(A);
	
	
	if(EVA.result>0)
	{
		best_points.exi[0]=1;
		
		best_points.eva[0]=EVA.score;
		
		for(i=0;i<15;i++)
		{
			for(j=0;j<15;j++)
			{
				if(A[i][j]==1)
				{
					best_points.coo_x[0]=i;
					
					best_points.coo_y[0]=j;
					
					goto the_end;
				}
			}
		}
	}
	
	for(i=0;i<15;i++)
	{
		for(j=0;j<15;j++)
		{
			if(A[i][j]!=0)
			{//a[i][j]处有子,在i,j的四周辐射3层 
				for(k=-3;k<=3;k++)
				{
					if(i+k>=0&&i+k<15)
					{
						B[i+k][j]=1;
						
						if(j+k>=0&&j+k<15)
						{
							B[i+k][j+k]=1;
						}
						if(j-k>=0&&j-k<15)
						{
							B[i+k][j-k]=1;
						}
					}
					if(j+k>=0&&j+k<15)
					{
						B[i][j+k]=1;
					}
				}
			}
		}
	}
	
	//对于棋盘A上的空点,评估在该处落子后的局势 
	for(i=0;i<15;i++)
	{
		for(j=0;j<15;j++)
		{
			worth[i][j]=-1000000;
			
			if(A[i][j]==0&&B[i][j]==1)
			{//从近到远,考察四周辐射的3层 
				A[i][j]=1;
				
				EVA=evaluate(A);
				
				worth[i][j]=EVA.score;
				
				A[i][j]=0;
			}
		}
	}

	int w;

	//筛选最佳的N个点 
	for(k=0;k<N;k++)
	{//最佳的N个点,[0]对应的点评估收益最高 
		w=-1000000;
		
		for(i=0;i<15;i++)
		{
			for(j=0;j<15;j++)
			{
				if(worth[i][j]>w)
				{
					w=worth[i][j];
					
					best_points.coo_x[k]=i;
					
					best_points.coo_y[k]=j;
				}
			}
		}
		//best_points.eva[0]是评估的N个里的最高收益,-w>3000,可以结束,因为收益已经很小 
		if( (k>0) && ((best_points.eva[0]-w)>3000) ) break;
		
		best_points.eva[k]=w;
		
		best_points.exi[k]=1;
		
		worth[best_points.coo_x[k]][best_points.coo_y[k]]=-1000000;
	}
	
	the_end:
	
	return best_points;
}

struct decision
{
	int coo_x;
	
	int coo_y;
	
	int eva;
};
//全局变量-记录决策的落子位置 
struct decision DECISION;

int level_limit=6;

//博弈树MinMax递归分析 AlphaBeta剪枝
int analyse(char A[][15],int level,int alpha,int beta)
{	
	if(level==level_limit)
	{
		struct points P;
		
		P=seek_points(A);
		
		alpha=P.eva[0];
		
		return alpha;
	}
	
	else if(level%2==0)
	{//max层,我方决策 
		struct points P;
		
		P=seek_points(A);
		
		for(int i=0;i<N;i++)
		{
			if(P.exi[i]==1)
			{
				char buff[15][15];
				
				copy_board(A,buff);
				//我方做出决策 
				buff[P.coo_x[i]][P.coo_y[i]]=1;
				
				int a;
				//递归分析我方决策后的棋局 
				a=analyse(buff,level+1,alpha,beta);
				//我方关注最大化收益,alpha初始-inf,只会增加或不变 
				if(a>alpha)
				{
					alpha=a;
					
					if(level==0)
					{
						DECISION.coo_x=P.coo_x[i];
						
						DECISION.coo_y=P.coo_y[i];
						
						DECISION.eva=a;
					}
				}
			}
			//剪枝策略
			if(beta<=alpha) break;
		}
		return alpha;
	}
	
	else if(level%2==1)
	{//min层,敌方决策 
		char BUFF[15][15];
		//在min层,对方采取使我方收益最小的策略,反转棋盘则过程同上了 
		inv_board(A,BUFF);
		
		struct points P; 
		//敌方在BUFF棋盘寻找最佳落子处(对我方最不利的) 
		P=seek_points(BUFF);
		
		for(int i=0;i<N;i++)
		{
			if(P.exi[i]==1)
			{
				char buff[15][15];
				
				copy_board(A,buff);
				//敌方落子 
				buff[P.coo_x[i]][P.coo_y[i]]=2;
				
				int a;
				//递归分析敌方决策后的棋局 
				a=analyse(buff,level+1,alpha,beta);
				
				if(a<beta)
				{
					beta=a;
				}
			}
			if(beta<=alpha) break;
		}
		return beta;
	}
}

bool start() {
	for(int i=0;i<15;i++)
		for(int j=0;j<15;j++)//开始棋盘为空 
			if(board[i][j]!=0) return 0;
	return 1;
}

Value pushout(int x,int y) {
	Json::Value action;
	action["x"]=x;
	action["y"]=y;
	return action;
}


int main()
{
	
	init_score();
	
	string str;
	getline(cin, str);
	Reader reader;
	Value input;
	reader.parse(str, input); 
	int turnID = input["responses"].size();
	

	int turnNum_rsp = input["responses"].size();
	int turnNum_req = input["requests"].size();
	//对方先手
	int zero = 0;
	if (input["requests"][zero]["x"] != -1)
	{
		//己方第四回合是否选择了换手,第六回合才能从resopnses判别
		if (turnNum_rsp >= 2 && input["responses"][1]["x"] == -1)
		{
			//换手前下的子
			for (int i = 0; i < 2; i++)
			{
				placeAt(input["requests"][i]["x"].asInt(),input["requests"][i]["y"].asInt(),1);
				placeAt(input["responses"][i]["x"].asInt(),input["responses"][i]["y"].asInt(),2);
			}
			//换手后下的子
			for (int i = 2; i < turnNum_rsp; i++)
			{
				placeAt(input["requests"][i]["x"].asInt(),input["requests"][i]["y"].asInt(),2);
				placeAt(input["responses"][i]["x"].asInt(),input["responses"][i]["y"].asInt(),1);
			}
			placeAt(input["requests"][turnNum_rsp]["x"].asInt(),input["requests"][turnNum_rsp]["y"].asInt(),2);
		}
		//未达第六回合或未换手
		else
		{
			for (int i = 0; i < turnNum_rsp; i++)
			{
				placeAt(input["requests"][i]["x"].asInt(),input["requests"][i]["y"].asInt(),2);
				placeAt(input["responses"][i]["x"].asInt(),input["responses"][i]["y"].asInt(),1);
			}
			placeAt(input["requests"][turnNum_rsp]["x"].asInt(),input["requests"][turnNum_rsp]["y"].asInt(),2);
		}
	}
	//己方先手
	else
	{
		//对方选择了换手
		if (turnNum_req >= 3 && input["requests"][2]["x"] == -1)
		{
			//换手前下的子
			for (int i = 0; i < 2; i++)
			{
				placeAt(input["requests"][i]["x"].asInt(),input["requests"][i]["y"].asInt(),1);
				placeAt(input["responses"][i]["x"].asInt(),input["responses"][i]["y"].asInt(),2);
			}
			//换手后下的子
			for (int i = 2; i < turnNum_rsp; i++)
			{
				placeAt(input["requests"][i]["x"].asInt(),input["requests"][i]["y"].asInt(),2);
				placeAt(input["responses"][i]["x"].asInt(),input["responses"][i]["y"].asInt(),1);
			}
			placeAt(input["requests"][turnNum_rsp]["x"].asInt(),input["requests"][turnNum_rsp]["y"].asInt(),2);
		}
		//未达到第五回合或未换手
		else
		{
			for (int i = 0; i < turnNum_rsp; i++)
			{
				placeAt(input["requests"][i]["x"].asInt(),input["requests"][i]["y"].asInt(),2);
				placeAt(input["responses"][i]["x"].asInt(),input["responses"][i]["y"].asInt(),1);
			}
			placeAt(input["requests"][turnNum_rsp]["x"].asInt(),input["requests"][turnNum_rsp]["y"].asInt(),2);
		}
	}
	
	Value ret;
	if(start()) 
	{
		ret["response"]=pushout(7,7);
	}
	else if(input["requests"][zero]["x"] != -1&&turnID == 1)
	{
		int x1=input["requests"][zero]["x"].asInt();
		int y1=input["requests"][zero]["y"].asInt();
		int x2=input["requests"][1]["x"].asInt();
		int y2=input["requests"][1]["y"].asInt();
		int a=(x1-x2)*(x1-x2);
		int b=(y1-y2)*(y1-y2);
		if(a<=1&&b<=1)
		{
			ret["response"]=pushout(-1,-1);
		}
		else
		{
			//核心函数-analyse 
		analyse(board,0,-1000000,1000000);
		board[DECISION.coo_x][DECISION.coo_y]=1;
		ret["response"]=pushout(DECISION.coo_x,DECISION.coo_y);
		}
		
	}
	 else {
		//核心函数-analyse 
		analyse(board,0,-1000000,1000000);
		board[DECISION.coo_x][DECISION.coo_y]=1;
		ret["response"]=pushout(DECISION.coo_x,DECISION.coo_y);
	}
	FastWriter writer;
	cout<<writer.write(ret)<<endl;
	
	return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值