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

转载请标明出处:https://blog.csdn.net/sm9sun/article/details/70859884  文章出自:九日王朝


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

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

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


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

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


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

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


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


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


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


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


   
   
   
   
  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. clsGameSituation.DiPai[ 0] = Cards[j];
  18. clsGameSituation.DiPai[ 1] = Cards[j+ 1];
  19. clsGameSituation.DiPai[ 2] = Cards[j+ 2];
  20. return;
  21. }

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


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

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


   
   
   
   
  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. if (clsGameSituation.nNowDiZhuID == -1)
  11. {
  12. cout << "无人叫地主" << endl;
  13. return;
  14. }
  15. cout << clsGameSituation.nNowDiZhuID << "号玩家是地主,叫分为:" << clsGameSituation.nNowLandScore << endl;
  16. clsGameSituation.nDiZhuID=clsGameSituation.nNowDiZhuID;
  17. clsGameSituation.nLandScore =clsGameSituation.nNowLandScore;
  18. //将三张底牌给地主
  19. arrHandCardData[clsGameSituation.nDiZhuID].color_nHandCardList.push_back(clsGameSituation.DiPai[ 0]);
  20. arrHandCardData[clsGameSituation.nDiZhuID].color_nHandCardList.push_back(clsGameSituation.DiPai[ 1]);
  21. arrHandCardData[clsGameSituation.nDiZhuID].color_nHandCardList.push_back(clsGameSituation.DiPai[ 2]);
  22. //地主手牌刷新
  23. arrHandCardData[clsGameSituation.nDiZhuID].Init();
  24. //出牌玩家ID
  25. int indexID= clsGameSituation.nDiZhuID;
  26. cout << endl;
  27. cout << "0号玩家牌为:" << endl;
  28. arrHandCardData[ 0].PrintAll();
  29. cout << "1号玩家牌为:" << endl;
  30. arrHandCardData[ 1].PrintAll();
  31. cout << "2号玩家牌为:" << endl;
  32. arrHandCardData[ 2].PrintAll();
  33. //当前控手玩家先为地主
  34. clsGameSituation.nCardDroit = indexID;

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



   
   
   
   
  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. if (arrHandCardData[indexID].nHandCardCount == 0)
  11. {
  12. clsGameSituation.Over = true;
  13. if (indexID == clsGameSituation.nDiZhuID)
  14. {
  15. cout << "地主" << indexID << "号玩家获胜" << endl;
  16. }
  17. else
  18. {
  19. for ( int i = 0; i < 3; i++) {
  20. if (i != clsGameSituation.nDiZhuID)
  21. {
  22. cout << "农民" << i << "号玩家获胜" << endl;
  23. }
  24. }
  25. }
  26. }
  27. if (arrHandCardData[indexID].uctPutCardType.cgType != cgZERO)
  28. {
  29. clsGameSituation.nCardDroit = indexID;
  30. clsGameSituation.uctNowCardGroup = arrHandCardData[indexID].uctPutCardType;
  31. }
  32. indexID == 2 ? indexID = 0 : indexID++;
  33. }

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


   
   
   
   
  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算法——第十五章の测试模块_第1张图片斗地主AI算法——第十五章の测试模块_第2张图片斗地主AI算法——第十五章の测试模块_第3张图片


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

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

你可能感兴趣的:(AI)