斗地主AI算法——第十二章の主动出牌(1)

本章开始,我们介绍主动出牌的算法,和被动出牌类似,我们第一步把主要架子搭起来。

 

首先清空出牌序列

 

[cpp] view plain copy
 
  1. clsHandCardData.ClearPutCardList();  



 

主动出牌的策略按照优先级大体可以分为三类:

【一】能直接一手牌出去,优先出。

【二】两手牌出去且有绝对大牌,先出绝对大牌。

【三】出一手牌使得接下来自己手牌价值最大化。

 

 

[cpp] view plain copy
 
  1. //剪枝:如果能出去最后一手牌直接出  
  2.     CardGroupData SurCardGroupData = ins_SurCardsType(clsHandCardData.value_aHandCardList);  
  3.     //如果能一次性出去且没有炸弹,因为有炸弹的话权值可能会更大  
  4.     if (SurCardGroupData.cgType != cgERROR&&!HasBoom(clsHandCardData.value_aHandCardList))  
  5.     {  
  6.             Put_All_SurCards(clsHandCardData, SurCardGroupData);  
  7.             return;  
  8.     }  



然后是【二】:目前2.0版本我们暂时不考虑记牌功能,所以绝对大牌只支持王炸,以后在这里可以做更多智能的处理。

 

 

[cpp] view plain copy
 
  1. /*王炸——当前策略只处理王炸作为倒数第二手的优先出牌逻辑,后续版本会在此基础上优化*/  
  2.     if (clsHandCardData.value_aHandCardList[17] > 0 && clsHandCardData.value_aHandCardList[16] > 0)  
  3.     {  
  4.   
  5.         clsHandCardData.value_aHandCardList[17] --;  
  6.         clsHandCardData.value_aHandCardList[16] --;  
  7.         clsHandCardData.nHandCardCount -= 2;  
  8.         HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);  
  9.         clsHandCardData.value_aHandCardList[16] ++;  
  10.         clsHandCardData.value_aHandCardList[17] ++;  
  11.         clsHandCardData.nHandCardCount += 2;  
  12.         if (tmpHandCardValue.NeedRound == 1)  
  13.         {  
  14.             clsHandCardData.value_nPutCardList.push_back(17);  
  15.             clsHandCardData.value_nPutCardList.push_back(16);  
  16.             clsHandCardData.uctPutCardType = get_GroupData(cgKING_CARD, 17, 2);  
  17.             return;  
  18.         }  
  19.     }  


接下来就是第三步, 就是我们选择打出一手牌尽量使得接下来自己手牌价值最大化。

 

与被动出牌不一样的是,主动出牌我们没有限制条件,我也尝试过全部枚举,不过时间消耗肯定是爆炸的。于是我定制了一个基本的出牌优先级策略:

 

①三带一、飞机等牌优先打出,因为这种牌型可以把小牌带出。其实这里对比较大的三带一不是很公平,后续版本可以在此处做分支处理,比如说三带一的话只循环到10,J以上先不着急打出。飞机倒还好说,那玩意基本管不到别人,所以出了就出了。至于四带二嘛。。。四带二是个什么东西?我不知道,我眼里只有炸弹。

所以,这部分的架子应该是这样的。

若可以出这几种牌型,选择一种价值最高的打出。因为要枚举所有的牌,所以在循环外根据最佳策略进行出牌处理。

 

②没有上述牌型后,优先处理当前最小的一张牌。若是该牌有四张,先不处理。

这里出牌处理就放在循环内了,因为当确定了这个i值后无论如何都是要打出一手牌的,且打完牌就可以return了。

 

③如果没有从3到2的非炸牌,那么看看有没有单王,如果有,可以出。

 

[cpp] view plain copy
 
  1. //如果没有3-2的非炸牌,则看看有没有单王  
  2. if (clsHandCardData.value_aHandCardList[16] == 1 && clsHandCardData.value_aHandCardList[17] == 0)  
  3. {  
  4.     clsHandCardData.value_nPutCardList.push_back(16);  
  5.     clsHandCardData.uctPutCardType = get_GroupData(cgSINGLE, 16, 1);  
  6.     return;  
  7.    }  
  8. if (clsHandCardData.value_aHandCardList[16] == 0 && clsHandCardData.value_aHandCardList[17] == 1)  
  9. {  
  10.     clsHandCardData.value_nPutCardList.push_back(17);  
  11.     clsHandCardData.uctPutCardType = get_GroupData(cgSINGLE, 17, 1);  
  12.     return;  
  13. }  


④单王也没有,出炸弹。

 

 

[cpp] view plain copy
 
  1. //单王也没有,出炸弹  
  2.     for (int i = 3; i < 16; i++)  
  3.     {  
  4.         if (clsHandCardData.value_aHandCardList[i] == 4)  
  5.         {  
  6.             clsHandCardData.value_nPutCardList.push_back(i);  
  7.             clsHandCardData.value_nPutCardList.push_back(i);  
  8.             clsHandCardData.value_nPutCardList.push_back(i);  
  9.             clsHandCardData.value_nPutCardList.push_back(i);  
  10.   
  11.             clsHandCardData.uctPutCardType = get_GroupData(cgBOMB_CARD, i, 4);  
  12.   
  13.             return;  
  14.         }  
  15.     }  


这里可能有人会想,需不需要再加上炸弹也没有,出王炸呢?其实不存在的,因为如果你真的没牌打了就剩王炸了,早在前面剪枝部分就处理了。

 

所以如果走到这里都没有返回的话,肯定是出现错误了。

把上述的各个模块连接起来,即构成主动出牌的基本架子:

 

[cpp] view plain copy
 
  1. void get_PutCardList_2(HandCardData &clsHandCardData)  
  2. {  
  3.       
  4.     clsHandCardData.ClearPutCardList();  
  5.   
  6.     //剪枝:如果能出去最后一手牌直接出  
  7.     CardGroupData SurCardGroupData = ins_SurCardsType(clsHandCardData.value_aHandCardList);  
  8.     //如果能一次性出去且不是四带二,因为主动出牌若手上剩四带二牌的话可以考虑先打一手然后炸,获得双倍积分  
  9.     if (SurCardGroupData.cgType != cgERROR&&SurCardGroupData.cgType!=cgFOUR_TAKE_ONE&&SurCardGroupData.cgType !=cgFOUR_TAKE_TWO)  
  10.     {  
  11.     }  
  12.   
  13.     /*王炸——当前策略只处理王炸作为倒数第二手的优先出牌逻辑,后续版本会在此基础上优化*/  
  14.     if (clsHandCardData.value_aHandCardList[17] > 0 && clsHandCardData.value_aHandCardList[16] > 0)  
  15.     {  
  16.     }  
  17.   
  18.     //暂存最佳的价值  
  19.     HandCardValue BestHandCardValue;  
  20.     BestHandCardValue.NeedRound = 20;  
  21.     BestHandCardValue.SumValue = MinCardsValue;  
  22.     //我们认为不出牌的话会让对手一个轮次,即加一轮(权值减少7)便于后续的对比参考。  
  23.     BestHandCardValue.NeedRound += 1;  
  24.   
  25.     //暂存最佳的组合  
  26.     CardGroupData BestCardGroup;  
  27.   
  28.     //带出去的牌  
  29.     int tmp_1 = 0;  
  30.     int tmp_2 = 0;  
  31.     int tmp_3 = 0;  
  32.     int tmp_4 = 0;  
  33.     //优先处理三牌、飞机等牌  
  34.     for (int i = 3; i < 16; i++)  
  35.     {         
  36.     }  
  37.     //这部分出牌处理放到循环外  
  38.     if (BestCardGroup.cgType == cgTHREE_TAKE_ONE)  
  39.         {  
  40.   
  41.         }  
  42.     else if (BestCardGroup.cgType == cgTHREE_TAKE_TWO)  
  43.         {  
  44.   
  45.         }  
  46.     else if (BestCardGroup.cgType == cgTHREE_TAKE_ONE_LINE)  
  47.         {  
  48.   
  49.         }  
  50.     else if (BestCardGroup.cgType == cgTHREE_TAKE_TWO_LINE)  
  51.         {  
  52.   
  53.         }  
  54.           
  55.   
  56.     //次之处理当前价值最低的牌,现在不必再考虑这张牌可能被三牌带出等情况  
  57.     for (int i = 3; i < 16; i++)   
  58.     {  
  59.     }  
  60.     //如果没有3-2的非炸牌,则看看有没有单王  
  61.     if (clsHandCardData.value_aHandCardList[16] == 1 && clsHandCardData.value_aHandCardList[17] == 0)  
  62.     {         
  63.     }  
  64.     if (clsHandCardData.value_aHandCardList[16] == 0 && clsHandCardData.value_aHandCardList[17] == 1)  
  65.     {  
  66.     }  
  67.     //单王也没有,出炸弹  
  68.     for (int i = 3; i < 16; i++)  
  69.     {  
  70.     }  
  71.       
  72.     //异常错误  
  73.     clsHandCardData.uctPutCardType = get_GroupData(cgERROR, 0, 0);  
  74.     return;  
  75.   
  76. }  


至此主动出牌的架子就搭好了,且除了三带牌型出牌策略及解决最小值牌出牌策略这两个大部分,其他部分代码本章均已给出,下一章我们开始实现三带牌型的出牌策略。

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

导航