用 Python 打撲克牌——炸金花

在學(xué)習(xí)《流暢的Python》一書中,第一節(jié) Python 數(shù)據(jù)類型講解的過程中,實現(xiàn)了一副撲克牌,代碼實現(xiàn)非常簡潔。當(dāng)看完之后,聯(lián)想到生活中我們玩過的炸金花,便著手設(shè)計了程序,用來實現(xiàn)雙人炸金花這種游戲模型。

本程序中主要分為三個對象類,Poker 類(撲克牌),Player 類(玩家),Winner 類(游戲取勝機制)。

一、Poker 類(撲克牌)

Card = collections.namedtuple('Card', ['rank', 'suit'])

class Poker(MutableSequence):
    # 撲克牌的相關(guān)定義
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades hearts diamonds clubs'.split()  # 黑桃,紅桃,方塊,梅花
    suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)#黑桃最大,紅桃次之,方塊再次之,梅花最小

    def __init__(self):
        self._cards = [Card(rank, suit) for rank in self.ranks
                                        for suit in self.suits]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, position):  # 僅僅實現(xiàn)了__getitem__方法,該對象就變成可迭代的
        return self._cards[position]

    def __setitem__(self, position, value):
        self._cards[position] = value

    def __delitem__(self, position):
        del self._cards[position]

    def insert(self, position, value):
        self._cards[position] = value

一副撲克牌有 54 張牌,其中 52 張是正牌,另2張是副牌(大王和小王)。本程序中未涉及到大小王。52 張正牌又均分為 13 張一組,并以黑桃、紅桃、梅花、方塊四種花色表示各組,每組花色的牌包括從 2-10 以及 J、Q、K、A 標(biāo)示的 13 張牌。

二、Player 類(玩家)

Own_Poker = collections.namedtuple('Own_Poker', ['id', 'rank', 'suit', 'score'])

class Player():
    '''
    牌型  豹子:三張同樣大小的牌?! №樈穑夯ㄉ嗤娜龔堖B牌?! 〗鸹ǎ喝龔埢ㄉ嗤呐??!?    順子:三張花色不全相同的連牌?! ψ樱喝龔埮浦杏袃蓮埻瑯哟笮〉呐??! 螐垼撼陨吓菩偷呐啤?    '''

    def __init__(self, id, poker):
        self.id = id
        self.poker = poker  #一副撲克牌
        self.pokers = []    #玩家手中的牌
        self.type = 0  # 每個人初始都假定為三張毫無關(guān)系的牌,也就是撲克牌贏法中的“單張”

    def set_card_score(self, card):
        '''
        按照點數(shù)判定撲克牌的大小
        :param card:撲克牌卡片
        :return:撲克牌點數(shù)大小
        '''
        rank_value = Poker.ranks.index(card.rank)
        suit_values = Poker.suit_values
        return rank_value * len(suit_values) + suit_values[card.suit]

    def sort_card_index(self, rank_index_list):
        '''
        通過值減下標(biāo)的方式分組,如果三個值連續(xù)則被分配到同一個g中
        比如說ll=[3,4,5,7,8],分組時,enumerate(ll)=[(0,3),(1,4),(2,5),(3,7),(4,8)],fun函數(shù)值減下標(biāo),結(jié)果一樣的,就歸為一組
        在本程序中,如果是三張連續(xù)的撲克牌,則應(yīng)該是同一個g中,此時返回為Ture,否則為False
        :param rank_index_list:
        :return:
        '''
        fun = lambda x: x[1] - x[0]
        for k, g in groupby(enumerate(rank_index_list), fun):  # 返回一個產(chǎn)生按照fun進(jìn)行分組后的值集合的迭代器.
            if len([v for i, v in g]) == 3:
                return True
        return False

    def judge_type(self):
        '''
        玩家隨機發(fā)完三張牌后,根據(jù)撲克牌玩法進(jìn)行區(qū)分,對手中的牌進(jìn)行判別屬于哪種類型
        :return:
        '''
        suit_list = []
        rank_list = []
        score_list = []
        for poker in self.pokers:
            suit_list.append(poker.suit)
            rank_list.append(poker.rank)
            score_list.append(poker.score)

        rank_index_list = []  # 撲克牌卡片在Poker中rank中的index
        for rank in rank_list:
            index = self.poker.ranks.index(rank)
            rank_index_list.append(index)

        if len(set(rank_list)) == 1:
            self.type = 5  # 豹子
        elif len(set(suit_list)) == 1:
            if self.sort_card_index(rank_index_list):
                self.type = 4  # 順金
            else:
                self.type = 3  # 金花
        elif self.sort_card_index(rank_index_list):
            self.type = 2  # 順子
        elif len(set(rank_list)) == 2:
            self.type = 1  # 對子

    def play(self):
        self.judge_type()

每位玩家都有一個名稱,用同一副撲克牌,炸金花游戲要求每人手中有三張牌,根據(jù)手中的牌,程序初步判斷屬于哪種牌型,用于后續(xù)游戲取勝機制做判斷。

三、Winner 類(游戲取勝機制)

class Winner():
    def __init__(self, player1, player2):
        self.player1 = player1
        self.player2 = player2

    def get_max_card(self, player):
        '''
        篩選出三張牌中最大的牌,這里返回的是在ranks中的index
        :param player:
        :return:
        '''
        ranks = Poker.ranks
        rank_index_list = []  # 撲克牌卡片在Poker中rank中的index
        for poker in player.pokers:
            index = ranks.index(poker.rank)
            rank_index_list.append(index)
        return max(rank_index_list)

    def get_card_suit(self, player):
        '''
        返回?fù)淇伺苹ㄉ笮?        :param player:
        :return:
        '''
        suit_values = Poker.suit_values
        suit = player.pokers[0].suit
        return suit_values[suit]

    def get_card_value(self, player):
        '''
        當(dāng)牌型是對子的時候,經(jīng)過匹配找出是對子的牌和單個的牌,這里返回的是牌的index,便于比較大小
        :param player:
        :return:
        '''
        ranks = Poker.ranks
        rank_index_dict = {}  # 撲克牌卡片在Poker中rank中的index
        repeat_rank_value = 0  # 成對的兩張撲克牌的大小
        single_rank_value = 0  # 單個的撲克牌的大小
        for poker in player.pokers:
            index = ranks.index(poker.rank)
            if index in rank_index_dict:
                rank_index_dict[index] += 1
            else:
                rank_index_dict[index] = 1
        rank_index_dict = sorted(rank_index_dict.items(), key=lambda d: d[1], reverse=True)
        n = 0
        for key in rank_index_dict:
            if n == 0:
                repeat_rank_value = key
            else:
                single_rank_value = key
            n += 1
        return repeat_rank_value, single_rank_value

    def get_player_score(self, player):
        '''
        當(dāng)牌型為單牌時,計算手中的牌相加后的值大小
        :param player:
        :return:
        '''
        ranks = Poker.ranks
        score = 0
        for poker in player.pokers:
            index = ranks.index(poker.rank)  # 撲克牌卡片在Poker中rank中的index
            score += index
        return score

    def get_winner(self):
        player1, player2 = self.player1, self.player2
        # 先比較玩家手中的牌型,大的勝出,玩牌的規(guī)則暫時不涉及到牌色,如有修改可以在此基礎(chǔ)上調(diào)整
        # 豹子> 順金 > 金花 > 順子 > 對子 > 單張
        if player1.type > player2.type:
            return player1
        elif player1.type < player2.type:
            return player2
        else:  # 當(dāng)玩家雙方手中的牌型一致時,根據(jù)贏法一一判斷
            if player1.type == 5 or player1.type == 4 or player1.type == 2:  # 豹子、順金、順子 規(guī)則說明:按照比點
                if self.get_max_card(player1) > self.get_max_card(player2):
                    return player1
                else:
                    return player2
            elif player1.type == 1:  # 對子 規(guī)則說明:先比較相同兩張的值的大小,誰大誰勝出;如果對子相同,再比較單個
                repeat_rank_value1, single_rank_value1 = self.get_card_value(player1)
                repeat_rank_value2, single_rank_value2 = self.get_card_value(player1)
                if repeat_rank_value1 > repeat_rank_value2:
                    return player1
                elif repeat_rank_value1 < repeat_rank_value2:
                    return player2
                else:
                    if single_rank_value1 > single_rank_value2:
                        return player1
                    elif single_rank_value1 < single_rank_value2:
                        return player2
                    else:
                        return None  # 平局,大家手上的牌一樣大
            else:  # 單牌,金花   規(guī)則:比較所有牌的點數(shù)大小,不區(qū)分牌色
                if self.get_player_score(player1) > self.get_player_score(player2):
                    return player1
                elif self.get_player_score(player1) < self.get_player_score(player2):
                    return player2
                else:
                    return None

由于不是很清楚炸金花的游戲規(guī)則,這里我們采用的是最簡單的游戲規(guī)則。

牌型 豹子:三張同樣大小的牌。順金:花色相同的三張連牌。金花:三張花色相同的牌。 順子:三張花色不全相同的連牌。 對子:三張牌中有兩張同樣大小的牌。單張:除以上牌型的牌。

玩法比較簡單,豹子> 順金 > 金花 > 順子 > 對子 > 單張,當(dāng)牌型不一致的話,誰牌型大誰勝出;當(dāng)牌型一致的時候,又分為三種情況,一是豹子、順金、順子,比較玩家手中牌的最大值,誰擁有最大牌面值誰勝出;二是對子,比較玩家手中對子的牌面大小,如果相同再另行比較;三是金花、單張,比較玩家手中所有牌面大小之和。

除了上述三個對象類外,還需要一個發(fā)牌者(荷官)來主持洗牌和發(fā)牌。

def compare_card(card1, card2):
    '''
    比較兩種撲克牌是否相同
    :param card1:
    :param card2:
    :return: 相同返回為True,否則為False
    '''
    if card1.rank == card2.rank and card1.suit == card2.suit:
        return True
    return False


def dutch_official_work(poker, player1, player2):
    '''
    發(fā)牌人(荷官)給兩位玩家輪替發(fā)牌,發(fā)出去的牌都需要從這副撲克牌中剔除出去
    :param poker: 那一副撲克牌
    :param player1:玩家1
    :param player2:玩家2
    :return:整理后的撲克牌
    '''
    def distribute_card(player):
        card = choice(poker)  # 發(fā)牌
        player.pokers.append(Own_Poker(player.id, card.rank, card.suit, player.set_card_score(card)))
        for i in range(len(poker)):
            if compare_card(card, poker[i]):
                poker.__delitem__(i)
                break

    shuffle(poker)  # 洗牌
    for k in range(3):
        distribute_card(player1)
        distribute_card(player2)

    return poker

總結(jié):多看,多想,多動手。紙上得來終覺淺,絕知此事要躬行。
對了,詳細(xì)代碼可以訪問 https://github.com/Acorn2/fluentPyStudy/blob/master/chapter01/Poker_Demo.py

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 昨天聽了欲罷不能這本書,講述的是關(guān)于上癮的行為產(chǎn)生。其中關(guān)于網(wǎng)絡(luò)上癮對孩子的負(fù)面影響,讓我感覺很可怕,很恐怖。 一...
    一只青柚子閱讀 459評論 2 2
  • 第二十七節(jié) 被逐出宮 中華民國十二年(1923年)的建福宮大火剛剛過去,溥儀就頒下“上諭”把紫禁城里除了少數(shù)太妃身...
    狼2639閱讀 226評論 0 0
  • 昨天我在每天讀點故事看到一篇故事《全世界用謊言來愛我》,女主顧昭爾二十五歲之前的記憶一片空白,她父母告訴她,她曾愛...
    蘭奕閱讀 654評論 0 0
  • 兩個普洱的朋友來到上海,被評選為普洱的男神女神,男神是旅居項目的倡導(dǎo)者,女神是茶文化的傳播者。有幸喝到一杯清茶,聽...
    道簡術(shù)心閱讀 198評論 0 1
  • 雀兒歡悅舞清風(fēng)。嘰喳粉蕊叢。 翩躚只為兩情濃。花開笑靨紅。 時光淡,菀嬌容。疏疏朗朗空。 癡狂廝守一人終。春秋連夏...
    不惑而歌閱讀 508評論 10 27

友情鏈接更多精彩內(nèi)容