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)