斗地主AI算法——第十一章の被动出牌(5)

本章是被动出牌的最后一章,截止目前,我们已经解决了大部分牌型。只剩下飞机和炸弹了。

飞机无疑是最复杂的类型,他等于顺子和三带的结合体,但又增加了很多难度。

根据上一章的算法,我们可以大概想到,若是带出去一张我就加一个循环,若是带出去两张我就加俩循环,但是这个飞机长度不一致,带出去的牌个数也就不一致,这TM怎么加啊!!我一开始的想法是外置一个全排列函数,给定count个数,然后列举所有的方案去改变value_aHandCardList数组再筛选出最优解。但这样做有坏处,第一,这个外置函数非常的麻烦,因为他全排列的全集和子集个数都不确定。第二,影响了程序分支一致性,别的模块都差不多,就飞机这里搞特殊化不太好。第三,不一定安全,因为这种做法意味着我又要多了一个可以影响value_aHandCardList的模块,我并不希望这样。

所以思考了很久,我觉得宁愿多做几个分支,毕竟飞机的个数还是可控的,就2-4。虽然这种代码写起来跟看起来都很傻逼,但是也没有办法。。。

 

飞机带单:

 

[cpp] view plain copy
 
  1. //暂存最佳的价值  
  2.     HandCardValue BestHandCardValue = get_HandCardValue(clsHandCardData);  
  3.   
  4.   
  5.     //我们认为不出牌的话会让对手一个轮次,即加一轮(权值减少7)便于后续的对比参考。  
  6.     BestHandCardValue.NeedRound += 1;  
  7.   
  8.     //暂存最佳的牌号  
  9.     int BestMaxCard = 0;  
  10.     //是否出牌的标志  
  11.     bool PutCards = false;  
  12.     //验证顺子的标志  
  13.     int prov = 0;  
  14.     //顺子起点  
  15.     int start_i = 0;  
  16.     //顺子终点  
  17.     int end_i = 0;  
  18.     //顺子长度  
  19.     int length = clsGameSituation.uctNowCardGroup.nCount / 4;  
  20.   
  21.     int tmp_1 = 0;  
  22.     int tmp_2 = 0;  
  23.     int tmp_3 = 0;  
  24.     int tmp_4 = 0;  
  25.     //2与王不参与顺子,从当前已打出的顺子最小牌值+1开始遍历  
  26.     for (int i = clsGameSituation.uctNowCardGroup.nMaxCard - length + 2; i < 15; i++)  
  27.     {  
  28.         if (clsHandCardData.value_aHandCardList[i] > 2)  
  29.         {  
  30.             prov++;  
  31.         }  
  32.         else  
  33.         {  
  34.             prov = 0;  
  35.         }  
  36.         if (prov >= length)  
  37.         {  
  38.             end_i = i;  
  39.             start_i = i - length + 1;  
  40.   
  41.             for (int j = start_i; j <= end_i; j++)  
  42.             {  
  43.                 clsHandCardData.value_aHandCardList[j] -= 3;  
  44.             }             
  45.             clsHandCardData.nHandCardCount -= clsGameSituation.uctNowCardGroup.nCount;  
  46.   
  47.             /*本来想做全排列选取带出的牌然后枚举出最高价值的,但考虑到当飞机长度也就是在2-4之间 
  48.             所以干脆做三个分支处理算了*/  
  49.             //为两连飞机  
  50.             if (length == 2)  
  51.             {  
  52.                 for (int j = 3; j < 18; j++)  
  53.                 {  
  54.                     if (clsHandCardData.value_aHandCardList[j] > 0)  
  55.                     {  
  56.                         clsHandCardData.value_aHandCardList[j] -= 1;  
  57.                         for (int k = 3; k < 18; k++)  
  58.                         {  
  59.                             if (clsHandCardData.value_aHandCardList[k] > 0)  
  60.                             {  
  61.                                 clsHandCardData.value_aHandCardList[k] -= 1;  
  62.                                 HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);  
  63.                                 clsHandCardData.value_aHandCardList[k] += 1;  
  64.   
  65.                                 //选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正  
  66.                                 if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))  
  67.                                 {  
  68.                                     BestHandCardValue = tmpHandCardValue;  
  69.                                     BestMaxCard = end_i;  
  70.                                     tmp_1 = j;  
  71.                                     tmp_2 = k;  
  72.                                     PutCards = true;  
  73.                                 }  
  74.                             }  
  75.                         }  
  76.                         clsHandCardData.value_aHandCardList[j] += 1;  
  77.                     }  
  78.   
  79.                 }  
  80.             }  
  81.             //为三连飞机  
  82.             if (length == 3)  
  83.             {  
  84.                 for (int j = 3; j < 18; j++)  
  85.                 {  
  86.                     if (clsHandCardData.value_aHandCardList[j] > 0)  
  87.                     {  
  88.                         clsHandCardData.value_aHandCardList[j] -= 1;  
  89.                         for (int k = 3; k < 18; k++)  
  90.                         {  
  91.                             if (clsHandCardData.value_aHandCardList[k] > 0)  
  92.                             {  
  93.                                 clsHandCardData.value_aHandCardList[k] -= 1;  
  94.                                 for (int l = 3; l < 18; l++)  
  95.                                 {  
  96.                                     if (clsHandCardData.value_aHandCardList[l] > 0)  
  97.                                     {  
  98.                                         clsHandCardData.value_aHandCardList[l] -= 1;  
  99.                                         HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);  
  100.                                         //选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正  
  101.                                         if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))  
  102.                                         {  
  103.                                             BestHandCardValue = tmpHandCardValue;  
  104.                                             BestMaxCard = end_i;  
  105.                                             tmp_1 = j;  
  106.                                             tmp_2 = k;  
  107.                                             tmp_3 = l;  
  108.                                             PutCards = true;  
  109.                                         }  
  110.                                         clsHandCardData.value_aHandCardList[l] += 1;  
  111.                                     }  
  112.                                 }  
  113.                                 clsHandCardData.value_aHandCardList[k] += 1;  
  114.                             }  
  115.                         }  
  116.                         clsHandCardData.value_aHandCardList[j] += 1;  
  117.                     }  
  118.                       
  119.   
  120.                 }  
  121.             }  
  122.             //为四连飞机  
  123.             if (length == 4)  
  124.             {  
  125.                 for (int j = 3; j < 18; j++)  
  126.                 {  
  127.                     if (clsHandCardData.value_aHandCardList[j] > 0)  
  128.                     {  
  129.                         clsHandCardData.value_aHandCardList[j] -= 1;  
  130.                         for (int k = 3; k < 18; k++)  
  131.                         {  
  132.                             if (clsHandCardData.value_aHandCardList[k] > 0)  
  133.                             {  
  134.                                 clsHandCardData.value_aHandCardList[k] -= 1;  
  135.                                 for (int l = 3; l < 18; l++)  
  136.                                 {  
  137.                                     if (clsHandCardData.value_aHandCardList[l] > 0)  
  138.                                     {  
  139.                                         clsHandCardData.value_aHandCardList[l] -= 1;  
  140.                                         for (int m = 3; m < 18; m++)  
  141.                                         {  
  142.                                             if (clsHandCardData.value_aHandCardList[m] > 0)  
  143.                                             {  
  144.                                                 clsHandCardData.value_aHandCardList[m] -= 1;  
  145.                                                 HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);  
  146.                                                 //选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正  
  147.                                                 if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))  
  148.                                                 {  
  149.                                                     BestHandCardValue = tmpHandCardValue;  
  150.                                                     BestMaxCard = end_i;  
  151.                                                     tmp_1 = j;  
  152.                                                     tmp_2 = k;  
  153.                                                     tmp_3 = l;  
  154.                                                     tmp_4 = m;  
  155.                                                     PutCards = true;  
  156.                                                 }  
  157.                                                 clsHandCardData.value_aHandCardList[m] += 1;  
  158.                                             }  
  159.                                         }  
  160.                                         clsHandCardData.value_aHandCardList[l] += 1;  
  161.                                     }  
  162.                                 }  
  163.                                 clsHandCardData.value_aHandCardList[k] += 1;  
  164.                             }  
  165.                         }  
  166.                         clsHandCardData.value_aHandCardList[j] += 1;  
  167.                     }  
  168.   
  169.   
  170.                 }  
  171.             }  
  172.           
  173.             for (int j = start_i; j <= end_i; j++)  
  174.             {  
  175.                 clsHandCardData.value_aHandCardList[j] += 3;  
  176.             }  
  177.             clsHandCardData.nHandCardCount += clsGameSituation.uctNowCardGroup.nCount;  
  178.         }  
  179.     }  
  180.   
  181.     if (PutCards)  
  182.     {  
  183.         for (int j = start_i; j <= end_i; j++)  
  184.         {  
  185.             clsHandCardData.value_nPutCardList.push_back(j);  
  186.             clsHandCardData.value_nPutCardList.push_back(j);  
  187.             clsHandCardData.value_nPutCardList.push_back(j);  
  188.         }  
  189.   
  190.         if (length == 2)  
  191.         {  
  192.             clsHandCardData.value_nPutCardList.push_back(tmp_1);  
  193.             clsHandCardData.value_nPutCardList.push_back(tmp_2);  
  194.         }  
  195.         if (length == 3)  
  196.         {  
  197.             clsHandCardData.value_nPutCardList.push_back(tmp_1);  
  198.             clsHandCardData.value_nPutCardList.push_back(tmp_2);  
  199.             clsHandCardData.value_nPutCardList.push_back(tmp_3);  
  200.   
  201.         }  
  202.         if (length == 4)  
  203.         {  
  204.             clsHandCardData.value_nPutCardList.push_back(tmp_1);  
  205.             clsHandCardData.value_nPutCardList.push_back(tmp_2);  
  206.             clsHandCardData.value_nPutCardList.push_back(tmp_3);  
  207.             clsHandCardData.value_nPutCardList.push_back(tmp_4);  
  208.         }  
  209.   
  210.         clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgTHREE_TAKE_ONE_LINE, BestMaxCard, clsGameSituation.uctNowCardGroup.nCount);  
  211.         return;  
  212.     }  


大家可以看到我回溯的处理方式和之前的不一样了,因为飞机类型很有可能把对牌当成两个单牌带出,甚至可以拆炸弹。所以每个循环内当确定了一个点就先处理value_aHandCardList状态,这样也相对安全,上一章中在四带二环节我也有提到过这方面。

 

飞机带对类似,而且这里是被动出牌,所以不存在4连飞机的情况,因为4连飞机带对的话就有20张牌了。只考虑2连和3连就可以了。

 

 

最后再说一下炸弹,这个炸弹就厉害了,我给的策略就是————————————

直接炸丫的!不要怂!!

 

[cpp] view plain copy
 
  1. else if (clsGameSituation.uctNowCardGroup.cgType == cgBOMB_CARD)  
  2. {  
  3.     //更大的炸弹——这里直接炸,不考虑拆分后果。因为信仰。  
  4.     for (int i = clsGameSituation.uctNowCardGroup.nMaxCard + 1; i < 16; i++)  
  5.     {  
  6.         if (clsHandCardData.value_aHandCardList[i] == 4)  
  7.         {  
  8.             clsHandCardData.value_nPutCardList.push_back(i);  
  9.             clsHandCardData.value_nPutCardList.push_back(i);  
  10.             clsHandCardData.value_nPutCardList.push_back(i);  
  11.             clsHandCardData.value_nPutCardList.push_back(i);  
  12.   
  13.             clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgBOMB_CARD, i, 4);  
  14.   
  15.             return;  
  16.         }  
  17.     }  
  18.     //王炸  
  19.     if (clsHandCardData.value_aHandCardList[17] > 0 && clsHandCardData.value_aHandCardList[16] > 0)  
  20.     {  
  21.   
  22.         clsHandCardData.value_nPutCardList.push_back(17);  
  23.         clsHandCardData.value_nPutCardList.push_back(16);  
  24.   
  25.         clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgKING_CARD, 17, 2);  
  26.   
  27.         return;  
  28.     }  
  29.     //管不上  
  30.     clsHandCardData.uctPutCardType = get_GroupData(cgZERO, 0, 0);  
  31.     return;  
  32.       
  33. }  


当然也可以改成考虑拆分后果什么的,或者如果你手上有多个炸弹是否对比一下出那个接下来更好 等等逻辑。

不过对于我来说,你都有俩炸弹了,还怕什么,肯定都是要炸的!宁输不拆!就是这么浪!

 

好了至此被动出牌模块就全部写完了,从下一章开始,我们讲主动出牌。

 

敬请关注下一章:斗地主AI算法——第十二章の主动出牌(1)

 

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

导航