斗地主AI算法——第十四章の主动出牌(3)
上一章已经排除了飞机、三带等牌型,那么除去炸弹王炸以外,我们只剩下单牌、对牌、三牌以及单顺、双顺、三顺了。
首先说单牌、对牌、三牌。其逻辑基本一样,只是出牌的个数有差别,即:如果该i牌数量满足这种牌型要求,即先打出,计算其剩余价值。
- //出单牌
- if (clsHandCardData.value_aHandCardList[i] > 0)
- {
- clsHandCardData.value_aHandCardList[i]--;
- clsHandCardData.nHandCardCount--;
- HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
- clsHandCardData.value_aHandCardList[i]++;
- clsHandCardData.nHandCardCount++;
- if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
- {
- BestHandCardValue = tmpHandCardValue;
- BestCardGroup= get_GroupData(cgSINGLE, i, 1);
- }
- }
- //出对牌
- 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;
- BestCardGroup = get_GroupData(cgDOUBLE, i, 2);
- }
- }
- //出三牌
- 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;
- BestCardGroup = get_GroupData(cgTHREE, i, 3);
- }
- }
至于顺子的算法,和被动出牌的有一点点差别,就是因为没有了数量限制,所以需要遍历以i牌为起点可以组成的所有顺子。
- //出单顺
- if (clsHandCardData.value_aHandCardList[i] > 0)
- {
- int prov = 0;
- for (int j = i; j < 15; j++)
- {
- if(clsHandCardData.value_aHandCardList[j]>0)
- {
- prov++;
- }
- else
- {
- break;
- }
- if (prov >= 5)
- {
- for (int k = i; k <= j; k++)
- {
- clsHandCardData.value_aHandCardList[k] --;
- }
- clsHandCardData.nHandCardCount -= prov;
- HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
- for (int k = i; k <= j; k++)
- {
- clsHandCardData.value_aHandCardList[k] ++;
- }
- clsHandCardData.nHandCardCount += prov;
- //选取总权值-轮次*7值最高的策略 因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
- if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
- {
- BestHandCardValue = tmpHandCardValue;
- BestCardGroup = get_GroupData(cgSINGLE_LINE, j, prov);
- }
- }
- }
- }
- //出双顺
- if (clsHandCardData.value_aHandCardList[i] > 1)
- {
- int prov = 0;
- for (int j = i; j < 15; j++)
- {
- if (clsHandCardData.value_aHandCardList[j]>1)
- {
- prov++;
- }
- else
- {
- break;
- }
- if (prov >= 3)
- {
- for (int k = i; k <= j; k++)
- {
- clsHandCardData.value_aHandCardList[k] -=2;
- }
- clsHandCardData.nHandCardCount -= prov*2;
- HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
- for (int k = i; k <= j; k++)
- {
- clsHandCardData.value_aHandCardList[k] +=2;
- }
- clsHandCardData.nHandCardCount += prov*2;
- //选取总权值-轮次*7值最高的策略 因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
- if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
- {
- BestHandCardValue = tmpHandCardValue;
- BestCardGroup = get_GroupData(cgDOUBLE_LINE, j, prov*2);
- }
- }
- }
- }
- //出三顺
- if(clsHandCardData.value_aHandCardList[i] > 2)
- {
- int prov = 0;
- for (int j = i; j < 15; j++)
- {
- if (clsHandCardData.value_aHandCardList[j]>2)
- {
- prov++;
- }
- else
- {
- break;
- }
- if (prov >= 2)
- {
- for (int k = i; k <= j; k++)
- {
- clsHandCardData.value_aHandCardList[k] -= 3;
- }
- clsHandCardData.nHandCardCount -= prov * 3;
- HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
- for (int k = i; k <= j; k++)
- {
- clsHandCardData.value_aHandCardList[k] += 3;
- }
- clsHandCardData.nHandCardCount += prov * 3;
- //选取总权值-轮次*7值最高的策略 因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
- if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
- {
- BestHandCardValue = tmpHandCardValue;
- BestCardGroup = get_GroupData(cgTHREE_LINE, j, prov * 3);
- }
- }
- }
- }
因为本策略是必须解决掉至少一个i牌的,所以出牌操作放在循环内进行,也就是说,只要你不是炸3,若你手牌有3,在处理3时一定会return 就绝对不会再走到4。
- if (BestCardGroup.cgType == cgERROR)
- {
- }
- else if (BestCardGroup.cgType == cgSINGLE)
- {
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.uctPutCardType = BestCardGroup;
- }
- else if (BestCardGroup.cgType == cgDOUBLE)
- {
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.uctPutCardType = BestCardGroup;
- }
- else if (BestCardGroup.cgType == cgTHREE)
- {
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.uctPutCardType = BestCardGroup;
- }
- else if (BestCardGroup.cgType == cgSINGLE_LINE)
- {
- for (int j = BestCardGroup.nMaxCard- BestCardGroup.nCount+1; j <= BestCardGroup.nMaxCard; j++)
- {
- clsHandCardData.value_nPutCardList.push_back(j);
- }
- clsHandCardData.uctPutCardType = BestCardGroup;
- }
- else if (BestCardGroup.cgType == cgDOUBLE_LINE)
- {
- for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount/2) + 1; j <= BestCardGroup.nMaxCard; j++)
- {
- clsHandCardData.value_nPutCardList.push_back(j);
- clsHandCardData.value_nPutCardList.push_back(j);
- }
- clsHandCardData.uctPutCardType = BestCardGroup;
- }
- else if (BestCardGroup.cgType == cgTHREE_LINE)
- {
- for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount / 3) + 1; j <= BestCardGroup.nMaxCard; j++)
- {
- clsHandCardData.value_nPutCardList.push_back(j);
- clsHandCardData.value_nPutCardList.push_back(j);
- clsHandCardData.value_nPutCardList.push_back(j);
- }
- clsHandCardData.uctPutCardType = BestCardGroup;
- }
- else if (BestCardGroup.cgType == cgTHREE_TAKE_ONE)
- {
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.value_nPutCardList.push_back(tmp_1);
- clsHandCardData.uctPutCardType = BestCardGroup;
- }
- else if (BestCardGroup.cgType == cgTHREE_TAKE_TWO)
- {
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
- clsHandCardData.value_nPutCardList.push_back(tmp_1);
- clsHandCardData.value_nPutCardList.push_back(tmp_1);
- clsHandCardData.uctPutCardType = BestCardGroup;
- }
- else if (BestCardGroup.cgType == cgTHREE_TAKE_ONE_LINE)
- {
- for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount / 4) + 1; j <= BestCardGroup.nMaxCard; j++)
- {
- clsHandCardData.value_nPutCardList.push_back(j);
- clsHandCardData.value_nPutCardList.push_back(j);
- clsHandCardData.value_nPutCardList.push_back(j);
- }
- if (BestCardGroup.nCount / 4 == 2)
- {
- clsHandCardData.value_nPutCardList.push_back(tmp_1);
- clsHandCardData.value_nPutCardList.push_back(tmp_2);
- }
- if (BestCardGroup.nCount / 4 == 3)
- {
- clsHandCardData.value_nPutCardList.push_back(tmp_1);
- clsHandCardData.value_nPutCardList.push_back(tmp_2);
- clsHandCardData.value_nPutCardList.push_back(tmp_3);
- }
- if (BestCardGroup.nCount / 4 == 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 = BestCardGroup;
- }
- else if (BestCardGroup.cgType == cgTHREE_TAKE_TWO_LINE)
- {
- for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount / 5) + 1; j <= BestCardGroup.nMaxCard; j++)
- {
- clsHandCardData.value_nPutCardList.push_back(j);
- clsHandCardData.value_nPutCardList.push_back(j);
- clsHandCardData.value_nPutCardList.push_back(j);
- }
- if (BestCardGroup.nCount / 5 == 2)
- {
- clsHandCardData.value_nPutCardList.push_back(tmp_1);
- clsHandCardData.value_nPutCardList.push_back(tmp_1);
- clsHandCardData.value_nPutCardList.push_back(tmp_2);
- clsHandCardData.value_nPutCardList.push_back(tmp_2);
- }
- if (BestCardGroup.nCount / 5 == 3)
- {
- clsHandCardData.value_nPutCardList.push_back(tmp_1);
- clsHandCardData.value_nPutCardList.push_back(tmp_1);
- clsHandCardData.value_nPutCardList.push_back(tmp_2);
- clsHandCardData.value_nPutCardList.push_back(tmp_2);
- clsHandCardData.value_nPutCardList.push_back(tmp_3);
- clsHandCardData.value_nPutCardList.push_back(tmp_3);
- }
- clsHandCardData.uctPutCardType = BestCardGroup;
- }
- return;
至此,主动出牌的所有逻辑均已实现,同时整个斗地主算法也基本完成了。接下来我们便可写一些测试模块来进行整合联调。
敬请关注下一章:斗地主AI算法——第十五章の测试模块
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)