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

来源:互联网 发布:义乌样品淘宝拍摄 编辑:程序博客网 时间:2024/05/26 15:55


本章是被动出牌的最后一章,截止目前,我们已经解决了大部分牌型。只剩下飞机和炸弹了。

飞机无疑是最复杂的类型,他等于顺子和三带的结合体,但又增加了很多难度。

根据上一章的算法,我们可以大概想到,若是带出去一张我就加一个循环,若是带出去两张我就加俩循环,但是这个飞机长度不一致,带出去的牌个数也就不一致,这TM怎么加啊!!我一开始的想法是外置一个全排列函数,给定count个数,然后列举所有的方案去改变value_aHandCardList数组再筛选出最优解。但这样做有坏处,第一,这个外置函数非常的麻烦,因为他全排列的全集和子集个数都不确定。第二,影响了程序分支一致性,别的模块都差不多,就飞机这里搞特殊化不太好。第三,不一定安全,因为这种做法意味着我又要多了一个可以影响value_aHandCardList的模块,我并不希望这样。

所以思考了很久,我觉得宁愿多做几个分支,毕竟飞机的个数还是可控的,就2-4。虽然这种代码写起来跟看起来都很傻逼,但是也没有办法。。。


飞机带单:

//暂存最佳的价值HandCardValue BestHandCardValue = get_HandCardValue(clsHandCardData);//我们认为不出牌的话会让对手一个轮次,即加一轮(权值减少7)便于后续的对比参考。BestHandCardValue.NeedRound += 1;//暂存最佳的牌号int BestMaxCard = 0;//是否出牌的标志bool PutCards = false;//验证顺子的标志int prov = 0;//顺子起点int start_i = 0;//顺子终点int end_i = 0;//顺子长度int length = clsGameSituation.uctNowCardGroup.nCount / 4;int tmp_1 = 0;int tmp_2 = 0;int tmp_3 = 0;int tmp_4 = 0;//2与王不参与顺子,从当前已打出的顺子最小牌值+1开始遍历for (int i = clsGameSituation.uctNowCardGroup.nMaxCard - length + 2; i < 15; i++){if (clsHandCardData.value_aHandCardList[i] > 2){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] -= 3;}clsHandCardData.nHandCardCount -= clsGameSituation.uctNowCardGroup.nCount;/*本来想做全排列选取带出的牌然后枚举出最高价值的,但考虑到当飞机长度也就是在2-4之间所以干脆做三个分支处理算了*///为两连飞机if (length == 2){for (int j = 3; j < 18; j++){if (clsHandCardData.value_aHandCardList[j] > 0){clsHandCardData.value_aHandCardList[j] -= 1;for (int k = 3; k < 18; k++){if (clsHandCardData.value_aHandCardList[k] > 0){clsHandCardData.value_aHandCardList[k] -= 1;HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);clsHandCardData.value_aHandCardList[k] += 1;//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7))){BestHandCardValue = tmpHandCardValue;BestMaxCard = end_i;tmp_1 = j;tmp_2 = k;PutCards = true;}}}clsHandCardData.value_aHandCardList[j] += 1;}}}//为三连飞机if (length == 3){for (int j = 3; j < 18; j++){if (clsHandCardData.value_aHandCardList[j] > 0){clsHandCardData.value_aHandCardList[j] -= 1;for (int k = 3; k < 18; k++){if (clsHandCardData.value_aHandCardList[k] > 0){clsHandCardData.value_aHandCardList[k] -= 1;for (int l = 3; l < 18; l++){if (clsHandCardData.value_aHandCardList[l] > 0){clsHandCardData.value_aHandCardList[l] -= 1;HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7))){BestHandCardValue = tmpHandCardValue;BestMaxCard = end_i;tmp_1 = j;tmp_2 = k;tmp_3 = l;PutCards = true;}clsHandCardData.value_aHandCardList[l] += 1;}}clsHandCardData.value_aHandCardList[k] += 1;}}clsHandCardData.value_aHandCardList[j] += 1;}}}//为四连飞机if (length == 4){for (int j = 3; j < 18; j++){if (clsHandCardData.value_aHandCardList[j] > 0){clsHandCardData.value_aHandCardList[j] -= 1;for (int k = 3; k < 18; k++){if (clsHandCardData.value_aHandCardList[k] > 0){clsHandCardData.value_aHandCardList[k] -= 1;for (int l = 3; l < 18; l++){if (clsHandCardData.value_aHandCardList[l] > 0){clsHandCardData.value_aHandCardList[l] -= 1;for (int m = 3; m < 18; m++){if (clsHandCardData.value_aHandCardList[m] > 0){clsHandCardData.value_aHandCardList[m] -= 1;HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7))){BestHandCardValue = tmpHandCardValue;BestMaxCard = end_i;tmp_1 = j;tmp_2 = k;tmp_3 = l;tmp_4 = m;PutCards = true;}clsHandCardData.value_aHandCardList[m] += 1;}}clsHandCardData.value_aHandCardList[l] += 1;}}clsHandCardData.value_aHandCardList[k] += 1;}}clsHandCardData.value_aHandCardList[j] += 1;}}}for (int j = start_i; j <= end_i; j++){clsHandCardData.value_aHandCardList[j] += 3;}clsHandCardData.nHandCardCount += clsGameSituation.uctNowCardGroup.nCount;}}if (PutCards){for (int j = start_i; j <= end_i; j++){clsHandCardData.value_nPutCardList.push_back(j);clsHandCardData.value_nPutCardList.push_back(j);clsHandCardData.value_nPutCardList.push_back(j);}if (length == 2){clsHandCardData.value_nPutCardList.push_back(tmp_1);clsHandCardData.value_nPutCardList.push_back(tmp_2);    }if (length == 3){clsHandCardData.value_nPutCardList.push_back(tmp_1);clsHandCardData.value_nPutCardList.push_back(tmp_2);clsHandCardData.value_nPutCardList.push_back(tmp_3);}if (length == 4){clsHandCardData.value_nPutCardList.push_back(tmp_1);clsHandCardData.value_nPutCardList.push_back(tmp_2);clsHandCardData.value_nPutCardList.push_back(tmp_3);clsHandCardData.value_nPutCardList.push_back(tmp_4);}clsHandCardData.uctPutCardType = clsGameSituation.uctNowCardGroup = get_GroupData(cgTHREE_TAKE_ONE_LINE, BestMaxCard, clsGameSituation.uctNowCardGroup.nCount);return;}

大家可以看到我回溯的处理方式和之前的不一样了,因为飞机类型很有可能把对牌当成两个单牌带出,甚至可以拆炸弹。所以每个循环内当确定了一个点就先处理value_aHandCardList状态,这样也相对安全,上一章中在四带二环节我也有提到过这方面。


飞机带对类似,而且这里是被动出牌,所以不存在4连飞机的情况,因为4连飞机带对的话就有20张牌了。只考虑2连和3连就可以了。



最后再说一下炸弹,这个炸弹就厉害了,我给的策略就是————————————

直接炸丫的!不要怂!!


else if (clsGameSituation.uctNowCardGroup.cgType == cgBOMB_CARD){//更大的炸弹——这里直接炸,不考虑拆分后果。因为信仰。for (int i = clsGameSituation.uctNowCardGroup.nMaxCard + 1; 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 = clsGameSituation.uctNowCardGroup = get_GroupData(cgBOMB_CARD, i, 4);return;}}//王炸if (clsHandCardData.value_aHandCardList[17] > 0 && clsHandCardData.value_aHandCardList[16] > 0){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;}

当然也可以改成考虑拆分后果什么的,或者如果你手上有多个炸弹是否对比一下出那个接下来更好 等等逻辑。

不过对于我来说,你都有俩炸弹了,还怕什么,肯定都是要炸的!宁输不拆!就是这么浪!



好了至此被动出牌模块就全部写完了,从下一章开始,我们讲主动出牌。


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





1 0
原创粉丝点击