斗地主最佳出牌算法解析


斗地主是一种广泛流行的纸牌游戏,其规则简单易懂,容易上手。在斗地主中,出牌策略对胜负至关重要,因此设计出一种最佳出牌算法可以极大提高胜率。

一、卡牌分析

首先,要实现最佳出牌算法,我们需要对斗地主的卡牌进行分析。斗地主使用54张牌,分为三种类型,包括17张单张,15张对子,13张三带一,14张炸弹和一张大小王。其中,三带一可以分为三带一单或者三带一对,因此其类型可以多达28种。

在分析卡牌的类型之后,我们需要对每种类型的卡牌进行权值赋予,以方便后续的计算。一般情况下,可以将大小王赋予最高的权值,炸弹次之,三带一的权值要高于对子和单张,但是不高于炸弹。

二、出牌策略

接下来,我们需要考虑的是出牌的策略。对于斗地主的出牌策略,可以从以下几个方面进行考虑。

1.单牌出牌策略

单牌出牌的策略一般是优先出所剩数量最少的单牌,以便更好的组合对子和三带一等牌型,提高下一轮出牌的胜率。

class SingleCardPolicy(object):
    def __init__(self):
        pass
    
    def put_cards(self, hand_cards, last_cards):
        cards, _ = get_all_single_card(hand_cards)
        if not cards:
            return None
        if not last_cards:
            return cards[-1]
        for c in cards:
            if c > last_cards[0]:
                return c
        return None

2.对子出牌策略

对于对子出牌,可以优先出现张较大的对子,避免留下不够成对的单牌,提高下一轮出牌的胜率。

class DoubleCardPolicy(object):
    def __init__(self):
        pass
    
    def put_cards(self, hand_cards, last_cards):
        cards, _ = get_all_double_card(hand_cards)
        if not cards:
            return None
        if not last_cards:
            return cards[-1]
        for c in cards:
            if c > last_cards[0]:
                return c
        return None

3.三带一出牌策略

对于三带一出牌,可以分为三带一单和三带一对两种情况。一般应该优先出现三个加大的牌,然后再选择单/双带牌(可加权选择出最佳带牌),以便增加下一轮出牌的组合选择性。

class ThreeOneCardPolicy(object):
    def __init__(self):
        pass
    
    def put_cards(self, hand_cards, last_cards):
        cards = get_all_three_one_card(hand_cards)
        if not cards:
            return None
        if not last_cards:
            return cards[-1]
        for c in cards:
            if c > last_cards[0]:
                return c
        return None

4.炸弹出牌策略

炸弹出牌的策略则非常简单,只需要在手中拥有炸弹的情况下,优先出牌。炸弹的权值比较高,可以在牌局中起到扭转局面的作用。

class BombCardPolicy(object):
    def __init__(self):
        pass
    
    def put_cards(self, hand_cards, last_cards):
        cards = get_all_bomb_card(hand_cards)
        if not cards:
            return None
        if not last_cards:
            return cards[-1]
        for c in cards:
            if c > last_cards[0]:
                return c
        return None

三、出牌策略的实现

最佳出牌策略应该能够利用上一轮出牌的信息,进行智能的出牌选择,同时避免拖延时间。我们可以按照下面的策略进行选择:

  • 1.如果是自己首次出牌,则选择最小的单牌出牌。
  • 2.如果上一家出的牌为炸弹,则出比上一家牌点数小的牌。
  • 3.如果对手出的牌比自己的牌大,则选择出牌策略中能排到它前头的牌型。
  • 4.如果上家出的是单牌,则优先选择连牌和飞机等牌型出牌。
  • 5.如果手中牌剩余数量很小,则优先出最小的牌以便更好地组合其他牌型。
  • 6.如果以上策略均不适用,则出牌策略按照单牌,对子,三带一和炸弹的优先级顺序依次进行出牌。
class BestPolicy(object):
    def __init__(self):
        self.single_policy=SingleCardPolicy()
        self.double_policy=DoubleCardPolicy()
        self.three_one_policy=ThreeOneCardPolicy()
        self.bomb_policy=BombCardPolicy()

    def put_cards(self, hand_cards, last_cards):
        if not last_cards:
            return self.single_policy.put_cards(hand_cards, last_cards)
        last_len = len(last_cards)
        if is_bomb(last_cards):
            return self.get_min_card(hand_cards, last_cards)
        index = get_card_index(last_cards[0])
        if last_len == 1:
            if index == 0 or index == 1:
                return self.get_min_card(hand_cards, last_cards)
            return self.double_policy.put_cards(hand_cards, last_cards)
        if last_len == 2:
            return self.double_policy.put_cards(hand_cards, last_cards)
        if last_len == 3:
            return self.get_policy(last_cards, hand_cards,
                                   self.three_one_policy, 
                                   self.get_min_card)
        if last_len == 4:
            if is_bomb(last_cards):
                return self.get_min_card(hand_cards, last_cards)
            return self.get_policy(last_cards, hand_cards,
                                   self.three_one_policy, 
                                   self.bomb_policy,
                                   self.get_min_card)
        if last_len == 5:
            if is_seq(last_cards) or is_plane(last_cards):
                return self.get_policy(last_cards, hand_cards,
                                       self.three_one_policy,
                                       self.get_min_card)
        if last_len == 6:
            if is_seq(last_cards) or is_plane(last_cards):
                if get_card_index(last_cards[-1] - 1) == index:
                    return self.three_one_policy.put_cards(
                        hand_cards, last_cards)
                return self.get_policy(last_cards, hand_cards,
                                       self.three_one_policy,
                                       self.get_min_card)
        return self.get_policy(last_cards, hand_cards,
                               self.single_policy, 
                               self.double_policy,
                               self.three_one_policy,
                               self.bomb_policy,
                               self.get_min_card)
    
    # 获取策略对应的牌型
    def get_policy(self, last_cards, hand_cards, *policies):
        for policy in policies:
            ret = policy.put_cards(hand_cards, last_cards)
            if ret is not None:
                return ret
        return None

    # 获取能出的最小牌
    def get_min_card(self, hand_cards, last_cards):
        for card in sorted(hand_cards, reverse=True):
            if card > last_cards[0]:
                return card
        return None

四、总结

最佳出牌算法,可以极大提高斗地主的胜率。通过对卡牌类型进行分析,我们可以对每个牌型进行权值赋予,并按照不同的类型编写相应的出牌策略。最后,我们将这些策略进行整合,得到了一套比较智能的出牌算法。

评论关闭