基于博弈树和alpha-beta剪枝算法的五子棋ai优化版2.0

基于博弈树和alpha-beta剪枝算法的五子棋ai优化版2.0

0x00 好久不见

​ 跟着《操作系统真象还原》动手写的项目已经到尾声了,我也准备开始c++ 计算机网络 和刷算法题了。动手做项目,心理比较踏实,掌握的也牢靠,这本书里非常好(可惜代码bug也不少,但这也强迫读者自己修复,必须理解才行)。偷一点时间,把五子棋代码优化了下,我觉得能下赢大部分人了,可惜室友是个bt,不过他也为我的优化提供了宝贵的建议。

0x01 代码

​ 以下代码无第三方依赖,windows linux下均可直接编译。(小技巧,输入996 996可以悔棋)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#define mapsize 225
#define maplen 15
#define dddd 4//思考的层数,建议4层 多了太慢了,并且奇数层和偶数层的评估函数要不同 
int map[mapsize];
int map_bak[mapsize];
typedef struct child* ch;
struct child{
	int*map_array;
	int map_cnt;
};
void print_map(int* data){
	int i,j;
	printf("\t 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 \n");
	printf("\t ———————————————\n");
	for(i=0;i<maplen;i++){
		printf("%d\t|",i); 
		for(j=0;j<maplen;j++){
			switch(data[i*maplen+j]){
				case 0:
					printf("  ");
					break;
				case 1:
					printf("X ");
					break;
				case -1:
					printf("O ");
					break ;
			}
		}
		printf("|"); 
		printf("\n");
	}
	printf("\t ———————————————\n");

}
static inline ch getchild(int data[maplen][maplen],int flag){
	ch ret=(ch)malloc(sizeof(struct child));
	ret->map_array=NULL;
	ret->map_cnt=0;
	int map_cnt=0;
	
	int map_t[maplen][maplen];
	memset(map_t,0,sizeof(map_t));
	int i,j,k;
	for(i=0;i<maplen;i++){
		for(j=0;j<maplen;j++){
			if(data[i][j]){
				if(i-1>=0&&j-1>=0) map_t[i-1][j-1]=!data[i-1][j-1];
				if(i-1>=0) map_t[i-1][j]=!data[i-1][j];
				if(i-1>=0&&j+1<maplen) map_t[i-1][j+1]=!data[i-1][j+1];
				if(j-1>=0) map_t[i][j-1]=!data[i][j-1];
				if(j+1<maplen) map_t[i][j+1]=!data[i][j+1];
				if(i+1<maplen&&j-1>=0) map_t[i+1][j-1]=!data[i+1][j-1];
				if(i+1<maplen) map_t[i+1][j]=!data[i+1][j];
				if(i+1<maplen&&j+1<maplen) map_t[i+1][j+1]=!data[i+1][j+1];
			}
		}
	}

	for(i=0;i<maplen;i++){
		for(j=0;j<maplen;j++){
			if(map_t[i][j]){
				map_cnt++;
			}
			
		}
	}
//	printf("map_cnt %d\n",map_cnt);
	ret->map_cnt=map_cnt;
	ret->map_array=(int*)malloc(map_cnt*sizeof(int)*mapsize);
	memset(ret->map_array,0,map_cnt*sizeof(int)*mapsize);
	int* p=(int*)ret->map_array;
	k=0;
	for(i=0;i<maplen;i++){
		for(j=0;j<maplen;j++){
			if(map_t[i][j]){
				
				memcpy(p+k*mapsize,data,sizeof(int)*mapsize);
				*(p+k*mapsize+i*maplen+j)=flag;
				k++;
			}
		}
	}	
	return ret;
}
static inline int getvalue(int *data,int flag){
	int i,j,k;
	int val[5]={0,0,0,0,0}; //分别代表 1子 2 3 4 5子相连的个数
		for(i=0;i<2*maplen-1;i++){
			int len=0;
			int len1=0;
			for(j=0;j<((i+1)>maplen?(2*maplen-1-i):(i+1));j++){
				
				int x=(i+1)>maplen?(maplen-1-j):(i-j);
				int y=(i+1)>maplen?(i+1-maplen+j):j;
				
				if(data[x*maplen+y]==flag){
					len1++;
					if(x-1<0||y+1>=maplen||(data[(x-1)*maplen+y+1]!=flag)){
						switch(len1){
							case 2:{
								if(x+2<maplen&&y-2>=0&&x-1>=0&&y+1<maplen&&!data[(x+2)*maplen+y-2]&&!data[(x-1)*maplen+y+1]){
									val[3]++;
								}
								break;
							}
							case 3:{
								if((x+3<maplen&&y-3>=0&&!data[(x+3)*maplen+y-3])&&(x-1>=0&&y+1<maplen&&!data[(x-1)*maplen+y+1])){
									val[0]+=8;
								}
								if((x+3<maplen&&y-3>=0&&!data[(x+3)*maplen+y-3])&&(x+4<maplen&&y-4>=0&&!data[(x+4)*maplen+y-4])){
									val[0]++;
								}
								if((x-2>=0&&y+2<maplen&&!data[(x-2)*maplen+y+2])&&(x-1>=0&&y+1<maplen&&!data[(x-1)*maplen+y+1])){
									val[0]++;
								}
								break;
							}
							case 4:{
								if((x-1>=0&&y+1<maplen&&!data[(x-1)*maplen+y+1])){
									val[1]++;
								}
								if((x+4<maplen&&y-4>=0&&!data[(x+4)*maplen+y-4])){
									val[1]++;
								}
								break;
							}
							case 5:{
								val[2]++;						
								break;
							}
						}
					} 
					
			}else{
				len1=0;
			}
			x=(i+1)>maplen?(1-maplen+i+j):(j);
			y=(i+1)>maplen?(j):(maplen-1+j-i); 
			
			if(data[x*maplen+y]==flag){
				len++;
				if(x+1>=maplen||y+1>=maplen||data[(x+1)*maplen+y+1]!=flag){
					switch(len){
						case 2:{
							if(x-2>=0&&y-2>=0&&x+1<maplen&&y+1<maplen&&!data[(x-2)*maplen+y-2]&&!data[(x+1)*maplen+y+1]){
								val[3]++;
							}
							break;
						}
						case 3:{
							if((x+1<maplen&&y+1<maplen&&!data[(x+1)*maplen+y+1])&&(x-3>=0&&y-3>=0&&!data[(x-3)*maplen+y-3])){
								val[0]+=8;
							}
							if((x+1<maplen&&y+1<maplen&&!data[(x+1)*maplen+y+1])&&(x+2<maplen&&y+2<maplen&&!data[(x+2)*maplen+y+2])){
								val[0]++;
							}
							if((x-4>=0&&y-4>=0&&!data[(x-4)*maplen+y-4])&&(x-3>=0&&y-3>=0&&!data[(x-3)*maplen+y-3])){
								val[0]++;
							}
							break;
						}
						case 4:{
							if((x+1<maplen&&y+1<maplen&&!data[(x+1)*maplen+y+1])){
								val[1]++;
							}
							if((x-4>=0&&y-4>=0&&!data[(x-4)*maplen+y-4])){
								val[1]++;
							}
							break;
						}
						case 5:{
							val[2]++;
							break;
						}
					}
				} 
				
			}else{
				len=0;
			}
			
		}
		


	} 

	//以下为横纵
	for(i=0;i<maplen;i++){
		int len=0;
		int len1=0;
		for(j=0;j<maplen;j++){
			if(data[j*maplen+i]==flag){
				len1++;
				if(j+1>=maplen||data[(j+1)*maplen+i]!=flag){
					switch(len1){
						case 2:{
							if(j-2>=0&&j+1<maplen&&!data[(j-2)*maplen+i]&&!data[(j+1)*maplen+i]){
								val[3]++;
							}
							break;
						}
						case 3:{
							if((j+1<maplen&&!data[(j+1)*maplen+i])&&(j-3>=0&&!data[(j-3)*maplen+i])){
								val[0]+=8;
							}
							if((j+1<maplen&&!data[(j+1)*maplen+i])&&(j+2<maplen&&!data[(j+2)*maplen+i])){
								val[0]++;
							}
							if((j-4>=0&&!data[(j-4)*maplen+i])&&(j-3>=0&&!data[(j-3)*maplen+i])){
								val[0]++;
							}
							
							break;
						}
						case 4:{
							if((j+1<maplen&&!data[(j+1)*maplen+i])){
								val[1]++;
							}
							if((j-4>=0&&!data[(j-4)*maplen+i])){
								val[1]++;
							}
							break;
						}
						case 5:{
							val[2]++;	
							break;
						}
					}
				}
				
			}else{
				len1=0;
			}
			
			if(data[i*maplen+j]==flag){
				len++;
				if(j+1>=maplen||data[i*maplen+j+1]!=flag){
					switch(len){
						case 2:{
							if(j-2>=0&&j+1<maplen&&!data[i*maplen+j-2]&&!data[i*maplen+j+1]){
								val[3]++;
							}
							break;
						}
						case 3:{
							if((j+1<maplen&&!data[i*maplen+j+1])&&(j-3>=0&&!data[i*maplen+j-3])){
								val[0]+=8;
							}
							if((j+1<maplen&&!data[i*maplen+j+1])&&(j+2<maplen&&!data[i*maplen+j+2])){
								val[0]++;
							}
							if((j-4>=0&&!data[i*maplen+j-4])&&(j-3>=0&&!data[i*maplen+j-3])){
								val[0]++;
							}
							
							break;
						}
						case 4:{
							if((j+1<maplen&&!data[i*maplen+j+1])){
								val[1]++;
							}
							if((j-4>=0&&!data[i*maplen+j-4])){
								val[1]++;
							}
							break;
						}
						case 5:{
							val[2]++;
							
							break;
						}
					}
				}	
			}else{
				len=0;
			}
		}
		
	}
	if(flag==1) return (val[2]<<15)+(val[1]<<10)+(val[0]<<8)+(val[3]<<2);
	else return (val[2]<<15)+(val[1]<<10)+(val[0]<<8)+(val[3]<<2);
}
int maxmin(int depth,int*data,int flag,int a,int b){
	if(!depth){
		return getvalue(data,1)-getvalue(data,-1);
	}
	if(flag==1){//轮到自己走了,选最大的 
		ch p=getchild(data,flag);
		int i;
		for(i=0;i<p->map_cnt;i++){
			int tmp=maxmin(depth-1,p->map_array+i*mapsize,flag*(-1),a,b);		
			if(tmp>a) {
				a=tmp;
				if(a>b) break;
				if(depth==dddd){
					memcpy(data,p->map_array+i*mapsize,sizeof(int)*mapsize);
					printf("电脑埋头苦算中:%d\n",a);
				}			
			}
			
		}
		free(p->map_array);
		free(p);
		
		return a;
	}else{
		ch p=getchild(data,flag);
		//printf("child cnt %d\n",p->map_cnt);
		int i;
		for(i=0;i<p->map_cnt;i++){
			int tmp=maxmin(depth-1,p->map_array+i*mapsize,flag*(-1),a,b);		
			if(tmp<b) {
				b=tmp;
				if(a>b) break;
					
			}
			
		}
		free(p->map_array);
		free(p);
		return b;
	}	
} 
void getnextstep(int*a,int*b,int*map){	
	int i;
    printf("your step,please:\n");
	while(scanf("%d%d",a,b)!=2||*a<0||*a>=maplen||*b<0||*b>=maplen||map[*a*maplen+*b]       ){
		if(*a==996&&*b==996){
			memcpy(map,map_bak,sizeof(int)*mapsize);
			printf("go back once !!!\n");
			print_map(map);
		}else{
			i=gets(NULL);
			printf("输入有误!请重新输入位置x,y(从0-%d)\n",maplen-1);	
		}
				
	}
	memcpy(map_bak,map,sizeof(int)*mapsize);
}


int main(void){
//	map[112]=1; 
	print_map(map);
	while(1){
		int x,y;
		getnextstep(&x,&y,map);
		map[x*maplen+y]=-1;
		print_map(map);
		if(getvalue(map,-1)>=32000){
			printf("you win!\n");
			
		}	
		maxmin(dddd,map,1,-100000,100000);
		print_map(map);
		if(getvalue(map,1)>=16000){
			printf("ai win!\n");
		}
	}
	return 0;
}