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

news/2024/11/17 2:43:01/


本章开始,我们介绍主动出牌的算法,和被动出牌类似,我们第一步把主要架子搭起来。


首先清空出牌序列

clsHandCardData.ClearPutCardList();


主动出牌的策略按照优先级大体可以分为三类:

【一】能直接一手牌出去,优先出。

【二】两手牌出去且有绝对大牌,先出绝对大牌。

【三】出一手牌使得接下来自己手牌价值最大化。


//剪枝:如果能出去最后一手牌直接出CardGroupData SurCardGroupData = ins_SurCardsType(clsHandCardData.value_aHandCardList);//如果能一次性出去且没有炸弹,因为有炸弹的话权值可能会更大if (SurCardGroupData.cgType != cgERROR&&!HasBoom(clsHandCardData.value_aHandCardList)){Put_All_SurCards(clsHandCardData, SurCardGroupData);return;}


然后是【二】:目前2.0版本我们暂时不考虑记牌功能,所以绝对大牌只支持王炸,以后在这里可以做更多智能的处理。

/*王炸——当前策略只处理王炸作为倒数第二手的优先出牌逻辑,后续版本会在此基础上优化*/if (clsHandCardData.value_aHandCardList[17] > 0 && clsHandCardData.value_aHandCardList[16] > 0){clsHandCardData.value_aHandCardList[17] --;clsHandCardData.value_aHandCardList[16] --;clsHandCardData.nHandCardCount -= 2;HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);clsHandCardData.value_aHandCardList[16] ++;clsHandCardData.value_aHandCardList[17] ++;clsHandCardData.nHandCardCount += 2;if (tmpHandCardValue.NeedRound == 1){clsHandCardData.value_nPutCardList.push_back(17);clsHandCardData.value_nPutCardList.push_back(16);clsHandCardData.uctPutCardType = get_GroupData(cgKING_CARD, 17, 2);return;}}

接下来就是第三步, 就是我们选择打出一手牌尽量使得接下来自己手牌价值最大化。

与被动出牌不一样的是,主动出牌我们没有限制条件,我也尝试过全部枚举,不过时间消耗肯定是爆炸的。于是我定制了一个基本的出牌优先级策略:


①三带一、飞机等牌优先打出,因为这种牌型可以把小牌带出。其实这里对比较大的三带一不是很公平,后续版本可以在此处做分支处理,比如说三带一的话只循环到10,J以上先不着急打出。飞机倒还好说,那玩意基本管不到别人,所以出了就出了。至于四带二嘛。。。四带二是个什么东西?我不知道,我眼里只有炸弹。


所以,这部分的架子应该是这样的。


若可以出这几种牌型,选择一种价值最高的打出。因为要枚举所有的牌,所以在循环外根据最佳策略进行出牌处理。


②没有上述牌型后,优先处理当前最小的一张牌。若是该牌有四张,先不处理。


这里出牌处理就放在循环内了,因为当确定了这个i值后无论如何都是要打出一手牌的,且打完牌就可以return了。


③如果没有从3到2的非炸牌,那么看看有没有单王,如果有,可以出。

	//如果没有3-2的非炸牌,则看看有没有单王if (clsHandCardData.value_aHandCardList[16] == 1 && clsHandCardData.value_aHandCardList[17] == 0){clsHandCardData.value_nPutCardList.push_back(16);clsHandCardData.uctPutCardType = get_GroupData(cgSINGLE, 16, 1);return;}if (clsHandCardData.value_aHandCardList[16] == 0 && clsHandCardData.value_aHandCardList[17] == 1){clsHandCardData.value_nPutCardList.push_back(17);clsHandCardData.uctPutCardType = get_GroupData(cgSINGLE, 17, 1);return;}

④单王也没有,出炸弹。

//单王也没有,出炸弹for (int i = 3; i < 16; i++){if (clsHandCardData.value_aHandCardList[i] == 4){clsHandCardData.value_nPutCardList.push_back(i);clsHandCardData.value_nPutCardList.push_back(i);clsHandCardData.value_nPutCardList.push_back(i);clsHandCardData.value_nPutCardList.push_back(i);clsHandCardData.uctPutCardType = get_GroupData(cgBOMB_CARD, i, 4);return;}}

这里可能有人会想,需不需要再加上炸弹也没有,出王炸呢?其实不存在的,因为如果你真的没牌打了就剩王炸了,早在前面剪枝部分就处理了。

所以如果走到这里都没有返回的话,肯定是出现错误了。

把上述的各个模块连接起来,即构成主动出牌的基本架子:

void get_PutCardList_2(HandCardData &clsHandCardData)
{clsHandCardData.ClearPutCardList();//剪枝:如果能出去最后一手牌直接出CardGroupData SurCardGroupData = ins_SurCardsType(clsHandCardData.value_aHandCardList);//如果能一次性出去且不是四带二,因为主动出牌若手上剩四带二牌的话可以考虑先打一手然后炸,获得双倍积分if (SurCardGroupData.cgType != cgERROR&&SurCardGroupData.cgType!=cgFOUR_TAKE_ONE&&SurCardGroupData.cgType !=cgFOUR_TAKE_TWO){}/*王炸——当前策略只处理王炸作为倒数第二手的优先出牌逻辑,后续版本会在此基础上优化*/if (clsHandCardData.value_aHandCardList[17] > 0 && clsHandCardData.value_aHandCardList[16] > 0){}//暂存最佳的价值HandCardValue BestHandCardValue;BestHandCardValue.NeedRound = 20;BestHandCardValue.SumValue = MinCardsValue;//我们认为不出牌的话会让对手一个轮次,即加一轮(权值减少7)便于后续的对比参考。BestHandCardValue.NeedRound += 1;//暂存最佳的组合CardGroupData BestCardGroup;//带出去的牌int tmp_1 = 0;int tmp_2 = 0;int tmp_3 = 0;int tmp_4 = 0;//优先处理三牌、飞机等牌for (int i = 3; i < 16; i++){		}//这部分出牌处理放到循环外if (BestCardGroup.cgType == cgTHREE_TAKE_ONE){}else if (BestCardGroup.cgType == cgTHREE_TAKE_TWO){}else if (BestCardGroup.cgType == cgTHREE_TAKE_ONE_LINE){}else if (BestCardGroup.cgType == cgTHREE_TAKE_TWO_LINE){}//次之处理当前价值最低的牌,现在不必再考虑这张牌可能被三牌带出等情况for (int i = 3; i < 16; i++) {}//如果没有3-2的非炸牌,则看看有没有单王if (clsHandCardData.value_aHandCardList[16] == 1 && clsHandCardData.value_aHandCardList[17] == 0){		}if (clsHandCardData.value_aHandCardList[16] == 0 && clsHandCardData.value_aHandCardList[17] == 1){}//单王也没有,出炸弹for (int i = 3; i < 16; i++){}//异常错误clsHandCardData.uctPutCardType = get_GroupData(cgERROR, 0, 0);return;}

至此主动出牌的架子就搭好了,且除了三带牌型出牌策略及解决最小值牌出牌策略这两个大部分,其他部分代码本章均已给出,下一章我们开始实现三带牌型的出牌策略。


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


http://www.ppmy.cn/news/583571.html

相关文章

我赢职场c语言数组斗地主,职场就行斗地主,你看懂了么?

1、小王都会被大王拍死。说明副职没有实权&#xff01; 2、没有一张大牌开路&#xff0c;再顺的小牌都出不去。说明领导很重要&#xff01; 3、无论你多会记牌、打牌&#xff0c;都抵不过人家手中的一把好牌。说明实力比能力重要&#xff01; 4、如果一堆小牌连不起来&#xff…

斗地主AI算法——第十七章の总结整理

2.0版本的斗地主AI算法在这里就算告一段落了。 **********************完结撒花********************** 不过后续应该还会开发更智能的版本&#xff0c;毕竟当前版本还有很多策略没有加入。 比如说角色位置&#xff08;地主上家下家打法&#xff09;、比如说记牌算牌、又比如…

天天QQ记牌器2.37 去广告绿色版

帮一个朋友忙 把这款软件的广告nop掉 听说是挺好的一款记牌器 已经上传到资源里 简单说下步骤 1.脱壳.. 用看雪大牛的 Aspr2.XX_unpacker_v1.0SC.osc 脚本脱壳 具体用法就不说了 gg下就知道了 2.最简单的方法是 搜索 tntn.cn 把字符串都替换成 about:blank 就OK了 不过还…

斗地主笔记

很喜欢斗地主&#xff0c;一起交流下。 文章目录 牌力分析王牌双鬼炸弹 一流强牌飞机大鬼22 强牌小鬼2AA&#xff0c;KK顶A顺子(长顺子)3连对(或以上) 大牌一般顺子三张A、KQQ 中牌(小牌)送命牌 叫牌哪些情况可以叫牌 对局 牌力分析 王牌 顶级牌&#xff0c;使用几乎无限制&…

js实现斗地主计分器

文章目录 起因实现1、页面html2、js操作--全局变量3、js操作--数据初始化4、js操作--每局结算5、js操作--撤销 源码获取 起因 现在大家斗地主的时候往往喜欢在每局结束后进行微信转账&#xff0c;然而这样的转帐过程往往比较浪费时间&#xff0c;所以咱们可以自己写一个简单的…

人机(AI)四人斗地主

最近在使用Cocos Creator做一款四人斗地主的手机游戏&#xff0c;半成品&#xff08;仅前端&#xff09;代码附在最后&#xff0c;仅供参考。游戏中的单机&#xff08;人机&#xff09;模式以及游戏过程中的托管都需要出牌算法的设计&#xff0c;因此借这篇博客梳理一下现有的一…

InnoDB的三种行锁(提供具体sql执行案例分析)

InnoDB存储引擎有3种行锁的算法&#xff0c;其分别是&#xff1a; Record Lock&#xff08;记录锁&#xff09;&#xff1a;单个行记录上的范围 (锁住某一行记录)Gap Lock&#xff08;间隙锁&#xff09;&#xff1a;间隙锁&#xff0c;锁定一个范围&#xff0c;但不包含记录本…

php斗地主

php斗地主判断大小&#xff0c;地主规则引入 <?php //是不是单 function isDan($temp) {if (count($temp) ! 1) {return false;} else {return {"type":"dan","value":".$temp[0]."};} }//是不是对 function isDui($temp) {if (…