这实际上是我学校的C语言程序设计课结课作业。整个作业代码适中,算法难度合适,是对初学者很友好的一件结课作业。
对五子棋而言最重要的还是估值函数的选择,如果一开始就写出了一个全盘估值的算法,那么很快就能改完了。
我的实现是这么考虑的,对于一个棋盘,黑白两色分别考虑,对于一个颜色,选择棋盘上分数最大的一个点作为这个颜色棋盘的分数,而这个分数的选择是基于活二活三等的数量统计出来的。
然后将两个颜色做差得到分数。

int F(int x,int y,int col){//get Fint ret=0;for(int dir=0;dir<4;++dir){int dx=DirX[dir];int dy=DirY[dir];int now=0;for(int i=-4;i<=4;++i){int nowx=x+i*dx;int nowy=y+i*dy;now|=Get_Val(nowx,nowy,col);now<<=2;}now>>=2;ret+=SCORE_V[now];}return ret;
}

在这里我对棋盘进行了hash映射,使用0、1、2代表空,己方有子,对方有子。因为一个棋子只需要考虑哈他四个方向计九个棋子的权值即可。
查找棋子分数我选择的是AC自动机实现。
在实现了AC自动机后,我又发现可能我们并不需要对每个局面都调用AC自动机,这个复杂度太高了,相反,我们可以用预处理的办法。
对于一个棋子,我认为在一条线上,其周围至多左四右四,合计9个棋子比较有用,他们只有黑白空三种情况,对一个子用2bit记录,只需要18个比特即可记录所有情况,这个总数是很少的,情况的总数只有区区262143种,可以直接先把这些情况权值全部算完再O(1)查询估值即可,这就是上文我说的hash映射,这使得估值的复杂度为至多16次位运算和四次访存。

#ifndef AC
#define AC
#define MAXLEN 10000 const int CHENG_5_SCORE = 5000000;const int HUO_4_SCORE = 100000;const int CHONG_4_SCORE = 10000;const int DAN_HUO_3_SCORE = 8000;const int TIAO_HUO_3_SCORE = 7000;const int MIAN_3_SCORE = 500;const int HUO_2_SCORE = 50;const int MIAN_2_SCORE = 10;char CHANG_LIAN[]="111111"; char CHENG_5[] = "11111";char HUO_4[] = "011110";char DAN_HUO_3_1[] = "001110";char DAN_HUO_3_2[] = "011100";char TIAO_HUO_3_1[] = "010110";char TIAO_HUO_3_2[] = "011010";char HUO_2_1[] = "001100";char HUO_2_2[] = "010100";char HUO_2_3[] = "001010";char CHONG_4_1[] = "011112";char CHONG_4_2[] = "211110";char CHONG_4_3[] = "10111";char CHONG_4_4[] = "11101";char CHONG_4_5[] = "11011";char MIAN_3_1[] = "001112";char MIAN_3_2[] = "211100";char MIAN_3_3[] = "010112";char MIAN_3_4[] = "211010";char MIAN_3_5[] = "011012";char MIAN_3_6[] = "210110";char MIAN_3_7[] = "10011";char MIAN_3_8[] = "11001";char MIAN_3_9[] = "10101";char MIAN_3_10[] = "2011102";char MIAN_2_1[] = "000112";char MIAN_2_2[] = "211000";char MIAN_2_3[] = "001012";char MIAN_2_4[] = "210100";char MIAN_2_5[] = "010012";char MIAN_2_6[] = "210010";char MIAN_2_7[] = "10001";char SHUANGCHONG_4_1[]= "1011101";char SHUANGCHONG_4_2[]= "11011011";char SHUANGCHONG_4_3[]= "111010111";
//the template of the AC_AUTOMATION
struct AC_AUTO{int fail;int vis[4];int cnt;
}T1[1001],T2[1001],T3[1001],T4[1001],T5[1001],T6[1001],T7[1001],T8[1001],T9[1001],T10[1001];
int ACcnt=0;
void build_T(char s[],struct AC_AUTO T[]){int len=strlen(s);int idx=0;for(int i=0;i<len;i++){if(!T[idx].vis[s[i]-'0']){T[idx].vis[s[i]-'0']=++ACcnt;idx=ACcnt; }else{idx=T[idx].vis[s[i]-'0'];}}T[idx].cnt++;
}
void build_fail_Arr(struct AC_AUTO T[]){int q[MAXLEN]={};int head=0;int tail=-1;for(int i=0;i<3;i++){if(T[0].vis[i]){T[0].fail=0;q[++tail]=T[0].vis[i];}}while(head<=tail){int x=q[head];head++;for(int i=0;i<3;i++){if(T[x].vis[i]){T[T[x].vis[i]].fail=T[T[x].fail].vis[i];q[++tail]=T[x].vis[i]; }elseT[x].vis[i]=T[T[x].fail].vis[i];}}
}
int AC_Quary(char s[],struct AC_AUTO T[]){int len=strlen(s);int idx=0;for(int i=0;i<len;i++){idx=T[idx].vis[s[i]-'0'];for(int j=idx;j&&T[j].cnt!=-1;j=T[j].fail){if(T[j].cnt>0)return 1;}}return 0;
}
//T1 MIAN_3
//T2 HUO_2
//T3_CHONG_4
//T4 TIAO_HUO_3
//T5 DAN_HUO_3
//T6 HUO_4
//T7 MIAN_2
//T8 CHENG_5
void AC_Build(){build_T(MIAN_3_1,T1);build_T(MIAN_3_2,T1);build_T(MIAN_3_3,T1);build_T(MIAN_3_4,T1);build_T(MIAN_3_5,T1);build_T(MIAN_3_6,T1);
//  build_T(MIAN_3_7,T1);
//  build_T(MIAN_3_8,T1);build_T(MIAN_3_9,T1);build_T(MIAN_3_10,T1);ACcnt=0;build_fail_Arr(T1); build_T(HUO_2_1,T2);build_T(HUO_2_2,T2);build_T(HUO_2_3,T2);ACcnt=0;build_fail_Arr(T2);build_T(CHONG_4_1,T3);build_T(CHONG_4_2,T3);build_T(CHONG_4_3,T3);build_T(CHONG_4_4,T3);build_T(CHONG_4_5,T3);ACcnt=0;build_fail_Arr(T3); build_T(TIAO_HUO_3_1,T4);build_T(TIAO_HUO_3_2,T4);ACcnt=0;build_fail_Arr(T4);build_T(DAN_HUO_3_1,T5);build_T(DAN_HUO_3_2,T5);ACcnt=0;build_fail_Arr(T5); build_T(HUO_4,T6);ACcnt=0;build_fail_Arr(T6);build_T(MIAN_2_1,T7);build_T(MIAN_2_2,T7);build_T(MIAN_2_3,T7);build_T(MIAN_2_4,T7);build_T(MIAN_2_5,T7);build_T(MIAN_2_6,T7);build_T(MIAN_2_7,T7);ACcnt=0;build_fail_Arr(T7);build_T(CHENG_5,T8);ACcnt=0;build_fail_Arr(T8);build_T(CHANG_LIAN,T9);ACcnt=0;build_fail_Arr(T9);build_T(SHUANGCHONG_4_1,T10);build_T(SHUANGCHONG_4_2,T10);build_T(SHUANGCHONG_4_3,T10);ACcnt=0;build_fail_Arr(T10);
}
#endif

预处理模块:

int GETSCORE(char s[]){int ret=0;if(AC_Quary(s,T8))ret+=CHENG_5_SCORE;if(AC_Quary(s,T7))ret+=MIAN_2_SCORE;if(AC_Quary(s,T6))ret+=HUO_4_SCORE;if(AC_Quary(s,T5))ret+=DAN_HUO_3_SCORE;if(AC_Quary(s,T4))ret+=TIAO_HUO_3_SCORE;if(AC_Quary(s,T3))ret+=CHONG_4_SCORE;if(AC_Quary(s,T2))ret+=HUO_2_SCORE;if(AC_Quary(s,T1))ret+=MIAN_3_SCORE;        return ret;
}
void SCORE_init(){//prepare for the Ffor(int i=0;i<=262143;i++){int x=i;char s[10];int flag=1;for(int i=0;i<9;++i){int now=x&3;if(now==3)flag=0;x>>=2;}if(flag==0)continue;x=i;for(int i=0;i<9;++i){int now=x&3;if(now==0)s[i]='0';if(now==1)s[i]='1';if(now==2)s[i]='2';x>>=2;}s[9]='\0';SCORE_V[i]=GETSCORE(s);CHANGLIAN[i]=AC_Quary(s,T9);CHENG5[i]=AC_Quary(s,T8);CHONG4[i]=((AC_Quary(s,T6)+AC_Quary(s,T3))>=1);HUO3[i]=((AC_Quary(s,T5)+AC_Quary(s,T4))>=1); SHUANGCHONG4[i]=AC_Quary(s,T10);}
}

如是,我们就得到了一个可以搜索一层,估值全盘的五子棋。
随后将其放置在min-max搜索上。
关于min-max搜索无须多叙,参考wiki的伪代码便可以轻松实现(前提是你有一个估值全盘的估值函数)

function  minimax(node, depth, maximizingPlayer) isif depth = 0 or node is a terminal node thenreturn the heuristic value of nodeif maximizingPlayer thenvalue := −∞for each child of node dovalue := max(value, minimax(child, depth − 1, FALSE))return valueelse (* minimizing player *)value := +∞for each child of node dovalue := min(value, minimax(child, depth − 1, TRUE))return value

另外,我发现在很多时候,前一步搜索结果可以被继承(即你下的那一步和对手下的那一步全部被搜索),因此我设计了一个树状链表记录搜索树。
关于禁手:
我的代码并没有设计需要迭代的复杂禁手判断,这主要的原因是为了增加搜索层数,普通禁手可以判断长连,双四,双三等最主要的禁手。依旧使用预处理思想尽快判断禁手,复杂度为稳定的48次运算。

int JudgeBan(int x,int y){//judge a point  int ret=0;//0 not ban 1 banint cntCHANGLIAN=0;int cntCHENG5=0;int cntCHONG4=0;int cntSHUANGCHONG4=0;int cntHUO3=0; for(int dir=0;dir<4;++dir){int dx=DirX[dir];int dy=DirY[dir];int now=0;for(int i=-4;i<=4;++i){int nowx=x+i*dx;int nowy=y+i*dy;now|=Get_Val(nowx,nowy,0);now<<=2;}now>>=2; if(CHANGLIAN[now]){cntCHANGLIAN++;}else{if(CHENG5[now]){cntCHENG5++;}else{if(CHONG4[now]){cntCHONG4++;}else{if(HUO3[now]){cntHUO3++;}}}}if(SHUANGCHONG4[now]){cntSHUANGCHONG4++;}}    if(cntCHANGLIAN)return 1;if(cntCHENG5)return 0;return ((cntHUO3>=2)||(cntCHONG4>=2)||(cntSHUANGCHONG4>=1));
}

一些值得注意的点:

  1. 编译优化很重要,如果使用的VS,released版本就很不错。
  2. 电脑配置也很重要,特别是在一场淘汰赛中取得较好成绩,在使用Core i7 gen 8时单核频率3.0Ghz,这份代码只能跑13层每层9个点,但在使用Core i9 gen 12时单核频率4.5Ghz,这份代码就可以跑13层每层均摊10.5个点了。

最终这份代码得到了最后的冠军(并列第一,因为我和对手都能持黑击败对方,但事实上我的代码持黑可以用更少步数击败对手)
核心代码wzq.c:已附加基本的注释

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include "opt.h"
#include "AC.h"
#include "IO.h"
#include "algorithm.h"
#include "opt.h"
#include "SCORE.h"
#include "tree.h"
//#include "heap.h"
//#define Debug 0
//#define Debug2 0
#define depth 10
#define SUM 10
int T=0;
int nowrate=0;int Limit1[13]={9,9,9,9,9,9,9,9,9,9,9,9,9};
//int Limit1[13]={10,9,9,9,9,8,8,8,8,8,8,8,8};
int Limit2[13]={9,9,9,9,9,8,8,8,8,8,8,8,8};
int Limit3[13]={8,8,8,7,7,7,7,7,6,6,6,6,6};
int (*Limit)[13];
struct Node{int x,y;int sum;
};
void putchess(int x,int y,int cur);
void UpdateV(int x,int y,int player);//update the value of the chessboard
void GetBan();
//
void player_to_player();
void computer_to_player();
void AC_Build();//build AC_AUTOMACHINE
void SCORE_init();
void pvalue();//print value  for debug
int checkwin();
void Tree_DFS(struct Tree_Node * now);//free the unnecessary tree node
void regret();//regret two steps
int main(){srand(233);printf("1:player to player ,2:player to computer \n");int x;scanf("%d",&x);if(x==1){player_to_player();}else{computer_to_player();      }return 0;
}
void player_to_player(){InitBoardArray();AC_Build();SCORE_init();DisplayBoard();for(int cur=0;;cur^=1){char c[40];int x,y;while(1){scanf("%s",c);int len=strlen(c);if(len==5){if(c[0]=='p'&&c[1]=='r'&&c[2]=='i'&&c[3]=='n'&&c[4]=='t'){print_step();}continue;}if(len==4){if(c[0]=='q'&&c[1]=='u'&&c[2]=='i'&&c[3]=='t'){return;}}if(len<=3&&len>1){int a;char b;b=(GETY(c));a=(GETX(c));y=(GETY(c))-'a';x=SIZE-(GETX(c));if(a==0||b=='z'||B.Array[x][y]!=2){printf("error input\n");}      else{break;}        }else{printf("error input\n");}   }y=(GETY(c))-'a';x=SIZE-(GETX(c));putchess(x,y,cur);GetBan();DisplayBoard();int check=checkwin();if(check!=2){if(check==0)puts("Black Win");else puts("White Win");return ;}}
}
//
int F(int x,int y,int player);//judge function
int CheckCHENG_5(int x,int y,int col);
struct Node GetNext(int alpha,int beta,int player,int dep,struct Tree_Node *now);void computer_to_player(){//0 black 1 whiteputs("1 you first,0 computer first");InitBoardArray();AC_Build();SCORE_init();int player;scanf("%d",&player);Limit=Limit3;int cur=1;if(player==0){//computer first, put the chess at H8cur^=1;putchess(7,7,cur);DisplayBoard();}if(player==1){DisplayBoard();//player first, giving a blank board}struct Tree_Node *head;head=malloc(sizeof(struct Tree_Node));memset(head,0,sizeof(struct Tree_Node));for(;;){if(B.cnt>=4){Limit=Limit1;}char c[40];int x,y;while(1){scanf("%s",c);int len=strlen(c);if(len==1){if(c[0]=='p'){//print the value of the boardpvalue();}continue; }if(len==6){if(c[0]=='r'&&c[1]=='e'&&c[2]=='g'&&c[3]=='r'&&c[4]=='e'&&c[5]=='t');{//regret,and give a new node to the tree.regret();Tree_DFS(head);head=malloc(sizeof(struct Tree_Node));memset(head,0,sizeof(struct Tree_Node));                 } }if(len==5){if(c[0]=='p'&&c[1]=='r'&&c[2]=='i'&&c[3]=='n'&&c[4]=='t'){print_step();}continue;}if(len==4){if(c[0]=='q'&&c[1]=='u'&&c[2]=='i'&&c[3]=='t'){return;}}if(len<=3&&len>1){int a;char b;b=(GETY(c));a=(GETX(c));y=(GETY(c))-'a';x=SIZE-(GETX(c));if(a==0||b=='z'||B.Array[x][y]!=2){printf("error input\n");}       else{break;}        }else{printf("error input\n");}   }y=(GETY(c))-'a';x=SIZE-(GETX(c));cur^=1;  putchess(x,y,cur);GetBan();DisplayBoard();int check=checkwin();if(check!=2){if(check==0)puts("Black Win");else puts("White Win");return ;}int findson=0;struct Tree_Node * tmpnow=head;//if already find some sons, just turn the head to that son.if(head->flag)for(int i=1;i<=head->cnt;++i){if(head->son[i]!=NULL&&head->son[i]->x==x&&head->son[i]->y==y){tmpnow=head->son[i];findson=1;}else{if(head->son[i]!=NULL){Tree_DFS(head->son[i]);}}}head=tmpnow;//not find the son, just create a new treeif(!findson){          head=malloc(sizeof(struct Tree_Node));memset(head,0,sizeof(struct Tree_Node));}//print the pos and predicting stepif(findson){printf("%p\n",head);for(int i=1;i<=head->cnt;++i){printf("%p %d %c\n",head->son[i],15-head->son[i]->x,'A'+head->son[i]->y);} }struct Node tmp;T=clock();tmp=GetNext(-1e9,1e9,player,depth,head);   cur^=1;#ifdef Debug2printf("%d %d %d\n",tmp.x,tmp.y,tmp.sum);#endifnowrate=tmp.sum;putchess(tmp.x,tmp.y,cur);tmpnow=head;for(int i=1;i<=head->cnt;++i){if(head->son[i]!=NULL&&head->son[i]->x==tmp.x&&head->son[i]->y==tmp.y){tmpnow=head->son[i];puts("IN");}else{if(head->son[i]!=NULL){Tree_DFS(head->son[i]);}}}        head=tmpnow;GetBan();DisplayBoard();printf("%d %c %d ms\n",15-tmp.x,'A'+tmp.y,(clock()-T)/1000);check=checkwin();if(check!=2){if(check==0)puts("Black Win");else puts("White Win");return ;}}
}
/*
function  minimax(node, depth, maximizingPlayer) isif depth = 0 or node is a terminal node thenreturn the heuristic value of nodeif maximizingPlayer thenvalue := -INFfor each child of node dovalue := max(value, minimax(child, depth ? 1, FALSE))return valueelse (* minimizing player *)value := +INFfor each child of node dovalue := min(value, minimax(child, depth ? 1, TRUE))return value
*/
void UpdateV(int x,int y,int col){if(B.Array[x][y]!=2)val[x][y]=F(x,y,B.Array[x][y]);else{val[x][y]=0;}for(int dir=0;dir<4;++dir){int dx=DirX[dir];int dy=DirY[dir];for(int i=-4;i<=4;i++){if(i==0)continue;int nowx=x+dx*i;int nowy=y+dy*i;if(inX(nowx)&&inY(nowy)&&B.Array[nowx][nowy]!=2){val[nowx][nowy]=F(nowx,nowy,B.Array[nowx][nowy]);}}}
}
struct Node GetNext(int alpha,int beta,int player,int dep,struct Tree_Node* nowNode){struct Node ret={-1,-1,-1e9}; // if((T-clock())/1000>5000&&B.cnt>=6)Limit=Limit2;int cnt=0;if(dep==0){int now=-1e9;for(int i=0;i<SIZE;i++){for(int j=0;j<SIZE;j++){int sum=0;if(B.Array[i][j]==2){//the board is blank, put the chessB.Array[i][j]=player;int Mx=F(i,j,player);B.Array[i][j]=(player^1);int Mn=F(i,j,(player^1));  B.Array[i][j]=2;sum=Mx+Mn;//calculating the value of the baord/*using insert sort to sort the board and find the Nth maxist to search*/ if(cnt<SUM-1){cnt++;nowNode->son[cnt]=malloc(sizeof(struct Tree_Node));memset(nowNode->son[cnt],0,sizeof(struct Tree_Node));nowNode->son[cnt]->x=i;nowNode->son[cnt]->y=j;nowNode->son[cnt]->sum=sum;for(int i=cnt-1;i>=1;i--){if((nowNode->son[i]->sum)<(nowNode->son[i+1]->sum)){struct Tree_Node* tmp=nowNode->son[i+1];nowNode->son[i+1]=nowNode->son[i];nowNode->son[i]=tmp;}}}else{if(nowNode->son[cnt]->sum<sum){memset(nowNode->son[cnt],0,sizeof(struct Tree_Node));nowNode->son[cnt]->x=i;nowNode->son[cnt]->y=j;nowNode->son[cnt]->sum=sum;for(int i=cnt-1;i>=1;i--){if((nowNode->son[i]->sum)<(nowNode->son[i+1]->sum)){struct Tree_Node* tmp=nowNode->son[i+1];nowNode->son[i+1]=nowNode->son[i];nowNode->son[i]=tmp;}                        }}}}        }   }nowNode->cnt=cnt;if((player==0&&(dep%2==0))||(player==1&&(dep%2==1))){//get the ban of the black goalfor(int i=1;i<=cnt;i++){nowNode->son[i]->ban=checkBan(nowNode->son[i]->x,nowNode->son[i]->y);}  }       nowNode->flag=1;int tot=0; for(int i=1;i<=nowNode->cnt;i++){if(nowNode->son[i]==NULL)continue; ++tot;if(tot>(*Limit)[depth-dep])break;if(((player==0&&(dep%2==0))||(player==1&&(dep%2==1)))&&nowNode->son[i]->ban){continue;}int sum=0;int x=nowNode->son[i]->x;int y=nowNode->son[i]->y;int nowcol=(dep%2==1)?(player^1):player;int Mx=-1e9;int Mn=-1e9;if(CheckCHENG_5(x,y,nowcol)){sum=5000000;}else{B.Array[x][y]=nowcol;UpdateV(x,y,nowcol);for(int u=0;u<SIZE;u++){for(int v=0;v<SIZE;v++){if(B.Array[u][v]==nowcol){Mx=max(Mx,val[u][v]);}if(B.Array[u][v]==(nowcol^1)){Mn=max(Mn,val[u][v]); }}}B.Array[x][y]=2;UpdateV(x,y,nowcol);sum=Mx-((nowcol==1&&nowrate<=2000)?4:1)*Mn;      }if(sum>now){now=sum;ret.x=x;ret.y=y;ret.sum=sum;}           } return ret;}if(B.cnt==1&&dep==depth){for(int dir=0;dir<8;++dir){int x=B.curx+DirX[dir];int y=B.cury+DirY[dir];if(inX(x)&&inY(y)&&B.Array[x][y]==2){cnt++;nowNode->son[cnt]=malloc(sizeof(struct Tree_Node));memset(nowNode->son[cnt],0,sizeof(struct Tree_Node));nowNode->son[cnt]->x=x;nowNode->son[cnt]->y=y;}}nowNode->cnt=cnt;nowNode->flag=1;}else{if((nowNode->flag)!=1){int nowcol=(dep%2==1)?(player^1):player;for(int i=0;i<SIZE;i++){for(int j=0;j<SIZE;j++){int sum=0;if(B.Array[i][j]==2){B.Array[i][j]=player;int Mx=F(i,j,player);B.Array[i][j]=(player^1);int Mn=F(i,j,(player^1));   B.Array[i][j]=2;sum=Mx+Mn; if(cnt<SUM-1){cnt++;nowNode->son[cnt]=malloc(sizeof(struct Tree_Node));memset(nowNode->son[cnt],0,sizeof(struct Tree_Node));nowNode->son[cnt]->x=i;nowNode->son[cnt]->y=j;nowNode->son[cnt]->sum=sum;for(int i=cnt-1;i>=1;i--){if((nowNode->son[i]->sum)<(nowNode->son[i+1]->sum)){struct Tree_Node* tmp=nowNode->son[i+1];nowNode->son[i+1]=nowNode->son[i];nowNode->son[i]=tmp;}}}else{if(nowNode->son[cnt]->sum<sum){memset(nowNode->son[cnt],0,sizeof(struct Tree_Node));nowNode->son[cnt]->x=i;nowNode->son[cnt]->y=j;nowNode->son[cnt]->sum=sum;for(int i=cnt-1;i>=1;i--){if((nowNode->son[i]->sum)<(nowNode->son[i+1]->sum)){struct Tree_Node* tmp=nowNode->son[i+1];nowNode->son[i+1]=nowNode->son[i];nowNode->son[i]=tmp;}                     }}}}}       }nowNode->cnt=cnt;if((player==0&&(dep%2==0))||(player==1&&(dep%2==1))){for(int i=1;i<=cnt;i++){nowNode->son[i]->ban=checkBan(nowNode->son[i]->x,nowNode->son[i]->y);} }nowNode->flag=1; }}//doing alpha and beta searchif(dep%2==1){ret.sum=beta;} else ret.sum=alpha;int tot=0;for(int i=1;i<=nowNode->cnt;i++){if(nowNode->son[i]==NULL)continue; ++tot;if(tot>(*Limit)[depth-dep])break;int x=nowNode->son[i]->x;int y=nowNode->son[i]->y;if(((player==0&&(dep%2==0))||(player==1&&(dep%2==1)))&&nowNode->son[i]->ban){continue;}if(dep%2==0){//max nodeB.Array[x][y]=player;UpdateV(x,y,player);if(CheckCHENG_5(x,y,player)){ret.x=x;ret.y=y;ret.sum=5000000;alpha=5000000;}else{struct Node now=GetNext(alpha,beta,player,dep-1,nowNode->son[i]);if(now.sum>ret.sum){ret.x=x;ret.y=y;ret.sum=now.sum;alpha=now.sum;}  if(dep==depth){printf("%d %c sum=%d\n",15-x,(char)('A'+y),now.sum);}                    }B.Array[x][y]=2;UpdateV(x,y,player);if(beta<=alpha)break;}else{//min nodeB.Array[x][y]=(1-player);UpdateV(x,y,(player^1));if(CheckCHENG_5(x,y,1-player)){ret.x=x;ret.y=y;ret.sum=-5000000;beta=-5000000;}else{struct Node now=GetNext(alpha,beta,player,dep-1,nowNode->son[i]);if(now.sum<ret.sum){ret.x=x;ret.y=y;ret.sum=now.sum;beta=now.sum;}                     }B.Array[x][y]=2;UpdateV(x,y,(player^1));if(beta<=alpha)break;}                }return ret;
}
void Tree_DFS(struct Tree_Node * now){//free the tree nodeif(now==NULL)return;for(int i=1;i<=now->cnt;i++){if(now->son[i]!=NULL){Tree_DFS(now->son[i]);}}free(now);
}
int GETSCORE(char s[]){int ret=0;if(AC_Quary(s,T8))ret+=CHENG_5_SCORE;if(AC_Quary(s,T7))ret+=MIAN_2_SCORE;if(AC_Quary(s,T6))ret+=HUO_4_SCORE;if(AC_Quary(s,T5))ret+=DAN_HUO_3_SCORE;if(AC_Quary(s,T4))ret+=TIAO_HUO_3_SCORE;if(AC_Quary(s,T3))ret+=CHONG_4_SCORE;if(AC_Quary(s,T2))ret+=HUO_2_SCORE;if(AC_Quary(s,T1))ret+=MIAN_3_SCORE;       return ret;
}
void SCORE_init(){//prepare for the Ffor(int i=0;i<=262143;i++){int x=i;char s[10];int flag=1;for(int i=0;i<9;++i){int now=x&3;if(now==3)flag=0;x>>=2;}if(flag==0)continue;x=i;for(int i=0;i<9;++i){int now=x&3;if(now==0)s[i]='0';if(now==1)s[i]='1';if(now==2)s[i]='2';x>>=2;}s[9]='\0';SCORE_V[i]=GETSCORE(s);CHANGLIAN[i]=AC_Quary(s,T9);CHENG5[i]=AC_Quary(s,T8);CHONG4[i]=((AC_Quary(s,T6)+AC_Quary(s,T3))>=1);HUO3[i]=((AC_Quary(s,T5)+AC_Quary(s,T4))>=1); SHUANGCHONG4[i]=AC_Quary(s,T10);}
}
inline int Get_Val(int x,int y,int col){//the same col return 1, blank point return 0 other return 2if(!inX(x)||!inY(y))return 2;if(B.Array[x][y]==2)return 0;return B.Array[x][y]==col?1:2;
}
int F(int x,int y,int col){//get Fint ret=0;for(int dir=0;dir<4;++dir){int dx=DirX[dir];int dy=DirY[dir];int now=0;for(int i=-4;i<=4;++i){int nowx=x+i*dx;int nowy=y+i*dy;now|=Get_Val(nowx,nowy,col);now<<=2;}now>>=2;ret+=SCORE_V[now];}return ret;
}
int CheckCHENG_5(int x,int y,int col){// 1 col 0 blank - other for(int dir=0;dir<4;++dir){int dx=DirX[dir];int dy=DirY[dir];int now=0;for(int i=-4;i<=4;++i){if(i==0){now|=1;now<<=2;continue;}int nowx=x+i*dx;int nowy=y+i*dy;now|=Get_Val(nowx,nowy,col);now<<=2;}now>>=2;       if(CHENG5[now])return 1;}return 0;
}
int JudgeBan(int x,int y){//judge a point  int ret=0;//0 not ban 1 banint cntCHANGLIAN=0;int cntCHENG5=0;int cntCHONG4=0;int cntSHUANGCHONG4=0;int cntHUO3=0; for(int dir=0;dir<4;++dir){int dx=DirX[dir];int dy=DirY[dir];int now=0;for(int i=-4;i<=4;++i){int nowx=x+i*dx;int nowy=y+i*dy;now|=Get_Val(nowx,nowy,0);now<<=2;}now>>=2; if(CHANGLIAN[now]){cntCHANGLIAN++;}else{if(CHENG5[now]){cntCHENG5++;}else{if(CHONG4[now]){cntCHONG4++;}else{if(HUO3[now]){cntHUO3++;}}}}if(SHUANGCHONG4[now]){cntSHUANGCHONG4++;}}   if(cntCHANGLIAN)return 1;if(cntCHENG5)return 0;return ((cntHUO3>=2)||(cntCHONG4>=2)||(cntSHUANGCHONG4>=1));
}
int checkBan(int x,int y){B.Array[x][y]=0;if(JudgeBan(x,y)){B.Array[x][y]=2;      return 1;}B.Array[x][y]=2;return 0;
}
int checkwin(){//check the whether winner existsfor(int i=0;i<SIZE;i++){for(int j=0;j<SIZE;j++){if(B.Array[i][j]!=2){int flag=0;for(int dir=0;dir<=3;++dir){int dx=i,dy=j;for(int k=1;k<=4;++k){dx+=DirX[dir];dy+=DirY[dir];if(!inX(dx)||!inY(dy))break;if(B.Array[dx][dy]!=B.Array[i][j])break;if(k==4)flag=1;}if(flag==1)break;}if(flag==1)return B.Array[i][j];}}}return 2;
}
void putchess(int x,int y,int cur){B.cntstep[++B.cnt][0]=x;B.cntstep[B.cnt][1]=y;B.curx=x;B.cury=y;B.Array[x][y]=cur;UpdateV(x,y,cur);
}
//printf the latest value
void pvalue(){for(int i=0;i<SIZE;i++){printf("%2d ",i);for(int j=0;j<SIZE;j++){printf("%d ",val[i][j]);}puts("");}for (char ary='A';ary<'A'+SIZE;ary++)printf("%c ",ary);printf("\n");
}
//Get the banned point
void GetBan(){memset(Ban,0,sizeof(Ban));for(int i=0;i<SIZE;++i){for(int j=0;j<SIZE;++j){if(B.Array[i][j]==2){Ban[i][j]=checkBan(i,j);}}}
}
//delete the latest two steps and refresh the banned point
void regret(){B.Array[B.cntstep[B.cnt][0]][B.cntstep[B.cnt][1]]=2;UpdateV(B.cntstep[B.cnt][0],B.cntstep[B.cnt][1],2);B.cnt--;B.Array[B.cntstep[B.cnt][0]][B.cntstep[B.cnt][1]]=2;UpdateV(B.cntstep[B.cnt][0],B.cntstep[B.cnt][1],2);B.cnt--;GetBan();DisplayBoard();
}

tree.h

#ifndef _Tree_
#define _Tree_
struct Tree_Node{int x,y,flag,sum,ban,cnt;struct Tree_Node* son[11];
};
#endif

heap.h

#ifndef heap
#define heap
struct Heap{struct HeapNode{int x;int y;int sum;}h[10];int heapcnt;
};
void push(struct HeapNode t,struct HeapNode h[], int *heapcnt){(*heapcnt)++;int now=*heapcnt;h[*heapcnt]=t;while(now>1&&h[now].sum<h[now>>1].sum){swap(h[now],h[now>>1]);now=now>>1;}
}
void pop(struct HeapNode h[],int *heapcnt){h[1]=h[*heapcnt];(*heapcnt)--;int now=1;int son;while(now*2<=*heapcnt){son=now*2;if(son+1<=*heapcnt&&h[son].sum>h[son+1].sum){son++;}if(h[now].sum<h[son].sum)return;swap(h[son],h[now]);now=son;}
}
struct HeapNode top(struct HeapNode h[]){return h[1];
}
#endif

增加编译速度的opt.h

#pragma once
#ifdef __GNUC__
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#endif

交互库IO.h

#ifndef IO
#define IO
#define SIZE 15
#define MAXSTEP 15*15+1
struct{int cntstep[MAXSTEP][2];int Array[SIZE][SIZE];//0 black 1 white 2 blank  int cnt,curx,cury;
}B;
int DirX[8]={-1,0,1,1,1,0,-1,-1};
int DirY[8]={1,1,1,0,-1,-1,-1,0};
int val[SIZE][SIZE];
int val0[SIZE][SIZE];
int val1[SIZE][SIZE];
int Board[SIZE][SIZE]={};
int Ban[SIZE][SIZE];
int neighbor[SIZE][SIZE]={};
void print_int(int x,int y);//print x y for debug
void print_board();
void print_step();
int GETX(char c[]);
char GETY(char c[]);
void InitBoardArray();
void DisplayBoard();
void Bclear();
void Bclear(){//initial the boardB.cnt=B.curx=B.cury=0;memset(B.cntstep,0,sizeof(B.cntstep));for(int i=0;i<SIZE;++i){for(int j=0;j<SIZE;++j){B.Array[i][j]=2;}}
}
void InitBoardArray(){Bclear(); Board[0][0]=1;Board[0][SIZE-1]=2;Board[SIZE-1][SIZE-1]=3;Board[SIZE-1][0]=4;for(int j=1;j<=SIZE-2;j++){Board[j][0] = 5;}for(int i=1;i<=SIZE-2;i++){Board[0][i] = 6;}for(int j=1;j<=SIZE-2;j++){Board[j][SIZE-1]=7;}for(int i=1;i<=SIZE-2;i++){Board[SIZE-1][i]=8;}for(int j=1;j<=SIZE-2;j++){for(int i=1;i<=SIZE-2;i++){Board[j][i] = 9;}}for(int i=0;i<SIZE;i++){for(int j=0;j<SIZE;j++){val[i][j]=0;}}
}void DisplayBoard(){   char line;#ifdef __linux__ system("clear");#endif#ifdef _WIN64system("clr");#endifputs("laujiyeung");for(int j = 0,line = 15; j <= SIZE - 1; j++){printf("%2d",line);line -= 1;for(int i = 0; i <= SIZE - 1; i++){if(Ban[j][i]){printf("× ");}else if(B.Array[j][i]!=2){if(B.curx==j&&B.cury==i){switch(B.Array[j][i]){case 0:printf("▲");break;case 1:printf("△");break;}} else{switch(B.Array[j][i]){case 0:printf("●");break;case 1:printf("◎");break;}}#ifdef __linux__if(i!=14) printf("─");#endif               }else{switch(Board[j][i]){case 1:printf("┌");break;case 2:printf("┐");break;case 3:printf("┘");break;case 4:printf("└");break;case 5:printf("├");break;case 6:printf("┬");break;case 7:printf("┤");break;case 8:printf("┴");break;case 9:printf("┼");break;}if(i!=14) printf("─");     }if(i == SIZE - 1){printf("\n");}}}printf("  ");for (char ary='A';ary<'A'+SIZE;ary++)printf("%c ",ary);printf("\n");
}
void print_int(int x,int y){printf("%d %d\n",x,y);
}
void print_board(){for(int i=0;i<SIZE;++i){for(int j=0;j<SIZE;++j){printf("%d ",B.Array[i][j]);}puts("");}
}
int GETX(char c[]){int x=0;int pos=0;int len=strlen(c);while((c[pos]<'0'||c[pos]>'9')&&pos<len)pos++;while(c[pos]>='0'&&c[pos]<='9'&&pos<len){x=x*10+c[pos]-'0';pos++;}return x;
}
char GETY(char c[]){char x='z';int pos=0;int len=strlen(c);while((c[pos]<'a'||c[pos]>'o')&&pos<len)pos++;x=c[pos];return x;
}
void print_step(){for(int i=1;i<=B.cnt;i++){printf("%s %d %c\n",(i%2?"black":"white"),SIZE-B.cntstep[i][0],(char)('A'+B.cntstep[i][1]));}
}
#endif

基本算法库:

#ifndef algorithm
#define algorithm
#define min(x,y) ({            \int __min1 = (x);          \int __min2 = (y);          \__min1 < __min2 ? __min1 : __min2; })#define max(x,y) ({            \int __max1 = (x);          \int __max2 = (y);          \__max1 > __max2 ? __max1 : __max2; })#define swap(x,y)   {struct Node t;t=x;x=y;y=t;}#define inX(x) ((x)>=0&&(x)<SIZE)
#define inY(y) ((y)>=0&&(y)<SIZE) //#define Get_Val(x,y,col)({           \
//  int _x=(x);                    \
//  int _y=(y);                    \
//  (!in(_x)||())
//})
#endif

基于min-max搜索和alpha-beta(α-β)剪枝的五子棋的c语言实现(带简单禁手)相关推荐

  1. 基于python的AI五子棋实现(极大极小值搜索和alpha beta剪枝)

    1.极大极小值搜索介绍 人机博弈是人工智能的重要分支,人们在这一领域探索的过程中产生了大量的研究成果,而极小化极大算法(minimax)是其中最基础的算法,它由Shannon在1950年正式提出. M ...

  2. 五子棋AI算法第三篇-Alpha Beta剪枝

    剪枝是必须的 五子棋AI教程第二版发布啦,地址:https://github.com/lihongxun945/myblog/labels/%E4%BA%94%E5%AD%90%E6%A3%8BAI% ...

  3. alpha-beta剪枝五子棋c语言,五子棋AI算法第三篇-Alpha Beta剪枝

    剪枝是必须的 上一篇讲了极大极小值搜索,其实单纯的极大极小值搜索算法并没有实际意义. 可以做一个简单的计算,平均一步考虑 50 种可能性的话,思考到第四层,那么搜索的节点数就是 50^4 = 6250 ...

  4. 五子棋AI算法-Alpha Beta剪枝

    上一篇讲了极大极小值搜索,其实单纯的极大极小值搜索算法并没有实际意义. 可以做一个简单的计算,平均一步考虑 50 种可能性的话,思考到第四层,那么搜索的节点数就是 50^4 = 6250000,在我的 ...

  5. alpha,beta剪枝详解

    α,β剪枝详解\alpha,\beta剪枝详解α,β剪枝详解 示例图 步骤详解 基础原理 这里我们先要理解什么是α,β\alpha,\betaα,β剪枝:α\alphaα是下界,β\betaβ是上界. ...

  6. alpha beta 剪枝算法

    摘自wikipedia alpha-β修剪的好处在于可以消除搜索树的分支.这样,搜索时间可以限制在"更有希望"的子​​树中,并且可以在同一时间执行更深入的搜索.该算法和极小化极大算 ...

  7. python alpha beta 剪枝_一看就懂的 Alpha-Beta 剪枝算法详解

    Alpha-Beta剪枝用于裁剪搜索树中没有意义的不需要搜索的树枝,以提高运算速度. 假设α为下界,β为上界,对于α ≤ N ≤ β: 若 α ≤ β  则N有解. 若 α > β 则N无解. ...

  8. python实现的基于蒙特卡洛树搜索(MCTS)与UCT RAVE的五子棋游戏

     转自: http://www.cnblogs.com/xmwd/p/python_game_based_on_MCTS_and_UCT_RAVE.html 更新 2017.2.23有更新,见文末 ...

  9. 蒙特卡洛搜索树python_python实现的基于蒙特卡洛树搜索(MCTS)与UCT RAVE的五子棋游戏...

    更新 2017.2.23有更新,见文末. MCTS与UCT 下面的内容引用自徐心和与徐长明的论文<计算机博弈原理与方法学概述>: 蒙特卡洛模拟对局就是从某一棋局出发,随机走棋.有人形象地比 ...

  10. Alpha-Beta剪枝(Alpha Beta Pruning)

    Alpha-Beta剪枝算法(Alpha Beta Pruning) [说明] 本文基于<<CS 161 Recitation Notes - Minimax with Alpha Bet ...

最新文章

  1. Android Support v4、v7、v13的区别和应用场景
  2. 使用nginx后如何在web应用中获取用户ip及原理解释
  3. HTML/CSS[收藏]
  4. Python高阶函数用法
  5. python安装notebook_安装python的jupyter notebook工具
  6. Pandas的学习(pandas中删除行以及重排(重建)行索引)
  7. 大数据学习,Hive是丢不掉的!Hive练习题50道,你也试试!
  8. 汇编语言:利用栈的特性对内存数据进行逆置
  9. Incapsula企业版测试项目
  10. conda 升级_jupyter notebook升级体验!笔记本神器—Jupyter Lab
  11. Redis的复制(Master/Slave)
  12. Linux的Namespace与Cgroups介绍
  13. Android IPC —— AIDL的原理
  14. Linux设置和取消定时关机,修改 linux 默认启动级别 设置系统时间   定时关机
  15. fastnest怎么一键排版_我的妈呀!一键排版也太好用了吧!3秒钟搞定排版!
  16. 单隐层神经网络可以拟合任意单值连续函数
  17. 消除Mac Word文档生成目录中的灰色底纹
  18. Pluecker coordinates普吕克坐标系介绍
  19. 类设计者的工具(四):面向对象程序设计 (继承)
  20. 服务器设置 Header响应头(Server、X-Frame-Options、X-Powered-By)

热门文章

  1. TestBench 基本写法与框架
  2. 阅读开源引擎源代码的方式学习游戏引擎好吗?
  3. 龙芯源码编译mysql_使用源码包在龙芯2F上安装mysql
  4. Loongson2f_龙芯逸珑8089A_扩容硬盘安装debian8(sd卡扩容)
  5. 01靶场环境搭建(Windows2008系统安装优化及phpstudy安装、cms)
  6. 不能创建对象qmdispatch_运行时错误 429,ACTIVEX部件不能创建对象的解决方法小结...
  7. TortoiseSVN汉化教程
  8. Python实现简单自动升级exe程序版本并自动运行
  9. PostgreSQL数据类型(中文手册)
  10. 用高维与低维“相交”的形式在低维空间“感受”高维空间