Source: Design of computer programs by Peter Norvig
This is a python implementation of the Texas Hold’em program, and the explanation is actually Peter Norvig
！ However, the rules of poker really confused me… Make some notes here in order to straighten out the logic.
1、 Basic settings
 Hand: hand. There are five playing cards in the number one card.
 Rank & suit. Like one ♦ 5. Suit is a diamond and rank is 5.

Hand rank. There are several types of cards, ranging in size from large to small
 straight flush: flush. Shunzi of the same design and color.
 4kindThere are four cards in the hand with the same number of points.
 full house: three same points + two same points, such as 10.06.
 flushFive in the same suit.
 straightShunzi. For example, 5 6 7 8 9.
 3kindThree cards of the same number in the hand.
 two pairsTwo pairs of cards with the same number of points plus a scatter card.
 pairA pair of cards with the same number of points plus three scattered cards.
 high card: not in accordance with any of the above. The size is determined by the card with the largest number of points. The order of size is a K Q J 109 8 7 6 5 4 3 2.
2、 Procedure subject
 The main procedures are as follows
poker(hands:list)>hand
。poker
You can accept a hand list and return the largest hand. hand_rank(hand)
。hand_rank
You can point out the type of hand. For example, for a pair of ♥ J ♠ J ♦ two ♣ two ♦ 5,hand_rank
It will be pointed out that this is a pairtwo pair。
3、 Questions
(1) Q: the builtin functions of Python include andpoker
Are functions similar in function?
A: Yes,max
。max
Inkey
Parameters allow the mapping of the subjects to be compared, such as usingabs
Change the original value to absolute value. You can use it herepoker
returnmax(hands, key=hand_rank)
Map the hand to the card type. thus,poker
The logic of can be simply expressed as:
def poker(hands):
"return the best hand: poker([hand,...]) => hand"
return max(hands, key=hand_rank)
(2) How to represent a hand（hand
)？
If you use a string to represent a card, the string list is good.["TC","9D","10D","JH","5C"]
In the middle,"TC"
It means Club 10,"JH"
Represents Jack heart.
(3) Q: how to express the card type so that it can be usedmax
Function comparison? For example, thestraight flushThe value is 8,4kind7,high cardThe value is 0. Is that all right?
A: No way.4kindHow to compare (10 10 7 and 9 9 6)? A tuple can be used to represent the card type. For example, 10, 10, 7 is (7,10,7) and 9, 9, 6 is (7,9,6). In Python, meta groups can also be compared with each other! The comparison method is to compare each element in turn. For example, (7,10,7) is greater than (7,9,6), because the first position is 10 > 9.
How to express other cards in a similar way?
 straight flush“Straight flush, Jack high!” which means J is the biggest card in the flush. Know the biggest card, you can compare the size of flush. (8,11) can completely describe the hand.
 4kindFour aces, and a queen killer.
 full house“Full house, eight over kings!” (6,8,13) means three eights with two KS.
 flush: this needs to list all the card points to compare the size. (5, [10,8,7,5,3]) denotes 10,8,7,5,3 hands of the same suit and number of points.
 straight“Straight, Jack high!” this also only needs the hand with the largest number of points to completely compare the size – (4,11).
 3kind(3, 7, [7, 7, 5, 2]) denotes 7, 7, 5, 2.
 two pair(2, 11, 3, [13, 11, 11, 3, 3]) denotes 11, 11, 3, 3, 13.
 pair(1, 2, [11, 6, 3, 2, 2]) denotes 2, 2, 3, 11, 6.
 high card(0,7,5,4,3,2) denotes 7,5,4,3,2.
These rules are represented by codes
def hand_rank(hand):
"Return a value indicating the ranking of a hand"
ranks = card_ Ranks (hand) # ranks refers to all hand points
if straight(ranks) and flush(hand):
return (8, max(ranks))
Elif kind (4, ranks): # that is, there are four cards with the same number of points in the hand
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 two_pair(ranks):
return (2, two_pair(ranks), ranks)
elif kind(2, ranks):
return (1, kind(2, ranks), ranks)
else:
return (0, ranks)
####(4) Q: how to realize itcard_ranks
How many points do you give the hand?
A：card_ranks
Give the number of cards in the hand. In short, just extract the first character of the card string. For example:
def card_ranks(hand):
ranks = [r for r,s in cards]
ranks.sort(reverse=True)
return ranks
But what about “AC”?
The initial idea is this: get a dictionary to store the value of tjqka.
def card_ranks(cards):
"Return a list of the ranks, sorted with higher first."
modifier_dict={
'T':10,
'J':11,
'Q':12,
'K':13,
'A':14
}
def modifier(x):
if x in modifier_dict.keys():
return modifier_dict[x]
else:
return int(x)
ranks = [r for r,s in cards]
ranks = list(map(modifier, ranks))
ranks.sort(reverse=True)
return ranks
It’s over. Then I also thought that this kind of “if you can find the key, return the corresponding value of the key, and if you can’t find it, return another value” is not very similardict.get()
It should be changed to:
def modifier(x):
modifier_dict={
'T':10,
'J':11,
'Q':12,
'K':13,
'A':14
}
return modifier_dict.get(x, int(x))
Well, he can’t. There is no errorint('A')
This kind of operation. Because whether you can find the key or notget
All of them willint
It’s done. You’re smart.
Peter, however, used only one line:
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
Use the position in the list corresponding to the number of hand points, it is very beautiful.
(4) How to judge straight and flush?
In other words, the number of points is five consecutive integers. The more ingenious method is to judge that these five numbers are different from each other, and the maximum minus the minimum is 4.max(ranks)  min(ranks) == 4 and len(set(ranks)) == 5
The same color, that is to say, only one element can be transformed into a set.len(set(suit))==1
This set of rules can not only be used to judge the flush in poker, but also to judge the win or lose of Gobang: if the abscissa of five pieces is the same, and the ordinate is five continuous values, then it is a win.
(5) How to realize the kind (ranks, n) function and give n cards in the hand?
The easiest way is to traverse+ list.count ()。 I think it’s the same. I’ll use it collections.Counter Count it. But there is no need to count all the elements.
def kind(n, ranks):
for r in ranks:
if ranks.count(r) == n:
return r
return None
(6) How to realize two_ Pair, judge whether there are two pairs of cards in the hand, and return the points of the two groups of cards if there are?
My idea: first the opponent card with a kind (ranks, 2), get the return value of R0, and then pop up all the cards with the number of R0 from the list, and then the rest of the list with a kind (ranks, 2), get the return value of R1.
Peter’s idea: the opponent card with a kind (ranks, 2) get R0, the list in reverse order, and then a kind (ranks, 2) get R1, R0 if not equal to R1 is two_ pair。
Well, make full use of the characteristics of data and functions… The characteristic is that kind only returns the number of points with exactly N cards in the first hand, and the ranks are arranged in order.
(7) How to deal with the draw?
poker
Function usesmax
To compare the hand size, butmax
Only the first maximum will be returned, which is unfair in the case of a draw. So we need to achieve our own goalsmax
It can return all the maximum values. It’s not hard:
def allmax(iterable, key=None):
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
(8) How to deal?
We need to give each player a random number of N cards.
Complex writing:
def deal(numhands, n=5, deck=None):
deck = copy(deck) or [r + s for r in '23456789TJQKA' for s in 'SHDC']
hands = []
for num in range(0, numhands):
hand = []
for _ in range(0, n):
card = random.choice(deck)
if deck:
deck.remove(card)
hand.append(card)
hands.append(hand)
return hands
Simple writing:
def deal(numhands, n=5, deck=None):
deck = copy(deck) or [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)]
The following writing method is not only simple in the algorithm implementation, but also more in line with the situation in daily life: who will randomly choose when drawing cards? It must be that the whole card group will be disrupted first, and then each person will give n cards.
4、 Testing
Wikipedia gives the probability of each card type. Among them, the rarest flush probability is 0.0015%, and the most common miscellaneous card probability is 50.11%. We can get the frequency of all the cards by running the program many times. If the result is consistent with that of Wikipedia, then our program is correct. So how many times should we run the program?
Although the more times, the better, but in practice must consider the burden on the machine. The key is to let the rarest card also appear enough times to ensure the robustness of the results. We can expect to make the flush appear about 10 times, then the program should run 10 / 0.0015% times, that is about 700000 times.
5、 Refactoring
hand_rank
Function repeated used many times rank, made the taboo of self repetition: there are four cards with the same number? Oh, No. are there three cards with the same number? Oh, no… it’s better to know what cards you have in your hand and how many cards you have.
def group(items):
groups = [(items.count(x), x) for x in set(items)]
Return sorted (groups, reverse = true) # put the one with the largest number in the front. If the number is the same, put the one with the largest number in the front
def hand_rank(hand):
groups = group(['23456789TJQKA'.index(r) for r,s in hand])
counts, ranks = unzip(groups) #7 10 7 9 7 => (3, 1, 1), (7, 10, 9)
if ranks == (14, 5, 4, 3, 2):
Ranks = (5, 4, 3, 2, 1) # rules in special cases
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
It’s not only more efficient, it’s also a clear demonstration of what Texas Hold’em is all about: it splits the number five in an orderly way.
5 = 4 + 1
= 3 + 2
= 3 + 1 + 1
= 2 + 2 + 1
= 2 + 1 + 1 + 1
= 1 + 1 + 1 + 1 + 1。