一、规则和权值定义
1、斗地主中存在很多种的牌型,比如:单张,对子,三带,顺子,连队,飞机,炸弹等 ,我的机器人主要根据权重去设计的,我给每一种牌型都制定了一个权重,比如3权重是多少,其他牌型的权重有时多少,定义了一个结构体如下:
type CardGroup struct{
cgType PattenType //枚举类型
value int //牌的价值
count int //牌的个数
maxCard int //最大的牌
}
根据牌型去获得对应的权值
func GetCardGroup(cgType PatternType, major int, c int)*CardGroup{
switch cgType{
case Single :
value=major-10
}
return &CardGroup(cgType ,value,c,major)
}
2、牌的大小
大小:3、4、5、6、7、8、9、10、J、Q、K、A、2、小王、大王
二、实现方面
1、机器人的逻辑先是叫地主,抢地主,根据手上的牌来计算出最优的牌型组合且权重得到最优,然后在选择叫地主和抢地主。
2、叫玩地主之后,选择加倍或者不加倍,这个时候底牌已经出来,然后机器人再重新计算出对应的权值来选择加倍或者不加倍。
3、加倍完成之后,则进入机器人出牌和跟牌的逻辑 ,这面的逻辑主要是包含一些策略,比如:一手牌牌策略,挡牌策略,让队友策略已经打出一手牌让自己手牌价值最大的策略等等。
注释:在计算权值的时候,会对手牌进行一个拆牌算法,将玩家手牌拆分成不同的组合,然后从拆分的组合中去得到最优的牌值拆分。这里面涉及到的算法是动态规划+递归+回溯(ps:在这里就不多描述了)代码贴一丢丢如下:
type ValueCalculatorR struct {
valueTbl map[string]*HandCardValue
}
func NewValueCalculatorR() *ValueCalculatorR {
calc := &ValueCalculatorR{
valueTbl: make(map[string]*HandCardValue),
}
return calc
}
// 手牌权值结构
type HandCardValue struct {
sumValue int // 手牌总价值
needRound int // 需要打几手牌
cardGroups []CardGroup // 记录拆牌结果
cardArrays []CardArray // 记录拆牌结果
}
计算出的主要是包含结构体中这些参数。下面来说一下我做这个机器人的一些策略,希望对大家有所帮助:
出牌策略:优先的话,
- 是一手牌策略,当自己手上只有一手牌的情况下,则直接打出去,eg:333,444,77,88,可以直接当飞机打出去。
- 当对手只有一张牌或者两张牌的情况下,则结合场面上的信息去进行分析,比如你的对手有两张牌,但是你的手里只有对子和一张目前场上最大的单张,则你应该选择拆对子去打牌。这样才能保证你赢。
- 让队友牌策略,当下家是队友的话,,比如队友只有一张或者两张的时候,打出自己手上较小的单张或者对子放队友走掉。
- 小顺子,飞机,三带牌型则优先打出去,以及在某些情况下可以打出四带二的牌型。
- 打出一手牌,让当前玩家的手牌的权值达到最大值。
跟牌策略
1、优先也是一手牌策略,当你手上只有一手牌的话,要的起的话,则跟出去。
2、分析对手的牌,当对手牌的数量只剩下一张或者两张的时候,这个情况下如何去选择跟牌。
3、跟牌必赢策略,当自己手上的权重较大的时候,在存在要的起的情况下,则会选择去跟牌。
4、让队友跟牌策略,当自己手牌需要拆牌,或者打出当前牌则牌权值较差的情况下,则会优先考虑让队友去跟牌。
5、两手牌策略,分析自己手上的牌型,根据场面上的信息,选择跟牌还是不跟,绝大部分要的起的话,是会跟牌,除一些特殊情况。
6、最后的兜底策略,分析各种情况,当压友军需要拆牌的情况下则不拆,当打出的是炸弹,但是队手的牌的权值太小的情况下,则不不打出炸弹,这个策略至关重要,直接影响到胜率。
以上的策略是从上往下,优先级从大到小。本人还写过五子棋机器人,斗地主炸弹机器人,组队2v2机器人。其实策略逻辑方面都差不多,只是需要考虑的场景不太一样,现在正在对机器人做进一步的优化。希望以后能越来越智能。现在机器人胜率普遍达到40-50%之间。