All Euler problems
Project Euler

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...

Source sync Apr 19, 2026
Problem #0054
Level Level 02
Solved By 40,709
Languages C++, Python
Answer 376
Length 701 words
probabilitylinear_algebrabrute_force

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:

HandPlayer 1Player 2Winner
15H 5C 6S 7S KD
\small Pair of Fives
2C 3S 8S 8D TD
\small Pair of Eights
Player 2
25D 8C 9S JS AC
\small Highest card Ace
2C 5C 7D 8S QH
\small Highest card Queen
Player 1
32D 9C AS AH AC
\small Three Aces
3D 6D 7D TD QD
\small Flush with Diamonds
Player 2
44D 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
52H 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 (v,s)(v, s) where v{2,3,,14}v \in \{2, 3, \ldots, 14\} is the value (with 11=J11 = \mathrm{J}, 12=Q12 = \mathrm{Q}, 13=K13 = \mathrm{K}, 14=A14 = \mathrm{A}) and s{,,,}s \in \{\clubsuit, \diamondsuit, \heartsuit, \spadesuit\} is the suit. A hand is an ordered 5-tuple of distinct cards H=(c1,c2,c3,c4,c5)H = (c_1, c_2, c_3, c_4, c_5).

Definition 2 (Derived Quantities). For a hand HH with values v1,,v5v_1, \ldots, v_5 and suits s1,,s5s_1, \ldots, s_5, define:

  • Frequency function: f(v)={i:vi=v}f(v) = |\{i : v_i = v\}| for each v{2,,14}v \in \{2, \ldots, 14\}.
  • Frequency partition: The multiset {f(v):f(v)>0}\{f(v) : f(v) > 0\}, sorted in decreasing order.
  • Flush predicate: ϕ(H)    s1=s2==s5\phi(H) \iff s_1 = s_2 = \cdots = s_5.
  • Straight predicate: σ(H)    \sigma(H) \iff the sorted distinct values form 5 consecutive integers, or equal {2,3,4,5,14}\{2, 3, 4, 5, 14\} (the ace-low, or “wheel,” straight).

Definition 3 (Hand Category). The category c(H){0,1,,9}c(H) \in \{0, 1, \ldots, 9\} is defined by the following decision procedure, evaluated in order of priority (highest first):

ccNameCondition
9Royal Flushσ(H)ϕ(H)max(vi)=14min(vi)=10\sigma(H) \wedge \phi(H) \wedge \max(v_i) = 14 \wedge \min(v_i) = 10
8Straight Flushσ(H)ϕ(H)¬(c=9)\sigma(H) \wedge \phi(H) \wedge \neg(c = 9)
7Four of a KindFrequency partition = (4,1)(4, 1)
6Full HouseFrequency partition = (3,2)(3, 2)
5Flushϕ(H)¬σ(H)\phi(H) \wedge \neg\sigma(H)
4Straightσ(H)¬ϕ(H)\sigma(H) \wedge \neg\phi(H)
3Three of a KindFrequency partition = (3,1,1)(3, 1, 1)
2Two PairsFrequency partition = (2,2,1)(2, 2, 1)
1One PairFrequency partition = (2,1,1,1)(2, 1, 1, 1)
0High CardFrequency partition = (1,1,1,1,1)¬σ(H)¬ϕ(H)(1, 1, 1, 1, 1) \wedge \neg\sigma(H) \wedge \neg\phi(H)

Definition 4 (Comparison Key). The comparison key κ(H)\kappa(H) is a tuple enabling lexicographic comparison:

κ(H)=(c(H),  τ1,  τ2,  ,  τm),\kappa(H) = \bigl(c(H),\; \tau_1,\; \tau_2,\; \ldots,\; \tau_m\bigr),

where (τ1,,τm)(\tau_1, \ldots, \tau_m) are tiebreaker values defined as follows:

  • For straights and straight flushes: τ1=\tau_1 = the high card of the straight (with the wheel having high card 5, not 14).
  • For all other categories: the distinct values appearing in HH, sorted by (f(v),v)(f(v), v) in descending order.

Theorem 1 (Correctness of Lexicographic Comparison). For any two hands H1,H2H_1, H_2 (with no ties, as guaranteed by the problem), H1H_1 beats H2H_2 if and only if κ(H1)>κ(H2)\kappa(H_1) > \kappa(H_2) in lexicographic order.

Proof. The proof proceeds by case analysis on the structure of the comparison key.

Primary comparison. If c(H1)c(H2)c(H_1) \neq c(H_2), the hand with higher category wins. This is correct since the category ordering reflects the standard poker hand hierarchy.

Tiebreaking within a category. Suppose c(H1)=c(H2)=cc(H_1) = c(H_2) = c. The standard poker rules prescribe:

  1. Four of a Kind (c=7c = 7): Compare the value of the quadruple, then the kicker. The key (τ1,τ2)(\tau_1, \tau_2) = (quad value, kicker value) achieves this.

  2. Full House (c=6c = 6): Compare the triple value, then the pair value. The key (τ1,τ2)(\tau_1, \tau_2) = (triple value, pair value) achieves this.

  3. Flush / High Card (c{0,5}c \in \{0, 5\}): Compare values in descending order. Since all frequencies are 1, sorting by (f(v),v)(f(v), v) descending is equivalent to sorting by vv descending.

  4. Straight / Straight Flush (c{4,8,9}c \in \{4, 8, 9\}): The straight’s high card determines the winner.

  5. Three of a Kind (c=3c = 3): Compare triple value, then kickers in descending order.

  6. Two Pairs (c=2c = 2): Sorting by (f(v),v)(f(v), v) descending yields (higher pair, lower pair, kicker), which is the correct comparison order.

  7. One Pair (c=1c = 1): Sorting yields (pair value, then kickers descending).

In each case, the lexicographic ordering of κ\kappa faithfully implements the poker comparison rules. \blacksquare

Lemma 1 (Ace-Low Straight Correction). When the sorted values are {2,3,4,5,14}\{2, 3, 4, 5, 14\} and the straight predicate holds, the ace must be reinterpreted as value 1. The comparison key uses high card =5= 5.

Proof. By standard poker rules, AA-22-33-44-55 is the lowest straight (high card 5). Without this correction, the ace (value 14) would make this straight rank above 22-33-44-55-66 (high card 6), contradicting the rules. \blacksquare

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 H=1000H = 1000 hands. Each hand evaluation involves:

  • Parsing 5 cards: O(5)=O(1)O(5) = O(1).
  • Sorting 5 values: O(5log5)=O(1)O(5 \log 5) = O(1).
  • Frequency counting over at most 13 values: O(1)O(1).
  • Category classification and key construction: O(1)O(1).
  • Lexicographic comparison of fixed-length tuples: O(1)O(1).

Total: O(H)=O(1000)O(H) = O(1000).

Proposition 2 (Space). O(1)O(1) per hand evaluation. O(H)O(H) if all lines are stored; O(1)O(1) if processed line-by-line.

Answer

376\boxed{376}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_054/solution.cpp
#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;
}