久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 45596|回復: 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 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 一级免费在线视频 | 久久久精品在线 | 亚洲综合婷婷 | 国产精品视频久久 | 久久精品一区二区三区四区 | a成人| 伊大人久久 | a级在线免费视频 | 欧美炮房 | 北条麻妃av一区二区三区 | 日本淫视频 | 国产成人精品久久二区二区 | 中文字幕av一区 | 欧美无乱码久久久免费午夜一区 | 久久久久久国产精品免费免费狐狸 | 韩日精品一区 | 亚洲欧美中文日韩在线v日本 | 午夜免费电影 | 久草视频在线播放 | 成人亚洲一区 | 国产精品久久久久影院色老大 | 免费色网址 | 亚洲天堂成人在线视频 | 狠狠干天天干 | 欧美一区二区激情三区 | 欧美一区二区综合 | 欧美日韩最新 | 精品视频999| 男女视频在线观看 | 日韩久草 | 欧美黄色一区 | 国产日韩欧美在线观看 | 宅男伊人 | 狠狠插狠狠操 | 国产中文在线观看 | 欧美一级特黄aaa大片在线观看 | 中文字幕第一页在线 | 国产欧美久久一区二区三区 | 日韩久久成人 | 久久精品视频在线播放 | 日韩欧美在线视频 |