斗地主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

相关文章

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 应

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Spring AI ectorStore的使用流程

《SpringAIectorStore的使用流程》SpringAI中的VectorStore是一种用于存储和检索高维向量数据的数据库或存储解决方案,它在AI应用中发挥着至关重要的作用,本文给大家介... 目录一、VectorStore的基本概念二、VectorStore的核心接口三、VectorStore的

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

Spring AI集成DeepSeek三步搞定Java智能应用的详细过程

《SpringAI集成DeepSeek三步搞定Java智能应用的详细过程》本文介绍了如何使用SpringAI集成DeepSeek,一个国内顶尖的多模态大模型,SpringAI提供了一套统一的接口,简... 目录DeepSeek 介绍Spring AI 是什么?Spring AI 的主要功能包括1、环境准备2

Spring AI集成DeepSeek实现流式输出的操作方法

《SpringAI集成DeepSeek实现流式输出的操作方法》本文介绍了如何在SpringBoot中使用Sse(Server-SentEvents)技术实现流式输出,后端使用SpringMVC中的S... 目录一、后端代码二、前端代码三、运行项目小天有话说题外话参考资料前面一篇文章我们实现了《Spring

Spring AI与DeepSeek实战一之快速打造智能对话应用

《SpringAI与DeepSeek实战一之快速打造智能对话应用》本文详细介绍了如何通过SpringAI框架集成DeepSeek大模型,实现普通对话和流式对话功能,步骤包括申请API-KEY、项目搭... 目录一、概述二、申请DeepSeek的API-KEY三、项目搭建3.1. 开发环境要求3.2. mav