513 lines
19 KiB
Python

#!/usr/bin/python
# In the card game poker, a hand consists of five cards and are ranked, from lowest to highest, in the following way:
#
# High Card: Highest value card.
# One Pair: Two cards of the same value.
# Two Pairs: Two different pairs.
# Three of a Kind: Three cards of the same value.
# Straight: All cards are consecutive values.
# Flush: All cards of the same suit.
# Full House: Three of a kind and a pair.
# Four of a Kind: Four cards of the same value.
# Straight Flush: All cards are consecutive values of same suit.
# Royal Flush: Ten, Jack, Queen, King, Ace, in same suit.
# 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 2C 3S 8S 8D TD Player 2
# Pair of Fives Pair of Eights
#
# 2 5D 8C 9S JS AC 2C 5C 7D 8S QH Player 1
# Highest card Ace Highest card Queen
#
# 3 2D 9C AS AH AC 3D 6D 7D TD QD Player 2
# Three Aces Flush with Diamonds
#
# 4 4D 6S 9H QH QC 3D 6D 7H QD QS Player 1
# Pair of Queens Pair of Queens
# Highest card Nine Highest card Seven
#
# 5 2H 2D 4C 4D 4S 3C 3D 3S 9S 9D Player 1
# Full House Full House
# With Three Fours With Three Threes
#
# The file, p054_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?
from __future__ import annotations
import sys
from typing import List, Optional
from enum import IntEnum
from projecteuler import timing
class Value(IntEnum):
Dummy = 0
Two = 1
Three = 2
Four = 3
Five = 4
Six = 5
Seven = 6
Eight = 7
Nine = 8
Ten = 9
Jack = 10
Queen = 11
King = 12
Ace = 13
def next(self):
v = self.value + 1
if v > 13:
raise ValueError('Enumeration ended')
return Value(v)
class Card():
def __init__(self) -> None:
self.value: Value
self.value = Value.Dummy
self.suit: str
self.suit = ''
def __lt__(self, other: Card) -> bool:
return self.value < other.value
def set_card(self, card: str) -> None:
if card[0] == '2':
self.value = Value.Two
elif card[0] == '3':
self.value = Value.Three
elif card[0] == '4':
self.value = Value.Four
elif card[0] == '5':
self.value = Value.Five
elif card[0] == '6':
self.value = Value.Six
elif card[0] == '7':
self.value = Value.Seven
elif card[0] == '8':
self.value = Value.Eight
elif card[0] == '9':
self.value = Value.Nine
elif card[0] == 'T':
self.value = Value.Ten
elif card[0] == 'J':
self.value = Value.Jack
elif card[0] == 'Q':
self.value = Value.Queen
elif card[0] == 'K':
self.value = Value.King
elif card[0] == 'A':
self.value = Value.Ace
self.suit = card[1]
class Hand():
def __init__(self) -> None:
self.cards: List[Card]
self.cards = []
def sort(self) -> None:
self.cards.sort()
class Game():
def __init__(self) -> None:
self.hand1: Optional[Hand]
self.hand1 = None
self.hand2: Optional[Hand]
self.hand2 = None
def play(self) -> int:
# If player 1 has a Royal Flush, player 1 wins.
assert self.hand1 is not None
assert self.hand2 is not None
if self.hand1.cards[4].value == Value.Ace and self.hand1.cards[3].value == Value.King and\
self.hand1.cards[2].value == Value.Queen and self.hand1.cards[1].value == Value.Jack and\
self.hand1.cards[0] == Value.Ten and self.hand1.cards[0].suit == self.hand1.cards[1].suit and\
self.hand1.cards[0].suit == self.hand1.cards[2].suit and\
self.hand1.cards[0].suit == self.hand1.cards[3].suit and\
self.hand1.cards[0].suit == self.hand1.cards[4].suit:
return 1
# If player 2 has a Royal Flush, player 2 wins.
if self.hand2.cards[4].value == Value.Ace and self.hand2.cards[3].value == Value.King and\
self.hand2.cards[2].value == Value.Queen and self.hand2.cards[1].value == Value.Jack and\
self.hand2.cards[0] == Value.Ten and self.hand2.cards[0].suit == self.hand2.cards[1].suit and\
self.hand2.cards[0].suit == self.hand2.cards[2].suit and\
self.hand2.cards[0].suit == self.hand2.cards[3].suit and\
self.hand2.cards[0].suit == self.hand2.cards[4].suit:
return -1
straightflush1 = 0
straightflush2 = 0
# Check if player 1 has a straight flush.
if self.hand1.cards[0].suit == self.hand1.cards[1].suit and self.hand1.cards[0].suit == self.hand1.cards[2].suit and\
self.hand1.cards[0].suit == self.hand1.cards[3].suit and self.hand1.cards[0].suit == self.hand1.cards[4].suit:
value = self.hand1.cards[0].value
straightflush1 = 1
for i in range(1, 5):
value = value.next()
if self.hand1.cards[i].value != value:
straightflush1 = 0
break
# Check if player 2 has a straight flush.
if self.hand2.cards[0].suit == self.hand2.cards[1].suit and self.hand2.cards[0].suit == self.hand2.cards[2].suit and\
self.hand2.cards[0].suit == self.hand2.cards[3].suit and self.hand2.cards[0].suit == self.hand2.cards[4].suit:
value = self.hand2.cards[0].value
straightflush2 = 1
for i in range(1, 5):
value = value.next()
if self.hand2.cards[i].value != value:
straightflush2 = 0
break
# If player 1 has a straight flush and player 2 doesn't, player 1 wins
if straightflush1 and not straightflush2:
return 1
# If player 2 has a straight flush and player 1 doesn't, player 2 wins
if not straightflush1 and straightflush2:
return -1
# If both players have a straight flush, the one with the highest value wins.
# Any card can be compared, because in the straight flush the values are consecutive
# by definition.
if straightflush1 and straightflush2:
if self.hand1.cards[0].value > self.hand2.cards[0].value:
return 1
return -1
four1 = 0
four2 = 0
# Check if player 1 has four of a kind. Since cards are ordered, it is sufficient
# to check if the first card is equal to the fourth or if the second is equal to the fifth.
if self.hand1.cards[0].value == self.hand1.cards[3].value or self.hand1.cards[1].value == self.hand1.cards[4].value:
four1 = 1
# Check if player 2 has four of a kind
if self.hand2.cards[0].value == self.hand2.cards[3].value or self.hand2.cards[1].value == self.hand2.cards[4].value:
four2 = 1
# If player 1 has four of a kind and player 2 doesn't, player 1 wins.
if four1 and not four2:
return 1
# If player 2 has four of a kind and player 1 doesn't, player 2 wins.
if not four1 and four2:
return -1
# If both players have four of a kind, check who has the highest value for those four cards.
if four1 and four2:
if self.hand1.cards[1].value > self.hand2.cards[1].value:
return 1
if self.hand1.cards[1].value < self.hand2.cards[1].value:
return -1
full1 = 0
full2 = 0
# Check if player 1 has a full house.
if self.hand1.cards[0].value == self.hand1.cards[1].value and self.hand1.cards[3].value == self.hand1.cards[4].value and\
(self.hand1.cards[1].value == self.hand1.cards[2].value or self.hand1.cards[2].value == self.hand1.cards[3].value):
full1 = 1
# Check if player 2 has a full house.
if self.hand2.cards[0].value == self.hand2.cards[1].value and self.hand2.cards[3].value == self.hand2.cards[4].value and\
(self.hand2.cards[1].value == self.hand2.cards[2].value or self.hand2.cards[2].value == self.hand2.cards[3].value):
full2 = 1
# If player 1 has a full house and player 2 doesn't, player 1 wins.
if full1 and not full2:
return 1
# If player 2 has a full house and player 1 doesn't, player 2 wins.
if not full1 and full2:
return -1
# If both players have a full house, check who has the highest value for the three equal cards (the third card in the array will be part
# of the set of three).
if full1 and full2:
if self.hand1.cards[2].value > self.hand2.cards[2].value:
return 1
if self.hand1.cards[2].value < self.hand2.cards[2].value:
return -1
flush1 = 0
flush2 = 0
# Check if player 1 has a flush.
if self.hand1.cards[0].suit == self.hand1.cards[1].suit and self.hand1.cards[0].suit == self.hand1.cards[2].suit and\
self.hand1.cards[0].suit == self.hand1.cards[3].suit and self.hand1.cards[0].suit == self.hand1.cards[4].suit:
flush1 = 1
# Check if player 2 has a flush.
if self.hand2.cards[0].suit == self.hand2.cards[1].suit and self.hand2.cards[0].suit == self.hand2.cards[2].suit and\
self.hand2.cards[0].suit == self.hand2.cards[3].suit and self.hand2.cards[0].suit == self.hand2.cards[4].suit:
flush2 = 1
# If player 1 has a flush and player 2 doesn't, player 1 wins.
if flush1 and not flush2:
return 1
# If player 2 has a flush and player 1 doesn't, player 2 wins.
if not flush1 and flush2:
return -1
straight1 = 1
straight2 = 1
# Check if player 1 has a straight.
value = self.hand1.cards[0].value
for i in range(1, 5):
value = value.next()
if self.hand1.cards[i].value != value:
straight1 = 0
break
# Check if player 2 has a straight.
value = self.hand2.cards[0].value
for i in range(1, 5):
value = value.next()
if self.hand2.cards[i].value != value:
straight2 = 0
break
# If player 1 has a straight and player 2 doesn't, player 1 wins.
if straight1 and not straight2:
return 1
# If player 2 has a straight and player 1 doesn't, player 2 wins.
if not straight1 and straight2:
return -1
three1 = 0
three2 = 0
# Check if player 1 has three of a kind.
if (self.hand1.cards[0].value == self.hand1.cards[1].value and self.hand1.cards[0].value == self.hand1.cards[2].value) or\
(self.hand1.cards[1].value == self.hand1.cards[2].value and self.hand1.cards[1].value == self.hand1.cards[3].value) or\
(self.hand1.cards[2].value == self.hand1.cards[3].value and self.hand1.cards[2].value == self.hand1.cards[4].value):
three1 = 1
# Check if player 2 has three of a kind.
if (self.hand2.cards[0].value == self.hand2.cards[1].value and self.hand2.cards[0].value == self.hand2.cards[2].value) or\
(self.hand2.cards[1].value == self.hand2.cards[2].value and self.hand2.cards[1].value == self.hand2.cards[3].value) or\
(self.hand2.cards[2].value == self.hand2.cards[3].value and self.hand2.cards[2].value == self.hand2.cards[4].value):
three2 = 1
# If player 1 has three of a kind and player 2 doesn't, player 1 wins.
if three1 and not three2:
return 1
# If player 2 has three of a kind and player 1 doesn't, player 2 wins.
if not three1 and three2:
return -1
if three1 and three2:
if self.hand1.cards[2].value > self.hand2.cards[2].value:
return 1
if self.hand1.cards[2].value < self.hand2.cards[2].value:
return -1
twopairs1 = 0
twopairs2 = 0
# Check if player 1 has two pairs.
if (self.hand1.cards[0].value == self.hand1.cards[1].value and self.hand1.cards[2].value == self.hand1.cards[3].value) or\
(self.hand1.cards[0].value == self.hand1.cards[1].value and self.hand1.cards[3].value == self.hand1.cards[4].value) or\
(self.hand1.cards[1].value == self.hand1.cards[2].value and self.hand1.cards[3].value == self.hand1.cards[4].value):
twopairs1 = 1
# Check if player 2 has two pairs.
if (self.hand2.cards[0].value == self.hand2.cards[1].value and self.hand2.cards[2].value == self.hand2.cards[3].value) or\
(self.hand2.cards[0].value == self.hand2.cards[1].value and self.hand2.cards[3].value == self.hand2.cards[4].value) or\
(self.hand2.cards[1].value == self.hand2.cards[2].value and self.hand2.cards[3].value == self.hand2.cards[4].value):
twopairs2 = 1
# If player 1 has two pairs and player 2 doesn't, player 1 wins.
if twopairs1 and not twopairs2:
return 1
# If player 2 has two pairs and player 1 doesn't, player 2 wins.
if not twopairs1 and twopairs2:
return -1
# If both players have two pairs, check who has the highest pair. If it's equal,
# check the other pair. If it's still equal, check the remaining card.
if twopairs1 and twopairs2:
if self.hand1.cards[3].value > self.hand2.cards[3].value:
return 1
if self.hand1.cards[3].value < self.hand2.cards[3].value:
return -1
if self.hand1.cards[1].value > self.hand2.cards[1].value:
return 1
if self.hand1.cards[1].value < self.hand2.cards[1].value:
return -1
for i in range(4, -1, -1):
if self.hand1.cards[i].value > self.hand2.cards[i].value:
return 1
if self.hand1.cards[i].value < self.hand2.cards[i].value:
return -1
pair1 = 0
pair2 = 0
# Check if player 1 has a pair of cards.
if self.hand1.cards[0].value == self.hand1.cards[1].value or self.hand1.cards[1].value == self.hand1.cards[2].value or\
self.hand1.cards[2].value == self.hand1.cards[3].value or self.hand1.cards[3].value == self.hand1.cards[4].value:
pair1 = 1
# Check if player 2 has a pair of cards.
if self.hand2.cards[0].value == self.hand2.cards[1].value or self.hand2.cards[1].value == self.hand2.cards[2].value or\
self.hand2.cards[2].value == self.hand2.cards[3].value or self.hand2.cards[3].value == self.hand2.cards[4].value:
pair2 = 1
# If player 1 has a pair of cards and player 2 doesn't, player 1 wins.
if pair1 and not pair2:
return 1
# If player 2 has a pair of cards and player 1 doesn't, player 2 wins.
if not pair1 and pair2:
return -1
# If both players have a pair of cards, check who has the highest pair. Since cards are
# ordered by value, either card[1] will be part of the pair (card[0]=card[1] or
# card[1]=card[2]) or card[3] will be part of the pair (card[2]=card[3] or
# card[3]=card[4]).
if pair1 and pair2:
if self.hand1.cards[0].value == self.hand1.cards[1].value or self.hand1.cards[1].value == self.hand1.cards[2].value:
value = self.hand1.cards[1].value
if self.hand2.cards[0].value == self.hand2.cards[1].value or self.hand2.cards[1].value == self.hand2.cards[2].value:
if value > self.hand2.cards[1].value:
return 1
if value < self.hand2.cards[1].value:
return -1
if self.hand2.cards[2].value == self.hand2.cards[3].value or self.hand2.cards[3].value == self.hand2.cards[4].value:
if value > self.hand2.cards[3].value:
return 1
if value < self.hand2.cards[3].value:
return -1
else:
value = self.hand1.cards[3].value
if self.hand2.cards[0].value == self.hand2.cards[1].value or self.hand2.cards[1].value == self.hand2.cards[2].value:
if value > self.hand2.cards[1].value:
return 1
if value < self.hand2.cards[1].value:
return -1
elif self.hand2.cards[2].value == self.hand2.cards[3].value or self.hand2.cards[3].value == self.hand2.cards[4].value:
if value > self.hand2.cards[3].value:
return 1
if value < self.hand2.cards[3].value:
return -1
# If all other things are equal, check who has the highest card, if it's equal check the second highest and so on.
for i in range(4, -1, -1):
if self.hand1.cards[i].value > self.hand2.cards[i].value:
return 1
if self.hand1.cards[i].value < self.hand2.cards[i].value:
return -1
# If everything is equal, return 0
return 0
@timing
def p054() -> None:
try:
with open('p054_poker.txt', 'r', encoding='utf-8') as fp:
games = fp.readlines()
except FileNotFoundError:
print('Error while opening file p054_poker.txt')
sys.exit(1)
count = 0
for i in games:
line = ''.join(i).strip().split(' ')
count_hand = 0
hand1 = Hand()
hand2 = Hand()
for j in line:
card = Card()
card.set_card(j)
if count_hand < 5:
hand1.cards.append(card)
else:
hand2.cards.append(card)
count_hand = count_hand + 1
hand1.sort()
hand2.sort()
for _ in hand1.cards:
game = Game()
game.hand1 = hand1
game.hand2 = hand2
if game.play() == 1:
count = count + 1
print('Project Euler, Problem 54')
print(f'Answer: {count}')
if __name__ == '__main__':
p054()