上一章已经排除了飞机、三带等牌型,那么除去炸弹王炸以外,我们只剩下单牌、对牌、三牌以及单顺、双顺、三顺了。


首先说单牌、对牌、三牌。其逻辑基本一样,只是出牌的个数有差别,即:如果该i牌数量满足这种牌型要求,即先打出,计算其剩余价值。


  1. //出单牌
  2. if (clsHandCardData.value_aHandCardList[i] > 0)
  3. {
  4. clsHandCardData.value_aHandCardList[i]--;
  5. clsHandCardData.nHandCardCount--;
  6. HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
  7. clsHandCardData.value_aHandCardList[i]++;
  8. clsHandCardData.nHandCardCount++;
  9. if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
  10. {
  11. BestHandCardValue = tmpHandCardValue;
  12. BestCardGroup= get_GroupData(cgSINGLE, i, 1);
  13. }
  14. }
  15. //出对牌
  16. if (clsHandCardData.value_aHandCardList[i] > 1)
  17. {
  18. //尝试打出一对牌,估算剩余手牌价值
  19. clsHandCardData.value_aHandCardList[i] -= 2;
  20. clsHandCardData.nHandCardCount -= 2;
  21. HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
  22. clsHandCardData.value_aHandCardList[i] += 2;
  23. clsHandCardData.nHandCardCount += 2;

  24. //选取总权值-轮次*7值最高的策略 因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
  25. if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
  26. {
  27. BestHandCardValue = tmpHandCardValue;
  28. BestCardGroup = get_GroupData(cgDOUBLE, i, 2);
  29. }
  30. }
  31. //出三牌
  32. if (clsHandCardData.value_aHandCardList[i] > 2)
  33. {
  34. clsHandCardData.value_aHandCardList[i] -= 3;
  35. clsHandCardData.nHandCardCount -= 3;
  36. HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
  37. clsHandCardData.value_aHandCardList[i] += 3;
  38. clsHandCardData.nHandCardCount += 3;

  39. //选取总权值-轮次*7值最高的策略 因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
  40. if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
  41. {
  42. BestHandCardValue = tmpHandCardValue;
  43. BestCardGroup = get_GroupData(cgTHREE, i, 3);
  44. }
  45. }


至于顺子的算法,和被动出牌的有一点点差别,就是因为没有了数量限制,所以需要遍历以i牌为起点可以组成的所有顺子。



  1. //出单顺
  2. if (clsHandCardData.value_aHandCardList[i] > 0)
  3. {
  4. int prov = 0;
  5. for (int j = i; j < 15; j++)
  6. {
  7. if(clsHandCardData.value_aHandCardList[j]>0)
  8. {
  9. prov++;
  10. }
  11. else
  12. {
  13. break;
  14. }
  15. if (prov >= 5)
  16. {

  17. for (int k = i; k <= j; k++)
  18. {
  19. clsHandCardData.value_aHandCardList[k] --;
  20. }
  21. clsHandCardData.nHandCardCount -= prov;
  22. HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
  23. for (int k = i; k <= j; k++)
  24. {
  25. clsHandCardData.value_aHandCardList[k] ++;
  26. }
  27. clsHandCardData.nHandCardCount += prov;

  28. //选取总权值-轮次*7值最高的策略 因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
  29. if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
  30. {
  31. BestHandCardValue = tmpHandCardValue;
  32. BestCardGroup = get_GroupData(cgSINGLE_LINE, j, prov);
  33. }
  34. }
  35. }

  36. }
  37. //出双顺
  38. if (clsHandCardData.value_aHandCardList[i] > 1)
  39. {
  40. int prov = 0;
  41. for (int j = i; j < 15; j++)
  42. 斗地主AI算法——第十四章の主动出牌(3)
  43. {
  44. if (clsHandCardData.value_aHandCardList[j]>1)
  45. {
  46. prov++;
  47. }
  48. else
  49. {
  50. break;
  51. }
  52. if (prov >= 3)
  53. {

  54. for (int k = i; k <= j; k++)
  55. {
  56. clsHandCardData.value_aHandCardList[k] -=2;
  57. }
  58. clsHandCardData.nHandCardCount -= prov*2;
  59. HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
  60. for (int k = i; k <= j; k++)
  61. {
  62. clsHandCardData.value_aHandCardList[k] +=2;
  63. }
  64. clsHandCardData.nHandCardCount += prov*2;

  65. //选取总权值-轮次*7值最高的策略 因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
  66. if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
  67. {
  68. BestHandCardValue = tmpHandCardValue;
  69. BestCardGroup = get_GroupData(cgDOUBLE_LINE, j, prov*2);
  70. }
  71. }
  72. }
  73. }
  74. //出三顺
  75. if(clsHandCardData.value_aHandCardList[i] > 2)
  76. {
  77. int prov = 0;
  78. for (int j = i; j < 15; j++)
  79. {
  80. if (clsHandCardData.value_aHandCardList[j]>2)
  81. {
  82. prov++;
  83. }
  84. else
  85. {
  86. break;
  87. }
  88. if (prov >= 2)
  89. {

  90. for (int k = i; k <= j; k++)
  91. {
  92. clsHandCardData.value_aHandCardList[k] -= 3;
  93. }
  94. clsHandCardData.nHandCardCount -= prov * 3;
  95. HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
  96. for (int k = i; k <= j; k++)
  97. {
  98. clsHandCardData.value_aHandCardList[k] += 3;
  99. }
  100. clsHandCardData.nHandCardCount += prov * 3;

  101. //选取总权值-轮次*7值最高的策略 因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
  102. if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
  103. {
  104. BestHandCardValue = tmpHandCardValue;
  105. BestCardGroup = get_GroupData(cgTHREE_LINE, j, prov * 3);
  106. }
  107. }
  108. }
  109. }



因为本策略是必须解决掉至少一个i牌的,所以出牌操作放在循环内进行,也就是说,只要你不是炸3,若你手牌有3,在处理3时一定会return  就绝对不会再走到4。



  1. if (BestCardGroup.cgType == cgERROR)
  2. {

  3. }
  4. else if (BestCardGroup.cgType == cgSINGLE)
  5. {
  6. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  7. clsHandCardData.uctPutCardType = BestCardGroup;
  8. }
  9. else if (BestCardGroup.cgType == cgDOUBLE)
  10. {
  11. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  12. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  13. clsHandCardData.uctPutCardType = BestCardGroup;
  14. }
  15. else if (BestCardGroup.cgType == cgTHREE)
  16. {
  17. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  18. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  19. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  20. clsHandCardData.uctPutCardType = BestCardGroup;
  21. }
  22. else if (BestCardGroup.cgType == cgSINGLE_LINE)
  23. {
  24. for (int j = BestCardGroup.nMaxCard- BestCardGroup.nCount+1; j <= BestCardGroup.nMaxCard; j++)
  25. {
  26. clsHandCardData.value_nPutCardList.push_back(j);
  27. }
  28. clsHandCardData.uctPutCardType = BestCardGroup;
  29. }
  30. else if (BestCardGroup.cgType == cgDOUBLE_LINE)
  31. {
  32. for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount/2) + 1; j <= BestCardGroup.nMaxCard; j++)
  33. {
  34. clsHandCardData.value_nPutCardList.push_back(j);
  35. clsHandCardData.value_nPutCardList.push_back(j);
  36. }
  37. clsHandCardData.uctPutCardType = BestCardGroup;
  38. }
  39. else if (BestCardGroup.cgType == cgTHREE_LINE)
  40. {
  41. for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount / 3) + 1; j <= BestCardGroup.nMaxCard; j++)
  42. {
  43. clsHandCardData.value_nPutCardList.push_back(j);
  44. clsHandCardData.value_nPutCardList.push_back(j);
  45. clsHandCardData.value_nPutCardList.push_back(j);
  46. }
  47. clsHandCardData.uctPutCardType = BestCardGroup;
  48. }
  49. else if (BestCardGroup.cgType == cgTHREE_TAKE_ONE)
  50. {
  51. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  52. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  53. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  54. clsHandCardData.value_nPutCardList.push_back(tmp_1);
  55. clsHandCardData.uctPutCardType = BestCardGroup;
  56. }
  57. else if (BestCardGroup.cgType == cgTHREE_TAKE_TWO)
  58. {
  59. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  60. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  61. clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
  62. clsHandCardData.value_nPutCardList.push_back(tmp_1);
  63. clsHandCardData.value_nPutCardList.push_back(tmp_1);
  64. clsHandCardData.uctPutCardType = BestCardGroup;
  65. }
  66. else if (BestCardGroup.cgType == cgTHREE_TAKE_ONE_LINE)
  67. {
  68. for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount / 4) + 1; j <= BestCardGroup.nMaxCard; j++)
  69. {
  70. clsHandCardData.value_nPutCardList.push_back(j);
  71. clsHandCardData.value_nPutCardList.push_back(j);
  72. clsHandCardData.value_nPutCardList.push_back(j);
  73. }

  74. if (BestCardGroup.nCount / 4 == 2)
  75. {
  76. clsHandCardData.value_nPutCardList.push_back(tmp_1);
  77. clsHandCardData.value_nPutCardList.push_back(tmp_2);
  78. }
  79. if (BestCardGroup.nCount / 4 == 3)
  80. {
  81. clsHandCardData.value_nPutCardList.push_back(tmp_1);
  82. clsHandCardData.value_nPutCardList.push_back(tmp_2);
  83. clsHandCardData.value_nPutCardList.push_back(tmp_3);
  84. }
  85. if (BestCardGroup.nCount / 4 == 4)
  86. {
  87. clsHandCardData.value_nPutCardList.push_back(tmp_1);
  88. clsHandCardData.value_nPutCardList.push_back(tmp_2);
  89. clsHandCardData.value_nPutCardList.push_back(tmp_3);
  90. clsHandCardData.value_nPutCardList.push_back(tmp_4);
  91. }

  92. clsHandCardData.uctPutCardType = BestCardGroup;
  93. }
  94. else if (BestCardGroup.cgType == cgTHREE_TAKE_TWO_LINE)
  95. {
  96. for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount / 5) + 1; j <= BestCardGroup.nMaxCard; j++)
  97. {
  98. clsHandCardData.value_nPutCardList.push_back(j);
  99. clsHandCardData.value_nPutCardList.push_back(j);
  100. clsHandCardData.value_nPutCardList.push_back(j);
  101. }
  102. if (BestCardGroup.nCount / 5 == 2)
  103. {
  104. clsHandCardData.value_nPutCardList.push_back(tmp_1);
  105. clsHandCardData.value_nPutCardList.push_back(tmp_1);
  106. clsHandCardData.value_nPutCardList.push_back(tmp_2);
  107. clsHandCardData.value_nPutCardList.push_back(tmp_2);
  108. }
  109. if (BestCardGroup.nCount / 5 == 3)
  110. {
  111. clsHandCardData.value_nPutCardList.push_back(tmp_1);
  112. clsHandCardData.value_nPutCardList.push_back(tmp_1);
  113. clsHandCardData.value_nPutCardList.push_back(tmp_2);
  114. clsHandCardData.value_nPutCardList.push_back(tmp_2);
  115. clsHandCardData.value_nPutCardList.push_back(tmp_3);
  116. clsHandCardData.value_nPutCardList.push_back(tmp_3);
  117. }
  118. clsHandCardData.uctPutCardType = BestCardGroup;
  119. }
  120. return;




至此,主动出牌的所有逻辑均已实现,同时整个斗地主算法也基本完成了。接下来我们便可写一些测试模块来进行整合联调。


敬请关注下一章:斗地主AI算法——第十五章の测试模块