6评论

以斗地主AI为例,探讨数值体系的设计和后期调整方案(一)

统一 2017-08-11 3.5k浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏策划行业精英群361498939

以我对数值策划的浅显体会,我认为数值策划主要做两件事:

一、              why

我为什么要做成这种体验

二、              how

我如何做出这种体验

 

很可惜,暂时大部分的why我都无法说出逻辑清晰的所以然,于是我只能在鬼蟹的“lol平衡性调整”中看的很爽,但无法结合实际的东西加以分析。

不过,以斗地主为例的话,就很容易说清楚这个why了,我要做的就是尽可能把AI做的像真人一样,让玩家觉得时刻都能找到“人”陪他玩。

于是,顺理成章可以进入我比较擅长的how的部分了。

 

首先,how对应的对象是机器人、是程序、是死物,对它们而言,是没有“感觉不对”、“感觉慢了”这类的说法,对他们而言只有条件判断if和数值(数字)。以斗地主为例,我们要把平常的出牌经验,全部抽象成为数字,例如我说我的牌好,要转化成分数高;出这手牌好,要转化成为,我(或者我的队友)的分数变高了,或者对手的分数降低了;这手牌能不能出,要转化为,出了之后收益多大,但让对手胜利的概率又有多大;等等。

 

这篇文章讲的就是

1.       如何把场上的条件转化为数字(数值体系的建立)

2.       遇到很难转化的情况如何额外处理(附加判定机制)

3.       前期(整个游戏不能跑起来之前)如何调整数值

4.       后期(可以亲身玩游戏之后)面对各种疑难杂症,又如何调整数值,甚至推翻原来的体系。

5.       附加内容:做出一个能跑起来的斗地主程序,有发牌、叫地主、出牌直到一局结束。

 

(注:由于目标是嵌套到已存在的斗地主程序里面,而程序员希望他要做的事情越少越好,所以这个斗地主是一个不完整版,它缺少记录出牌流程并加以分析的模块,只有对应当前的状态,选择合适的出牌策略的流程。不过测试结果已经比较接近真人的行为了,完整版将会在未来补全。并且牌的数值代号是以他的规则定义的,而不是最合适AI设计的定义,所以需要有一些转换的函数,只是看起来同一张牌有多个数值定义,但对实际功能是没有任何影响的)

 ——————————————正文分割线———————————————————


 

 

第一步:定义我们需要的基础信息(注:现在只是罗列,之后会逐行解析)

// Pai 手牌的信息

type Pai struct {

         handcard     []int       //原始的牌101-1402

         initstate      InitState    //初始状态

         arrstate      []FinalState  //基本遍历所有情况

         bestcardtype  [][]int      //最好的手牌分配

         bestscore    int         //最好的手牌分配的分数

         tactics       int         //确定出牌的策略,1:优势牌全攻,2:中势牌半攻半守,3:劣势牌,全防守

         dizhuseat    int         //地主的位置(我的位置是0,下家的位置是1,上家的位置是2

         myseat      string      //我对应地主的位置

         bigcardscore  int        //手上大牌的分数,用来确定要不要叫地主

}

   

原始的牌是101-104(四个A),1301-1304(四个K),1401小王,1402大王。

设计是很清晰明了的,但是我们AI是不看花色的,只分大小王,而且A是比K大的。

所以我们的排序将成为3-1314=A15=216=小王,17=大王

(注:即34567891011121314151617

当然这样的分配也有个问题,顺子是不能A22小王这样顺的,不过我们会在函数里面做判断。

同时,我们还要转化为一个数组,把牌放进入。

arrpai  [15]int 我们一共15个大小的牌,找15个盒子把他们装起来,盒子最少装0个,最多装4个。

虽然这样我们看起来不够清晰,但是增删改查起来要更方便,这是我们最常操作的数据了。

 

第一步,我们要让AI学会“看牌”,就是把牌分好类让他记住。

 

// InitState 手牌的初始牌型分布

type InitState struct {

       arrpai    [15]int //把手牌变成数组,方便统计

       shunzi5   []int   //只记录最小的那张牌,例如[3,4,5,6,7]只记录3,变成[3],后续再append

       shunzi6   []int

       shunzi7   []int

       shunzi8   []int

       shunzi9   []int

       shunzi10  []int

       shunzi11  []int

       shunzi12  []int

       liandui3  []int

       liandui4  []int

       liandui5  []int

       liandui6  []int

       liandui7  []int

       liandui8  []int

       liandui9  []int

       liandui10 []int

       sanshun2  []int

       sanshun3  []int

       sanshun4  []int

       sanshun5  []int

       sanshun6  []int

       zhadan    []int

       arr       [22][]int  //0-7存放顺子,8-15存放连对,16-20存放三顺(三顺一般都进入飞机组了),21放炸弹

       state     FinalState //把上面的,信息,归纳进入最终状态

}

 

第一步:我们要把原来的数组,转化为新的数组。

func changeToArr(pai []int) [15]int  // 把后端发来的牌,转化成[15]int,方便处理

(注:所有函数代码都会在文章末段贴出,本文主将数值部分,所以代码部分可看可不看)

 

第二步:我们要找出所有长的特殊牌型,顺子、连对、三顺和炸弹,并把他们存放起来。

(注:由于牌型是有规律的,所以只需要记录第一张 牌型的分类,就可以知道他的完整信息了)

 

第三步:创建一个arr  [22][]int 把以上的所有信息都存放起来,这一步我们就把准备工作都做好了,炒菜!

(注:剩下的牌型会在下一步提炼出来,并且本版本炸弹并不会转化为42。)

 

// FinalState 遍历所有牌型分类,得出这一种牌型的最终状态

type FinalState struct {

       finalpai   [15]int   //把手牌记录下来(复制arrpai

       finalarr   [25][]int  //在上面22种牌型的基础上,再添加三张、对子、单张

       plane    [][]int   //把飞机找都出来(注:这时候就不能用一个[]int存放了,因为是无规则的

       three    [][]int   //把三带一、三带一对、三张(但没东西带的)都找出来,此时所有牌型都有了。

       clear     [][]int   //遍历出不能再细分的牌型。

       score    int       //找出分类好的牌型,找出对应的分数,为了最好的牌型准备

}

 

FinalState最终状态,也就是我们把牌分的足够细了,不能继续分牌的状态。

        有多少种分牌方法,就有多少种最终状态,这个时候我们不会在分牌的时候就把AA拆成A A,也不会把AAAK 拆成AA A K。不过未来出牌的策略,依然可能会拆开来出。

 

例如:{1012012022033013023034016016027017021101110211031301}

(注:一个A,三个2, 三个3 一个4,一对6,一对7,一个J,一对K

其中一个finalState就会拆成:3334667711&111314151515

(注:因为这种牌型比较简单,没有顺子,如果顺子多的话将会拆出十几种分类,或更多)

一个三带一,三对,2个单牌,三个2是不搭配单牌的,基本都是用来灵活出来。

 

然后,其实至今为止,依然是在准备材料,我们真正的想要的,是score,就是找出如何分配最高分。

下一篇,将会介绍,如何定义一手牌的分值,和一些简单的数值优化。

 

 

 

完整斗地主AI代码链接https://pan.baidu.com/s/1c3yuCI

 

本篇相关:struct.go  findbest.go

主要内容:结构体定义

 找顺子各种牌型出来的函数

             遍历牌型分类函数

             计分函数

             找出最高分的牌型的函数

golang语言编写)

 查看后续:以斗地主AI为例,探讨数值体系的设计和后期调整方案(二)

以斗地主AI为例,探讨数值体系的设计和后期调整方案(三)