您的位置:首页 > 编程语言

五子棋人机对弈代码——之博弈树算法

2011-02-09 07:03 393 查看
#include<iostream.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
/* 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<GRID_NUM) && (y>=0 && y<GRID_NUM)

//定义了枚举型的数据类型,精确,下边界,上边界
enum ENTRY_TYPE{exact,lower_bound,upper_bound};

//哈希表中元素的结构定义
typedef struct HASHITEM
{
__int64 checksum;	  //64位校验码
ENTRY_TYPE entry_type;//数据类型
short depth;		  //取得此值时的层次
short eval;			  //节点的值
}HashItem;

typedef struct Node
{
int x;
int y;
}POINT;

//用以表示棋子位置的结构
typedef struct _stoneposition
{
unsigned char x;
unsigned char y;
}STONEPOS;

typedef struct _movestone
{
unsigned char nRenjuID;
POINT ptMovePoint;
}MOVESTONE;
//这个结构用以表示走法

typedef struct _stonemove
{
STONEPOS StonePos;//棋子位置
int Score;		  //走法的分数
}STONEMOVE;
//=================================================================//
int AnalysisLine(unsigned char* position,int GridNum,int StonePos);
int AnalysisRight(unsigned char position[][GRID_NUM],int i,int j);
int AnalysisLeft(unsigned char position[][GRID_NUM],int i,int j);
int AnalysisVertical(unsigned char position[][GRID_NUM],int i,int j);
int AnalysisHorizon(unsigned char position[][GRID_NUM],int i,int j);
int Eveluate(unsigned int position[][GRID_NUM],bool bIsWhiteTurn);
int AddMove(int nToX, int nToY, int nPly);
int CreatePossibleMove(unsigned char position[][GRID_NUM], int nPly, int nSide);
void MergeSort(STONEMOVE* source,int n,bool direction);
void MergePass(STONEMOVE* source,STONEMOVE* target,const int s,const int n,const bool direction);
void Merge_A(STONEMOVE* source,STONEMOVE* target,int l,int m,int r);
void Merge(STONEMOVE* source,STONEMOVE* target,int l,int m,int r);
void EnterHistoryScore(STONEMOVE* move,int depth);
int GetHistoryScore(STONEMOVE* move);
void ResetHistoryTable();
int NegaScout(int depth,int alpha,int beta);
void SearchAGoodMove(unsigned char position[][GRID_NUM],int Type);
int IsGameOver(unsigned char position[][GRID_NUM],int nDepth);
void UnMakeMove(STONEMOVE* move);
unsigned char MakeMove(STONEMOVE* move,int type);
void _CSearchEngine();
void InitializeHashKey();
void EnterHashTable(ENTRY_TYPE entry_type, short eval, short depth, int TableNo);
int LookUpHashTable(int alpha, int beta, int depth, int TableNo);
void Hash_UnMakeMove(STONEMOVE *move,unsigned char CurPosition[][GRID_NUM]);
void Hash_MakeMove(STONEMOVE *move,unsigned char CurPosition[][GRID_NUM]);
void CalculateInitHashKey(unsigned char CurPosition[][GRID_NUM]);
__int64 rand64();
long rand32();
void CTranspositionTable();
void _CTranspositionTable();
bool OnInitDialog();
//=================================================================//

int m_HistoryTable[GRID_NUM][GRID_NUM];//历史得分表
STONEMOVE m_TargetBuff[225];    //排序用的缓冲队列
unsigned int m_nHashKey32[15][10][9];	   //32位随机树组,用以生成32位哈希值
unsigned __int64 m_ulHashKey64[15][10][9];//64位随机树组,用以生成64位哈希值
HashItem *m_pTT[10];			   //置换表头指针
unsigned int m_HashKey32;				   //32位哈希值
__int64 m_HashKey64;			   //64 位哈希值
STONEMOVE m_MoveList[10][225];//用以记录走法的数组
unsigned char m_LineRecord[30];				   //存放AnalysisLine分析结果的数组
int TypeRecord[GRID_NUM ][GRID_NUM][4];//存放全部分析结果的数组,有三个维度,用于存放水平、垂直、左斜、右斜 4 个方向上所有棋型分析结果
int TypeCount[2][20];				   //存放统记过的分析结果的数组
int m_nMoveCount;//此变量用以记录走法的总数
unsigned char CurPosition[GRID_NUM][GRID_NUM];//搜索时用于当前节点棋盘状态的数组
STONEMOVE m_cmBestMove;				 //记录最佳走法的变量
//CMoveGenerator* m_pMG;				 //走法产生器指针
//CEveluation* m_pEval;				 //估值核心指针
int m_nSearchDepth;					 //最大搜索深度
int m_nMaxDepth;					 //当前搜索的最大搜索深度
unsigned char m_RenjuBoard[GRID_NUM][GRID_NUM];//棋盘数组,用于显示棋盘
int m_nUserStoneColor;				  //用户棋子的颜色
//CSearchEngine* m_pSE;				  //搜索引擎指针
int X,Y;

//位置重要性价值表,此表从中间向外,越往外价值越低
int PosValue[GRID_NUM][GRID_NUM]=
{
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
{0,1,2,2,2,2,2,2,2,2,2,2,2,1,0},
{0,1,2,3,3,3,3,3,3,3,3,3,2,1,0},
{0,1,2,3,4,4,4,4,4,4,4,3,2,1,0},
{0,1,2,3,4,5,5,5,5,5,4,3,2,1,0},
{0,1,2,3,4,5,6,6,6,5,4,3,2,1,0},
{0,1,2,3,4,5,6,7,6,5,4,3,2,1,0},
{0,1,2,3,4,5,6,6,6,5,4,3,2,1,0},
{0,1,2,3,4,5,5,5,5,5,4,3,2,1,0},
{0,1,2,3,4,4,4,4,4,4,4,3,2,1,0},
{0,1,2,3,3,3,3,3,3,3,3,3,2,1,0},
{0,1,2,2,2,2,2,2,2,2,2,2,2,1,0},
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};

//全局变量,用以统计估值函数的执行遍数
int count=0;

int Eveluate(unsigned char position[][GRID_NUM],bool bIsWhiteTurn)
{
int i,j,k;
unsigned char nStoneType;

count++;//计数器累加

//清空棋型分析结果
memset(TypeRecord,TOBEANALSIS,GRID_COUNT*4*4);
memset(TypeCount,0,40*4);

for(i=0;i<GRID_NUM;i++)
for(j=0;j<GRID_NUM;j++)
{
if(position[i][j]!=NOSTONE)
{
//如果水平方向上没有分析过
if(TypeRecord[i][j][0]==TOBEANALSIS)
AnalysisHorizon(position,i,j);

//如果垂直方向上没有分析过
if(TypeRecord[i][j][1]==TOBEANALSIS)
AnalysisVertical(position,i,j);

//如果左斜方向上没有分析过
if(TypeRecord[i][j][2]==TOBEANALSIS)
AnalysisLeft(position,i,j);

//如果右斜方向上没有分析过
if(TypeRecord[i][j][3]==TOBEANALSIS)
AnalysisRight(position,i,j);
}
}

//对分析结果进行统计,得到每种棋型的数量
for(i=0;i<GRID_NUM;i++)
for(j=0;j<GRID_NUM;j++)
for(k =0;k<4;k++)
{
nStoneType=position[i][j];
if(nStoneType!=NOSTONE)
{
switch(TypeRecord[i][j][k])
{
case FIVE://五连
TypeCount[nStoneType][FIVE]++;
break;

case FOUR://活四
TypeCount[nStoneType][FOUR]++;
break;

case SFOUR://冲四
TypeCount[nStoneType][SFOUR]++;
break;

case THREE://活三
TypeCount[nStoneType][THREE]++;
break;

case STHREE://眠三
TypeCount[nStoneType][STHREE]++;
break;

case TWO://活二
TypeCount[nStoneType][TWO]++;
break;

case STWO://眠二
TypeCount[nStoneType][STWO]++;
break;

default:
break;
}
}
}

//如果已五连,返回极值
if(bIsWhiteTurn)
{
if(TypeCount[BLACK][FIVE])
return -9999;

if(TypeCount[WHITE][FIVE])
return 9999;
}
else
{
if(TypeCount[BLACK][FIVE])
return 9999;

if(TypeCount[WHITE][FIVE])
return -9999;
}

//两个冲四等于一个活四
if(TypeCount[WHITE][SFOUR]>1)
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;i<GRID_NUM;i++)
for(j=0;j<GRID_NUM;j++)
{
nStoneType=position[i][j];
if(nStoneType!=NOSTONE)
if(nStoneType==BLACK)
BValue+=PosValue[i][j];
else
WValue+=PosValue[i][j];
}

//返回估值
if(!bIsWhiteTurn)
return BValue-WValue;
else
return WValue-BValue;
}

//分析棋盘上某点在水平方向上的棋型
int AnalysisHorizon(unsigned char position[][GRID_NUM],int i,int j)
{
//调用直线分析函数分析
AnalysisLine(position[i],15,j);

//拾取分析结果
for(int s=0;s<15;s++)
if(m_LineRecord[s]!=TOBEANALSIS)
TypeRecord[i][s][0]= m_LineRecord[s];

return TypeRecord[i][j][0];
}

//分析棋盘上某点在垂直方向上的棋型
int AnalysisVertical(unsigned char position[][GRID_NUM],int i,int j)
{
unsigned char tempArray[GRID_NUM];

//将垂直方向上的棋子转入一维数组
for(int k=0;k<GRID_NUM;k++)
tempArray[k]=position[k][j];

//调用直线分析函数分析
AnalysisLine(tempArray,GRID_NUM,i);

//拾取分析结果
for(int s=0;s<GRID_NUM;s++)
if(m_LineRecord[s]!=TOBEANALSIS)
TypeRecord[s][j][1]=m_LineRecord[s];

return TypeRecord[i][j][1];
}

//分析棋盘上某点在左斜方向上的棋型
int AnalysisLeft(unsigned char position[][GRID_NUM],int i,int j)
{
unsigned char tempArray[GRID_NUM];
int x,y;

if(i<j)
{
y=0;
x=j-i;
}
else
{
x=0;
y=i-j;
}

//将斜方向上的棋子转入一维数组
for(int k=0;k<GRID_NUM;k++)
{
if(x+k>14 || y+k>14)
break;
tempArray[k]=position[y+k][x+k];
}

//调用直线分析函数分析
AnalysisLine(tempArray,k,j-x);

//拾取分析结果
for(int s=0;s<k;s++)
if(m_LineRecord[s]!=TOBEANALSIS)
TypeRecord[y+s][x+s][2]=m_LineRecord[s];

return TypeRecord[i][j][2];
}

//分析棋盘上某点在右斜方向上的棋型
int AnalysisRight(unsigned char position[][GRID_NUM],int i,int j)
{
unsigned char tempArray[GRID_NUM];
int x,y,realnum;

if(14-i<j)
{
y=14;
x=j-14+i;
realnum=14-i;
}
else
{
x=0;
y=i+j;
realnum=j;
}
//将斜方向上的棋子转入一维数组
for(int k=0;k<GRID_NUM;k++)
{
if(x+k>14 || y-k<0)
break;
tempArray[k]=position[y-k][x+k];
}
//调用直线分析函数分析
AnalysisLine(tempArray,k,j-x);

//拾取分析结果
for(int s=0;s<k;s++)
if(m_LineRecord[s]!=TOBEANALSIS)
TypeRecord[y-s][x+s][3]=m_LineRecord[s];

return TypeRecord[i][j][3];
}

int AnalysisLine(unsigned char* position,int GridNum,int StonePos)
{
unsigned char StoneType;
unsigned char AnalyLine[30];
int nAnalyPos;
int LeftEdge,RightEdge;
int LeftRange,RightRange;

if(GridNum<5)
{
//数组长度小于5没有意义
memset(m_LineRecord,ANALSISED,GridNum);
return 0;
}

nAnalyPos=StonePos;
memset(m_LineRecord,TOBEANALSIS,30);
memset(AnalyLine,0x0F,30);
//将传入数组装入AnalyLine;
memcpy(&AnalyLine,position,GridNum);
GridNum--;
StoneType=AnalyLine[nAnalyPos];
LeftEdge=nAnalyPos;
RightEdge=nAnalyPos;

//算连续棋子左边界
while(LeftEdge>0)
{
if(AnalyLine[LeftEdge-1]!=StoneType)
break;
LeftEdge--;
}

//算连续棋子右边界
while(RightEdge<GridNum)
{
if(AnalyLine[RightEdge+1]!=StoneType)
break;
RightEdge++;
}
LeftRange=LeftEdge;
RightRange=RightEdge;

//下面两个循环算出棋子可下的范围
while(LeftRange>0)
{
if(AnalyLine[LeftRange -1]==!StoneType)
break;
LeftRange--;
}
while(RightRange<GridNum)
{
if(AnalyLine[RightRange+1]==!StoneType)
break;
RightRange++;
}

//如果此范围小于4则分析没有意义
if(RightRange-LeftRange<4)
{
for(int k=LeftRange;k<=RightRange;k++)
m_LineRecord[k]=ANALSISED;
return false;
}

//将连续区域设为分析过的,防止重复分析此一区域
for(int k=LeftEdge;k<=RightEdge;k++)
m_LineRecord[k]=ANALSISED;

if(RightEdge-LeftEdge>3)
{
//如待分析棋子棋型为五连
m_LineRecord[nAnalyPos]=FIVE;
return FIVE;
}

if(RightEdge-LeftEdge== 3)
{
//如待分析棋子棋型为四连
bool Leftfour=false;
if(LeftEdge>0)
if(AnalyLine[LeftEdge-1]==NOSTONE)
Leftfour=true;//左边有气

if(RightEdge<GridNum)
//右边未到边界
if(AnalyLine[RightEdge+1]==NOSTONE)
//右边有气
if(Leftfour==true)//如左边有气
m_LineRecord[nAnalyPos]=FOUR;//活四
else
m_LineRecord[nAnalyPos]=SFOUR;//冲四
else
if(Leftfour==true)//如左边有气
m_LineRecord[nAnalyPos]=SFOUR;//冲四
else
if(Leftfour==true)//如左边有气
m_LineRecord[nAnalyPos]=SFOUR;//冲四

return m_LineRecord[nAnalyPos];
}

if(RightEdge-LeftEdge==2)
{
//如待分析棋子棋型为三连
bool LeftThree=false;

if(LeftEdge>1)
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(RightEdge<GridNum)
if(AnalyLine[RightEdge+1]==NOSTONE)
//右边有气
if(RightEdge<GridNum-1 && AnalyLine[RightEdge+2]==AnalyLine[RightEdge])
{
//右边隔1个己方棋子
m_LineRecord[RightEdge]=SFOUR;//冲四
m_LineRecord[RightEdge+2]=ANALSISED;
}
else
if(LeftThree==true)//如左边有气
m_LineRecord[RightEdge]=THREE;//活三
else
m_LineRecord[RightEdge]=STHREE; //冲三
else
{
if(m_LineRecord[LeftEdge]==SFOUR)//如左冲四
return m_LineRecord[LeftEdge];//返回

if(LeftThree==true)//如左边有气
m_LineRecord[nAnalyPos]=STHREE;//眠三
}
else
{
if(m_LineRecord[LeftEdge]==SFOUR)//如左冲四
return m_LineRecord[LeftEdge];//返回
if(LeftThree==true)//如左边有气
m_LineRecord[nAnalyPos]=STHREE;//眠三
}

return m_LineRecord[nAnalyPos];
}

if(RightEdge-LeftEdge==1)
{
//如待分析棋子棋型为二连
bool Lefttwo=false;
bool Leftthree=false;

if(LeftEdge>2)
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(RightEdge<GridNum-2)
if(AnalyLine[RightEdge+1]==NOSTONE)
//右边有气
if(RightEdge+1<GridNum-1 && AnalyLine[RightEdge+2]==AnalyLine[RightEdge])
if(AnalyLine[RightEdge+3]==AnalyLine[RightEdge])
{
//右边隔两个己方棋子
m_LineRecord[RightEdge+3]=ANALSISED;
m_LineRecord[RightEdge+2]=ANALSISED;
m_LineRecord[RightEdge]=SFOUR;//冲四
}
else
if(AnalyLine[RightEdge+3]==NOSTONE)
{
//右边隔 1 个己方棋子
m_LineRecord[RightEdge+2]=ANALSISED;
m_LineRecord[RightEdge]=STHREE;//眠三
}
else
{
if(m_LineRecord[LeftEdge]==SFOUR)//左边冲四
return m_LineRecord[LeftEdge];//返回

if(m_LineRecord[LeftEdge]==STHREE)//左边眠三
return m_LineRecord[LeftEdge];

if(Lefttwo==true)
m_LineRecord[nAnalyPos]=TWO;//返回活二
else
m_LineRecord[nAnalyPos]=STWO;//眠二
}
else
{
if(m_LineRecord[LeftEdge]==SFOUR)//冲四返回
return m_LineRecord[LeftEdge];

if(Lefttwo==true)//眠二
m_LineRecord[nAnalyPos]=STWO;
}

return m_LineRecord[nAnalyPos];
}

return 0;
}

//将历史记录表中所有项目全置为初值
void ResetHistoryTable()
{
memset(m_HistoryTable,10,GRID_COUNT*sizeof(int));
}

//从历史得分表中取给定走法的历史得分
int GetHistoryScore(STONEMOVE* move)
{
return m_HistoryTable[move->StonePos.x][move->StonePos.y];
}

//将一最佳走法汇入历史记录
void EnterHistoryScore(STONEMOVE* move,int depth)
{
m_HistoryTable[move->StonePos.x][move->StonePos.y]+=2<<depth;
}

//对走法队列从小到大排序
//STONEMOVE* source原始队列
//STONEMOVE* target目标队列
//合并source[l…m]和 source[m +1…r]至target[l…r]
void Merge(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];
}

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+s<n)//剩余的元素个数小于2s
{
if(direction)
Merge(source,target,i,i+s-1,n-1);
else
Merge_A(source,target,i,i+s-1,n-1);
}
else
for(int j=i;j<=n-1;j++)
target[j]=source[j];
}

void MergeSort(STONEMOVE* source,int n,bool direction)
{
int s=1;

while(s<n)
{
MergePass(source,m_TargetBuff,s,n,direction);
s+=s;

MergePass(m_TargetBuff,source,s,n,direction);
s+=s;
}
}

int CreatePossibleMove(unsigned char position[][GRID_NUM], int nPly, int nSide)
{
int i,j;

m_nMoveCount=0;

for(i=0;i<GRID_NUM;i++)
for(j=0;j<GRID_NUM;j++)
{
if(position[i][j]==(unsigned char)NOSTONE)
AddMove(j,i,nPly);
}

//使用历史启发类中的静态归并排序函数对走法队列进行排序
//这是为了提高剪枝效率
//	CHistoryHeuristic history;
MergeSort(m_MoveList[nPly],m_nMoveCount,0);

return m_nMoveCount;//返回合法走法个数
}

//在m_MoveList中插入一个走法
//nToX是目标位置横坐标
//nToY是目标位置纵坐标
//nPly是此走法所在的层次
int AddMove(int nToX, int nToY, int nPly)
{
m_MoveList[nPly][m_nMoveCount].StonePos.x=nToX;
m_MoveList[nPly][m_nMoveCount].StonePos.y=nToY;

m_nMoveCount++;

m_MoveList[nPly][m_nMoveCount].Score=PosValue[nToY][nToX];//使用位置价值表评估当前走法的价值

return m_nMoveCount;
}

void CNegaScout_TT_HH()
{
ResetHistoryTable();
//	m_pThinkProgress=NULL;
}

void SearchAGoodMove(unsigned char position[][GRID_NUM],int Type)
{
int Score;

memcpy(CurPosition,position,GRID_COUNT);
m_nMaxDepth=m_nSearchDepth;
CalculateInitHashKey(CurPosition);
ResetHistoryTable();
Score=NegaScout(m_nMaxDepth,-20000,20000);
X=m_cmBestMove.StonePos.y;
Y=m_cmBestMove.StonePos.x;
MakeMove(&m_cmBestMove,Type);
memcpy(position,CurPosition,GRID_COUNT);
}

int IsGameOver(unsigned char position[][GRID_NUM],int nDepth)
{
int score,i;//计算要下的棋子颜色

i=(m_nMaxDepth-nDepth)%2;
score=Eveluate(position,i);//调用估值函数
if(abs(score)>8000)//如果估值函数返回极值,给定局面游戏结束
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;i<Count;i++)
m_MoveList[depth][i].Score=GetHistoryScore(&m_MoveList[depth][i]);

MergeSort(m_MoveList[depth],Count,0);

int bestmove=-1;

a=alpha;
b=beta;

int eval_is_exact=0;

for(i=0;i<Count;i++)
{
type=MakeMove(&m_MoveList[depth][i],side);
Hash_MakeMove(&m_MoveList[depth][i],CurPosition);

t=-NegaScout(depth-1,-b,-a);//递归搜索子节点,对第 1 个节点是全窗口,其后是空窗探测

if(t>a && t<beta && i>0)
{
//对于第一个后的节点,如果上面的搜索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<t)
{
eval_is_exact=1;
a=t;
if(depth==m_nMaxDepth)
m_cmBestMove=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;j<GRID_NUM;j++)
for(k=0;k<GRID_NUM;k++)
{
nStoneType=CurPosition[j][k];
if(nStoneType!=0xFF)
{
m_HashKey32=m_HashKey32^m_nHashKey32[nStoneType][j][k];
m_HashKey64=m_HashKey64^m_ulHashKey64[nStoneType][j][k];
}
}
}

void Hash_MakeMove(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];
}

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: