CS212 - poker程序

poker程序文档 by huangke

基于 https://www.udacity.com/wiki/cs212/unit-1

Unit 1: 决胜扑克游戏

内容

  1. S212 - Unit 1: Winning Poker Hands

    • 1.1 Outlining the Problem

      • 1.1.1 练习: Representing Hands
    • 1.2 Wild West Poker

      • 1.2.1 练习: Poker 函数
      • 1.2.2 练习: 掌握 Max
      • 1.2.3 练习: 使用 Max
      • 1.2.4 练习: 测试函数
      • 1.2.5 练习: Extreme Values
      • 1.2.6 练习: Hand Rank Attempt
      • 1.2.7 练习: Representing Rank
    • 1.3 Back to Hand Rank

      • 1.3.1 练习: Testing Hand Rank
      • 1.3.2 练习: Writing Hand Rank
      • 1.3.3 练习: Testing Card Rank
      • 1.3.4 练习: Fixing Card Rank
      • 1.3.5 练习: Straight and Flush
      • 1.3.6 练习: Kind Function
      • 1.3.7 练习: Two Pair Function
      • 1.3.8 练习: Making Changes
      • 1.3.9 练习: What to Change
      • 1.3.10 Ace Low Straight
      • 1.3.11 练习: Handling Ties
      • 1.3.12 练习: Allmax
    • 1.4 Deal

      • 1.4.1 练习: Hand Frequencies
      • 1.5 Dimensions of Programming
      • 1.6 Refactoring
      • 1.7 Summary
      • 1.8 Bonus - Shuffling
      • 1.9 Complete Code For Poker Problem
      • 1.10 Complete Code For Homeworks (Warning: Refer this only after submitting homework)

归纳和描述问题

Unit1- 2

写poker程序是一个很好的示例,程序设计从普遍意义上来说主要分为三步:

  1. 理解问题
  2. 准确描述
  3. 设计程序

这个设计过程需要你从一个对问题的模糊理解开始,提炼出正式和准确的问题描述,然后将问题分解为更小的部分,你进一步细化描述你对这些部分的理解,直到可以将其代码化,当设计过程结束后,最后一项工作就是编写代码。

Step 1: 理解问题

从对问题的模糊理解开始. 在这一步我们要列出一个概念清单出来,谈到写一个poker程序,应该从“一手牌”(hand)这个概念开始,a hand表示有5张牌,而且,每张牌有不同的数字和花色。

另一个需要定义的概念是“hand rank”,每一hand 对应的rank,它针对不同的hand返回代表此hand的细节表示

Step 2: 准备描述

明确这个问题应该如何以代码来描述,这个你要明确的主函数叫poker,它应该以一个hands列表作为输入,以highest rank的hand作为输出。

highest hand描述如下:
http://en.wikipedia.org/wiki/List_of_poker_hands

这些规则指明了hands之间的比较规则。

There are three concepts that make up the hand rank:
为了完成hand rank,需要3个概念:

  1. Kind表示有多少张数字一样的牌,'n-kind',n可以为1,2,3,4
  2. Straight表示5张牌数字相连,花色不重要
  3. Flush表示5张牌花色一样,数字不重要

现在你明确了你要处理的数据类型,"hands","ranks","suits",你也知道了他们对应的函数 n-kind, straight, flush,现在我们可以进入第3步了,程序设计阶段

Step 3: Design working code

Unit1-3/

Quiz: Representing Hands

Which of the possible representations for a hand makes the most sense. There may be more than one.

a. ['JS', 'JD', '2S', '2C', '7H']
b. [(11, 'S'), (11, 'D'), (2, 'S'), (2, 'C'), (7, 'H')]
c. set (['JJ', 'JD', '2S', '2C', '7H')])
d. "JS JD 2S 2C 7H"
Wild West Poker

答案:

正确答案: 1、2

set 为什么不行?  因为有百搭牌的存在(大小王)
str 为为什么不行? 因为还有split一次,不直观

Unit1-11/

A poker player will call out his hand when he has to reveal it. With this information you will know how to rank and assign the hand.
一个扑克玩家会在他必须展示牌的时候call out他的牌,由此你可以知道这手牌的rank和其他信息。

"Straight flush, Jack high!"

这手牌的信息:一个straight,最大牌是J.

Here is a ranking table of the possible hands:
这些是所有手牌对应的ranking:

  • 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

当你写程序的时候使用这些手牌对应的数字

"Straight flush, Jack high!" 可以写成下面这样表示:
(8, 11)

Here the eight stands for straight flush and the number 11 stands for the Jack.
这里8代表了straight flush的rank, 11代表了Jack

Here are a few examples of what a player might say and how you would write their hand:
这里是一些手牌的叫法和对应的写法:

Four aces and a queen kicker?
(7, 14, 12)

注意,当你有1对,2对,3张一样的,4张一样的牌的时候,就会有一个单独的参数来表明你拥有的kind。
7 - rank , 14 - ace , 12 - extra

Full house, eights over kings?
(6, 8, 13)	

即使K比8大,但是8有三张,所以8更重要,排前面

Flush, 10-8!

一般来说,只需要最大的前两张牌就可以区分开不同的flush, 但是极端情况下你需要全部5张,从大到小排列以方便比较。

(5, [10, 8, 7, 5, 3])
Straight, Jack high

对于straight,你只需要知道最大的就可以了

(4, 11)
Three sevens!

3-kind 极端情况下需要把另外两张也写出来以分胜负

(3, 7, [7, 7, 7, 5, 2])
Two pairs, Jacks and threes

先写高的一对牌的值,然后是低的一对,然后是所有牌的sorted(list)

(2, 11, 3, [13, 11, 11, 3, 3])
Pair of twos, Jack high

先写对牌数值,再写sorted(list)

(1, 2, [11, 6, 3, 2, 2])

Got nothing

high card, 5张牌排序即可

(0, 7, 5, 4, 3, 2)

Quiz: Poker Function

Unit1-4/

对于一列hands,我们希望poker程序返回 the highest-ranking hand,你知道Python中哪个内建函数返回一个列表中的最大值吗?

Given:

def poker(hands):
        "return the best hand: poker([hand,...]) => hand"
        return ???
 
 答案: max


小测试: Understanding Max

print(max([3, 4, 5, 0]), max([3, 4, -5, 0], key=abs))

答案: 5, 5

Unit1-5/

What will the two max calls return?

def poker(hands):
"Return the best hand: poker ([hand, ...]) => hand"
return max
Quiz: Using Max

Unit1-6/

Assume that you have defined a function hand_rank, which takes a hand as input and returns some sort of a rank. Given this, how would you write the definition of the function poker to return the maximum hand according to the highest ranked?

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

def hand_rank(hand):
return ???

print max([3, 4, 5, 0]), max ([3, 4, -5, 0], key = abs)
Quiz: Testing

Unit1-7/

Modify the test() function to include two new test cases:

  1. four of a kind (fk) vs. full house (fh) returns fk.
  2. full house (fh) vs. full house (fh) returns fh.

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

def test(): "Test cases for the functions in poker program" sf = "6C 7C 8C 9C TC".split() # => ['6C', '7C', '8C', '9C', 'TC'] fk = "9D 9H 9S 9C 7D".split() fh = "TD TC TH 7C 7D".split() assert poker([sf, fk, fh]) == sf assert poker([fk, fh]) == fk assert poker([fh, fh]) == fh

  • Add 2 new assert statements here. The first

  • should check that when fk plays fh, fk

  • is the winner. The second should confirm that

  • fh playing against fh returns fh.

print test()

Quiz: Extreme Values

Unit1-8/

Quiz: Hand Rank Attempt

Unit1-9/

Quiz: Representing Rank

Unit1-10/

Back to Hand Rank

Unit1-12/

Quiz: Testing Hand Rank

Unit1-13/

Quiz: Writing Hand Rank

Unit1-14/

Quiz: Testing Card Rank

Unit1-15/

Quiz: Fixing Card Rank

Unit1-16/

Quiz: Straight and Flush

Unit1-17/

Quiz: Kind Function

Unit1-18/

Quiz: Two Pair Function

Unit1-19/

Quiz: Making Changes

Unit1-20/

Quiz: What to Change

Unit1-21/

Ace Low Straight

Unit1-22/

Quiz: Handling Ties

Unit1-23/

Quiz: Allmax

Unit1-24/

Deal

Unit1-25/

Quiz: Hand Frequencies

Unit1-26/

This is the procedure Peter gave us to calculate hand frequencies:

def hand_percentages(n=7001000):
counts = [0]
9
for i in range(n/10):
for hand in deal(10):
ranking = hand_rank(hand)[0]
counts[ranking] += 1
for i in reversed(range(9)):
print "%15s: %6.3f %%" % (hand_names[i], 100.*counts[i]/n)
Dimensions of Programming

Unit1-27/

Refactoring

Unit1-28/

The two alternative versions of hand_rank that Peter gave in the refactoring class are:

def hand_rank_alt(hand):
"Return a value indicating how high the hand ranks."
# count is the count of each rank; ranks lists corresponding ranks
# E.g. '7 T 7 9 7' => counts = (3, 1, 1) ranks = (7, 10, 9)
groups = group(['--23456789TJQKA'.index(r) for r,s in hand])
counts, ranks = unzip(groups)
if ranks == (14, 5, 4, 3, 2): # Ace low straight
ranks = (5, 4, 3, 2, 1)
straight = len(ranks) == 5 and max(ranks) - min(ranks) == 4
flush = len(set([s for r,s in hand])) == 1
return (9 if (5,) == counts else
8 if straight and flush else
7 if (4, 1) == counts else
6 if (3, 2) == counts else
5 if flush else
4 if straight else
3 if (3, 1, 1) == counts else
2 if (2, 2, 1) == counts else
1 if (2, 1, 1, 1) == counts else
0), ranks

def group(items):
"Return a list of [(count, x), ...], highest count first, then highest x first"
groups = [(items.count(x), x) for x in set(items)]
return sorted(groups, reverse = True)

def unzip(iterable):
"Return a tuple of lists from a list of tuples : e.g. [(2, 9), (2, 7)] => ([2, 2], [9, 7])"
return zip(*iterable)
The table-based lookup version:

count_rankings = {(5,): 10, (4, 1): 7, (3, 2): 6, (3, 1, 1): 3, (2, 2, 1): 2,
(2, 1, 1, 1): 1, (1, 1, 1, 1, 1): 0}

def hand_rank_table(hand):
"Return a value indicating how high the hand ranks."
# count is the count of each rank; ranks lists corresponding ranks
# E.g. '7 T 7 9 7' => counts = (3, 1, 1) ranks = (7, 10, 9)
groups = group(['--23456789TJQKA'.index(r) for r,s in hand])
counts, ranks = unzip(groups)
if ranks == (14, 5, 4, 3, 2): # Ace low straight
ranks = (5, 4, 3, 2, 1)
straight = len(ranks) == 5 and max(ranks) - min(ranks) == 4
flush = len(set([s for r,s in hand])) == 1
return max(count_rankings[counts], 4straight + 5flush), ranks
Summary

Unit1-29/

Bonus - Shuffling

Bad Shuffle/
Shuffle Runtime/
Good Shuffle/
Is it Random/
Testing Shuffles/
Comparing Shuffles/
Computing or Doing/
The shuffling procedures from the bonus videos are:

def shuffle1(p):
n = len(p)
swapped = [False]*n
while not all(swapped):
i, j = random.randrange(n), random.randrange(n)
swap(p, i, j)
swapped[i] = swapped[j] = True

def shuffle2(p):
n = len(p)
swapped = [False]*n
while not all(swapped):
i, j = random.randrange(n), random.randrange(n)
swap(p, i, j)
swapped[i] = True

def shuffle3(p):
n = len(p)
for i in range(n):
swap(p, i, random.randrange(n))

def knuth(p):
n = len(p)
for i in range(n-1):
swap(p, i, random.randrange(i, n))
def swap(p, i, j):
p[i], p[j] = p[j], p[i]
The procedures for testing the different shuffles were:

def test_shuffle(shuffler, deck = 'abcd', n = 10000):
counts = defaultdict(int)
for _ in range(n):
input = list(deck)
shuffler(input)
counts[''.join(input)] += 1
e = n * 1./factorial(len(deck))
ok = all((0.9 <= counts[item]/e <= 1.1) for item in counts)
name = shuffler.name
print '%s(%s) %s' % (name, deck, ('ok' if ok else '*** BAD ***'))
print ' ',
for item, count in sorted(counts.items()):
print "%s:%4.1f" % (item, count * 100. / n),
print

def test_shufflers(shufflers = [knuth, shuffle1, shuffle2, shuffle3], decks = ['abc', 'ab']):
for deck in decks:
print
for f in shufflers:
test_shuffle(f, deck)
def factorial(n):
return 1 if n<= 1 else n * factorial(n-1)
Complete Code For Poker Problem

The complete code given by Peter in this unit including some additional test cases:

! /usr/bin/env python

import random

def poker(hands):
"Return a list of winning hands: poker([hand,...]) => [hand,...]"
return allmax(hands, key=hand_rank)

def allmax(iterable, key=None):
"Return a list of all items equal to the max of the iterable."
iterable.sort(key=key,reverse=True)
result = [iterable[0]]
maxValue = key(iterable[0]) if key else iterable[0]
for value in iterable[1:]:
v = key(value) if key else value
if v == maxValue: result.append(value)
else: break
return result

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

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

def straight(ranks):
"Return True if the ordered ranks form a 5-card straight."
return (max(ranks)-min(ranks) == 4) and len(set(ranks)) == 5

def kind(n, ranks):
"""Return the first rank that this hand has exactly n-of-a-kind 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

def two_pair(ranks):
"If there are two pair here, return the two ranks of the two pairs, else None."
pair = kind(2, ranks)
lowpair = kind(2, list(reversed(ranks)))
if pair and lowpair != pair:
return (pair, lowpair)
else:
return None

def hand_rank(hand):
"Return a value indicating the ranking of a hand."
ranks = card_ranks(hand)
if straight(ranks) and flush(hand):
return (8, max(ranks))
elif kind(4, ranks):
return (7, kind(4, ranks), kind(1, ranks))
elif kind(3, ranks) and kind(2, ranks):
return (6, kind(3, ranks), kind(2, ranks))
elif flush(hand):
return (5, ranks)
elif straight(ranks):
return (4, max(ranks))
elif kind(3, ranks):
return (3, kind(3, ranks), ranks)
elif two_pair(ranks):
return (2, two_pair(ranks), ranks)
elif kind(2, ranks):
return (1, kind(2, ranks), ranks)
else:
return (0, ranks)

def hand_rank_alt(hand):
"Return a value indicating how high the hand ranks."
# count is the count of each rank; ranks lists corresponding ranks
# E.g. '7 T 7 9 7' => counts = (3, 1, 1) ranks = (7, 10, 9)
groups = group(['--23456789TJQKA'.index(r) for r,s in hand])
counts, ranks = unzip(groups)
if ranks == (14, 5, 4, 3, 2): # Ace low straight
ranks = (5, 4, 3, 2, 1)
straight = len(ranks) == 5 and max(ranks) - min(ranks) == 4
flush = len(set([s for r,s in hand])) == 1
return (9 if (5,) == counts else
8 if straight and flush else
7 if (4, 1) == counts else
6 if (3, 2) == counts else
5 if flush else
4 if straight else
3 if (3, 1, 1) == counts else
2 if (2, 2, 1) == counts else
1 if (2, 1, 1, 1) == counts else
0), ranks

count_rankings = {(5,): 10, (4, 1): 7, (3, 2): 6, (3, 1, 1): 3, (2, 2, 1): 2,
(2, 1, 1, 1): 1, (1, 1, 1, 1, 1): 0}

def hand_rank_table(hand):
"Return a value indicating how high the hand ranks."
# count is the count of each rank; ranks lists corresponding ranks
# E.g. '7 T 7 9 7' => counts = (3, 1, 1) ranks = (7, 10, 9)
groups = group(['--23456789TJQKA'.index(r) for r,s in hand])
counts, ranks = unzip(groups)
if ranks == (14, 5, 4, 3, 2): # Ace low straight
ranks = (5, 4, 3, 2, 1)
straight = len(ranks) == 5 and max(ranks) - min(ranks) == 4
flush = len(set([s for r,s in hand])) == 1
return max(count_rankings[counts], 4straight + 5flush), ranks

def group(items):
"Return a list of [(count, x), ...], highest count first, then highest x first"
groups = [(items.count(x), x) for x in set(items)]
return sorted(groups, reverse = True)

def unzip(iterable):
"Return a list of tuples from a list of tuples : e.g. [(2, 9), (2, 7)] => [(2, 2), (9, 7)]"
return zip(*iterable)

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

def deal(numhands, n=5, deck=mydeck):
random.shuffle(mydeck)
return [mydeck[ni:n(i+1)] for i in range(numhands)]

hand_names = ["Straight flush", "Four of a kind", "Full house", "Flush", "Straight",
"Three of a kind", "Two pair", "One pair", "High card"]

def hand_percentages(n=7001000):
counts = [0]
9
for i in range(n/10):
for hand in deal(10):
ranking = hand_rank(hand)[0]
counts[ranking] += 1
for i in reversed(range(9)):
print "%15s: %6.3f %%" % (hand_names[i], 100.*counts[i]/n)

def test():
"Test cases for the functions in poker program."
sf1 = "6C 7C 8C 9C TC".split() # Straight Flush
sf2 = "6D 7D 8D 9D TD".split() # Straight Flush
fk = "9D 9H 9S 9C 7D".split() # Four of a Kind
fh = "TD TC TH 7C 7D".split() # Full House
tp = "5D 2C 2H 9H 5C".split() # Two Pair

# Testing allmax
assert allmax([2,4,7,5,1]) == [7]
assert allmax([2,4,7,5,7]) == [7,7]
assert allmax([2]) == [2]
assert allmax([0,0,0]) == [0,0,0]

# Testing card_ranks
assert card_ranks(sf1) == [10, 9, 8, 7, 6]
assert card_ranks(fk) == [9, 9, 9, 9, 7]
assert card_ranks(fh) == [10, 10, 10, 7, 7]

# Testing flush
assert flush([]) == False
assert flush(sf1) == True
assert flush(fh) == False

# Testing straight
assert straight(card_ranks(sf1)) == True
assert straight(card_ranks(fk)) == False

# Testing kind
assert kind(3, card_ranks(sf1)) == None
assert kind(4, card_ranks(fk)) == 9

# Tesing two pair
assert two_pair(card_ranks(sf1)) == None
assert two_pair(card_ranks(tp)) == (5,2)

# Testing group
assert group([2,3,4,6,2,1,9]) == [(2,2),(1,9),(1,6),(1,4),(1,3),(1,1)]
assert group([8,8,8,8]) == [(4,8)]
assert group([2,6,1]) == [(1,6),(1,2),(1,1)]

# Testing unzip
assert unzip([(2,2),(1,9),(1,6),(1,4),(1,3),(1,1)]) == [(2,1,1,1,1,1),(2,9,6,4,3,1)]
assert unzip([(1,6),(1,2),(1,1)]) == [(1,1,1),(6,2,1)]
assert unzip([(2, 9), (2, 7)]) == [(2, 2), (9, 7)]

# Testing hand rank
assert hand_rank(sf1) == (8,10)
assert hand_rank(fk) == (7,9,7)
assert hand_rank(fh) == (6,10,7)

# Testing hand rank alt
assert hand_rank_alt(sf1) == (8, (10,9,8,7,6))
assert hand_rank_alt(fk) == (7,(9,7))
assert hand_rank_alt(fh) == (6,(10,7))

# Testing hand rank table
assert hand_rank_table(sf1) == (9, (10,9,8,7,6))
assert hand_rank_table(fk) == (7,(9,7))
assert hand_rank_table(fh) == (6,(10,7))

# Testing poker
assert poker([sf1, fk, fh]) == [sf1]
assert poker([fk, fh]) == [fk]
assert poker([fh, fh]) == [fh, fh]
assert poker([fh]) == [fh]
assert poker([sf2] + 99*[fh]) == [sf2]
assert poker([sf1, sf2, fk, fh]) == [sf1, sf2]

return 'tests pass'

Complete Code For Homeworks (Warning: Refer this only after submitting homework)

The complete homework solution code given by Peter in this unit.

Homework 1:

CS 212, hw1-1: 7-card stud

-----------------

User Instructions

Write a function best_hand(hand) that takes a seven

card hand as input and returns the best possible 5

card hand. The itertools library has some functions

that may help you solve this problem.

-----------------

Grading Notes

Muliple correct answers will be accepted in cases

where the best hand is ambiguous (for example, if

you have 4 kings and 3 queens, there are three best

hands: 4 kings along with any of the three queens).

import itertools

def best_hand(hand):
"From a 7-card hand, return the best 5 card hand."
return max(itertools.combinations(hand, 5), key=hand_rank)

------------------

Provided Functions

You may want to use some of the functions which

you have already defined in the unit to write

your best_hand function.

def hand_rank(hand):
"Return a value indicating the ranking of a hand."
ranks = card_ranks(hand)
if straight(ranks) and flush(hand):
return (8, max(ranks))
elif kind(4, ranks):
return (7, kind(4, ranks), kind(1, ranks))
elif kind(3, ranks) and kind(2, ranks):
return (6, kind(3, ranks), kind(2, ranks))
elif flush(hand):
return (5, ranks)
elif straight(ranks):
return (4, max(ranks))
elif kind(3, ranks):
return (3, kind(3, ranks), ranks)
elif two_pair(ranks):
return (2, two_pair(ranks), ranks)
elif kind(2, ranks):
return (1, kind(2, ranks), ranks)
else:
return (0, 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

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

def straight(ranks):
"""Return True if the ordered
ranks form a 5-card straight."""
return (max(ranks)-min(ranks) == 4) and len(set(ranks)) == 5

def kind(n, ranks):
"""Return the first rank that this hand has
exactly n-of-a-kind 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

def two_pair(ranks):
"""If there are two pair here, return the two
ranks of the two pairs, else None."""
pair = kind(2, ranks)
lowpair = kind(2, list(reversed(ranks)))
if pair and lowpair != pair:
return (pair, lowpair)
else:
return None

def test_best_hand():
assert (sorted(best_hand("6C 7C 8C 9C TC 5C JS".split()))
== ['6C', '7C', '8C', '9C', 'TC'])
assert (sorted(best_hand("TD TC TH 7C 7D 8C 8S".split()))
== ['8C', '8S', 'TC', 'TD', 'TH'])
assert (sorted(best_hand("JD TC TH 7C 7D 7S 7H".split()))
== ['7C', '7D', '7H', '7S', 'JD'])
return 'test_best_hand passes'

print test_best_hand()
Homework 2:

CS 212, hw1-2: Jokers Wild

-----------------

User Instructions

Write a function best_wild_hand(hand) that takes as

input a 7-card hand and returns the best 5 card hand.

In this problem, it is possible for a hand to include

jokers. Jokers will be treated as 'wild cards' which

can take any rank or suit of the same color. The

black joker, '?B', can be used as any spade or club

and the red joker, '?R', can be used as any heart

or diamond.

The itertools library may be helpful. Feel free to

define multiple functions if it helps you solve the

problem.

-----------------

Grading Notes

Muliple correct answers will be accepted in cases

where the best hand is ambiguous (for example, if

you have 4 kings and 3 queens, there are three best

hands: 4 kings along with any of the three queens).

import itertools

Deck adds two cards:

'?B': black joker; can be used as any black card (S or C)

'?R': red joker; can be used as any red card (H or D)

allranks = '23456789TJQKA'
redcards = [r+s for r in allranks for s in 'DH']
blackcards = [r+s for r in allranks for s in 'SC']

def best_wild_hand(hand):
"Try all values for jokers in all 5-card selections."
hands = set(best_hand(h)
for h in itertools.product(*map(replacements, hand)))
return max(hands, key=hand_rank)

def replacements(card):
"""Return a list of the possible replacements for a card.
There will be more than 1 only for wild cards."""
if card == '?B': return blackcards
elif card == '?R': return redcards
else: return [card]

def best_hand(hand):
"From a 7-card hand, return the best 5 card hand."
return max(itertools.combinations(hand, 5), key=hand_rank)

def test_best_wild_hand():
assert (sorted(best_wild_hand("6C 7C 8C 9C TC 5C ?B".split()))
== ['7C', '8C', '9C', 'JC', 'TC'])
assert (sorted(best_wild_hand("TD TC 5H 5C 7C ?R ?B".split()))
== ['7C', 'TC', 'TD', 'TH', 'TS'])
assert (sorted(best_wild_hand("JD TC TH 7C 7D 7S 7H".split()))
== ['7C', '7D', '7H', '7S', 'JD'])
return 'test_best_wild_hand passes'

------------------

Provided Functions

You may want to use some of the functions which

you have already defined in the unit to write

your best_hand function.

def hand_rank(hand):
"Return a value indicating the ranking of a hand."
ranks = card_ranks(hand)
if straight(ranks) and flush(hand):
return (8, max(ranks))
elif kind(4, ranks):
return (7, kind(4, ranks), kind(1, ranks))
elif kind(3, ranks) and kind(2, ranks):
return (6, kind(3, ranks), kind(2, ranks))
elif flush(hand):
return (5, ranks)
elif straight(ranks):
return (4, max(ranks))
elif kind(3, ranks):
return (3, kind(3, ranks), ranks)
elif two_pair(ranks):
return (2, two_pair(ranks), ranks)
elif kind(2, ranks):
return (1, kind(2, ranks), ranks)
else:
return (0, 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

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

def straight(ranks):
"""Return True if the ordered
ranks form a 5-card straight."""
return (max(ranks)-min(ranks) == 4) and len(set(ranks)) == 5

def kind(n, ranks):
"""Return the first rank that this hand has
exactly n-of-a-kind 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

def two_pair(ranks):
"""If there are two pair here, return the two
ranks of the two pairs, else None."""
pair = kind(2, ranks)
lowpair = kind(2, list(reversed(ranks)))
if pair and lowpair != pair:
return (pair, lowpair)
else:
return None

print test_best_wild_hand()
This page was last edited on 2014/04/25 08:51:12.

Udacity
POPULAR NANODEGREE PROGRAMS

posted @ 2017-10-21 19:14  云石海涯  阅读(382)  评论(0编辑  收藏  举报