今天我們來製作poker 這個牌類遊戲
understanding(理解問題)
Start with a vague understanding that you refine into a problem.
specify (明確問題)
Specify how this problem can be made amenable to being coded.
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]