#include #include #include #include /* Program of Game -- wuziqi Written by Zhang shuai, DEC.25th, 2010 */ #define GRID_NUM 15 //每一行(列)的棋盘交点数 #define GRID_COUNT 225//棋盘上交点总数 #define BLACK 0 //黑棋用0表示 #define WHITE 1 //白棋用1表示 #define NOSTONE 0xFF //没有棋子 //这组宏定义了用以代表几种棋型的数字 #define STWO 1 //眠二 #define STHREE 2 //眠三 #define SFOUR 3 //冲四 #define TWO 4 //活二 #define THREE 5 //活三 #define FOUR 6 //活四 #define FIVE 7 //五连 #define NOTYPE 11 //未定义 #define ANALSISED 255//已分析过的 #define TOBEANALSIS 0 //已分析过的 //这个宏用以检查某一坐标是否是棋盘上的有效落子点 #define IsValidPos(x,y) ((x>=0 && x=0 && y1) TypeCount[WHITE][FOUR]++; if(TypeCount[BLACK][SFOUR]>1) TypeCount[BLACK][FOUR]++; int WValue=0,BValue=0; if(bIsWhiteTurn)//轮到白棋走 { if(TypeCount[WHITE][FOUR]) return 9990;//活四,白胜返回极值 if(TypeCount[WHITE][SFOUR]) return 9980;//冲四,白胜返回极值 if(TypeCount[BLACK][FOUR]) return -9970;//白无冲四活四,而黑有活四,黑胜返回极值 if(TypeCount[BLACK][SFOUR] && TypeCount[BLACK][THREE]) return -9960;//而黑有冲四和活三,黑胜返回极值 if(TypeCount[WHITE][THREE] && TypeCount[BLACK][SFOUR]== 0) return 9950;//白有活三而黑没有四,白胜返回极值 if(TypeCount[BLACK][THREE]>1 && TypeCount[WHITE][SFOUR]==0 && TypeCount[WHITE][THREE]==0 && TypeCount[WHITE][STHREE]==0) return -9940;//黑的活三多于一个,而白无四和三,黑胜返回极值 if(TypeCount[WHITE][THREE]>1) WValue+=2000;//白活三多于一个,白棋价值加2000 else //否则白棋价值加200 if(TypeCount[WHITE][THREE]) WValue+=200; if(TypeCount[BLACK][THREE]>1) BValue+=500;//黑的活三多于一个,黑棋价值加500 else //否则黑棋价值加100 if(TypeCount[BLACK][THREE]) BValue+=100; //每个眠三加10 if(TypeCount[WHITE][STHREE]) WValue+=TypeCount[WHITE][STHREE]*10; //每个眠三加10 if(TypeCount[BLACK][STHREE]) BValue+=TypeCount[BLACK][STHREE]*10; //每个活二加4 if(TypeCount[WHITE][TWO]) WValue+=TypeCount[WHITE][TWO]*4; //每个活二加4 if(TypeCount[BLACK][STWO]) BValue+=TypeCount[BLACK][TWO]*4; //每个眠二加1 if(TypeCount[WHITE][STWO]) WValue+=TypeCount[WHITE][STWO]; //每个眠二加1 if(TypeCount[BLACK][STWO]) BValue+=TypeCount[BLACK][STWO]; } else//轮到黑棋走 { if(TypeCount[BLACK][FOUR]) return 9990;//活四,黑胜返回极值 if(TypeCount[BLACK][SFOUR]) return 9980;//冲四,黑胜返回极值 if(TypeCount[WHITE][FOUR]) return -9970;//活四,白胜返回极值 if(TypeCount[WHITE][SFOUR] && TypeCount[WHITE][THREE]) return -9960;//冲四并活三,白胜返回极值 if(TypeCount[BLACK][THREE] && TypeCount[WHITE][SFOUR]==0) return 9950;//黑活三,白无四。黑胜返回极值 if(TypeCount[WHITE][THREE]>1 && TypeCount[BLACK][SFOUR]==0 && TypeCount[BLACK][THREE]==0 && TypeCount[BLACK][STHREE]==0) return -9940;//白的活三多于一个,而黑无四和三,白胜返回极值 //黑的活三多于一个,黑棋价值加2000 if(TypeCount[BLACK][THREE]>1) BValue+=2000; else //否则黑棋价值加200 if(TypeCount[BLACK][THREE]) BValue+=200; //白的活三多于一个,白棋价值加 500 if(TypeCount[WHITE][THREE]>1) WValue+=500; else //否则白棋价值加100 if(TypeCount[WHITE][THREE]) WValue+=100; //每个眠三加10 if(TypeCount[WHITE][STHREE]) WValue+=TypeCount[WHITE][STHREE]*10; //每个眠三加10 if(TypeCount[BLACK][STHREE]) BValue+=TypeCount[BLACK][STHREE]*10; //每个活二加4 if(TypeCount[WHITE][TWO]) WValue+=TypeCount[WHITE][TWO]*4; //每个活二加4 if(TypeCount[BLACK][STWO]) BValue+=TypeCount[BLACK][TWO]*4; //每个眠二加1 if(TypeCount[WHITE][STWO]) WValue+=TypeCount[WHITE][STWO]; //每个眠二加1 if(TypeCount[BLACK][STWO]) BValue+=TypeCount[BLACK][STWO]; } //加上所有棋子的位置价值 for(i=0;i14 || y+k>14) break; tempArray[k]=position[y+k][x+k]; } //调用直线分析函数分析 AnalysisLine(tempArray,k,j-x); //拾取分析结果 for(int s=0;s14 || y-k<0) break; tempArray[k]=position[y-k][x+k]; } //调用直线分析函数分析 AnalysisLine(tempArray,k,j-x); //拾取分析结果 for(int s=0;s0) { if(AnalyLine[LeftEdge-1]!=StoneType) break; LeftEdge--; } //算连续棋子右边界 while(RightEdge0) { if(AnalyLine[LeftRange -1]==!StoneType) break; LeftRange--; } while(RightRange3) { //如待分析棋子棋型为五连 m_LineRecord[nAnalyPos]=FIVE; return FIVE; } if(RightEdge-LeftEdge== 3) { //如待分析棋子棋型为四连 bool Leftfour=false; if(LeftEdge>0) if(AnalyLine[LeftEdge-1]==NOSTONE) Leftfour=true;//左边有气 if(RightEdge1) if(AnalyLine[LeftEdge-1]==NOSTONE) //左边有气 if(LeftEdge>1 && AnalyLine[LeftEdge-2]==AnalyLine[LeftEdge]) { //左边隔一空白有己方棋子 m_LineRecord[LeftEdge]=SFOUR;//冲四 m_LineRecord[LeftEdge-2]=ANALSISED; } else LeftThree=true; if(RightEdge2) if(AnalyLine[LeftEdge-1]==NOSTONE) //左边有气 if(LeftEdge-1>1 && AnalyLine[LeftEdge-2]==AnalyLine[LeftEdge]) if(AnalyLine[LeftEdge-3]==AnalyLine[LeftEdge]) { //左边隔2个己方棋子 m_LineRecord[LeftEdge-3]=ANALSISED; m_LineRecord[LeftEdge-2]=ANALSISED; m_LineRecord[LeftEdge]=SFOUR;//冲四 } else if(AnalyLine[LeftEdge-3]==NOSTONE) { //左边隔1个己方棋子 m_LineRecord[LeftEdge-2]=ANALSISED; m_LineRecord[LeftEdge]=STHREE;//眠三 } else Lefttwo=true; if(RightEdgeStonePos.x][move->StonePos.y]; } //将一最佳走法汇入历史记录 void EnterHistoryScore(STONEMOVE* move,int depth) { m_HistoryTable[move->StonePos.x][move->StonePos.y]+=2<m) for(int q=j;q<=r;q++) target[k++]=source[q]; else for(int q=i;q<=m;q++) target[k++]=source[q]; } void Merge_A(STONEMOVE* source,STONEMOVE* target,int l,int m,int r) { //从大到小排序 int i=l; int j=m+1; int k=l; while(i<=m &&j<=r) if(source[i].Score>=source[j].Score) target[k++]=source[i++]; else target[k++]=source[j++]; if(i>m) for(int q=j;q<=r;q++) target[k++]=source[q]; else for(int q=i;q<=m;q++) target[k++]=source[q]; } //合并大小为 S 的相邻子数组 //direction 是标志,指明是从大到小还是从小到大排序 void MergePass(STONEMOVE* source,STONEMOVE* target,const int s,const int n,const bool direction) { int i=0; while(i<=n-2*s) { //合并大小为 s的相邻二段子数组 if(direction) Merge(source,target,i,i+s-1,i+2*s-1); else Merge_A(source,target,i,i+s-1,i+2*s-1); i=i+2*s; } if(i+s8000)//如果估值函数返回极值,给定局面游戏结束 return score;//返回极值 return 0;//返回未结束 } int NegaScout(int depth,int alpha,int beta) { int Count,i; unsigned char type; int a,b,t; int side; int score; /* if(depth>0) { i= IsGameOver(CurPosition,depth); if(i!=0) return i; //已分胜负,返回极值 } */ side=(m_nMaxDepth-depth)%2;//计算当前节点的类型,极大0/极小1 score=LookUpHashTable(alpha,beta,depth,side); if(score!=66666) return score; if(depth<=0)//叶子节点取估值 { score=Eveluate(CurPosition,side); EnterHashTable(exact,score,depth,side);//将估值存入置换表 return score; } Count=CreatePossibleMove(CurPosition,depth,side); for(i=0;ia && t0) { //对于第一个后的节点,如果上面的搜索failhigh a=-NegaScout(depth-1,-beta,-t);//re-search eval_is_exact=1;//设数据类型为精确值 if(depth==m_nMaxDepth) m_cmBestMove=m_MoveList[depth][i]; bestmove=i; } Hash_UnMakeMove(&m_MoveList[depth][i],CurPosition); UnMakeMove(&m_MoveList[depth][i]); if(a= beta) { EnterHashTable(lower_bound,a,depth,side); EnterHistoryScore(&m_MoveList[depth][i],depth); return a; } b=a+1; /* set new null window */ } if(bestmove!=-1) EnterHistoryScore(&m_MoveList[depth][bestmove], depth); if(eval_is_exact) EnterHashTable(exact,a,depth,side); else EnterHashTable(upper_bound,a,depth,side); return a; } unsigned char MakeMove(STONEMOVE* move,int type) { CurPosition[move->StonePos.y][move->StonePos.x]=type; return 0; } void UnMakeMove(STONEMOVE* move) { CurPosition[move->StonePos.y][move->StonePos.x]=NOSTONE; } __int64 rand64(void) { return rand()^((__int64)rand()<<15)^((__int64)rand()<<30)^ ((__int64)rand()<<45)^((__int64)rand()<<60); } //生成32位随机数 long rand32(void) { return rand()^((long)rand()<<15)^((long)rand()<<30); } void CTranspositionTable() { InitializeHashKey();//建立哈希表,创建随机数组 } void _CTranspositionTable() { //释放哈希表所用空间 delete m_pTT[0]; delete m_pTT[1]; } void CalculateInitHashKey(unsigned char CurPosition[][GRID_NUM]) { int j,k,nStoneType; m_HashKey32=0; m_HashKey32=0; //将所有棋子对应的哈希数加总 for(j=0;jStonePos.y][move->StonePos.x];//将棋子在目标位置的随机数添入 m_HashKey32=m_HashKey32^m_nHashKey32[type][move->StonePos.y][move->StonePos.x]; m_HashKey64=m_HashKey64^m_ulHashKey64[type][move->StonePos.y][move->StonePos.x]; } void Hash_UnMakeMove(STONEMOVE *move,unsigned char CurPosition[][GRID_NUM]) { int type; type=CurPosition[move->StonePos.y][move->StonePos.x];//将棋子现在位置上的随机数从哈希值当中去除 m_HashKey32=m_HashKey32^m_nHashKey32[type][move->StonePos.y][move->StonePos.x]; m_HashKey64=m_HashKey64^m_ulHashKey64[type][move->StonePos.y][move->StonePos.x]; } int LookUpHashTable(int alpha, int beta, int depth, int TableNo) { int x; HashItem* pht; //计算二十位哈希地址,如果读者设定的哈希表大小不是 1M*2 的, //而是 TableSize*2,TableSize为读者设定的大小 //则需要修改这一句为m_HashKey32% TableSize //下一个函数中这一句也一样 x=m_HashKey32 & 0xFFFFF; pht=&m_pTT[TableNo][x];//取到具体的表项指针 if(pht->depth>=depth && pht->checksum==m_HashKey64) { switch(pht->entry_type)//判断数据类型 { case exact://确切值 return pht->eval; case lower_bound://下边界 if(pht->eval>=beta) return pht->eval; else break; case upper_bound://上边界 if (pht->eval<=alpha) return pht->eval; else break; } } return 66666; } void EnterHashTable(ENTRY_TYPE entry_type, short eval, short depth, int TableNo) { int x; HashItem* pht; x=m_HashKey32 & 0xFFFFF;//计算二十位哈希地址 pht=&m_pTT[TableNo][x]; //取到具体的表项指针 //将数据写入哈希表 pht->checksum=m_HashKey64; //64位校验码 pht->entry_type=entry_type;//表项类型 pht->eval=eval; //要保存的值 pht->depth=depth; //层次 } void InitializeHashKey() { int i,j,k; srand((unsigned)time(NULL)); //填充随机数组 for(i=0;i<15;i++) for(j=0;j<10;j++) for(k=0;k<9;k++) { m_nHashKey32[i][j][k]=rand32(); m_ulHashKey64[i][j][k]=rand64(); } //申请置换表所用空间。1M "2 个条目,读者也可指定其他大小 m_pTT[0]=new HashItem[1024*1024];//用于存放取极大值的节点数据 m_pTT[1]=new HashItem[1024*1024];//用于存放取极小值的节点数据 } int main() { int colour; char command[10]; //用于保存命令的字符串 for (int i = 0; i < GRID_NUM; i++) for (int j = 0; j < GRID_NUM; j++) m_RenjuBoard[i][j] = NOSTONE; //棋盘初始化 cin >> command; if(strcmp(command, "[START]") != 0)//读入第一条命令 { return 0; //如果不是[START]则停止程序 } cin >> colour; //读入己方颜色 colour=colour-1; m_nUserStoneColor=1-colour; while (true) { int rival_x, rival_y; //用于保存对手上一步落子点 cin >> command; //读入命令 if (strcmp(command, "[PUT]") != 0) break; //如果不是[PUT]则停止程序 cin >> rival_x >> rival_y; //读入对手上一步落子点 if(colour == 0 && rival_x == -1 && rival_y == -1) //如果己方执黑且是第一步,则占据棋盘中心位置 { m_RenjuBoard[GRID_NUM / 2][GRID_NUM / 2] = colour; //更新棋盘信息 cout << GRID_NUM / 2 << ' ' << GRID_NUM / 2 << endl; //输出 cout << flush; //刷新缓冲区 } else { m_RenjuBoard[rival_x][rival_y] = 1 - colour; //更新棋盘信息 m_nSearchDepth=3; //最大搜索深度 do { CNegaScout_TT_HH(); //创建NegaScout_TT_HH搜索引擎 CTranspositionTable(); SearchAGoodMove(m_RenjuBoard,colour); m_RenjuBoard[X][Y]=colour; cout << X << ' ' << Y << endl; //输出 cout << flush; //刷新缓冲区 _CTranspositionTable(); break; //结束循环 } while (true); //循环直至随机得到一个空位置 } } return 0; }