上一章已经说明了被动出牌算法基本的出牌思路,且以单牌为例给出了具体的代码。

对牌、三不带牌型实现方法与单牌基本类似。改动的地方主要是枚举牌类型,出牌时value_nPutCardList的处理,回溯时value_aHandCardList和nHandCardCount的变化等几个方面。下面给出完整代码:

/对牌类型else if (clsGameSituation.uctNowCardGroup.cgType == cgDOUBLE){//剪枝:如果能出去最后一手牌直接出CardGroupData SurCardGroupData = ins_SurCardsType(clsHandCardData.value_aHandCardList);if (SurCardGroupData.cgType != cgERROR){if (SurCardGroupData.cgType == cgDOUBLE&&SurCardGroupData.nMaxCard>clsGameSituation.uctNowCardGroup.nMaxCard){Put_All_SurCards(clsGameSituation, clsHandCardData, SurCardGroupData);return;}else if (SurCardGroupData.cgType == cgBOMB_CARD || SurCardGroupData.cgType == cgKING_CARD){Put_All_SurCards(clsGameSituation, clsHandCardData, SurCardGroupData);return;}}//-------------------------------------------对牌-------------------------------------------//暂存最佳的价值HandCardValue BestHandCardValue = get_HandCardValue(clsHandCardData);//我们认为不出牌的话会让对手一个轮次,即加一轮(权值减少7)便于后续的对比参考。BestHandCardValue.NeedRound += 1;//暂存最佳的牌号int BestMaxCard = 0;//是否出牌的标志bool PutCards = false;for (int i = clsGameSituation.uctNowCardGroup.nMaxCard + 1; i < 18; i++){if (clsHandCardData.value_aHandCardList[i] > 1){//尝试打出一对牌,估算剩余手牌价值clsHandCardData.value_aHandCardList[i]-=2;clsHandCardData.nHandCardCount-=2;HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);clsHandCardData.value_aHandCardList[i]+=2;clsHandCardData.nHandCardCount+=2;//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7))){BestHandCardValue = tmpHandCardValue;BestMaxCard = i;PutCards = true;}}}if (PutCards){clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgDOUBLE, BestMaxCard, 2);return;}//-------------------------------------------炸弹-------------------------------------------for (int i = 3; i < 16; i++){if (clsHandCardData.value_aHandCardList[i] ==4){//尝试打出炸弹,估算剩余手牌价值,因为炸弹可以参与顺子,不能因为影响顺子而任意出炸clsHandCardData.value_aHandCardList[i] -= 4;clsHandCardData.nHandCardCount -= 4;HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);clsHandCardData.value_aHandCardList[i] += 4;clsHandCardData.nHandCardCount += 4;//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7))//如果剩余手牌价值为正,证明出去的几率很大, 那么可以用炸获得先手|| tmpHandCardValue.SumValue > 0){BestHandCardValue = tmpHandCardValue;BestMaxCard = i;PutCards = true;}}}if (PutCards){clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgBOMB_CARD, BestMaxCard, 4);return;}//王炸if (clsHandCardData.value_aHandCardList[17] > 0 && clsHandCardData.value_aHandCardList[16] > 0){//如果剩余手牌价值为正,证明出去的几率很大,那么可以用炸获得先手,王炸20分if (BestHandCardValue.SumValue > 20){clsHandCardData.value_nPutCardList.push_back(17);clsHandCardData.value_nPutCardList.push_back(16);clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgKING_CARD, 17, 2);return;}}//管不上clsHandCardData.uctPutCardType = get_GroupData(cgZERO, 0, 0);return;}//三牌类型else if (clsGameSituation.uctNowCardGroup.cgType == cgTHREE){//剪枝:如果能出去最后一手牌直接出CardGroupData SurCardGroupData = ins_SurCardsType(clsHandCardData.value_aHandCardList);if (SurCardGroupData.cgType != cgERROR){if (SurCardGroupData.cgType == cgTHREE&&SurCardGroupData.nMaxCard>clsGameSituation.uctNowCardGroup.nMaxCard){Put_All_SurCards(clsGameSituation, clsHandCardData, SurCardGroupData);return;}else if (SurCardGroupData.cgType == cgBOMB_CARD || SurCardGroupData.cgType == cgKING_CARD){Put_All_SurCards(clsGameSituation, clsHandCardData, SurCardGroupData);return;}}//-------------------------------------------三牌-------------------------------------------//暂存最佳的价值HandCardValue BestHandCardValue = get_HandCardValue(clsHandCardData);//我们认为不出牌的话会让对手一个轮次,即加一轮(权值减少7)便于后续的对比参考。BestHandCardValue.NeedRound += 1;//暂存最佳的牌号int BestMaxCard = 0;//是否出牌的标志bool PutCards = false;for (int i = clsGameSituation.uctNowCardGroup.nMaxCard + 1; i < 18; i++){if (clsHandCardData.value_aHandCardList[i] > 2){//尝试打出一对牌,估算剩余手牌价值clsHandCardData.value_aHandCardList[i] -= 3;clsHandCardData.nHandCardCount -= 3;HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);clsHandCardData.value_aHandCardList[i] += 3;clsHandCardData.nHandCardCount += 3;//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7))){BestHandCardValue = tmpHandCardValue;BestMaxCard = i;PutCards = true;}}}if (PutCards){clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgTHREE, BestMaxCard, 3);return;}//-------------------------------------------炸弹-------------------------------------------for (int i = 3; i < 16; i++){if (clsHandCardData.value_aHandCardList[i] == 4){//尝试打出炸弹,估算剩余手牌价值,因为炸弹可以参与顺子,不能因为影响顺子而任意出炸clsHandCardData.value_aHandCardList[i] -= 4;clsHandCardData.nHandCardCount -= 4;HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);clsHandCardData.value_aHandCardList[i] += 4;clsHandCardData.nHandCardCount += 4;//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7))//如果剩余手牌价值为正,证明出去的几率很大, 那么可以用炸获得先手|| tmpHandCardValue.SumValue > 0){BestHandCardValue = tmpHandCardValue;BestMaxCard = i;PutCards = true;}}}if (PutCards){clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.value_nPutCardList.push_back(BestMaxCard);clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgBOMB_CARD, BestMaxCard, 4);return;}//王炸if (clsHandCardData.value_aHandCardList[17] > 0 && clsHandCardData.value_aHandCardList[16] > 0){//如果剩余手牌价值为正,证明出去的几率很大,那么可以用炸获得先手,王炸20分if (BestHandCardValue.SumValue > 20){clsHandCardData.value_nPutCardList.push_back(17);clsHandCardData.value_nPutCardList.push_back(16);clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgKING_CARD, 17, 2);return;}}//管不上clsHandCardData.uctPutCardType = get_GroupData(cgZERO, 0, 0);return;}

接下来我们说一下顺子的处理方法。以单顺为例:

首先在第一阶段判断是否同类型牌里要额外增加一个条件,即顺子长度要一致

if (SurCardGroupData.cgType == cgSINGLE_LINE&&SurCardGroupData.nMaxCard>clsGameSituation.uctNowCardGroup.nMaxCard&&SurCardGroupData.nCount== clsGameSituation.uctNowCardGroup.nCount){Put_All_SurCards(clsGameSituation, clsHandCardData, SurCardGroupData);return;}

然后除了设置BestHandCardValue等变量外,我们需要额外设置几个关于顺子的标志

     //验证顺子的标志int prov = 0;//顺子起点int start_i = 0;//顺子终点int end_i = 0;//顺子长度int length = clsGameSituation.uctNowCardGroup.nCount;

遍历顺子的方法有点类似于 最大子段和问题,大家可以参考下我以前的博客 http://blog.csdn.net/sm9sun/article/details/53240992

解决思路就是如果出现某张牌个数为0,那么必然不存在经过他的顺子,此时就把计数器置零,如果计数器长度大于等于length,即可以组成顺子,我们以当前下标i为最高标志构造出(i-length+1)~i的顺子。

举个例子:对方牌型为34567,我从4遍历至8,若满足,此时end_i=8,即45678,继续走到9,若还满足,end_i=9。即56789,若没有10,则prov归零,下一次循环若11存在,则prov=1。

     for (int i = clsGameSituation.uctNowCardGroup.nMaxCard - length + 2; i < 15; i++){if (clsHandCardData.value_aHandCardList[i] > 0){prov++;}else{prov = 0;}if (prov >= length){end_i = i;start_i = i - length + 1;for (int j = start_i; j <= end_i; j++){clsHandCardData.value_aHandCardList[j] --;}clsHandCardData.nHandCardCount -= clsGameSituation.uctNowCardGroup.nCount;HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);for (int j = start_i; j <= end_i; j++){clsHandCardData.value_aHandCardList[j] ++;}clsHandCardData.nHandCardCount += clsGameSituation.uctNowCardGroup.nCount;//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7))){BestHandCardValue = tmpHandCardValue;BestMaxCard = end_i;PutCards = true;}}}

最后打出顺子的话在start_i和 end_i区间内依次减一即可。

     if (PutCards){for (int j = start_i; j <= end_i; j++){clsHandCardData.value_nPutCardList.push_back(j);}       clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgSINGLE_LINE, BestMaxCard, clsGameSituation.uctNowCardGroup.nCount);return;}

以上就是单顺的处理方法,下一章我们继续填充其他牌型的出牌方法。

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

斗地主AI算法——第九章の被动出牌(3)相关推荐

  1. 斗地主AI算法——第八章の被动出牌(2)

    上一章我们已经搭好了被动出牌的基本架子,本章我们主要说明一下被动出牌算法的基本步骤. 我把出牌逻辑分为四个阶段,也就是策略的优先级.分别是:[直接打光手牌]→[同类型牌压制]→[炸弹王炸压制]→[不出 ...

  2. 斗地主AI算法——第十章の被动出牌(4)

    上一章已经说明了单顺的实现方法,双顺.三不带顺牌型实现方法与单牌基本类似.改动的地方除了上一章说的枚举牌类型,出牌时value_nPutCardList的处理,回溯时value_aHandCardLi ...

  3. 斗地主AI算法——第七章の被动出牌(1)

    哎,之前扯了那么多蛋,终于讲出牌了! 本章开始讲被动出牌的逻辑算法.首先我们先把架子搭起来,被动出牌我们肯定是要知道场上目前打出的是什么牌型. 在第二章数据结构里我们定义过,游戏全局类里面有一个存放当 ...

  4. 斗地主AI算法——第一章の业务逻辑

    转眼间快到了五月,帝都的天气也变的非常梦幻. 时而酷暑炎热,时而狂风席卷. 而不管外面如何,我们也只能在办公室里茕茕无依的撸着代码,无可奈何的负着韶华. 世界是寂寞的,寂寞到不只是寂寞,而是死一般的寂 ...

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

    本章是被动出牌的最后一章,截止目前,我们已经解决了大部分牌型.只剩下飞机和炸弹了. 飞机无疑是最复杂的类型,他等于顺子和三带的结合体,但又增加了很多难度. 根据上一章的算法,我们可以大概想到,若是带出 ...

  6. 斗地主AI算法——第二章の数据结构

    上一章我们已经确立了基本的业务逻辑处理流程.从本章开始,我们便进入开发阶段. 首先就是明确我们都需要哪些数据,且它们以怎样的形式存储. 首先从上一章反复提到的手牌权值结构说起,也就是F()的返回值,他 ...

  7. 斗地主AI算法实现 一(拆牌)

    源代码下载                    ps: 前面已经写了几篇地主游戏的基本算法实现,今天来讲讲单机地主中最重要的,也是开发中最难得AI算法实现.在此声明,本博文只适合像我一样的菜鸟阅读, ...

  8. 斗地主AI算法——第十四章の主动出牌(3)

    上一章已经排除了飞机.三带等牌型,那么除去炸弹王炸以外,我们只剩下单牌.对牌.三牌以及单顺.双顺.三顺了. 首先说单牌.对牌.三牌.其逻辑基本一样,只是出牌的个数有差别,即:如果该i牌数量满足这种牌型 ...

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

    本章开始,我们介绍主动出牌的算法,和被动出牌类似,我们第一步把主要架子搭起来. 首先清空出牌序列 clsHandCardData.ClearPutCardList(); 主动出牌的策略按照优先级大体可 ...

最新文章

  1. 结构光系统标定方法详解
  2. GPT-3英文版的接口
  3. ASP.NET 框架 之HttpHandler
  4. 创客编程帮助孩子提升学习成绩,是一项长远投资!
  5. ubuntu16.04 在cuda9.0环境下编译安装opencv2.4.13.7
  6. 【EOJ Monthly 2019.02 - E】中位数(二分 ,中位数 ,−1/1变换,dp求解DAG最长路)
  7. 世界第一台电脑_2020世界计算机大会今日开幕 给市民带来全方位观展体验 - 三湘万象 - 湖南在线...
  8. Python高手之路【十】python基础之反射
  9. 账户注销完自动登录账户,并且不需要再点击屏幕的账户头像
  10. RTSP之主流安防厂家地址
  11. kali字典爆破wifi密码
  12. 七个非常好用的黑科技APP,免费/小众/超实用,一次性全给你
  13. 玩转基因组浏览器之使用IGV查看基因结构信息
  14. .NET之盛派微信SDK简单操作
  15. 小雷的冰茶几(并查集)
  16. android 实现返回键执行home键方法
  17. 英国《新科学家》杂志:研究显示人们上假新闻的当可能是因懒得思考
  18. [Done]FindBugs: boxing/unboxing to parse a primitive
  19. 前端小练————天猫国际登录页
  20. ElasticSearch6.5.4快速入门

热门文章

  1. sql datetime字段 取年月日_写一手好SQL,你该从哪里入手?
  2. python出现的意义_[转]Python中下划线以及命名空间的意义
  3. python 计算每日累计_5分钟学会用Python可视化数据分析美股
  4. python爬虫基础扫盲之URL
  5. Batch Normalization标准化(精)==>一方面可以简化计算过程,一方面经过规范化处理后让数据尽可能保留原始表达能力
  6. [C++] 在连续统上的重复性质:滑动窗口
  7. vue实现数字“滚动式增加”效果 【插件化封装】
  8. 彩虹云任务极致精简版--PHPcron程序
  9. Oreo授权系统V1.0.6公益开源版本
  10. 【WP主题】仿下载吧全开源无加密wordpress主题模板内含newzhan2.60无授权版本