找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 44237|回复: 8
打印 上一主题 下一主题
收起左侧

博弈树加上α-β剪枝算法做出来的五子棋AI往前推6步无压力

[复制链接]
跳转到指定楼层
楼主
ID:72008 发表于 2015-1-11 19:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

AI棋力已经非常叼了我反正是一次都赢不了它,所有棋类AI基本都是一个原理只需要根据游戏规则来改写自己的局面评分函数就欧了
以下代码是在博弈树加上α-β剪枝算法做出来的五子棋AI往前推6步无压力在DEV环境下编译的
  1. #include <cstdlib>
  2. #include <iostream.h>
  3. #include<stdlib.h>
  4. #include<string.h>
  5. #include<time.h>
  6. #define GRID_NUM    15 //每一行(列)的棋盘交点数
  7. #define GRID_COUNT  225//棋盘上交点总数
  8. #define BLACK        0 //黑棋用0表示
  9. #define WHITE        1 //白棋用1表示
  10. #define NOSTONE     '+'  //没有棋子
  11. //这组宏定义了用以代表几种棋型的数字
  12. #define STWO        1  //眠二
  13. #define STHREE        2  //眠三
  14. #define SFOUR        3  //冲四
  15. #define TWO        4  //活二
  16. #define THREE        5  //活三
  17. #define FOUR        6  //活四
  18. #define FIVE        7  //五连
  19. #define NOTYPE        11 //未定义
  20. #define ANALSISED   255//已分析过的
  21. #define TOBEANALSIS 0  //已分析过的
  22. //这个宏用以检查某一坐标是否是棋盘上的有效落子点
  23. #define IsValidPos(x,y) ((x>=0 && x<GRID_NUM) && (y>=0 && y<GRID_NUM)
  24. //定义了枚举型的数据类型,精确,下边界,上边界
  25. enum ENTRY_TYPE{exact,lower_bound,upper_bound};
  26. //哈希表中元素的结构定义
  27. typedef struct HASHITEM
  28. {
  29. __int64 checksum;         //64位校验码
  30. ENTRY_TYPE entry_type;//数据类型
  31. short depth;         //取得此值时的层次
  32. short eval;         //节点的值
  33. }HashItem;

  34. typedef struct Node
  35. {
  36. int x;
  37. int y;
  38. }POINT;

  39. //用以表示棋子位置的结构
  40. typedef struct _stoneposition
  41. {
  42. unsigned char x;
  43. unsigned char y;
  44. }STONEPOS;

  45. typedef struct _movestone
  46. {
  47. unsigned char nRenjuID;
  48. POINT ptMovePoint;
  49. }MOVESTONE;
  50. //这个结构用以表示走法

  51. typedef struct _stonemove
  52. {
  53. STONEPOS StonePos;//棋子位置
  54. int Score;         //走法的分数
  55. }STONEMOVE;
  56. //=================================================================//
  57. int AnalysisLine(unsigned char* position,int GridNum,int StonePos);
  58. int AnalysisRight(unsigned char position[][GRID_NUM],int i,int j);
  59. int AnalysisLeft(unsigned char position[][GRID_NUM],int i,int j);
  60. int AnalysisVertical(unsigned char position[][GRID_NUM],int i,int j);
  61. int AnalysisHorizon(unsigned char position[][GRID_NUM],int i,int j);
  62. int Eveluate(unsigned int position[][GRID_NUM],bool bIsWhiteTurn);
  63. int AddMove(int nToX, int nToY, int nPly);
  64. int CreatePossibleMove(unsigned char position[][GRID_NUM], int nPly, int nSide);
  65. void MergeSort(STONEMOVE* source,int n,bool direction);
  66. void MergePass(STONEMOVE* source,STONEMOVE* target,const int s,const int n,const bool direction);
  67. void Merge_A(STONEMOVE* source,STONEMOVE* target,int l,int m,int r);
  68. void Merge(STONEMOVE* source,STONEMOVE* target,int l,int m,int r);
  69. void EnterHistoryScore(STONEMOVE* move,int depth);
  70. int GetHistoryScore(STONEMOVE* move);
  71. void ResetHistoryTable();
  72. int NegaScout(int depth,int alpha,int beta);
  73. void SearchAGoodMove(unsigned char position[][GRID_NUM],int Type);
  74. int IsGameOver(unsigned char position[][GRID_NUM],int nDepth);
  75. void UnMakeMove(STONEMOVE* move);
  76. unsigned char MakeMove(STONEMOVE* move,int type);
  77. void _CSearchEngine();
  78. void InitializeHashKey();
  79. void EnterHashTable(ENTRY_TYPE entry_type, short eval, short depth, int TableNo);
  80. int LookUpHashTable(int alpha, int beta, int depth, int TableNo);
  81. void Hash_UnMakeMove(STONEMOVE *move,unsigned char CurPosition[][GRID_NUM]);
  82. void Hash_MakeMove(STONEMOVE *move,unsigned char CurPosition[][GRID_NUM]);
  83. void CalculateInitHashKey(unsigned char CurPosition[][GRID_NUM]);
  84. __int64 rand64();
  85. long rand32();
  86. void CTranspositionTable();
  87. void _CTranspositionTable();
  88. bool OnInitDialog();
  89. //=================================================================//
  90. int m_HistoryTable[GRID_NUM][GRID_NUM];//历史得分表
  91. STONEMOVE m_TargetBuff[225];    //排序用的缓冲队列
  92. unsigned int m_nHashKey32[15][10][9];          //32位随机树组,用以生成32位哈希值
  93. unsigned __int64 m_ulHashKey64[15][10][9];//64位随机树组,用以生成64位哈希值
  94. HashItem *m_pTT[10];          //置换表头指针
  95. unsigned int m_HashKey32;          //32位哈希值
  96. __int64 m_HashKey64;          //64 位哈希值
  97. STONEMOVE m_MoveList[10][225];//用以记录走法的数组
  98. unsigned char m_LineRecord[30];          //存放AnalysisLine分析结果的数组
  99. int TypeRecord[GRID_NUM ][GRID_NUM][4];//存放全部分析结果的数组,有三个维度,用于存放水平、垂直、左斜、右斜 4 个方向上所有棋型分析结果
  100. int TypeCount[2][20];          //存放统记过的分析结果的数组
  101. int m_nMoveCount;//此变量用以记录走法的总数
  102. unsigned char CurPosition[GRID_NUM][GRID_NUM];//搜索时用于当前节点棋盘状态的数组
  103. STONEMOVE m_cmBestMove;        //记录最佳走法的变量       
  104. //CMoveGenerator* m_pMG;        //走法产生器指针       
  105. //CEveluation* m_pEval;        //估值核心指针       
  106. int m_nSearchDepth;        //最大搜索深度
  107. int m_nMaxDepth;        //当前搜索的最大搜索深度
  108. unsigned char m_RenjuBoard[GRID_NUM][GRID_NUM];//棋盘数组,用于显示棋盘
  109. int m_nUserStoneColor;         //用户棋子的颜色
  110. //CSearchEngine* m_pSE;         //搜索引擎指针
  111. int X,Y;                               //AI输出落子位置
  112. int SL;                                  //胜利标记
  113. //位置重要性价值表,此表从中间向外,越往外价值越低
  114. int PosValue[GRID_NUM][GRID_NUM]=
  115. {
  116. {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  117. {0,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
  118. {0,1,2,2,2,2,2,2,2,2,2,2,2,1,0},
  119. {0,1,2,3,3,3,3,3,3,3,3,3,2,1,0},
  120. {0,1,2,3,4,4,4,4,4,4,4,3,2,1,0},
  121. {0,1,2,3,4,5,5,5,5,5,4,3,2,1,0},
  122. {0,1,2,3,4,5,6,6,6,5,4,3,2,1,0},
  123. {0,1,2,3,4,5,6,7,6,5,4,3,2,1,0},
  124. {0,1,2,3,4,5,6,6,6,5,4,3,2,1,0},
  125. {0,1,2,3,4,5,5,5,5,5,4,3,2,1,0},
  126. {0,1,2,3,4,4,4,4,4,4,4,3,2,1,0},
  127. {0,1,2,3,3,3,3,3,3,3,3,3,2,1,0},
  128. {0,1,2,2,2,2,2,2,2,2,2,2,2,1,0},
  129. {0,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
  130. {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  131. };

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

  134. int Eveluate(unsigned char position[][GRID_NUM],bool bIsWhiteTurn)
  135. {
  136. int i,j,k;
  137. unsigned char nStoneType;
  138. count++;//计数器累加

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

  142. for(i=0;i<GRID_NUM;i++)
  143. for(j=0;j<GRID_NUM;j++)
  144. {
  145. if(position[i][j]!=NOSTONE)
  146. {
  147. //如果水平方向上没有分析过
  148. if(TypeRecord[i][j][0]==TOBEANALSIS)
  149. AnalysisHorizon(position,i,j);

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

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

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

  161. //对分析结果进行统计,得到每种棋型的数量
  162. for(i=0;i<GRID_NUM;i++)
  163. for(j=0;j<GRID_NUM;j++)
  164. for(k =0;k<4;k++)
  165. {
  166. nStoneType=position[i][j];
  167. if(nStoneType!=NOSTONE)
  168. {
  169. switch(TypeRecord[i][j][k])
  170. {
  171. case FIVE://五连
  172. TypeCount[nStoneType][FIVE]++;
  173. break;
  174. case FOUR://活四
  175. TypeCount[nStoneType][FOUR]++;
  176. break;
  177. case SFOUR://冲四
  178. TypeCount[nStoneType][SFOUR]++;
  179. break;
  180. case THREE://活三
  181. TypeCount[nStoneType][THREE]++;
  182. break;
  183. case STHREE://眠三
  184. TypeCount[nStoneType][STHREE]++;
  185. break;
  186. case TWO://活二
  187. TypeCount[nStoneType][TWO]++;
  188. break;
  189. case STWO://眠二
  190. TypeCount[nStoneType][STWO]++;
  191. break;
  192. default:
  193. break;
  194. }  
  195. }
  196. }

  197. //如果已五连,返回极值
  198. if(bIsWhiteTurn)
  199. {
  200. if(TypeCount[BLACK][FIVE])
  201. {
  202.             
  203.          
  204.                  
  205. return -9999;
  206.         }
  207. if(TypeCount[WHITE][FIVE])
  208. {
  209.                
  210.             
  211.                         
  212. return 9999;
  213.         }
  214. }
  215. else
  216. {
  217. if(TypeCount[BLACK][FIVE])
  218. {
  219.               
  220.          
  221.                                 
  222. return 9999;
  223.         }
  224. if(TypeCount[WHITE][FIVE])
  225. {
  226.             
  227.      
  228.                              
  229. return -9999;
  230.         }
  231. }
  232. //两个冲四等于一个活四
  233. if(TypeCount[WHITE][SFOUR]>1)
  234. TypeCount[WHITE][FOUR]++;
  235. if(TypeCount[BLACK][SFOUR]>1)
  236. TypeCount[BLACK][FOUR]++;
  237. int WValue=0,BValue=0;

  238. if(bIsWhiteTurn)//轮到白棋走
  239. {       
  240. if(TypeCount[WHITE][FOUR])       
  241.         {       
  242.             
  243.          
  244. return 9990;//活四,白胜返回极值
  245.          }
  246. if(TypeCount[WHITE][SFOUR])       
  247.         {       
  248.             
  249.          
  250. return 9980;//冲四,白胜返回极值
  251.         }
  252. if(TypeCount[BLACK][FOUR])       
  253. {
  254.            
  255.             
  256. return -9970;//白无冲四活四,而黑有活四,黑胜返回极值
  257.         }
  258. if(TypeCount[BLACK][SFOUR] && TypeCount[BLACK][THREE])
  259. {
  260.             
  261.                      
  262. return -9960;//而黑有冲四和活三,黑胜返回极值
  263.         }
  264. if(TypeCount[WHITE][THREE] && TypeCount[BLACK][SFOUR]== 0)       
  265.         {       
  266.            
  267.             
  268. return 9950;//白有活三而黑没有四,白胜返回极值
  269.         }
  270. if(TypeCount[BLACK][THREE]>1 && TypeCount[WHITE][SFOUR]==0 && TypeCount[WHITE][THREE]==0 && TypeCount[WHITE][STHREE]==0)
  271.         {       
  272.             
  273.             
  274. return -9940;//黑的活三多于一个,而白无四和三,黑胜返回极值
  275.         }
  276. if(TypeCount[WHITE][THREE]>1)
  277. WValue+=2000;//白活三多于一个,白棋价值加2000
  278. else
  279. //否则白棋价值加200
  280. if(TypeCount[WHITE][THREE])
  281. WValue+=200;
  282. if(TypeCount[BLACK][THREE]>1)
  283. BValue+=500;//黑的活三多于一个,黑棋价值加500
  284. else
  285. //否则黑棋价值加100
  286. if(TypeCount[BLACK][THREE])
  287. BValue+=100;
  288. //每个眠三加10
  289. if(TypeCount[WHITE][STHREE])
  290. WValue+=TypeCount[WHITE][STHREE]*10;
  291. //每个眠三加10
  292. if(TypeCount[BLACK][STHREE])
  293. BValue+=TypeCount[BLACK][STHREE]*10;
  294. //每个活二加4
  295. if(TypeCount[WHITE][TWO])
  296. WValue+=TypeCount[WHITE][TWO]*4;
  297. //每个活二加4
  298. if(TypeCount[BLACK][STWO])
  299. BValue+=TypeCount[BLACK][TWO]*4;
  300. //每个眠二加1
  301. if(TypeCount[WHITE][STWO])
  302. WValue+=TypeCount[WHITE][STWO];
  303. //每个眠二加1
  304. if(TypeCount[BLACK][STWO])
  305. BValue+=TypeCount[BLACK][STWO];
  306. }
  307. else//轮到黑棋走
  308. {       
  309. if(TypeCount[BLACK][FOUR])       
  310.         {       
  311.    
  312. return 9990;//活四,黑胜返回极值
  313.           }
  314. if(TypeCount[BLACK][SFOUR])       
  315.         {       
  316.            
  317. return 9980;//冲四,黑胜返回极值
  318.         }
  319. if(TypeCount[WHITE][FOUR])
  320. return -9970;//活四,白胜返回极值
  321.       
  322. if(TypeCount[WHITE][SFOUR] && TypeCount[WHITE][THREE])       
  323. return -9960;//冲四并活三,白胜返回极值

  324. if(TypeCount[BLACK][THREE] && TypeCount[WHITE][SFOUR]==0)
  325. return 9950;//黑活三,白无四。黑胜返回极值

  326. if(TypeCount[WHITE][THREE]>1 && TypeCount[BLACK][SFOUR]==0 && TypeCount[BLACK][THREE]==0 && TypeCount[BLACK][STHREE]==0)
  327. return -9940;//白的活三多于一个,而黑无四和三,白胜返回极值

  328. //黑的活三多于一个,黑棋价值加2000
  329. if(TypeCount[BLACK][THREE]>1)
  330. BValue+=2000;
  331. else
  332. //否则黑棋价值加200
  333. if(TypeCount[BLACK][THREE])
  334. BValue+=200;

  335. //白的活三多于一个,白棋价值加 500
  336. if(TypeCount[WHITE][THREE]>1)
  337. WValue+=500;
  338. else
  339. //否则白棋价值加100
  340. if(TypeCount[WHITE][THREE])
  341. WValue+=100;

  342. //每个眠三加10
  343. if(TypeCount[WHITE][STHREE])
  344. WValue+=TypeCount[WHITE][STHREE]*10;
  345. //每个眠三加10
  346. if(TypeCount[BLACK][STHREE])
  347. BValue+=TypeCount[BLACK][STHREE]*10;

  348. //每个活二加4
  349. if(TypeCount[WHITE][TWO])
  350. WValue+=TypeCount[WHITE][TWO]*4;
  351. //每个活二加4
  352. if(TypeCount[BLACK][STWO])
  353. BValue+=TypeCount[BLACK][TWO]*4;

  354. //每个眠二加1
  355. if(TypeCount[WHITE][STWO])
  356. WValue+=TypeCount[WHITE][STWO];
  357. //每个眠二加1
  358. if(TypeCount[BLACK][STWO])
  359. BValue+=TypeCount[BLACK][STWO];
  360. }

  361. //加上所有棋子的位置价值
  362. for(i=0;i<GRID_NUM;i++)
  363. for(j=0;j<GRID_NUM;j++)
  364. {
  365. nStoneType=position[i][j];
  366. if(nStoneType!=NOSTONE)
  367. if(nStoneType==BLACK)
  368. BValue+=PosValue[i][j];
  369. else
  370. WValue+=PosValue[i][j];
  371. }

  372. //返回估值
  373. if(!bIsWhiteTurn)
  374. return BValue-WValue;
  375. else
  376. return WValue-BValue;
  377. }

  378. //分析棋盘上某点在水平方向上的棋型
  379. int AnalysisHorizon(unsigned char position[][GRID_NUM],int i,int j)
  380. {
  381. //调用直线分析函数分析
  382. AnalysisLine(position[i],15,j);
  383. //拾取分析结果
  384. for(int s=0;s<15;s++)
  385. if(m_LineRecord[s]!=TOBEANALSIS)
  386. TypeRecord[i][s][0]= m_LineRecord[s];

  387. return TypeRecord[i][j][0];
  388. }

  389. //分析棋盘上某点在垂直方向上的棋型
  390. int AnalysisVertical(unsigned char position[][GRID_NUM],int i,int j)
  391. {
  392. unsigned char tempArray[GRID_NUM];
  393. //将垂直方向上的棋子转入一维数组
  394. for(int k=0;k<GRID_NUM;k++)
  395. tempArray[k]=position[k][j];
  396. //调用直线分析函数分析
  397. AnalysisLine(tempArray,GRID_NUM,i);
  398. //拾取分析结果
  399. for(int s=0;s<GRID_NUM;s++)
  400. if(m_LineRecord[s]!=TOBEANALSIS)
  401. TypeRecord[s][j][1]=m_LineRecord[s];

  402. return TypeRecord[i][j][1];
  403. }

  404. //分析棋盘上某点在左斜方向上的棋型
  405. int AnalysisLeft(unsigned char position[][GRID_NUM],int i,int j)
  406. {
  407. unsigned char tempArray[GRID_NUM];
  408. int x,y;
  409. int k;
  410. if(i<j)
  411. {
  412. y=0;
  413. x=j-i;
  414. }
  415. else
  416. {
  417. x=0;
  418. y=i-j;
  419. }
  420. //将斜方向上的棋子转入一维数组
  421. for(k=0;k<GRID_NUM;k++)
  422. {
  423. if(x+k>14 || y+k>14)
  424. break;
  425. tempArray[k]=position[y+k][x+k];
  426. }
  427. //调用直线分析函数分析
  428. AnalysisLine(tempArray,k,j-x);
  429. //拾取分析结果
  430. for(int s=0;s<k;s++)
  431. if(m_LineRecord[s]!=TOBEANALSIS)
  432. TypeRecord[y+s][x+s][2]=m_LineRecord[s];

  433. return TypeRecord[i][j][2];
  434. }

  435. //分析棋盘上某点在右斜方向上的棋型
  436. int AnalysisRight(unsigned char position[][GRID_NUM],int i,int j)
  437. {
  438. unsigned char tempArray[GRID_NUM];
  439. int x,y,realnum;
  440. int k;
  441. if(14-i<j)
  442. {
  443. y=14;
  444. x=j-14+i;
  445. realnum=14-i;
  446. }
  447. else
  448. {
  449. x=0;
  450. y=i+j;
  451. realnum=j;
  452. }
  453. //将斜方向上的棋子转入一维数组
  454. for(k=0;k<GRID_NUM;k++)
  455. {
  456. if(x+k>14 || y-k<0)
  457. break;
  458. tempArray[k]=position[y-k][x+k];
  459. }
  460. //调用直线分析函数分析
  461. AnalysisLine(tempArray,k,j-x);
  462. //拾取分析结果
  463. for(int s=0;s<k;s++)
  464. if(m_LineRecord[s]!=TOBEANALSIS)
  465. TypeRecord[y-s][x+s][3]=m_LineRecord[s];

  466. return TypeRecord[i][j][3];
  467. }

  468. int AnalysisLine(unsigned char* position,int GridNum,int StonePos)
  469. {
  470. unsigned char StoneType;
  471. unsigned char AnalyLine[30];
  472. int nAnalyPos;
  473. int LeftEdge,RightEdge;
  474. int LeftRange,RightRange;

  475. if(GridNum<5)
  476. {
  477. //数组长度小于5没有意义
  478. memset(m_LineRecord,ANALSISED,GridNum);
  479. return 0;
  480. }
  481. nAnalyPos=StonePos;
  482. memset(m_LineRecord,TOBEANALSIS,30);
  483. memset(AnalyLine,0x0F,30);
  484. //将传入数组装入AnalyLine;
  485. memcpy(&AnalyLine,position,GridNum);
  486. GridNum--;
  487. StoneType=AnalyLine[nAnalyPos];
  488. LeftEdge=nAnalyPos;
  489. RightEdge=nAnalyPos;
  490. //算连续棋子左边界
  491. while(LeftEdge>0)
  492. {
  493. if(AnalyLine[LeftEdge-1]!=StoneType)
  494. break;
  495. LeftEdge--;
  496. }

  497. //算连续棋子右边界
  498. while(RightEdge<GridNum)
  499. {
  500. if(AnalyLine[RightEdge+1]!=StoneType)
  501. break;
  502. RightEdge++;
  503. }
  504. LeftRange=LeftEdge;
  505. RightRange=RightEdge;
  506. //下面两个循环算出棋子可下的范围
  507. while(LeftRange>0)
  508. {
  509. if(AnalyLine[LeftRange -1]==!StoneType)
  510. break;
  511. LeftRange--;
  512. }
  513. while(RightRange<GridNum)
  514. {
  515. if(AnalyLine[RightRange+1]==!StoneType)
  516. break;
  517. RightRange++;
  518. }
  519. //如果此范围小于4则分析没有意义
  520. if(RightRange-LeftRange<4)
  521. {
  522. for(int k=LeftRange;k<=RightRange;k++)
  523. m_LineRecord[k]=ANALSISED;
  524. return false;
  525. }
  526. //将连续区域设为分析过的,防止重复分析此一区域
  527. for(int k=LeftEdge;k<=RightEdge;k++)
  528. m_LineRecord[k]=ANALSISED;
  529. if(RightEdge-LeftEdge>3)
  530. {
  531. //如待分析棋子棋型为五连
  532. m_LineRecord[nAnalyPos]=FIVE;
  533. return FIVE;
  534. }

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

  542. if(RightEdge<GridNum)
  543. //右边未到边界
  544. if(AnalyLine[RightEdge+1]==NOSTONE)
  545. //右边有气
  546. if(Leftfour==true)//如左边有气
  547. m_LineRecord[nAnalyPos]=FOUR;//活四
  548. else
  549. m_LineRecord[nAnalyPos]=SFOUR;//冲四
  550. else
  551. if(Leftfour==true)//如左边有气
  552. m_LineRecord[nAnalyPos]=SFOUR;//冲四
  553. else
  554. if(Leftfour==true)//如左边有气
  555. m_LineRecord[nAnalyPos]=SFOUR;//冲四

  556. return m_LineRecord[nAnalyPos];
  557. }

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

  562. if(LeftEdge>1)
  563. if(AnalyLine[LeftEdge-1]==NOSTONE)
  564. //左边有气
  565. if(LeftEdge>1 && AnalyLine[LeftEdge-2]==AnalyLine[LeftEdge])
  566. {
  567. //左边隔一空白有己方棋子
  568. m_LineRecord[LeftEdge]=SFOUR;//冲四
  569. m_LineRecord[LeftEdge-2]=ANALSISED;
  570. }
  571. else
  572. LeftThree=true;

  573. if(RightEdge<GridNum)
  574. if(AnalyLine[RightEdge+1]==NOSTONE)
  575. //右边有气
  576. if(RightEdge<GridNum-1 && AnalyLine[RightEdge+2]==AnalyLine[RightEdge])
  577. {
  578. //右边隔1个己方棋子
  579. m_LineRecord[RightEdge]=SFOUR;//冲四
  580. m_LineRecord[RightEdge+2]=ANALSISED;
  581. }
  582. else
  583. if(LeftThree==true)//如左边有气
  584. m_LineRecord[RightEdge]=THREE;//活三
  585. else
  586. m_LineRecord[RightEdge]=STHREE; //冲三
  587. else
  588. {
  589. if(m_LineRecord[LeftEdge]==SFOUR)//如左冲四
  590. return m_LineRecord[LeftEdge];//返回

  591. if(LeftThree==true)//如左边有气
  592. m_LineRecord[nAnalyPos]=STHREE;//眠三
  593. }
  594. else
  595. {
  596. if(m_LineRecord[LeftEdge]==SFOUR)//如左冲四
  597. return m_LineRecord[LeftEdge];//返回
  598. if(LeftThree==true)//如左边有气
  599. m_LineRecord[nAnalyPos]=STHREE;//眠三
  600. }

  601. return m_LineRecord[nAnalyPos];
  602. }

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

  608. if(LeftEdge>2)
  609. if(AnalyLine[LeftEdge-1]==NOSTONE)
  610. //左边有气
  611. if(LeftEdge-1>1 && AnalyLine[LeftEdge-2]==AnalyLine[LeftEdge])
  612. if(AnalyLine[LeftEdge-3]==AnalyLine[LeftEdge])
  613. {
  614. //左边隔2个己方棋子
  615. m_LineRecord[LeftEdge-3]=ANALSISED;
  616. m_LineRecord[LeftEdge-2]=ANALSISED;
  617. m_LineRecord[LeftEdge]=SFOUR;//冲四
  618. }
  619. else
  620. if(AnalyLine[LeftEdge-3]==NOSTONE)
  621. {
  622. //左边隔1个己方棋子
  623. m_LineRecord[LeftEdge-2]=ANALSISED;
  624. m_LineRecord[LeftEdge]=STHREE;//眠三
  625. }
  626. else
  627. Lefttwo=true;

  628. if(RightEdge<GridNum-2)
  629. if(AnalyLine[RightEdge+1]==NOSTONE)
  630. //右边有气
  631. if(RightEdge+1<GridNum-1 && AnalyLine[RightEdge+2]==AnalyLine[RightEdge])
  632. if(AnalyLine[RightEdge+3]==AnalyLine[RightEdge])
  633. {
  634. //右边隔两个己方棋子
  635. m_LineRecord[RightEdge+3]=ANALSISED;
  636. m_LineRecord[RightEdge+2]=ANALSISED;
  637. m_LineRecord[RightEdge]=SFOUR;//冲四
  638. }
  639. else
  640. if(AnalyLine[RightEdge+3]==NOSTONE)
  641. {
  642. //右边隔 1 个己方棋子
  643. m_LineRecord[RightEdge+2]=ANALSISED;
  644. m_LineRecord[RightEdge]=STHREE;//眠三
  645. }
  646. else
  647. {
  648. if(m_LineRecord[LeftEdge]==SFOUR)//左边冲四
  649. return m_LineRecord[LeftEdge];//返回

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

  652. if(Lefttwo==true)
  653. m_LineRecord[nAnalyPos]=TWO;//返回活二
  654. else
  655. m_LineRecord[nAnalyPos]=STWO;//眠二
  656. }
  657. else
  658. {
  659. if(m_LineRecord[LeftEdge]==SFOUR)//冲四返回
  660. return m_LineRecord[LeftEdge];

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

  664. return m_LineRecord[nAnalyPos];
  665. }

  666. return 0;
  667. }

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

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

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

  683. //对走法队列从小到大排序
  684. //STONEMOVE* source原始队列
  685. //STONEMOVE* target目标队列
  686. //合并source[l…m]和 source[m +1…r]至target[l…r]
  687. void Merge(STONEMOVE* source,STONEMOVE* target,int l,int m,int r)
  688. {
  689. //从小到大排序
  690. int i=l;
  691. int j=m+1;
  692. int k=l;
  693. while(i<=m && j<=r)
  694. if(source[i].Score<=source[j].Score)
  695. target[k++]=source[i++];
  696. else
  697. target[k++]=source[j++];
  698. if(i>m)
  699. for(int q=j;q<=r;q++)
  700. target[k++]=source[q];
  701. else
  702. for(int q=i;q<=m;q++)
  703. target[k++]=source[q];
  704. }

  705. void Merge_A(STONEMOVE* source,STONEMOVE* target,int l,int m,int r)
  706. {
  707. //从大到小排序
  708. int i=l;
  709. int j=m+1;
  710. int k=l;
  711. while(i<=m &&j<=r)
  712. if(source[i].Score>=source[j].Score)
  713. target[k++]=source[i++];
  714. else
  715. target[k++]=source[j++];
  716. if(i>m)
  717. for(int q=j;q<=r;q++)
  718. target[k++]=source[q];
  719. else
  720. for(int q=i;q<=m;q++)
  721. target[k++]=source[q];
  722. }

  723. //合并大小为 S 的相邻子数组
  724. //direction 是标志,指明是从大到小还是从小到大排序
  725. void MergePass(STONEMOVE* source,STONEMOVE* target,const int s,const int n,const bool direction)
  726. {
  727. int i=0;
  728. while(i<=n-2*s)
  729. {
  730. //合并大小为 s的相邻二段子数组
  731. if(direction)
  732. Merge(source,target,i,i+s-1,i+2*s-1);
  733. else
  734. Merge_A(source,target,i,i+s-1,i+2*s-1);
  735. i=i+2*s;
  736. }
  737. if(i+s<n)//剩余的元素个数小于2s
  738. {
  739. if(direction)
  740. Merge(source,target,i,i+s-1,n-1);
  741. else
  742. Merge_A(source,target,i,i+s-1,n-1);
  743. }
  744. else
  745. for(int j=i;j<=n-1;j++)
  746. target[j]=source[j];
  747. }

  748. void MergeSort(STONEMOVE* source,int n,bool direction)
  749. {
  750. int s=1;
  751. while(s<n)
  752. {
  753. MergePass(source,m_TargetBuff,s,n,direction);
  754. s+=s;
  755. MergePass(m_TargetBuff,source,s,n,direction);
  756. s+=s;
  757. }
  758. }

  759. int CreatePossibleMove(unsigned char position[][GRID_NUM], int nPly, int nSide)
  760. {
  761. int i,j;
  762. m_nMoveCount=0;
  763. for(i=0;i<GRID_NUM;i++)
  764. for(j=0;j<GRID_NUM;j++)
  765. {
  766. if(position[i][j]==(unsigned char)NOSTONE)
  767. AddMove(j,i,nPly);
  768. }

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

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

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

  783. m_nMoveCount++;
  784. m_MoveList[nPly][m_nMoveCount].Score=PosValue[nToY][nToX];//使用位置价值表评估当前走法的价值
  785. return m_nMoveCount;
  786. }

  787. void CNegaScout_TT_HH()
  788. {
  789. ResetHistoryTable();
  790. //        m_pThinkProgress=NULL;
  791. }

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

  795. memcpy(CurPosition,position,GRID_COUNT);
  796. m_nMaxDepth=m_nSearchDepth;
  797. CalculateInitHashKey(CurPosition);
  798. ResetHistoryTable();
  799. Score=NegaScout(m_nMaxDepth,-20000,20000);
  800. X=m_cmBestMove.StonePos.y;
  801. Y=m_cmBestMove.StonePos.x;
  802. MakeMove(&m_cmBestMove,Type);
  803. memcpy(position,CurPosition,GRID_COUNT);
  804. }

  805. int IsGameOver(unsigned char position[][GRID_NUM],int nDepth)
  806. {
  807. int score,i;//计算要下的棋子颜色
  808. i=(m_nMaxDepth-nDepth)%2;
  809. score=Eveluate(position,i);//调用估值函数
  810. if(abs(score)>8000)//如果估值函数返回极值,给定局面游戏结束
  811. return score;//返回极值
  812. return 0;//返回未结束
  813. }

  814. int NegaScout(int depth,int alpha,int beta)
  815. {
  816. int Count,i;
  817. unsigned char type;
  818. int a,b,t;
  819. int side;
  820. int score;
  821. /*        if(depth>0)
  822. {
  823. i= IsGameOver(CurPosition,depth);
  824. if(i!=0)
  825. return i;  //已分胜负,返回极值
  826. }
  827. */
  828. side=(m_nMaxDepth-depth)%2;//计算当前节点的类型,极大0/极小1
  829. score=LookUpHashTable(alpha,beta,depth,side);
  830. if(score!=66666)
  831. return score;
  832. if(depth<=0)//叶子节点取估值
  833. {
  834. score=Eveluate(CurPosition,side);
  835. EnterHashTable(exact,score,depth,side);//将估值存入置换表

  836. return score;
  837. }
  838. Count=CreatePossibleMove(CurPosition,depth,side);
  839. for(i=0;i<Count;i++)
  840. m_MoveList[depth][i].Score=GetHistoryScore(&m_MoveList[depth][i]);

  841. MergeSort(m_MoveList[depth],Count,0);
  842. int bestmove=-1;
  843.     a=alpha;
  844.     b=beta;

  845.     int eval_is_exact=0;

  846.     for(i=0;i<Count;i++)
  847. {
  848. type=MakeMove(&m_MoveList[depth][i],side);
  849. Hash_MakeMove(&m_MoveList[depth][i],CurPosition);
  850. t=-NegaScout(depth-1,-b,-a);//递归搜索子节点,对第 1 个节点是全窗口,其后是空窗探测
  851. if(t>a && t<beta && i>0)
  852. {
  853. //对于第一个后的节点,如果上面的搜索failhigh
  854. a=-NegaScout(depth-1,-beta,-t);//re-search
  855. eval_is_exact=1;//设数据类型为精确值
  856. if(depth==m_nMaxDepth)
  857. m_cmBestMove=m_MoveList[depth][i];
  858. bestmove=i;
  859. }
  860. Hash_UnMakeMove(&m_MoveList[depth][i],CurPosition);
  861. UnMakeMove(&m_MoveList[depth][i]);
  862. if(a<t)
  863. {
  864. eval_is_exact=1;
  865. a=t;
  866. if(depth==m_nMaxDepth)
  867. m_cmBestMove=m_MoveList[depth][i];
  868. }
  869. if(a>= beta)
  870. {
  871. EnterHashTable(lower_bound,a,depth,side);
  872. EnterHistoryScore(&m_MoveList[depth][i],depth);
  873. return a;
  874. }
  875. b=a+1;                      /* set new null window */
  876. }
  877. if(bestmove!=-1)
  878. EnterHistoryScore(&m_MoveList[depth][bestmove], depth);
  879. if(eval_is_exact)
  880. EnterHashTable(exact,a,depth,side);
  881. else
  882. EnterHashTable(upper_bound,a,depth,side);

  883. return a;
  884. }

  885. unsigned char MakeMove(STONEMOVE* move,int type)
  886. {
  887. CurPosition[move->StonePos.y][move->StonePos.x]=type;
  888. return 0;
  889. }

  890. void UnMakeMove(STONEMOVE* move)
  891. {
  892. CurPosition[move->StonePos.y][move->StonePos.x]=NOSTONE;
  893. }

  894. __int64 rand64(void)
  895. {
  896.     return rand()^((__int64)rand()<<15)^((__int64)rand()<<30)^
  897. ((__int64)rand()<<45)^((__int64)rand()<<60);
  898. }

  899. //生成32位随机数
  900. long rand32(void)
  901. {
  902.     return rand()^((long)rand()<<15)^((long)rand()<<30);
  903. }

  904. void CTranspositionTable()
  905. {
  906. InitializeHashKey();//建立哈希表,创建随机数组
  907. }

  908. void _CTranspositionTable()
  909. {
  910. //释放哈希表所用空间
  911. delete m_pTT[0];
  912. delete m_pTT[1];
  913. }

  914. void CalculateInitHashKey(unsigned char CurPosition[][GRID_NUM])
  915. {
  916. int j,k,nStoneType;

  917. m_HashKey32=0;
  918. m_HashKey32=0;
  919. //将所有棋子对应的哈希数加总
  920. for(j=0;j<GRID_NUM;j++)
  921. for(k=0;k<GRID_NUM;k++)
  922. {
  923. nStoneType=CurPosition[j][k];
  924. if(nStoneType!=0xFF)
  925. {
  926. m_HashKey32=m_HashKey32^m_nHashKey32[nStoneType][j][k];
  927. m_HashKey64=m_HashKey64^m_ulHashKey64[nStoneType][j][k];
  928. }
  929. }
  930. }

  931. void Hash_MakeMove(STONEMOVE *move,unsigned char CurPosition[][GRID_NUM])
  932. {
  933. int type;

  934. type=CurPosition[move->StonePos.y][move->StonePos.x];//将棋子在目标位置的随机数添入
  935. m_HashKey32=m_HashKey32^m_nHashKey32[type][move->StonePos.y][move->StonePos.x];
  936. m_HashKey64=m_HashKey64^m_ulHashKey64[type][move->StonePos.y][move->StonePos.x];
  937. }

  938. void Hash_UnMakeMove(STONEMOVE *move,unsigned char CurPosition[][GRID_NUM])
  939. {
  940. int type;
  941. type=CurPosition[move->StonePos.y][move->StonePos.x];//将棋子现在位置上的随机数从哈希值当中去除
  942. m_HashKey32=m_HashKey32^m_nHashKey32[type][move->StonePos.y][move->StonePos.x];
  943. m_HashKey64=m_HashKey64^m_ulHashKey64[type][move->StonePos.y][move->StonePos.x];
  944. }

  945. int LookUpHashTable(int alpha, int beta, int depth, int TableNo)
  946. {
  947. int x;
  948. HashItem* pht;

  949. //计算二十位哈希地址,如果读者设定的哈希表大小不是 1M*2 的,
  950. //而是 TableSize*2,TableSize为读者设定的大小
  951. //则需要修改这一句为m_HashKey32% TableSize
  952. //下一个函数中这一句也一样
  953. x=m_HashKey32 & 0xFFFFF;
  954. pht=&m_pTT[TableNo][x];//取到具体的表项指针

  955.     if(pht->depth>=depth && pht->checksum==m_HashKey64)
  956. {
  957. switch(pht->entry_type)//判断数据类型
  958. {
  959. case exact://确切值
  960. return pht->eval;

  961. case lower_bound://下边界
  962. if(pht->eval>=beta)
  963. return pht->eval;
  964. else
  965. break;

  966. case upper_bound://上边界
  967. if (pht->eval<=alpha)
  968. return pht->eval;
  969. else
  970. break;
  971.         }
  972. }

  973. return 66666;
  974. }

  975. void EnterHashTable(ENTRY_TYPE entry_type, short eval, short depth, int TableNo)
  976. {
  977. int x;
  978. HashItem* pht;

  979. x=m_HashKey32 & 0xFFFFF;//计算二十位哈希地址
  980. pht=&m_pTT[TableNo][x]; //取到具体的表项指针

  981. //将数据写入哈希表
  982. pht->checksum=m_HashKey64; //64位校验码
  983. pht->entry_type=entry_type;//表项类型
  984. pht->eval=eval;          //要保存的值
  985. pht->depth=depth;          //层次
  986. }

  987. void InitializeHashKey()
  988. {
  989. int i,j,k;
  990. srand((unsigned)time(NULL));
  991. //填充随机数组
  992. for(i=0;i<15;i++)
  993. for(j=0;j<10;j++)
  994. for(k=0;k<9;k++)
  995. {
  996. m_nHashKey32[i][j][k]=rand32();
  997. m_ulHashKey64[i][j][k]=rand64();
  998. }

  999. //申请置换表所用空间。1M "2 个条目,读者也可指定其他大小
  1000. m_pTT[0]=new HashItem[1024*1024];//用于存放取极大值的节点数据
  1001. m_pTT[1]=new HashItem[1024*1024];//用于存放取极小值的节点数据
  1002. }


  1003. //using namespace std;

  1004. int main(int argc, char *argv[])
  1005. {
  1006. SL=0;
  1007. int colour;
  1008. char command[10]; //用于保存命令的字符串
  1009. for (int i = 0; i < GRID_NUM; i++)
  1010. for (int j = 0; j < GRID_NUM; j++)
  1011. m_RenjuBoard[i][j] = NOSTONE; //棋盘初始化

  1012.     int XS;
  1013.     printf("请选择先手棋手:1表示AI先手 0表示棋手先手\n");
  1014. cin >> XS; //是否电脑先手
  1015. colour=BLACK;
  1016. m_nUserStoneColor=WHITE;
  1017. while (!SL)
  1018. {
  1019.         printf("\n请输入落子坐标:\n");
  1020. int rival_x, rival_y; //用于保存对手上一步落子点
  1021. cin >> rival_x >> rival_y; //读入对手上一步落子点  
  1022. if(XS == 1 && rival_x == -1 && rival_y == -1) //如果己方执黑且是第一步,则占据棋盘中心位置
  1023. {
  1024. m_RenjuBoard[GRID_NUM / 2][GRID_NUM / 2] = colour; //更新棋盘信息
  1025. //cout << GRID_NUM / 2 << ' ' << GRID_NUM / 2 << endl; //输出
  1026. //cout << flush; //刷新缓冲区  
  1027. }
  1028. else
  1029. {
  1030. m_RenjuBoard[rival_x][rival_y] =m_nUserStoneColor;
  1031. //更新棋盘信息
  1032.            for (int i = 0; i < GRID_NUM; i++)
  1033.       {
  1034. for (int j = 0; j < GRID_NUM; j++)
  1035. {
  1036.             if(m_RenjuBoard[i][j]==WHITE)
  1037. printf("O "); else  if(m_RenjuBoard[i][j]==BLACK)printf("X "); else printf("+ ");
  1038.         }
  1039.           printf("\n");
  1040.         }
  1041. m_nSearchDepth=4;//AI难度等级设置
  1042. //最大搜索深度       
  1043. do
  1044. {
  1045. CNegaScout_TT_HH();         //创建NegaScout_TT_HH搜索引擎
  1046. CTranspositionTable();
  1047. SearchAGoodMove(m_RenjuBoard,colour);
  1048. m_RenjuBoard[X][Y]=colour;
  1049. printf("\nAI落子坐标为:");
  1050. cout << X << ' ' << Y << endl; //输出
  1051. cout << flush; //刷新缓冲区
  1052. _CTranspositionTable();
  1053. break; //结束循环
  1054. }
  1055. while (!SL);
  1056. //循环直至随机得到一个空位置
  1057. for (int i = 0; i < GRID_NUM; i++)
  1058.     {
  1059. for (int j = 0; j < GRID_NUM; j++)
  1060. {
  1061.             if(m_RenjuBoard[i][j]==WHITE)
  1062. printf("O "); else  if(m_RenjuBoard[i][j]==BLACK)printf("X "); else printf("+ ");
  1063.         }
  1064.           printf("\n");
  1065.         }
  1066.         
  1067.         if(SL==1)printf("很遗憾AI胜利了继续努力吧少年!\n");else if(SL==2)printf("少年你太叼了居然打败了AI!\n");
  1068.         
  1069.         
  1070.      }
  1071. }
  1072.    
  1073.     system("PAUSE");
  1074.     return EXIT_SUCCESS;
  1075. }
复制代码



分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:106636 发表于 2016-3-6 23:31 | 只看该作者
本帖最后由 1095222570 于 2016-3-7 11:02 编辑

99999999999999999999999999999999
回复

使用道具 举报

板凳
ID:108772 发表于 2016-3-14 00:26 | 只看该作者

RE: 博弈树加上α-β剪枝算法做出来的五子棋AI往前推6步无压力

楼主的代码有个bug,ai不会防守眠4,胜局时也不会自己连5,希望改全,邮箱1490325170@qq.com
回复

使用道具 举报

地板
ID:295341 发表于 2018-3-22 09:17 | 只看该作者
ccl 发表于 2016-3-14 00:26
**** 作者被禁止或删除 内容自动屏蔽 ****

兄弟,楼主有回复你吗,有的话,代码发我一下,
回复

使用道具 举报

5#
ID:295341 发表于 2018-3-26 09:22 | 只看该作者
楼主真大佬,代码能跑起来,有4个错误,改了就好了
回复

使用道具 举报

6#
ID:315397 发表于 2018-4-24 15:25 | 只看该作者
堕落小CY 发表于 2018-3-26 09:22
楼主真大佬,代码能跑起来,有4个错误,改了就好了

请问是哪四个错误啊
回复

使用道具 举报

7#
ID:572771 发表于 2019-7-4 15:09 | 只看该作者
堕落小CY 发表于 2018-3-26 09:22
**** 作者被禁止或删除 内容自动屏蔽 ****

你好,请问是那几个错误呢
回复

使用道具 举报

8#
ID:623380 发表于 2019-10-13 17:05 | 只看该作者
我第一局13步就赢了,而且它判胜负还有问题
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表