斗地主AI算法——第五章の总值计算

2024-01-10 06:18

本文主要是介绍斗地主AI算法——第五章の总值计算,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


本章算是比较重点的一章,前一章已经对各个牌型做出了价值定义,本章主要实现计算手牌总价值模块函数。

根据之前的思路,我们设定一下输入输出:

输入:手牌数据类(主要用手牌个数nHandCardCount以及手牌状态数组clsHandCardData.value_aHandCardList)

输出:手牌价值结构 HandCardValue


先处理剪枝部分,如果剩下的手牌是一手牌,我们即直接返回该牌型的价值,这个下一章会写出,我们先假设存在这个函数ins_SurCardsType。

其返回值设计:若是一手牌,返回牌型,若不是一手牌,返回错误牌型。

	CardGroupData uctCardGroupData = ins_SurCardsType(clsHandCardData.value_aHandCardList);//如果能一次性出去且没有炸弹,因为有炸弹的话权值可能会更大if (uctCardGroupData.cgType != cgERROR&& !HasBoom(clsHandCardData.value_aHandCardList)){uctHandCardValue.SumValue = uctCardGroupData.nValue;uctHandCardValue.NeedRound = 1;return uctHandCardValue;}


若不是一手牌的情况,我们通过get_PutCardList函数(后续几章会写出实现方法)获取最佳的出牌策略 进行出牌,


/*只是获取出牌的序列,即clsHandCardData.value_nPutCardList及clsHandCardData.uctPutCardType其他成员均无改变,也不会调用出牌函数,get_PutCardList返回最优方案*/get_PutCardList_2(clsHandCardData);//要保存当前的clsHandCardData.value_nPutCardList及clsHandCardData.uctPutCardType用于回溯CardGroupData NowPutCardType = clsHandCardData.uctPutCardType;vector<int> NowPutCardList = clsHandCardData.value_nPutCardList;if (clsHandCardData.uctPutCardType.cgType == cgERROR){cout << "PutCardType ERROR!" << endl;}



获取完出牌序列后,打出这些牌(只改变value_aHandCardList数组),再计算剩余的牌总价值。


for (vector<int>::iterator iter = NowPutCardList.begin();iter != NowPutCardList.end(); iter++){clsHandCardData.value_aHandCardList[*iter]--;}clsHandCardData.nHandCardCount -= NowPutCardType.nCount;//---回溯↑HandCardValue tmp_SurValue = get_HandCardValue(clsHandCardData);//递归剩余牌价值//---回溯↓	for (vector<int>::iterator iter = NowPutCardList.begin();iter != NowPutCardList.end(); iter++){clsHandCardData.value_aHandCardList[*iter]++;}clsHandCardData.nHandCardCount += NowPutCardType.nCount;//---回溯↑uctHandCardValue.SumValue = NowPutCardType.nValue + tmp_SurValue.SumValue;uctHandCardValue.NeedRound = tmp_SurValue.NeedRound + 1;



最后将返回的价值与当前打出的牌价值相加即时该阶段手牌总价值。



下面给出完整代码

/*
通过回溯dp的方式获取手牌价值
与get_PutCardList作为交替递归调用
返回:价值结构体HandCardValue
权值的计算规则参考头文件评分逻辑思维
*/HandCardValue get_HandCardValue(HandCardData &clsHandCardData)
{//首先清空出牌队列,因为剪枝时是不调用get_PutCardList的clsHandCardData.ClearPutCardList();HandCardValue uctHandCardValue;//出完牌了,其实这种情况只限于手中剩下四带二且被动出牌的情况,因为四带二剪枝做了特殊处理。if (clsHandCardData.nHandCardCount == 0){uctHandCardValue.SumValue = 0;uctHandCardValue.NeedRound = 0;return uctHandCardValue;}//————以下为剪枝:判断是否可以一手出完牌CardGroupData uctCardGroupData = ins_SurCardsType(clsHandCardData.value_aHandCardList);//————不到万不得已我们都不会出四带二,都尽量保炸弹if (uctCardGroupData.cgType != cgERROR&&uctCardGroupData.cgType != cgFOUR_TAKE_ONE&&uctCardGroupData.cgType != cgFOUR_TAKE_TWO){uctHandCardValue.SumValue = uctCardGroupData.nValue;uctHandCardValue.NeedRound = 1;return uctHandCardValue;}//非剪枝操作,即非一手能出完的牌/*只是获取出牌的序列,即clsHandCardData.value_nPutCardList及clsHandCardData.uctPutCardType其他成员均无改变,也不会调用出牌函数,get_PutCardList返回最优方案*/get_PutCardList_2(clsHandCardData);//要保存当前的clsHandCardData.value_nPutCardList及clsHandCardData.uctPutCardType用于回溯CardGroupData NowPutCardType = clsHandCardData.uctPutCardType;vector<int> NowPutCardList = clsHandCardData.value_nPutCardList;if (clsHandCardData.uctPutCardType.cgType == cgERROR){cout << "PutCardType ERROR!" << endl;}//---回溯↓for (vector<int>::iterator iter = NowPutCardList.begin();iter != NowPutCardList.end(); iter++){clsHandCardData.value_aHandCardList[*iter]--;}clsHandCardData.nHandCardCount -= NowPutCardType.nCount;//---回溯↑HandCardValue tmp_SurValue = get_HandCardValue(clsHandCardData);//递归剩余牌价值//---回溯↓	for (vector<int>::iterator iter = NowPutCardList.begin();iter != NowPutCardList.end(); iter++){clsHandCardData.value_aHandCardList[*iter]++;}clsHandCardData.nHandCardCount += NowPutCardType.nCount;//---回溯↑uctHandCardValue.SumValue = NowPutCardType.nValue + tmp_SurValue.SumValue;uctHandCardValue.NeedRound = tmp_SurValue.NeedRound + 1;return uctHandCardValue;}



注:当前出牌策略是2.0版本,所以是get_PutCardList_2,后续还会写出更多的版本,敬请期待。


如果你之前了解回溯算法的话,那么应该很好理解上述代码,若没有接触过回溯,可以看我以前的博客http://blog.csdn.net/sm9sun/article/details/53244484


总值计算的模块就算完成了,其实并没有太多东西,除了一个回溯递归以外就是里面调用了两个函数ins_SurCardsType判断是否是一手牌以及get_PutCardList出牌逻辑。那么下一章我们便要实现这个ins_SurCardsType函数。


敬请关注下一章:斗地主AI算法——第六章の牌型判断

这篇关于斗地主AI算法——第五章の总值计算的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/589829

相关文章

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Spring AI 实现 STDIO和SSE MCP Server的过程详解

《SpringAI实现STDIO和SSEMCPServer的过程详解》STDIO方式是基于进程间通信,MCPClient和MCPServer运行在同一主机,主要用于本地集成、命令行工具等场景... 目录Spring AI 实现 STDIO和SSE MCP Server1.新建Spring Boot项目2.a

Java计算经纬度距离的示例代码

《Java计算经纬度距离的示例代码》在Java中计算两个经纬度之间的距离,可以使用多种方法(代码示例均返回米为单位),文中整理了常用的5种方法,感兴趣的小伙伴可以了解一下... 目录1. Haversine公式(中等精度,推荐通用场景)2. 球面余弦定理(简单但精度较低)3. Vincenty公式(高精度,

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

Springboot实现推荐系统的协同过滤算法

《Springboot实现推荐系统的协同过滤算法》协同过滤算法是一种在推荐系统中广泛使用的算法,用于预测用户对物品(如商品、电影、音乐等)的偏好,从而实现个性化推荐,下面给大家介绍Springboot... 目录前言基本原理 算法分类 计算方法应用场景 代码实现 前言协同过滤算法(Collaborativ

windows和Linux使用命令行计算文件的MD5值

《windows和Linux使用命令行计算文件的MD5值》在Windows和Linux系统中,您可以使用命令行(终端或命令提示符)来计算文件的MD5值,文章介绍了在Windows和Linux/macO... 目录在Windows上:在linux或MACOS上:总结在Windows上:可以使用certuti

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

基于Flask框架添加多个AI模型的API并进行交互

《基于Flask框架添加多个AI模型的API并进行交互》:本文主要介绍如何基于Flask框架开发AI模型API管理系统,允许用户添加、删除不同AI模型的API密钥,感兴趣的可以了解下... 目录1. 概述2. 后端代码说明2.1 依赖库导入2.2 应用初始化2.3 API 存储字典2.4 路由函数2.5 应