In the card game poker, a hand consists of five cards and are ranked, from lowest to highest, in the following way:
The cards are valued in the order:
2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King, Ace.
If two players have the same ranked hands then the rank made up of the highest value wins; for example, a pair of eights beats a pair of fives (see example 1 below). But if two ranks tie, for example, both players have a pair of queens, then highest cards in each hand are compared (see example 4 below); if the highest cards tie then the next highest cards are compared, and so on.
Consider the following five hands dealt to two players:
Hand | Player 1 | Player 2 | Winner | |||
1 | 5H 5C 6S 7S KD Pair of Fives | 2C 3S 8S 8D TD Pair of Eights | Player 2 | |||
2 | 5D 8C 9S JS AC Highest card Ace | 2C 5C 7D 8S QH Highest card Queen | Player 1 | |||
3 | 2D 9C AS AH AC Three Aces | 3D 6D 7D TD QD Flush with Diamonds | Player 2 | |||
4 | 4D 6S 9H QH QC Pair of Queens Highest card Nine | 3D 6D 7H QD QS Pair of Queens Highest card Seven | Player 1 | |||
5 | 2H 2D 4C 4D 4S Full House With Three Fours | 3C 3D 3S 9S 9D Full House with Three Threes | Player 1 |
The file, poker.txt, contains one-thousand random hands dealt to two players. Each line of the file contains ten cards (separated by a single space): the first five are Player 1's cards and the last five are Player 2's cards. You can assume that all hands are valid (no invalid characters or repeated cards), each player's hand is in no specific order, and in each hand there is a clear winner.
How many hands does Player 1 win?
Quite direct solution. Just be patient.
From highest to lowest, check if a certain rank pattern exists in player's hand cards.
from urllib.request import urlopen
from collections import Counter
with urlopen('https://projecteuler.net/project/resources/p054_poker.txt') as f:
resp = f.read().decode('utf-8')
def transform_value(v):
if '2' <= v <= '9':
return int(v)
else:
if v == 'T':
return 10
elif v == 'J':
return 11
elif v == 'Q':
return 12
elif v == 'K':
return 13
elif v == 'A':
return 14
else:
raise ValueError('Wrong representation')
[transform_value(v) for v in list(map(str, range(2, 10))) + ['T', 'J', 'Q', 'K', 'A']]
def transform_hand(hand):
values = list(map(transform_value, [c[0] for c in hand]))
suits = [c[1] for c in hand]
return sorted(zip(values, suits))
transform_hand(['5H', '5C', '6S', '7S', 'KD'])
def split_hand(hand):
cards = hand.split(' ')
p1, p2 = cards[:5], cards[-5:]
return transform_hand(p1), transform_hand(p2)
split_hand('5H 5C 6S 7S KD 2C 3S 8S 8D TD')
def generate_hand(text):
hands = text.splitlines()
for hand in hands:
yield split_hand(hand)
def get_royal_flush(hand):
if hand[0][0] == 10 and hand[-1][0] == 14 and \
len([c[0] for c in hand]) == len(set([c[0] for c in hand])) and \
len(set([c[1] for c in hand])) == 1:
return 'BOOM'
else:
return None
get_royal_flush([(10, 'C'), (11, 'C'), (12, 'C'), (13, 'C'), (14, 'C')])
get_royal_flush([(10, 'C'), (11, 'C'), (12, 'C'), (12, 'C'), (14, 'C')])
def get_straight_flush(hand):
if len([c[0] for c in hand]) == len(set([c[0] for c in hand])) and \
hand[-1][0] - hand[0][0] == 4 and \
len(set([c[1] for c in hand])) == 1:
return hand[0][0]
else:
return None
get_straight_flush([(2, 'C'), (3, 'C'), (4, 'C'), (5, 'C'), (6, 'C')])
get_straight_flush([(2, 'C'), (3, 'C'), (4, 'C'), (5, 'C'), (10, 'C')])
def get_a_kind(hand):
c = Counter([c[0] for c in hand])
vk = [(v, k) for k, v in c.items()]
return sorted(vk, reverse=True)
get_a_kind([(2, 'C'), (2, 'H'), (2, 'S'), (3, 'C'), (4, 'C')])
get_a_kind([(2, 'C'), (2, 'H'), (2, 'S'), (3, 'C'), (3, 'H')])
get_a_kind([(2, 'C'), (2, 'H'), (3, 'S'), (3, 'C'), (4, 'C')])
get_a_kind([(2, 'C'), (2, 'H'), (5, 'S'), (3, 'C'), (4, 'C')])
get_a_kind([(2, 'C'), (10, 'H'), (11, 'S'), (3, 'C'), (4, 'C')])
def get_four_of_a_kind(hand):
h = get_a_kind(hand)
if h[0][0] == 4:
return h
else:
return None
def get_full_house(hand):
h = get_a_kind(hand)
if h[0][0] == 3 and h[1][0] == 2:
return h
else:
return None
def get_flush(hand):
h = get_a_kind(hand)
if len(set([c[1] for c in hand])) == 1:
return h
else:
return None
def get_straight(hand):
if len([c[0] for c in hand]) == len(set([c[0] for c in hand])) and \
hand[-1][0] - hand[0][0] == 4:
return hand[0][0]
else:
return None
def get_three_of_a_kind(hand):
h = get_a_kind(hand)
if h[0][0] == 3:
return h
else:
return None
def get_two_pairs(hand):
h = get_a_kind(hand)
if h[0][0] == 2 and h[1][0] == 2:
return h
else:
return None
def get_others(hand):
return get_a_kind(hand)
check_ranks = [get_royal_flush, get_straight_flush, get_four_of_a_kind,
get_full_house, get_flush, get_straight,
get_three_of_a_kind, get_two_pairs, get_others]
def solve():
p1_win_cnt = 0
p2_win_cnt = 0
for p1, p2 in generate_hand(resp):
p1_result = [rank(p1) for rank in check_ranks]
p2_result = [rank(p2) for rank in check_ranks]
for r1, r2 in zip(p1_result, p2_result):
if r1 is None and r2 is None:
continue
elif r1 and r2 is None:
p1_win_cnt += 1
elif r2 and r1 is None:
p2_win_cnt += 1
elif r1 > r2:
p1_win_cnt += 1
elif r2 > r1:
p2_win_cnt += 1
else:
raise ValueError('A tie occurs')
break
assert p1_win_cnt + p2_win_cnt == 1000
return p1_win_cnt
solve()