Project Euler 54: How many hands did player one win in the game of poker?

Project Euler 54: How many hands did player one win in the game of poker?

Written by on 30 July 2011

Topics: Project Euler

Before I even start the post on Problem 54 of Project Euler I might as well disappoint you and say that I wont write a whole lot about the solution. The problem reads

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

Pair of Fives
2C 3S 8S 8D TD

Pair of Eights
Player 2
25D 8C 9S JS AC

Highest card Ace
2C 5C 7D 8S QH

Highest card Queen
Player 1
32D 9C AS AH AC

Three Aces
3D 6D 7D TD QD

Flush with Diamonds
Player 2
44D 6S 9H QH QC

Pair of Queens
Highest card Nine
3D 6D 7H QD QS

Pair of Queens
Highest card Seven
Player 1
52H 2D 4C 4D 4S

Full House
With Three Fours
3C 3D 3S 9S 9D

Full House
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?

I did enjoy programming the solution, but I don’t think it was a particular math minded problem. I wont go into the details of the code, but you can study the source code if you like. it is the worst sort of brute force code I could come up with, but it did the job.

The result of running it was

Player 1 won 376 hands
Solution took 12 ms

I am sure there are prettier or more compact solutions out there. So any suggestions or comment is of course welcome, I would like to learn how to make prettier code. One very short solution I found was the one at dreamshire written in Perl.

The photo is taken by images_of_money and kindly shared under the Creative Commons license.

14 Comments For This Post I'd Love to Hear Yours!

  1. SuprDewd says:

    I thought this was a very fun problem.
    Pretty amazing how compact Dreamshire’s code is.

  2. Kristian says:

    I agree with you, it was fun coding. I just really don’t have anything clever to say about the solution.

    /Kristian

  3. Rahat says:

    #can you tell why my code returns 379?
    f=open(‘C:\\users\\user\\documents\\poker.txt’).read()
    k=f.split(‘\n’)
    cards=[‘2′,’3′,’4′,’5′,’6′,’7′,’8′,’9′,’T’,’J’,’Q’,’K’,’A’]
    r=set(‘TJQKA’)
    def highcard(m,n):
    ”’Highest value card”’
    if len(m)==0:
    print(‘High card fails’)
    return 0

    max1=0
    max2=0
    max1=max(cards.index(i) for i in m)
    max2=max(cards.index(i) for i in n)
    if max1>max2:
    print(‘Highest card’,cards[max1],cards[max2])
    return 1
    if max1p2:
    print(‘High card on same’,cards[p1],cards[p2])
    return 1
    if p1<p2:
    print('High card on same',cards[p1],cards[p2])
    return 2
    if p1==p2:
    return 0
    def check(a,b):
    '''royal flush'''
    m,n=a[0::2],b[0::2]
    p,q=a[1::2],b[1::2]
    s1,s2=set(m),set(n)
    ss1,ss2=set(p),set(q)
    d1,d2=len(s1),len(s2)
    e1,e2=len(ss1),len(ss2)
    rflush=''
    if len(r-set(a))==0 and e1==1:
    rflush+='1'
    '''royal flush'''
    if len(r-set(b))==0 and e2==1:
    rflush+='2'
    if rflush=='1':
    print('Royal flush')
    return 1
    if rflush=='2':
    print('Royal flush')
    return 2
    if rflush=='12':
    print('problem')
    return False
    '''straight flush'''
    if e1==1 and e2!=1:

    if consequtive(m):
    print('straight flush')
    return 1
    if e1!=1 and e2==1:
    if consequtive(n):
    print('straight flush')
    return 2
    if e1==1 and e2==1:
    print('Straight flush ties')
    p=highcard(m,n)
    if p!=0:
    return p
    cat,dog=samecounter(m,4),samecounter(n,4)
    if cat and not dog:
    print('four of a kind')
    return 1
    if not cat and dog:
    print('four of a kind')
    return 2
    if cat and dog:
    print('four of a kind ties')
    p=highONsame(m,n,4)
    if p!=0:
    return p
    p=highcard(m,n)
    if p!=0:
    return p

    '''full house'''
    if d1==2 and d2!=2:
    print('full house p1')
    return 1
    if d1!=2 and d2==2:
    print('full house p2')
    return 2
    if d1==2 and d2==2:
    print('full house ties')
    p=highONsame(m,n,3)
    if p!=0:
    return p
    p=highcard(m,n)
    if p!=0:
    return p
    '''flush'''
    if e1==1 and e2!=1:
    print('flush')
    return 1
    if e1!=1 and e2==1:
    print('flush')
    return 2
    if e1==1 and e2==1:
    print('flush ties')
    p=highcard(m,n)
    if p!=0:
    return p
    '''three of a kind'''
    cat,dog=samecounter(m,3),samecounter(n,3)
    if cat and not dog:
    print('Three of a kind')
    return 1
    if not cat and dog:
    print('Three of a kind')
    return 2
    if cat and dog:
    print('Three of a kind ties')
    p=highONsame(m,n,3)
    if p!=0:
    return p
    p=highcard(m,n)
    if p!=0:
    return p

    if d1==3 and d2!=3:
    print('Two pairs')
    return 1
    if d1!=3 and d2==3:
    print('Two pairs')
    return 2
    if d1==3 and d2==3:
    print('Two pair ties')
    q=highONsame(m,n,1)
    for i in m:
    if m.count(i)==1:
    g=i
    break
    for i in n:
    if n.count(i)==1:
    h=i
    break
    m=m.replace(g,'')
    n=n.replace(h,'')
    p=highcard(m,n)
    if p!=0:
    return p
    if q!=0:
    return q
    else:
    return False

    if d1==4 and d2!=4:
    print('one pair')
    return 1
    if d1!=4 and d2==4:
    print('one pair')
    return 2
    if d1==4 and d2==4:
    print('one pair ties')
    p=highONsame(m,n,2)
    if p!=0:
    return p
    p=highcard(m,n)
    if p!=0:
    return p
    return highcard(m,n)

    def form(a):
    a=a.replace(' ','')
    return a

    count=0
    v=0

    for i in k:
    if i=='':
    break
    t=check(form(i[:14]),form(i[15:]))
    if t==False:
    input('SEE')
    print(t,i[:14],'|',i[15:])
    if t==1:
    v+=1
    count+=1

    if count%100==0:
    input('…………')

    print(v)

  4. Shobhit says:

    This one was a heck of a ride. I relied on the C# library sort routine and assumed that it would give the right result. It didn’t and I ended up wasting couple of hours to debug it.
    I agree that this is more of a programming problem than a mathematical problem. I looked at your code and I can make couple of suggestions (I didn’t study it in detail).
    1) Although it may sound boring, commenting out a code makes it a lot more readable for others.
    2) I sort out the cards after parsing the input. I think you also do that because you implement Icomparable. If it is sorted then you don’t have to do so many comparisons to calculate IsFourOfAKind. Just compare 0 with 3 and then compare 1 with 4.
    Same with calculating IsFullHouse.
    3) One thing I did different than yours is, I calculate the rank of hands separately and compare them. I assume that for most the cases, there won’t be a tie and if rank of player 1 is more than rank of player 2 then pl1 wins. I am not sure if it gives any performance advantage or not, but it makes it easy to code.
    4) I used more classes. One class is for rank calculation only. All the member function but one of that class are private. There is only public member which takes a hand (a set of five cards), does some sanity check and gives the result. It gives a better encapsulation. Again nothing fancy but makes code more modular.
    5) I used FindWinner class which calls the rankcalculator for pl1 and pl2. Compares them and decides the winner. This class also breaks the tie if ranks are same.
    6) I used a struct for Card which does not add much value but provides a good abstraction.
    None of this is very clever but makes a long program more readable. I have been bitten by clever programs several times and I prefer a readable program over a clever program any day.
    Your program is still very much readable. However, a quick glance at the code does not reveal obvious abstractions. Still a very good job. I would have been disappointed with myself if you would have found out something clever to say in this problem 🙂
    Happy coding !

  5. Kristian says:

    Thanks for the elaborate comment. I fully agree on 1). I continue to find it hard to comment code. Because when I write the code, it is so painfully obvious what it all does. However, comming back to it…. Not quite as obvious anymore.

    Regarding 6) I also agree on most aspects. Though when solving Project Euler problems, I do think that clever programs are really fun. When programming for real, I much prefer readable and maintainable code any day.

    For the rest of the comments I think you are absolutly right, and those are things that I should consider working into the code if I ever decide to rewrite part of it 🙂

  6. Steve says:

    Hi Kristian

    I am not sure if the given problem is affected or not, but it looks like your code to determine a ‘straight’ did not allow for ace low (ie Ace, 2, 3, 4, 5 should be a straight).

    Reading the problem, it seems like they also ignored this possibility, but I thought I would ask before coding the solution.

    Thanks,
    Steve

  7. Kristian says:

    Hi Steve

    Yes, I can see that my code does not take that combination into account. Apparantly it doesn’t matter though 🙂

  8. Quintin says:

    I get 379 also, did you figure out what the problem was?

  9. Trystan says:

    I haven’t finished this problem yet. Still working on my solution. While researching I found a link to a site that did an algorithm for a poker game. It’s not related to the Euler problem but how to find the value of the 5 cards. It’s a real interesting read. http://www.suffecool.net/poker/evaluator.html it involves tables and prime numbers.

  10. Emil says:

    pair 41%
    2xPairs 6.9%
    set 4.6%
    straight 0.289%
    flush 0.387%
    full hous 0.42%
    qare 0.2146%
    straightFl0.00105%
    royal 0.000124%
    number of tests= 100 000 000
    why?
    I done the problem 54. After, i tried to do this, I cant understand…

  11. Raj says:

    I think you can use data structures for straights and flushes to improve the dreamshire’s code. I ll post a C++ code as soon as it is done.

  12. holhen says:

    I don’t think it’s a perfect algorithm, but I’m not sure. For example for a Flush you only check the highest Card of both hands but don’t check the next highest hands. For a FourOfAKind you only check the 4 cards that are the same but don’t check the fifth different card if it’s egal. Maybe I misunderstand something but I think it needs some improvement.

  13. M.Umer says:

    My Solution 0.008 Sec

    private int[] getSuitForP1(string hand)
    {
    string[] cards = hand.Split(‘ ‘);
    int[] Suit = new int[5];
    for (int i = 0; i < 5; i++)
    Suit[i] = getCardSuit(cards[i]);
    return BubbleSort(Suit);
    }
    private int[] getSuitForP2(string hand)
    {
    string[] cards = hand.Split(‘ ‘);
    int[] Suit = new int[5];
    for (int i = 0; i < 5; i++)
    Suit[i] = getCardSuit(cards[i + 5]);
    return BubbleSort(Suit);
    }
    private int getCardSuit(string c)
    {
    //Suit Values: D=0; C=1; H=2; S=3;
    if (c[1].ToString() == "D")
    return 0;
    if (c[1].ToString() == "C")
    return 1;
    if (c[1].ToString() == "H")
    return 2;
    if (c[1].ToString() == "S")
    return 3;
    return -1;
    }

    private int[] getValueForP1(string hand)
    {
    string[] cards = hand.Split(‘ ‘);
    int[] Values = new int[5];
    for (int i = 0; i < 5; i++)
    Values[i] = getCardValue(cards[i]);
    return BubbleSort(Values);
    }
    private int[] getValueForP2(string hand)
    {
    string[] cards = hand.Split(‘ ‘);
    int[] Values = new int[5];
    for (int i = 0; i < 5; i++)
    Values[i] = getCardValue(cards[i + 5]);
    return BubbleSort(Values);
    }
    private int getCardValue(string c)
    {
    if (c[0].ToString() == "2")
    return 2;
    if (c[0].ToString() == "3")
    return 3;
    if (c[0].ToString() == "4")
    return 4;
    if (c[0].ToString() == "5")
    return 5;
    if (c[0].ToString() == "6")
    return 6;
    if (c[0].ToString() == "7")
    return 7;
    if (c[0].ToString() == "8")
    return 8;
    if (c[0].ToString() == "9")
    return 9;
    if (c[0].ToString() == "T")
    return 10;
    if (c[0].ToString() == "J")
    return 11;
    if (c[0].ToString() == "Q")
    return 12;
    if (c[0].ToString() == "K")
    return 13;
    if (c[0].ToString() == "A")
    return 14;
    return 0;
    }

    private bool IsRoyalFlush(int[] Suit, int[] Value)
    {
    if (Value[0] == 10 && Value[1] == 11 && Value[2] == 12 && Value[3] == 13 && Value[4] == 14 && IsFlush(Suit))
    return true;
    return false;
    }
    private bool IsStraightFlush(int[] Suit, int[] Value)
    {
    if (IsFlush(Suit) && IsStraight(Value))
    return true;
    return false;
    }
    private bool IsFourOfAKind(int[] Value)
    {
    if ((Value[0] == Value[1] && Value[1] == Value[2] && Value[2] == Value[3]) || (Value[1] == Value[2] && Value[2] == Value[3] && Value[3] == Value[4]))
    return true;
    return false;
    }
    private bool IsFullHouse(int[] Value)
    {
    if ((Value[0] == Value[1] && Value[1] == Value[2] && Value[3] == Value[4]) || (Value[0] == Value[1] && Value[2] == Value[3] && Value[3] == Value[4]))
    return true;
    return false;
    }
    private bool IsFlush(int[] Suit)
    {
    if (Suit[0] == Suit[1] && Suit[1] == Suit[2] && Suit[2] == Suit[3] && Suit[3] == Suit[4])
    return true;
    return false;
    }
    private bool IsStraight(int[] Value)
    {
    if (Value[0] + 1 == Value[1] && Value[1] + 1 == Value[2] && Value[2] + 1 == Value[3] && Value[3] + 1 == Value[4])
    return true;
    return false;
    }
    private bool IsThreeOfAKind(int[] Value)
    {
    if ((Value[0] == Value[1] && Value[1] == Value[2]) || (Value[1] == Value[2] && Value[2] == Value[3]) || (Value[2] == Value[3] && Value[3] == Value[4]))
    return true;
    return false;
    }
    private bool IsTwoPair(int[] Value)
    {
    if ((Value[0] == Value[1] && Value[2] == Value[3]) || (Value[0] == Value[1] && Value[3] == Value[4]) || (Value[1] == Value[2] && Value[3] == Value[4]))
    return true;
    return false;
    }
    private bool IsAPair(int[] Value)
    {
    if (Value[0] == Value[1] || Value[1] == Value[2] || Value[2] == Value[3] || Value[3] == Value[4])
    return true;
    return false;
    }

    private void Problem54()
    {
    string text = File.ReadAllText(Environment.CurrentDirectory + @"..\..\..\Resources\poker.txt");
    string[] pokerhands = text.Split(‘\n’);
    int[] P1Suit = new int[5];
    int[] P1Value = new int[5];
    int[] P2Suit = new int[5];
    int[] P2Value = new int[5];
    int P1Win = 0;
    foreach (var item in pokerhands)
    {
    P1Suit = getSuitForP1(item);
    P1Value = getValueForP1(item);

    P2Suit = getSuitForP2(item);
    P2Value = getValueForP2(item);
    if (IsRoyalFlush(P1Suit, P1Value) && !IsRoyalFlush(P2Suit, P2Value))
    {
    P1Win++;
    continue;
    }
    if (IsRoyalFlush(P2Suit, P2Value))
    continue;
    if (IsStraightFlush(P1Suit, P1Value) && !IsStraightFlush(P2Suit, P2Value))
    {
    P1Win++;
    continue;
    }
    if (IsStraightFlush(P2Suit, P2Value))
    continue;
    if (IsFourOfAKind(P1Value) && !IsFourOfAKind(P2Value))
    {
    P1Win++;
    continue;
    }
    if (IsFourOfAKind(P1Value) && IsFourOfAKind(P2Value))
    {
    int highP1 = P1Value[2];
    int highP2 = P2Value[2];

    if (highP1 > highP2)
    P1Win++;
    continue;
    }
    if (IsFourOfAKind(P2Value))
    continue;
    if (IsFullHouse(P1Value) && !IsFullHouse(P2Value))
    {
    P1Win++;
    continue;
    }
    if (IsFullHouse(P2Value))
    continue;
    if(IsFlush(P1Suit) && !IsFlush(P2Suit))
    {
    P1Win++;
    continue;
    }
    if (IsFlush(P2Suit) && IsFlush(P1Suit))
    {
    if (IsStraight(P1Value) && !IsStraight(P2Value))
    {
    P1Win++;
    continue;
    }
    if (IsStraight(P1Value) && IsStraight(P2Value))
    {
    if (P1Value[4] > P2Value[4])
    P1Win++;
    continue;
    }
    if (IsStraight(P2Value))
    continue;
    }
    if (IsFlush(P2Suit))
    continue;
    if (IsStraight(P1Value) && !IsStraight(P2Value))
    {
    P1Win++;
    continue;
    }
    if (IsStraight(P1Value) && IsStraight(P2Value))
    {
    if (P1Value[4] > P2Value[4])
    P1Win++;
    continue;
    }
    if (IsStraight(P2Value))
    continue;
    if (IsThreeOfAKind(P1Value) && !IsThreeOfAKind(P2Value))
    {
    P1Win++;
    continue;
    }
    if (IsThreeOfAKind(P1Value) && IsThreeOfAKind(P2Value))
    {
    int highP1 = 0;
    int highP2 = 0;

    if (P1Value[0] == P1Value[1] && P1Value[1] == P1Value[2])
    highP1 = P1Value[0];
    else if (P1Value[1] == P1Value[2] && P1Value[2] == P1Value[3])
    highP1 = P1Value[1];
    else if (P1Value[2] == P1Value[3] && P1Value[3] == P1Value[4])
    highP1 = P1Value[2];

    if (P2Value[0] == P2Value[1] && P2Value[1] == P2Value[2])
    highP2 = P2Value[0];
    else if (P2Value[1] == P2Value[2] && P2Value[2] == P2Value[3])
    highP2 = P2Value[1];
    else if (P2Value[2] == P2Value[3] && P2Value[3] == P2Value[4])
    highP2 = P2Value[2];

    if (highP1 > highP2)
    P1Win++;
    continue;
    }
    if (IsThreeOfAKind(P2Value))
    continue;
    if (IsTwoPair(P1Value) && !IsTwoPair(P2Value))
    {
    P1Win++;
    continue;
    }
    if (IsTwoPair(P1Value) && IsTwoPair(P2Value))
    {
    int highP1 = 0;
    int highP2 = 0;

    if (P1Value[0] == P1Value[1] && P1Value[2] == P1Value[3])
    highP1 = P1Value[3];
    else if (P1Value[0] == P1Value[1] && P1Value[3] == P1Value[4])
    highP1 = P1Value[4];
    else if (P1Value[1] == P1Value[2] && P1Value[3] == P1Value[4])
    highP1 = P1Value[4];

    if (P2Value[0] == P2Value[1] && P2Value[2] == P2Value[3])
    highP2 = P2Value[3];
    else if (P2Value[0] == P2Value[1] && P2Value[3] == P2Value[4])
    highP2 = P2Value[4];
    else if (P2Value[1] == P2Value[2] && P2Value[3] == P2Value[4])
    highP2 = P2Value[4];

    if (highP1 > highP2)
    P1Win++;
    continue;
    }
    if (IsTwoPair(P2Value))
    continue;
    if (IsAPair(P1Value) && !IsAPair(P2Value))
    {
    P1Win++;
    continue;
    }
    if (IsAPair(P1Value) && IsAPair(P2Value))
    {
    int highP1 = 0;
    int highP2 = 0;

    if (P1Value[0] == P1Value[1])
    highP1 = P1Value[1];
    else if (P1Value[1] == P1Value[2])
    highP1 = P1Value[2];
    else if (P1Value[2] == P1Value[3])
    highP1 = P1Value[3];
    else if (P1Value[3] == P1Value[4])
    highP1 = P1Value[4];

    if (P2Value[0] == P2Value[1])
    highP2 = P2Value[1];
    else if (P2Value[1] == P2Value[2])
    highP2 = P2Value[2];
    else if (P2Value[2] == P2Value[3])
    highP2 = P2Value[3];
    else if (P2Value[3] == P2Value[4])
    highP2 = P2Value[4];

    if (highP1 > highP2)
    P1Win++;
    continue;
    }
    if (IsAPair(P2Value))
    continue;
    if(P1Value[4]>P2Value[4])
    {
    P1Win++;
    continue;
    }
    if (P1Value[4] == P2Value[4])
    {
    if (P1Value[3] > P2Value[3])
    {
    P1Win++;
    continue;
    }
    if (P1Value[3] == P2Value[3])
    {
    if (P1Value[2] > P2Value[2])
    {
    P1Win++;
    continue;
    }
    if (P1Value[2] == P2Value[2])
    {
    if (P1Value[1] > P2Value[1])
    {
    P1Win++;
    continue;
    }
    if (P1Value[1] == P2Value[1])
    {
    if (P1Value[0] > P2Value[0])
    {
    P1Win++;
    continue;
    }
    }
    }
    }
    }
    }
    Console.WriteLine(P1Win);
    }

  14. Director says:

    For anyone joining at a later date wondering why their code is returning the wrong answer of 379, check your straights

Leave a Comment Here's Your Chance to Be Heard!

You can use these html tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

You can use short tags like: [code language="language"][/code]

The code tag supports the following languages: as3, bash, coldfusion, c, cpp, csharp, css, delphi, diff,erlang, groovy, javascript, java, javafx, perl, php, plain, powershell, python, ruby, scala, sql, tex, vb, xml

You can use [latex]your formula[/latex] if you want to format something using latex

This site uses cookies.