poker 遊戲實現 (python)

今天我們來製作poker 這個牌類遊戲

  1. understanding(理解問題)

    Start with a vague understanding that you refine into a problem.

  2. specify (明確問題)

    Specify how this problem can be made amenable to being coded.

  3. design (設計程序)

    Coding


遊戲規則:

所有可能的手牌
        • 0- High Card
        • 1- One Pair
        • 2- Two Pair
        • 3- Three of a Kind
        • 4- Straight
        • 5- Flush
        • 6- Full House
        • 7- Four of a Kind
        • 8- Straight Flush

【注:我們用其對應的數字作爲得分】

詳細遊戲規則自行上維基百科上查看

如何在hands 中存儲我們的手牌,手牌由數字和花色組成(可以採用如下方式):

  sf = ['6C', '7C', '8C', '9C', 'TC'] # Straight Flush
  fk = ['9D', '9H', '9S', '9C' ,'7D'] # Four of a Kind
  fh = ['TD', 'TC', 'TH', '7C', '7D']  #Full House

用何種數據結構來存放這些手牌類型:
這裏寫圖片描述


這裏寫圖片描述


這裏寫圖片描述


這裏寫圖片描述


這裏寫圖片描述


這裏寫圖片描述


這裏寫圖片描述


這裏寫圖片描述


這裏寫圖片描述


我們最終要判斷哪副手牌可以獲勝,所以我們需要一個判斷勝負的函數

Def poker(hands):  
    "return the best hand"

進一步分析我們要判斷大小,那麼我們需要對手牌有一個大小的衡量機制

Def hand_rank(hand): 
    "return  the rank of hand"

有了這個大小的度量機制之後

return max(hands, key=hand_rank)   就能返回 best hand

於是我們得到我們的poker 函數

def poker(hands):
    "Return the best hand: poker([hand,...]) => hand"
    return max(hands, key=hand_rank)

接下來我們要完成hand_rank() 函數,因爲hand_rank 函數涉及到手牌的類型,所以我們要完成對手牌類型的判斷

Straight

straight(ranks):
returns True if the hand is a straight.

Flush

flush(hand):
returns True if the hand is a flush

Kind

kind(n, ranks):
returns the first rank that the hand has exactly n of. For A hand with 4 sevens this function would return 7.

two_pair

two_pair(ranks):
if there is a two pair, this function returns their corresponding ranks as a tuple. For example, a hand with 2 twos and 2 fours would cause this function to return (4, 2).

card_ranks

card_ranks(hand):
returns an ORDERED tuple of the ranks in a hand (where the order goes from highest to lowest rank).

假定以上函數已經完成,那麼我們的hand_rank 函數如下

    def hand_rank(hand):
        ranks = card_ranks(hand)
        if straight(ranks) and flush(hand):            # straight flush
            return (8, max(ranks))
        elif kind(4, ranks):                           # 4 of a kind
            return (7, kind(4, ranks), kind(1, ranks))
        elif kind(3, ranks) and kind(2, ranks):        # full house
            return (6,kind(3, ranks),kind(2, ranks))
        elif flush(hand):                              # flush
            return (5,ranks)
        elif straight(ranks):                          # straight
            return (4,max(ranks))
        elif kind(3, ranks):                           # 3 of a kind
            return (3,kind(3,ranks),ranks)
        elif two_pair(ranks):                          # 2 pair
            return (2,two_pair(ranks),ranks)
        elif kind(2, ranks):                           # kind
            return (1,kind(2,ranks),ranks)
        else:                                          # high card
            return (0,ranks)

爲了檢驗我們的程序是否成功,我們常常需要做測試,例如可以通過assert 來檢驗,樣例代碼如下

    def test():
        "Test cases for the functions in poker program"
        sf = "6C 7C 8C 9C TC".split() # Straight Flush
        fk = "9D 9H 9S 9C 7D".split() # Four of a Kind
        fh = "TD TC TH 7C 7D".split() # Full House
        assert poker([sf, fk, fh]) == sf
        assert poker([fk, fh]) == fk
        assert poker([fh, fh]) == fh
        assert poker([sf]) == sf
        assert poker([sf] + 99*[fh]) == sf
        assert hand_rank(sf) == (8, 10)
        assert hand_rank(fk) == (7, 9, 7)
        assert hand_rank(fh) == (6, 10, 7)
        return 'tests pass'

接下來我們來具體實現判斷手牌類型的函數

Straight

def straight(ranks):
    "Return True if the ordered ranks form a 5-card straight."
    min_num = ranks[0]
    List = [min_num - i for i in range(len(ranks))]
    if List == ranks:
        return True
    else:
        return False

def straight(ranks):
    "return true if the ordered ranks from a 5-card straight"
    return (max(ranks)-min(ranks) == 4) and len(set(ranks)) == 5

Flush

def flush(hand):
    "Return True if all the cards have the same suit."
    suit = [s for n,s in hand]
    if [suit[0]]*len(suit) == suit:
        return True
    else:
        return False

def flush(hand):
    "return true if all the cards have the same suit"
    suits = [s for r,s in hand]

return len(set(suits)) ==1

Kind

def kind(n, ranks):
    """Return the first rank that this hand has exactly n of.
    Return None if there is no n-of-a-kind in the hand."""
    from collections import Counter
    c = Counter(ranks)
    for i in set(ranks):
        if c.get(i) == n:
            return i
            break

    return None

def kind(n,ranks):
    "Return the first rank that this hand has exactly n of.Return None if there is no n-of-a-kind in the hand."
    for r in ranks :
        if ranks.count(r) == n : return r

    return None

two_pair

def two_pair(ranks):
    """If there are two pair, return the two ranks as a
    tuple: (highest, lowest); otherwise return None."""
    pair=[]
    for i in set(ranks):
        if ranks.count(i) == 2 : pair.append(i)
    if len(pair) == 2:
        return tuple(sorted(pair,reverse = True))
    else:
        return None

def two_pair(ranks):
     "If there are two pair, return the two ranks as a
    tuple: (highest, lowest); otherwise return None."
    pair = kind(2,ranks)
    lowpair = kind(2,list(reversed(ranks)))
    if pair and lowpair ! = pair
        return (pair , lowpair)
    else:
        return None

card_ranks

def card_ranks(cards):
    "Return a list of the ranks, sorted with higher first."
    def map(r):
        if r =='T':
            return 10
        elif r == 'J':
            return 11
        elif r == 'Q':
            return 12
        elif r == 'K':
            return 13
        elif r == 'A':
            return 14
        else:
            return int(r)
    ranks = [map(r) for r,s in cards]
    ranks.sort(reverse=True)

    return ranks

def card_ranks(hand):
    "return a list of the ranks , sorted with higher first"
    ranks = ['--23456789TJQKA'.index(r) for r,s in hand]
    ranks.sort(reverse = True)

return ranks

注意到,我們希望A12345 也是straight ,故我們需要對代碼進行修改

讀者可以考慮下,是修改straight 函數呢還是修改card_ranks 函數

我們修改card_ranks 函數得到

def card_ranks(hand):
    "return a list of the ranks , sorted with higher first"
    ranks = ['--23456789TJQKA'.index(r) for r,s in hand]
    ranks.sort(reverse = True)
    return [5,4,3,2,1] if (ranks = [14,5,4,3,2]) else ranks

考慮到可能有多個一樣的最大,寫allmax 函數同時返回多個相同的最大

def allmax(iterable ,key = None):
    "return a list of all items equal to the max of the iterable"
    result , maxval =[],None
    key = key or (lambda: x:x)
    for x in iterable:
        xval = key(x)
        if not result or xval > maxval:
            result , maxval = [x] , xval
        elif xval == maxval:
            result.append(x)
    return result

於是我們修改poker 函數得到

def poker(hands):
    "Return the best hand: poker([hand,...]) => hand"
    return allmax(hands, key=hand_rank)

有了規則之後,我們就要進行洗牌和發牌了
首先我們構建一副牌

mydeck = [r+s for r in '23456789TJQKA' for s in 'SHDC'] 

然後考慮發牌規則和參賽人數(numhands),poker 默認每人5張牌

def deal(numhands, n=5, deck=mydeck):
    random.shuffle(deck)
    hands=[[' ']*n]*numhands
    for i in range(numhands):
        for j in range(n):
            if len(deck)>0:
                hands[i][j]=deck.pop(0)
            else:
                deck=mydeck
                random.shuffle(deck)
    return hands

def deal(numhands , n=5, deck = [r+s for r in '23456789TJQKA' for s in 'SHDC']):
    random.shuffle(deck)
    return [deck[n*i:n*(i+1)] for i in range(numhands)]

# only one deck

遊戲的組建全部完成,我們可以開始開心地遊戲了

In [38]: hands = deal(5)  

In [40]: hands
Out[40]: 
[['TS', '7H', '8D', 'AH', 'TC'],
 ['JS', 'QH', 'QD', '3C', 'KS'],
 ['9S', '8H', 'QS', '2S', '8C'],
 ['2H', '7D', '9D', 'KD', '5H'],
 ['JH', '2D', '3H', '9H', '6D']

In [39]: poker(hands)
Out[39]: [['JS', 'QH', 'QD', '3C', 'KS']

# have a look
In [41]: for h in hands:
    ...:     print hand_rank(h)
    ...:     
(1, 10, [14, 10, 10, 8, 7])
(1, 12, [13, 12, 12, 11, 3])
(1, 8, [12, 9, 8, 8, 2])
(0, [13, 9, 7, 5, 2])
(0, [11, 9, 6, 3, 2]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章