Poker Hands
The file poker.txt contains one-thousand random hands dealt to two players. Each line contains ten cards (separated by spaces): the first five are Player 1's hand and the last five are Player 2's h...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
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 \small Pair of Fives | 2C 3S 8S 8D TD \small Pair of Eights | Player 2 |
| 2 | 5D 8C 9S JS AC \small Highest card Ace | 2C 5C 7D 8S QH \small Highest card Queen | Player 1 |
| 3 | 2D 9C AS AH AC \small Three Aces | 3D 6D 7D TD QD \small Flush with Diamonds | Player 2 |
| 4 | 4D 6S 9H QH QC Pair of Queens Highest card Nine | 3D 6D 7H QD QS \small Pair of Queens \small Highest card Seven | Player 1 |
| 5 | 2H 2D 4C 4D 4S \small Full House \small With Three Fours | 3C 3D 3S 9S 9D \small Full House \small 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?
Problem 54: Poker Hands
Mathematical Development
Formal Development
Definition 1 (Card and Hand). A card is a pair where is the value (with , , , ) and is the suit. A hand is an ordered 5-tuple of distinct cards .
Definition 2 (Derived Quantities). For a hand with values and suits , define:
- Frequency function: for each .
- Frequency partition: The multiset , sorted in decreasing order.
- Flush predicate: .
- Straight predicate: the sorted distinct values form 5 consecutive integers, or equal (the ace-low, or “wheel,” straight).
Definition 3 (Hand Category). The category is defined by the following decision procedure, evaluated in order of priority (highest first):
| Name | Condition | |
|---|---|---|
| 9 | Royal Flush | |
| 8 | Straight Flush | |
| 7 | Four of a Kind | Frequency partition = |
| 6 | Full House | Frequency partition = |
| 5 | Flush | |
| 4 | Straight | |
| 3 | Three of a Kind | Frequency partition = |
| 2 | Two Pairs | Frequency partition = |
| 1 | One Pair | Frequency partition = |
| 0 | High Card | Frequency partition = |
Definition 4 (Comparison Key). The comparison key is a tuple enabling lexicographic comparison:
where are tiebreaker values defined as follows:
- For straights and straight flushes: the high card of the straight (with the wheel having high card 5, not 14).
- For all other categories: the distinct values appearing in , sorted by in descending order.
Theorem 1 (Correctness of Lexicographic Comparison). For any two hands (with no ties, as guaranteed by the problem), beats if and only if in lexicographic order.
Proof. The proof proceeds by case analysis on the structure of the comparison key.
Primary comparison. If , the hand with higher category wins. This is correct since the category ordering reflects the standard poker hand hierarchy.
Tiebreaking within a category. Suppose . The standard poker rules prescribe:
-
Four of a Kind (): Compare the value of the quadruple, then the kicker. The key = (quad value, kicker value) achieves this.
-
Full House (): Compare the triple value, then the pair value. The key = (triple value, pair value) achieves this.
-
Flush / High Card (): Compare values in descending order. Since all frequencies are 1, sorting by descending is equivalent to sorting by descending.
-
Straight / Straight Flush (): The straight’s high card determines the winner.
-
Three of a Kind (): Compare triple value, then kickers in descending order.
-
Two Pairs (): Sorting by descending yields (higher pair, lower pair, kicker), which is the correct comparison order.
-
One Pair (): Sorting yields (pair value, then kickers descending).
In each case, the lexicographic ordering of faithfully implements the poker comparison rules.
Lemma 1 (Ace-Low Straight Correction). When the sorted values are and the straight predicate holds, the ace must be reinterpreted as value 1. The comparison key uses high card .
Proof. By standard poker rules, ---- is the lowest straight (high card 5). Without this correction, the ace (value 14) would make this straight rank above ---- (high card 6), contradicting the rules.
Editorial
We read the file line by line, split each record into the two five-card hands, and evaluate both hands into comparison keys. Each key consists of the hand category together with the category-specific tiebreak values derived from frequencies, straights, and flushes, including the ace-low straight correction. Player 1 wins precisely when the lexicographic key of the first hand exceeds that of the second, so the final answer is the total number of such lines.
Pseudocode
Algorithm: Counting Poker Hands Won by Player 1
Require: A list of poker deals, each containing two five-card hands.
Ensure: The number of deals for which Player 1 has the stronger hand.
1: Read all game records and initialize wins ← 0.
2: For each record, split the ten cards into hands H_1 and H_2.
3: Evaluate each hand H into a comparison key kappa(H) determined by its category and the corresponding tie-break values.
4: If kappa(H_1) > kappa(H_2), update wins ← wins + 1.
5: Return wins.
Complexity Analysis
Proposition 1 (Time). The algorithm processes hands. Each hand evaluation involves:
- Parsing 5 cards: .
- Sorting 5 values: .
- Frequency counting over at most 13 values: .
- Category classification and key construction: .
- Lexicographic comparison of fixed-length tuples: .
Total: .
Proposition 2 (Space). per hand evaluation. if all lines are stored; if processed line-by-line.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
/*
* Problem 54: Poker Hands
*
* Usage:
* Place 'poker.txt' in the same directory and run:
* ./solution
* Or pipe via stdin:
* ./solution < poker.txt
*
* Download poker.txt from: https://projecteuler.net/problem=54
* (requires a free Project Euler account)
*
* Answer: 376
*/
int card_value(char c) {
if (c >= '2' && c <= '9') return c - '0';
if (c == 'T') return 10;
if (c == 'J') return 11;
if (c == 'Q') return 12;
if (c == 'K') return 13;
if (c == 'A') return 14;
return 0;
}
// Compute comparison key kappa(H) as a vector for lexicographic comparison
vector<int> evaluate(vector<pair<int,char>>& hand) {
vector<int> vals;
set<char> suits;
map<int,int> freq;
for (auto& [v, s] : hand) {
vals.push_back(v);
suits.insert(s);
freq[v]++;
}
sort(vals.begin(), vals.end());
bool is_flush = (suits.size() == 1);
bool is_straight = false;
int straight_high = 0;
set<int> unique_vals(vals.begin(), vals.end());
if (unique_vals.size() == 5) {
if (vals[4] - vals[0] == 4) {
is_straight = true;
straight_high = vals[4];
}
if (unique_vals.count(14) && unique_vals.count(2) &&
unique_vals.count(3) && unique_vals.count(4) && unique_vals.count(5)) {
is_straight = true;
straight_high = 5;
}
}
// Sort values by (frequency desc, value desc)
vector<pair<int,int>> freq_val;
for (auto& [v, f] : freq) freq_val.push_back({f, v});
sort(freq_val.begin(), freq_val.end(), [](auto& a, auto& b) {
if (a.first != b.first) return a.first > b.first;
return a.second > b.second;
});
vector<int> tiebreakers;
for (auto& [f, v] : freq_val) tiebreakers.push_back(v);
vector<int> counts;
for (auto& [f, v] : freq_val) counts.push_back(f);
int rank = 0;
if (is_straight && is_flush) {
rank = (straight_high == 14) ? 9 : 8;
tiebreakers = {straight_high};
} else if (counts.size() >= 1 && counts[0] == 4) {
rank = 7;
} else if (counts.size() >= 2 && counts[0] == 3 && counts[1] == 2) {
rank = 6;
} else if (is_flush) {
rank = 5;
} else if (is_straight) {
rank = 4;
tiebreakers = {straight_high};
} else if (counts.size() >= 1 && counts[0] == 3) {
rank = 3;
} else if (counts.size() >= 2 && counts[0] == 2 && counts[1] == 2) {
rank = 2;
} else if (counts.size() >= 1 && counts[0] == 2) {
rank = 1;
} else {
rank = 0;
}
vector<int> result = {rank};
for (int t : tiebreakers) result.push_back(t);
return result;
}
int main() {
ifstream fin("poker.txt");
if (!fin.is_open()) fin.open("p054_poker.txt");
istream& in = fin.is_open() ? fin : cin;
int p1_wins = 0;
string line;
while (getline(in, line)) {
if (line.empty()) continue;
istringstream iss(line);
vector<pair<int,char>> h1(5), h2(5);
string card;
bool valid = true;
for (int i = 0; i < 5; i++) {
if (!(iss >> card) || card.size() < 2) { valid = false; break; }
h1[i] = {card_value(card[0]), card[1]};
}
for (int i = 0; i < 5; i++) {
if (!(iss >> card) || card.size() < 2) { valid = false; break; }
h2[i] = {card_value(card[0]), card[1]};
}
if (!valid) continue;
auto s1 = evaluate(h1);
auto s2 = evaluate(h2);
if (s1 > s2) p1_wins++;
}
cout << p1_wins << endl;
return 0;
}
"""
Problem 54: Poker Hands
How many hands does Player 1 win in the poker game?
Usage:
Place 'poker.txt' (or 'p054_poker.txt') in the same directory, then run:
python solution.py
Or pipe via stdin:
python solution.py < poker.txt
Download poker.txt from: https://projecteuler.net/problem=54
(requires a free Project Euler account)
"""
import sys
import os
from collections import Counter
def card_value(c):
values = {'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}
return values[c]
def evaluate(cards):
"""
Compute the comparison key kappa(H) for a 5-card poker hand.
Returns a tuple for lexicographic comparison.
Categories: 0=High Card, 1=One Pair, 2=Two Pairs, 3=Three of a Kind,
4=Straight, 5=Flush, 6=Full House, 7=Four of a Kind,
8=Straight Flush, 9=Royal Flush
"""
values = sorted([card_value(c[0]) for c in cards], reverse=True)
suits = [c[1] for c in cards]
is_flush = len(set(suits)) == 1
unique_vals = sorted(set(values))
is_straight = False
straight_high = 0
if len(unique_vals) == 5:
if unique_vals[-1] - unique_vals[0] == 4:
is_straight = True
straight_high = unique_vals[-1]
if unique_vals == [2, 3, 4, 5, 14]:
is_straight = True
straight_high = 5
freq = Counter(values)
groups = sorted(freq.items(), key=lambda x: (-x[1], -x[0]))
counts = [f for v, f in groups]
tiebreakers = tuple(v for v, f in groups)
if is_straight and is_flush:
rank = 9 if straight_high == 14 else 8
return (rank, straight_high)
elif counts[0] == 4:
return (7,) + tiebreakers
elif counts[0] == 3 and len(counts) > 1 and counts[1] == 2:
return (6,) + tiebreakers
elif is_flush:
return (5,) + tiebreakers
elif is_straight:
return (4, straight_high)
elif counts[0] == 3:
return (3,) + tiebreakers
elif counts[0] == 2 and len(counts) > 1 and counts[1] == 2:
return (2,) + tiebreakers
elif counts[0] == 2:
return (1,) + tiebreakers
else:
return (0,) + tiebreakers
def solve():
lines = None
script_dir = os.path.dirname(os.path.abspath(__file__))
for filename in ['poker.txt', 'p054_poker.txt', '0054_poker.txt']:
filepath = os.path.join(script_dir, filename)
if os.path.exists(filepath):
with open(filepath) as f:
lines = f.readlines()
break
if lines is None:
if not sys.stdin.isatty():
lines = sys.stdin.readlines()
else:
print("Error: poker.txt not found.")
print("Download from https://projecteuler.net/problem=54")
print("Or pipe via stdin: python solution.py < poker.txt")
return
p1_wins = 0
for line in lines:
line = line.strip()
if not line:
continue
cards = line.split()
if len(cards) < 10:
continue
hand1 = cards[:5]
hand2 = cards[5:10]
if evaluate(hand1) > evaluate(hand2):
p1_wins += 1
print(p1_wins)
solve()