Skip to content

Instantly share code, notes, and snippets.

@noel-yap
Last active August 25, 2020 18:16
Show Gist options
  • Save noel-yap/b6ed2c6cb3825957679a3501443051b4 to your computer and use it in GitHub Desktop.
Save noel-yap/b6ed2c6cb3825957679a3501443051b4 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
from collections import Counter
from enum import Enum
from functools import total_ordering
from typing import List, TextIO, Dict
@total_ordering
class Card:
face: str
suit: str
def __init__(self, card: str):
self.face = card[0]
self.suit = card[1]
def __eq__(self, other):
return self.faceValue() == other.faceValue()
def __ne__(self, other):
return not (self == other)
def __lt__(self, other):
return self.faceValue() < other.faceValue()
def __str__(self):
return self.face + {
'C': '♣',
'H': '♥',
'D': '♦',
'S': '♠'
}.get(self.suit)
def __repr__(self):
return str(self)
def faceValue(self) -> int:
return {f: i for i, f in enumerate('23456789TJQKA', 2)}[self.face]
class CategoryPredicates:
@staticmethod
def is_flush(hand) -> bool:
first_suit = hand.cards[0].suit
return all(first_suit == hand.cards[ordinal].suit for ordinal in range(1, 5))
@staticmethod
def is_straight(hand) -> bool:
first_face_value = hand.cards[0].faceValue()
return all(hand.cards[ordinal].faceValue() == first_face_value + ordinal for ordinal in range(1, 5)) \
or all(hand.cards[ordinal].faceValue() == ordinal + 2 for ordinal in range(0, 4)) \
and hand.cards[4].faceValue() == 14
@staticmethod
def is_four_of_a_kind(hand) -> bool:
return 4 in hand.face_count.values()
@staticmethod
def is_full_house(hand) -> bool:
return 3 in hand.face_count.values() and 2 in hand.face_count.values()
@staticmethod
def is_three_of_a_kind(hand):
return 3 in hand.face_count.values() and 1 in hand.face_count.values()
@staticmethod
def is_two_pair(hand):
return list(hand.face_count.values()).count(2) == 2
@staticmethod
def is_one_pair(hand):
return list(hand.face_count.values()).count(2) == 1 and list(hand.face_count.values()).count(1) == 3
@staticmethod
def is_high_card(hand):
return len(hand.face_count) == 5
@total_ordering
class Category(Enum):
STRAIGHT_FLUSH = (9, lambda hand: CategoryPredicates.is_flush(hand) and CategoryPredicates.is_straight(hand))
FOUR_OF_A_KIND = (8, CategoryPredicates.is_four_of_a_kind)
FULL_HOUSE = (7, CategoryPredicates.is_full_house)
FLUSH = (6, CategoryPredicates.is_flush)
STRAIGHT = (5, CategoryPredicates.is_straight)
THREE_OF_A_KIND = (4, CategoryPredicates.is_three_of_a_kind)
TWO_PAIR = (3, CategoryPredicates.is_two_pair)
ONE_PAIR = (2, CategoryPredicates.is_one_pair)
HIGH_CARD = (1, CategoryPredicates.is_high_card)
def __eq__(self, other):
return self.value[0] == other.value[0]
def __ne__(self, other):
return not (self == other)
def __lt__(self, other):
return self.value[0] < other.value[0]
@staticmethod
def category(hand):
for c in list(Category):
if c.value[1](hand):
return c
else:
raise ValueError
@total_ordering
class Hand:
cards: List[Card]
face_count: Dict[int, int]
reverse_face_count: Dict[int, List[int]]
def __init__(self, cards: List[Card]):
self.cards = sorted(cards)
self.face_count = Counter([c.faceValue() for c in cards])
self.reverse_face_count = {}
for k, v in self.face_count.items():
self.reverse_face_count[v] = sorted(self.reverse_face_count.get(v, []) + [k], reverse=True)
def __str__(self):
return str(self.cards)
def __repr__(self):
return str(self)
def __eq__(self, other):
return self.category() == other.category() \
and all(self.highest_card(ordinal) == other.highest_card(ordinal) for ordinal in range(0, 5))
def __ne__(self, other):
return not (self == other)
def __lt__(self, other):
if self.category() < other.category():
return True
elif self.category() > other.category():
return False
else:
for ordinal in range(5):
if self.highest_card(ordinal) < other.highest_card(ordinal):
return True
elif self.highest_card(ordinal) > other.highest_card(ordinal):
return False
else:
return False
def highest_card(self, ordinal: int):
keys = self.reverse_face_count.keys()
return self.reverse_face_count[sorted(keys, reverse=True)[min(ordinal, len(keys))]][0]
def category(self) -> Category:
return Category.category(self)
def read_cards(line: str) -> List[Card]:
return [Card(c) for c in line.split(' ')]
try:
poker_hands: TextIO = open("poker.txt")
lines = [line.rstrip() for line in poker_hands.readlines()]
cards = [read_cards(line) for line in lines]
hands = [[Hand(c[:5]), Hand(c[5:])] for c in cards]
# print(hands)
print(sum(hand[0] > hand[1] for hand in hands))
finally:
poker_hands.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment