斗地主AI算法——第十五章の测试模块

前面几章已经把整个斗地主AI算法工程完成的差不多了,接下来进入整合联调以及模拟测试模块。

测试模块主要任务就是代替服务器给出我们需要的数据。因为我们本来的计划是封装成类库通过服务器调用获取,其调用的接口无非就是叫分、被动出牌、主动出牌。

被动出牌和主动出牌我们已经完成,叫分我们已经实现了权值的获取,只需要在外面加一个区间划分即可:

 

[cpp] view plain copy
 
  1. /* 
  2. 获取叫分函数 
  3. */  
  4.   
  5. int LandScore(GameSituation &clsGameSituation, HandCardData &clsHandCardData)  
  6. {  
  7.     int SumValue = 0;  
  8.   
  9.     clsHandCardData.uctHandCardValue=get_HandCardValue(clsHandCardData);  
  10.   
  11.     SumValue = clsHandCardData.uctHandCardValue.SumValue;  
  12.       
  13.     cout << "SumValue is :" << SumValue << ",";  
  14.   
  15.     cout << "NeedRound is :" << clsHandCardData.uctHandCardValue.NeedRound << endl;  
  16.   
  17.     if (SumValue<10)   
  18.     {  
  19.         return 0;  
  20.     }  
  21.     else if (SumValue < 15)  
  22.     {  
  23.         return 1;  
  24.     }  
  25.     else if (SumValue < 20)  
  26.     {  
  27.         return 2;  
  28.     }  
  29.     else   
  30.     {  
  31.         return 3;  
  32.     }  
  33. }  


接下来就是模拟数据了,首先完成洗牌,即初始化牌值与随机打乱。

 

 

[cpp] view plain copy
 
  1. //洗牌  
  2. void InitCards(vector <int> &Cards)  
  3. {  
  4.     //先清空Cards  
  5.     Cards.clear();  
  6.   
  7.     vector <int> tmpCards;  
  8.     int i;  
  9.   
  10.     //大王56,小王52,没有53,54,55号牌  
  11.     for (i = 0; i < 53; i++) {  
  12.         tmpCards.push_back(i);  
  13.     }  
  14.     tmpCards.push_back(56);  
  15.   
  16.   
  17.     //顺序随机打乱  
  18.     for (i = tmpCards.size(); i>0; i--) {  
  19.         srand(unsigned(time(NULL)));  
  20.         // 选中的随机下标  
  21.         int index = rand() % i;  
  22.         Cards.push_back(tmpCards[index]);  
  23.         tmpCards.erase(tmpCards.begin() + index);  
  24.     }  
  25.   
  26. }  


同时为了方便测试,我也做了一个指定牌型的函数。

 

 

[cpp] view plain copy
 
  1. //洗牌(指定牌型,用于测试)  
  2. void InitCards_Appoint(vector <int> &Cards)  
  3. {  
  4.     //先清空Cards  
  5.      Cards.clear();  
  6.   
  7.      /***********飞机与炸弹连续拆分逻辑测试**********/  
  8.       
  9.      Cards.push_back(48); Cards.push_back(50); Cards.push_back(49);  
  10.      Cards.push_back(44); Cards.push_back(47); Cards.push_back(35);  
  11.      Cards.push_back(40); Cards.push_back(46); Cards.push_back(34);  
  12.      Cards.push_back(36); Cards.push_back(45); Cards.push_back(33);  
  13.      Cards.push_back(23); Cards.push_back(43); Cards.push_back(31);  
  14.      Cards.push_back(22); Cards.push_back(42);  Cards.push_back(30);  
  15.      Cards.push_back(21); Cards.push_back(41); Cards.push_back(29);  
  16.      Cards.push_back(19); Cards.push_back(39); Cards.push_back(27);  
  17.      Cards.push_back(18); Cards.push_back(38); Cards.push_back(26);  
  18.      Cards.push_back(17); Cards.push_back(37); Cards.push_back(25);  
  19.      Cards.push_back(15); Cards.push_back(32);  Cards.push_back(20);  
  20.      Cards.push_back(14); Cards.push_back(28); Cards.push_back(16);  
  21.      Cards.push_back(13); Cards.push_back(24); Cards.push_back(12);  
  22.      Cards.push_back(11); Cards.push_back(3); Cards.push_back(7);  
  23.      Cards.push_back(10); Cards.push_back(2); Cards.push_back(6);  
  24.      Cards.push_back(9); Cards.push_back(1); Cards.push_back(5);  
  25.      Cards.push_back(8); Cards.push_back(0); Cards.push_back(4);  
  26.      Cards.push_back(51); Cards.push_back(52); Cards.push_back(56);  
  27.       
  28.   
  29. }  

 

 

洗完牌就是发牌了,发牌这里我们需要定义一个包含三个人手牌的结构,因为作为正常调用来说我们是不应该有这样的数据的。

 

[cpp] view plain copy
 
  1. //下发到三名玩家的手牌序列,此数据只用于测试,作为AI时不会获取  
  2.   
  3. struct ALLCardsList  
  4. {  
  5.     vector <int>  arrCardsList[3];  
  6. };  



然后依次发送到玩家对应的手牌数组里,最后三张为底牌。

 

 

[cpp] view plain copy
 
  1. //发牌  
  2. void SendCards(GameSituation & clsGameSituation, ALLCardsList &uctALLCardsList)  
  3. {  
  4.     //洗牌  
  5.     vector <int> Cards;  
  6.     InitCards(Cards);  
  7.     //InitCards_Appoint(Cards);  
  8.     int i, j, k;  
  9.     j = 0;  
  10.     for (k = 0; k < 17; k++) {  
  11.         for (i = 0; i < 3; i++,j++)  
  12.         {  
  13.             uctALLCardsList.arrCardsList[i].push_back(Cards[j]);  
  14.         }  
  15.     }  
  16.   
  17.     //三张底牌  
  18.     clsGameSituation.DiPai[0] = Cards[j];  
  19.     clsGameSituation.DiPai[1] = Cards[j+1];  
  20.     clsGameSituation.DiPai[2] = Cards[j+2];  
  21.           
  22.     return;  
  23. }  


再然后就是模拟游戏过程,首先定义游戏全局类,与三名玩家的手牌信息类。调用发牌函数完成发牌环节,可以用手牌信息类里面的PrintAll输出你想要的数据信息。

 

 

[cpp] view plain copy
 
  1. GameSituation clsGameSituation;  
  2.   
  3. ALLCardsList  uctALLCardsList;  
  4.   
  5. //发牌  
  6. SendCards(clsGameSituation, uctALLCardsList);  
  7.   
  8. HandCardData arrHandCardData[3];  
  9.   
  10. arrHandCardData[0].color_nHandCardList = uctALLCardsList.arrCardsList[0];  
  11. arrHandCardData[1].color_nHandCardList = uctALLCardsList.arrCardsList[1];  
  12. arrHandCardData[2].color_nHandCardList = uctALLCardsList.arrCardsList[2];  
  13.   
  14. for (int i = 0; i < 3; i++)  
  15. {  
  16.     arrHandCardData[i].Init();  
  17.     arrHandCardData[i].nOwnIndex = i;  
  18. }  
  19.   
  20. cout << "0号玩家牌为:" << endl;  
  21. arrHandCardData[0].PrintAll();  
  22. cout << "1号玩家牌为:" << endl;  
  23. arrHandCardData[1].PrintAll();  
  24. cout << "2号玩家牌为:" << endl;  
  25. arrHandCardData[2].PrintAll();  
  26.   
  27. cout << "底牌为:" << endl;  
  28. cout << get_CardsName(clsGameSituation.DiPai[0]) << ','  
  29.      << get_CardsName(clsGameSituation.DiPai[1]) << ','  
  30.      << get_CardsName(clsGameSituation.DiPai[2]) << endl;  
  31.   
  32. cout << endl;  

 

发完牌后开始叫地主,调用LandScore函数返回其叫的分值,只有比当前已叫的分值更高才可以刷新叫地主记录。若无人叫地主重新开一局,否则将三张底牌给地主,同时刷新地主手牌,且将地主设置成将要出牌的玩家

 

[cpp] view plain copy
 
  1. for (int i = 0; i < 3; i++)  
  2. {  
  3.     int  tmpLandScore = LandScore(clsGameSituation, arrHandCardData[i]);  
  4.     if (tmpLandScore > clsGameSituation.nNowLandScore)  
  5.     {  
  6.         clsGameSituation.nNowLandScore = tmpLandScore;  
  7.         clsGameSituation.nNowDiZhuID = i;  
  8.     }  
  9. }  
  10.   
  11. if (clsGameSituation.nNowDiZhuID == -1)  
  12. {  
  13.     cout << "无人叫地主" << endl;  
  14.     return;  
  15. }  
  16.   
  17. cout << clsGameSituation.nNowDiZhuID << "号玩家是地主,叫分为:" << clsGameSituation.nNowLandScore << endl;  
  18. clsGameSituation.nDiZhuID=clsGameSituation.nNowDiZhuID;  
  19. clsGameSituation.nLandScore =clsGameSituation.nNowLandScore;  
  20.   
  21.   
  22. //将三张底牌给地主  
  23. arrHandCardData[clsGameSituation.nDiZhuID].color_nHandCardList.push_back(clsGameSituation.DiPai[0]);  
  24. arrHandCardData[clsGameSituation.nDiZhuID].color_nHandCardList.push_back(clsGameSituation.DiPai[1]);  
  25. arrHandCardData[clsGameSituation.nDiZhuID].color_nHandCardList.push_back(clsGameSituation.DiPai[2]);  
  26.   
  27. //地主手牌刷新  
  28. arrHandCardData[clsGameSituation.nDiZhuID].Init();  
  29.   
  30. //出牌玩家ID  
  31. int indexID= clsGameSituation.nDiZhuID;  
  32.   
  33. cout << endl;  
  34.   
  35.   
  36. cout << "0号玩家牌为:" << endl;  
  37. arrHandCardData[0].PrintAll();  
  38. cout << "1号玩家牌为:" << endl;  
  39. arrHandCardData[1].PrintAll();  
  40. cout << "2号玩家牌为:" << endl;  
  41. arrHandCardData[2].PrintAll();  
  42. //当前控手玩家先为地主  
  43. clsGameSituation.nCardDroit = indexID;  


接下来就是循环进行出牌了。在游戏全局类里我们设置了一个标志是否结束的变量,可以用于控制循环。出牌时我们只需调用get_PutCardList出牌函数即可。若某个玩家出完牌后手牌为0,则游戏结束。若玩家出过牌,则刷新游戏全局类里面当前牌型信息。

 

 

 

[cpp] view plain copy
 
  1. while (!clsGameSituation.Over)  
  2.     {  
  3.         get_PutCardList_2(clsGameSituation, arrHandCardData[indexID]);//获取出牌序列  
  4.         arrHandCardData[indexID].PutCards();  
  5.         cout << indexID << "号玩家出牌:" << endl;  
  6.         for (vector<int>::iterator iter = arrHandCardData[indexID].color_nPutCardList.begin();  
  7.             iter != arrHandCardData[indexID].color_nPutCardList.end(); iter++)  
  8.             cout << get_CardsName(*iter) << (iter == arrHandCardData[indexID].color_nPutCardList.end() - 1 ? '\n' : ',');  
  9.         cout << endl;  
  10.   
  11.         if (arrHandCardData[indexID].nHandCardCount == 0)  
  12.         {  
  13.             clsGameSituation.Over = true;  
  14.   
  15.             if (indexID == clsGameSituation.nDiZhuID)  
  16.             {  
  17.                 cout << "地主" << indexID << "号玩家获胜" << endl;  
  18.             }  
  19.             else  
  20.             {  
  21.                 for (int i = 0; i < 3; i++) {  
  22.                     if (i != clsGameSituation.nDiZhuID)  
  23.                     {  
  24.                         cout << "农民" << i << "号玩家获胜" << endl;  
  25.                     }  
  26.                 }  
  27.             }  
  28.               
  29.         }  
  30.           
  31.         if (arrHandCardData[indexID].uctPutCardType.cgType != cgZERO)  
  32.         {  
  33.             clsGameSituation.nCardDroit = indexID;  
  34.             clsGameSituation.uctNowCardGroup = arrHandCardData[indexID].uctPutCardType;  
  35.         }  
  36.   
  37.         indexID == 2 ? indexID = 0 : indexID++;  
  38.   
  39.     }  


get_PutCardList函数做了一个分支,通过nCardDroit当前控手对象判断是主动出牌还是被动出牌

 

 

[cpp] view plain copy
 
  1. /* 
  2. 2.0版本策略  根据场上形势决定当前预打出的手牌——分支处理 
  3. */  
  4. void get_PutCardList_2(GameSituation &clsGameSituation, HandCardData &clsHandCardData)  
  5. {  
  6.     if (clsGameSituation.nCardDroit == clsHandCardData.nOwnIndex)  
  7.     {  
  8.         get_PutCardList_2_unlimit(clsGameSituation, clsHandCardData);  
  9.     }  
  10.     else  
  11.     {  
  12.         get_PutCardList_2_limit(clsGameSituation, clsHandCardData);  
  13.     }  
  14.     return;  
  15. }  



 

完成测试模块后,我们就可以调试程序了。

 

那么现在我们就可以愉快的玩耍了,下一章我们将观察几次对局情况进行样例的分析。

敬请关注下一章:斗地主AI算法——第十六章の样例分析

posted on 2018-06-12 22:44  &大飞  阅读(823)  评论(0编辑  收藏  举报

导航