斗地主AI算法——第五章の总值计算

本文详细介绍了斗地主AI算法中手牌总价值的计算方法,包括使用回溯算法和剪枝策略来优化计算过程。通过判断一手牌的价值和获取最佳出牌策略,实现了手牌价值的精确评估。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


本章算是比较重点的一章,前一章已经对各个牌型做出了价值定义,本章主要实现计算手牌总价值模块函数。

根据之前的思路,我们设定一下输入输出:

输入:手牌数据类(主要用手牌个数nHandCardCount以及手牌状态数组clsHandCardData.value_aHandCardList)

输出:手牌价值结构 HandCardValue


先处理剪枝部分,如果剩下的手牌是一手牌,我们即直接返回该牌型的价值,这个下一章会写出,我们先假设存在这个函数ins_SurCardsType。

其返回值设计:若是一手牌,返回牌型,若不是一手牌,返回错误牌型。


 
  1. CardGroupData uctCardGroupData = ins_SurCardsType(clsHandCardData.value_aHandCardList);
  2. //如果能一次性出去且没有炸弹,因为有炸弹的话权值可能会更大
  3. if (uctCardGroupData.cgType != cgERROR&& !HasBoom(clsHandCardData.value_aHandCardList))
  4. {
  5. uctHandCardValue.SumValue = uctCardGroupData.nValue;
  6. uctHandCardValue.NeedRound = 1;
  7. return uctHandCardValue;
  8. }


若不是一手牌的情况,我们通过get_PutCardList函数(后续几章会写出实现方法)获取最佳的出牌策略 进行出牌,



 
  1. /*只是获取出牌的序列,即clsHandCardData.value_nPutCardList及clsHandCardData.uctPutCardType
  2. 其他成员均无改变,也不会调用出牌函数,get_PutCardList返回最优方案*/
  3. get_PutCardList_2(clsHandCardData);
  4. //要保存当前的clsHandCardData.value_nPutCardList及clsHandCardData.uctPutCardType用于回溯
  5. CardGroupData NowPutCardType = clsHandCardData.uctPutCardType;
  6. vector< int> NowPutCardList = clsHandCardData.value_nPutCardList;
  7. if (clsHandCardData.uctPutCardType.cgType == cgERROR)
  8. {
  9. cout << "PutCardType ERROR!" << endl;
  10. }



获取完出牌序列后,打出这些牌(只改变value_aHandCardList数组),再计算剩余的牌总价值。



 
  1. for ( vector< int>::iterator iter = NowPutCardList.begin();
  2. iter != NowPutCardList.end(); iter++)
  3. {
  4. clsHandCardData.value_aHandCardList[*iter]--;
  5. }
  6. clsHandCardData.nHandCardCount -= NowPutCardType.nCount;
  7. //---回溯↑
  8. HandCardValue tmp_SurValue = get_HandCardValue(clsHandCardData); //递归剩余牌价值
  9. //---回溯↓
  10. for ( vector< int>::iterator iter = NowPutCardList.begin();
  11. iter != NowPutCardList.end(); iter++)
  12. {
  13. clsHandCardData.value_aHandCardList[*iter]++;
  14. }
  15. clsHandCardData.nHandCardCount += NowPutCardType.nCount;
  16. //---回溯↑
  17. uctHandCardValue.SumValue = NowPutCardType.nValue + tmp_SurValue.SumValue;
  18. uctHandCardValue.NeedRound = tmp_SurValue.NeedRound + 1;



最后将返回的价值与当前打出的牌价值相加即时该阶段手牌总价值。



下面给出完整代码


 
  1. /*
  2. 通过回溯dp的方式获取手牌价值
  3. 与get_PutCardList作为交替递归调用
  4. 返回:价值结构体HandCardValue
  5. 权值的计算规则参考头文件评分逻辑思维
  6. */
  7. HandCardValue get_HandCardValue(HandCardData &clsHandCardData)
  8. {
  9. //首先清空出牌队列,因为剪枝时是不调用get_PutCardList的
  10. clsHandCardData.ClearPutCardList();
  11. HandCardValue uctHandCardValue;
  12. //出完牌了,其实这种情况只限于手中剩下四带二且被动出牌的情况,因为四带二剪枝做了特殊处理。
  13. if (clsHandCardData.nHandCardCount == 0)
  14. {
  15. uctHandCardValue.SumValue = 0;
  16. uctHandCardValue.NeedRound = 0;
  17. return uctHandCardValue;
  18. }
  19. //————以下为剪枝:判断是否可以一手出完牌
  20. CardGroupData uctCardGroupData = ins_SurCardsType(clsHandCardData.value_aHandCardList);
  21. //————不到万不得已我们都不会出四带二,都尽量保炸弹
  22. if (uctCardGroupData.cgType != cgERROR&&uctCardGroupData.cgType != cgFOUR_TAKE_ONE&&uctCardGroupData.cgType != cgFOUR_TAKE_TWO)
  23. {
  24. uctHandCardValue.SumValue = uctCardGroupData.nValue;
  25. uctHandCardValue.NeedRound = 1;
  26. return uctHandCardValue;
  27. }
  28. //非剪枝操作,即非一手能出完的牌
  29. /*只是获取出牌的序列,即clsHandCardData.value_nPutCardList及clsHandCardData.uctPutCardType
  30. 其他成员均无改变,也不会调用出牌函数,get_PutCardList返回最优方案*/
  31. get_PutCardList_2(clsHandCardData);
  32. //要保存当前的clsHandCardData.value_nPutCardList及clsHandCardData.uctPutCardType用于回溯
  33. CardGroupData NowPutCardType = clsHandCardData.uctPutCardType;
  34. vector< int> NowPutCardList = clsHandCardData.value_nPutCardList;
  35. if (clsHandCardData.uctPutCardType.cgType == cgERROR)
  36. {
  37. cout << "PutCardType ERROR!" << endl;
  38. }
  39. //---回溯↓
  40. for ( vector< int>::iterator iter = NowPutCardList.begin();
  41. iter != NowPutCardList.end(); iter++)
  42. {
  43. clsHandCardData.value_aHandCardList[*iter]--;
  44. }
  45. clsHandCardData.nHandCardCount -= NowPutCardType.nCount;
  46. //---回溯↑
  47. HandCardValue tmp_SurValue = get_HandCardValue(clsHandCardData); //递归剩余牌价值
  48. //---回溯↓
  49. for ( vector< int>::iterator iter = NowPutCardList.begin();
  50. iter != NowPutCardList.end(); iter++)
  51. {
  52. clsHandCardData.value_aHandCardList[*iter]++;
  53. }
  54. clsHandCardData.nHandCardCount += NowPutCardType.nCount;
  55. //---回溯↑
  56. uctHandCardValue.SumValue = NowPutCardType.nValue + tmp_SurValue.SumValue;
  57. uctHandCardValue.NeedRound = tmp_SurValue.NeedRound + 1;
  58. return uctHandCardValue;
  59. }



注:当前出牌策略是2.0版本,所以是get_PutCardList_2,后续还会写出更多的版本,敬请期待。


如果你之前了解回溯算法的话,那么应该很好理解上述代码,若没有接触过回溯,可以看我以前的博客http://blog.youkuaiyun.com/sm9sun/article/details/53244484


总值计算的模块就算完成了,其实并没有太多东西,除了一个回溯递归以外就是里面调用了两个函数ins_SurCardsType判断是否是一手牌以及get_PutCardList出牌逻辑。那么下一章我们便要实现这个ins_SurCardsType函数。


敬请关注下一章:斗地主AI算法——第六章の牌型判断

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值