现在的位置: 首页 > 综合 > 正文

Qt单机版斗地主游戏

2014年09月05日 ⁄ 综合 ⁄ 共 2212字 ⁄ 字号 评论关闭

利用业余时间做了个Qt版单机斗地主(下载地址:http://download.csdn.net/detail/dowithsmiles/6415855),比较粗糙,还有很大改进空间。不过还是想趁现在把思路总结一下。

一、 牌型判断

斗地主中的牌型有:单张、对子、三个、三带一、三带二、飞机(如555 666)、飞机带两单、飞机带两对、连对(44 55 66...)、顺子(34567...)、炸弹、炸弹带一张、炸弹带一对、炸弹带两单、王炸、双王带一张、双王带一对、双王带两单。应该就这些了,可能某些玩法的牌型会有细小的差别。

我们构造四个数组,分别记录含有一张、两张、三张、四张牌的点数集合(以下都是伪代码):

vector<point> oneArray;
vector<point> twoArray;
vector<point> threeArray;
vector<point> fourArray;

比如有一手牌为55 6 77 888 999 KKKK,那么oneArray 有{ 6 },twoArray有{ 5, 7 },threeArray有{ 8, 9 },fourArray有{ K }。这样一来,牌型是不是很好判断了呢?挑几个例子吧。

oneArray.count == 1 &&
twoArray.isEmpty &&
threeArray.isEmpty &&
fourArray.isEmpty

这是单张。


oneArray.isEmpty &&
twoArray.count == 1 &&
threeArray.count == 1 &&
fourArray.isEmpty

这是三带二。


oneArray.sort(ASC);   // 按点数大小升序排列
oneArray.count == 4 &&
oneArray[0] != oneArray[1] &&
oneArray[2] == Small_Joker &&
oneArray[3] == Big_Joker &&
twoArray.isEmpty &&
threeArray.isEmpty &&
fourArray.isEmpty

这是双王带两单。


oneArray.sort(ASC);   // 按点数大小升序排列
oneArray.count >= 5 &&
oneArray.first >=3 &&
oneArray.last < 2 &&
(oneArray.last - oneArray.first == oneArray.count - 1) &&
twoArray.isEmpty &&
threeArray.isEmpty &&
fourArray.isEmpty

貌似挺复杂,其实很容易理解。看出来了吗?这是顺子。

好了,其他的以此类推吧,所有牌型都可以用这样的方式判断出来。

二、 判断是否大过另一手牌

定义一手牌,光确定牌型不够的。我还引用了两个变量basePoint和extraValue,basePoint为一手牌的主要点数。对于单牌,它为单牌点数;对于三带一,它是三张相同牌的点数;对于顺子和连对,它是最小点数。而extraValue仅用于确定顺子、连对和飞机的长度,下表是一些例子:


牌型 basePoint    extraValue
5 单张 5
0
66    对子 6 0
77722   三带二  7 0
456789   顺子      4 6
33445566    连对 3 4
555666777 飞机  5 3


对于非炸弹的普通牌型,B大过A的条件为

B.type == A.type && B.basePoint > A.basePoint

若A是顺子、连对、飞机,除了上面判断条件外,还有一个条件:B.extraValue == A.extraValue

若A是普通牌型,而B是炸弹或王炸(不带牌),则B可以大过A。

若A、B都是炸弹,B大过A的条件同样为 B.basePoint > A.basePoint

若B是王炸,那么不管A是什么,直接打翻在地。

好了,按这个逻辑去完善吧。


三、洗牌

关于洗牌网上有很多算法可以借鉴。我使用的是最简单的随机抽牌法,就是从原始的牌序列中随机抽出一张,依次放到另一个序列中,原始牌抽完,新序列就是洗好的牌了。


四、出牌策略

如何出牌是最核心的部分,也是主观性最强的部分,每个人都有自己的出牌方式,我说说自己的思路吧。出牌分为主动出牌和接牌两种情况,主动出牌我的大致步骤是:

1. 如果当前只剩一手牌,直接出完获胜。

2. 不是一手牌,则计算是否有较好的顺子,有的话出顺子。

3. 没有顺子的话,挑出飞机打出。

4. 没有飞机,挑出三带打出。

5. 没有三带,挑出连对打出。

6. 没有连对,就老老实实从最小的牌开始打起。

可能有人会问,第2点里,怎样才算“较好的顺子”?下面具体说明一下。

如果我们手上的牌是3 4 5 66 77 88 9 10 J,你会怎么出?大部分人应该都会先出345678,再出6789(10)J,无剩牌,这就是最优的顺子。如何用算法找出这组解呢?我用的是深度优先递归法,即先挑出一个顺子,除去,在剩下的牌中再挑出一个顺子,直到剩牌中无法构成顺子,然后记录顺子和最后的单牌。这样遍历出所有的顺子策略以及它们的剩牌,为了选出最优策略我们要比较一下剩牌的情况,我的计算方式是剩牌越少越好,剩牌一样多的情况下,单牌点数之和越大越好。

接牌策略相对简单,根据上家的牌型,挑出来打就是了,但仍然有许多要顾虑的情况。比如上家打了88,而你手上有AAA,如果上家是对家而且仅剩一张牌,那你肯定会打的。如果上家剩牌,可能你会先不急着去打。类似这样的情况很多,因此策略因人而异,要多做判断。

抱歉!评论已关闭.