euler/python/e054.py

139 lines
3.7 KiB
Python

def suit_to_value(suit):
return {
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"T": 10,
"J": 11,
"Q": 12,
"K": 13,
"A": 14,
}[suit]
assert(suit_to_value('K') == 13)
def is_flush(colors):
first_color = colors[0]
for color in colors:
if color != first_color:
return False
return True
assert(is_flush(["H", "H", "H", "H", "H"]))
assert(is_flush(["H", "H", "D", "H", "H"]) is False)
def is_straight(suits):
suits = sorted(suits)
first_suit = suits[0]
for suit in suits[1:]:
if first_suit + 1 != suit:
return False
first_suit = suit
return True
assert(is_straight([6, 3, 4, 5, 7]))
assert(is_straight([6, 3, 4, 5, 8]) is False)
def get_numbered_groups(ns):
""" Takes [0, 3, 0, 3, 1] and returns
[(2, 3), (2, 0), (1, 1)]
"""
rs = []
current_group = []
for n in sorted(ns, reverse=True):
if not current_group or n in current_group:
current_group.append(n)
else:
rs.append(current_group)
current_group = [n]
rs.append(current_group)
rs = sorted([(len(r), r[0]) for r in rs], reverse=True)
return rs
assert(get_numbered_groups([0, 3, 0, 3, 1]) == [(2, 3), (2, 0), (1, 1)])
def rank(hand):
""" A hand must be provided as a list of two letter strings.
The first letter is the suit and the second the suit of a card.
The function returns a tuple. The first value represents the hand as
an integer where 0 means high card and 9 means Straight Flush. The
second value is a list of integers ranking the value of the respective
rank. For example, a Royal Flush would be (9, [14, 13, 12, 11, 10]),
while 22aqj would be (1, [2, 2, 14, 12, 11]). By doing this we can
simply compare to hands by first comparing the rank itself and then
the list of integers.
We get something like ["5H", "6S", "7S", "5C", "KD"].
"""
suits, colors = zip(*(map(lambda s: (s[0], s[1]), hand)))
suits = sorted(map(suit_to_value, suits))
flush = is_flush(colors)
straight = is_straight(suits)
numbered_suits = get_numbered_groups(suits)
if flush and straight:
return [8, numbered_suits]
if flush:
return [5, numbered_suits]
if straight:
return [4, numbered_suits]
if numbered_suits[0][0] == 4:
return [7, numbered_suits]
if numbered_suits[0][0] == 3 and numbered_suits[1][0] == 2:
return [6, numbered_suits]
if numbered_suits[0][0] == 3:
return [3, numbered_suits]
if numbered_suits[0][0] == 2 and numbered_suits[1][0] == 2:
return [2, numbered_suits]
if numbered_suits[0][0] == 2:
return [1, numbered_suits]
return [0, numbered_suits]
def read_hands():
""" Reads a list of tuples where each field
in the tuple represents a hand.
"""
hands = []
with open("../txt/e054.txt", "r") as f:
for line in f.readlines():
cards = line.strip().split(" ")
hands.append((cards[:5], cards[5:]))
return hands
def euler_054():
p1_wins = 0
for p1_hand, p2_hand in read_hands():
if rank(p1_hand) > rank(p2_hand):
p1_wins += 1
# msg = "P1 hand {} wins over P2 hand {}."
# print(msg.format(p1_hand, p2_hand))
else:
pass
# msg = "P1 hand {} loses versus P2 hand {}."
# print(msg.format(p1_hand, p2_hand))
return p1_wins
if __name__ == "__main__":
print("e054.py: " + str(euler_054()))
assert(euler_054() == 376)