斗地主AI算法——第九章の被动出牌(3)
上一章已经说明了被动出牌算法基本的出牌思路,且以单牌为例给出了具体的代码。
对牌、三不带牌型实现方法与单牌基本类似。改动的地方主要是枚举牌类型,出牌时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)相关推荐
- 斗地主AI算法——第八章の被动出牌(2)
上一章我们已经搭好了被动出牌的基本架子,本章我们主要说明一下被动出牌算法的基本步骤. 我把出牌逻辑分为四个阶段,也就是策略的优先级.分别是:[直接打光手牌]→[同类型牌压制]→[炸弹王炸压制]→[不出 ...
- 斗地主AI算法——第十章の被动出牌(4)
上一章已经说明了单顺的实现方法,双顺.三不带顺牌型实现方法与单牌基本类似.改动的地方除了上一章说的枚举牌类型,出牌时value_nPutCardList的处理,回溯时value_aHandCardLi ...
- 斗地主AI算法——第七章の被动出牌(1)
哎,之前扯了那么多蛋,终于讲出牌了! 本章开始讲被动出牌的逻辑算法.首先我们先把架子搭起来,被动出牌我们肯定是要知道场上目前打出的是什么牌型. 在第二章数据结构里我们定义过,游戏全局类里面有一个存放当 ...
- 斗地主AI算法——第一章の业务逻辑
转眼间快到了五月,帝都的天气也变的非常梦幻. 时而酷暑炎热,时而狂风席卷. 而不管外面如何,我们也只能在办公室里茕茕无依的撸着代码,无可奈何的负着韶华. 世界是寂寞的,寂寞到不只是寂寞,而是死一般的寂 ...
- 斗地主AI算法——第十一章の被动出牌(5)
本章是被动出牌的最后一章,截止目前,我们已经解决了大部分牌型.只剩下飞机和炸弹了. 飞机无疑是最复杂的类型,他等于顺子和三带的结合体,但又增加了很多难度. 根据上一章的算法,我们可以大概想到,若是带出 ...
- 斗地主AI算法——第二章の数据结构
上一章我们已经确立了基本的业务逻辑处理流程.从本章开始,我们便进入开发阶段. 首先就是明确我们都需要哪些数据,且它们以怎样的形式存储. 首先从上一章反复提到的手牌权值结构说起,也就是F()的返回值,他 ...
- 斗地主AI算法实现 一(拆牌)
源代码下载 ps: 前面已经写了几篇地主游戏的基本算法实现,今天来讲讲单机地主中最重要的,也是开发中最难得AI算法实现.在此声明,本博文只适合像我一样的菜鸟阅读, ...
- 斗地主AI算法——第十四章の主动出牌(3)
上一章已经排除了飞机.三带等牌型,那么除去炸弹王炸以外,我们只剩下单牌.对牌.三牌以及单顺.双顺.三顺了. 首先说单牌.对牌.三牌.其逻辑基本一样,只是出牌的个数有差别,即:如果该i牌数量满足这种牌型 ...
- 斗地主AI算法——第十二章の主动出牌(1)
本章开始,我们介绍主动出牌的算法,和被动出牌类似,我们第一步把主要架子搭起来. 首先清空出牌序列 clsHandCardData.ClearPutCardList(); 主动出牌的策略按照优先级大体可 ...
最新文章
- 结构光系统标定方法详解
- GPT-3英文版的接口
- ASP.NET 框架 之HttpHandler
- 创客编程帮助孩子提升学习成绩,是一项长远投资!
- ubuntu16.04 在cuda9.0环境下编译安装opencv2.4.13.7
- 【EOJ Monthly 2019.02 - E】中位数(二分 ,中位数 ,−1/1变换,dp求解DAG最长路)
- 世界第一台电脑_2020世界计算机大会今日开幕 给市民带来全方位观展体验 - 三湘万象 - 湖南在线...
- Python高手之路【十】python基础之反射
- 账户注销完自动登录账户,并且不需要再点击屏幕的账户头像
- RTSP之主流安防厂家地址
- kali字典爆破wifi密码
- 七个非常好用的黑科技APP,免费/小众/超实用,一次性全给你
- 玩转基因组浏览器之使用IGV查看基因结构信息
- .NET之盛派微信SDK简单操作
- 小雷的冰茶几(并查集)
- android 实现返回键执行home键方法
- 英国《新科学家》杂志:研究显示人们上假新闻的当可能是因懒得思考
- [Done]FindBugs: boxing/unboxing to parse a primitive
- 前端小练————天猫国际登录页
- ElasticSearch6.5.4快速入门
热门文章
- sql datetime字段 取年月日_写一手好SQL,你该从哪里入手?
- python出现的意义_[转]Python中下划线以及命名空间的意义
- python 计算每日累计_5分钟学会用Python可视化数据分析美股
- python爬虫基础扫盲之URL
- Batch Normalization标准化(精)==>一方面可以简化计算过程,一方面经过规范化处理后让数据尽可能保留原始表达能力
- [C++] 在连续统上的重复性质:滑动窗口
- vue实现数字“滚动式增加”效果 【插件化封装】
- 彩虹云任务极致精简版--PHPcron程序
- Oreo授权系统V1.0.6公益开源版本
- 【WP主题】仿下载吧全开源无加密wordpress主题模板内含newzhan2.60无授权版本