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 2Winner15H 5C 6S 7S KDPair of Fives2C 3S 8S 8D TDPair of EightsPlayer 225D 8C 9S JS ACHighest card Ace2C 5C 7D 8S QHHighest card QueenPlayer 132D 9C AS AH ACThree Aces3D 6D 7D TD QDFlush with DiamondsPlayer 244D 6S 9H QH QCPair of Queens

Highest card Nine3D 6D 7H QD QSPair of Queens

Highest card SevenPlayer 152H 2D 4C 4D 4SFull House

With Three Fours3C 3D 3S 9S 9DFull House

with Three ThreesPlayer 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.

I thought this was a very fun problem.

Pretty amazing how compact Dreamshire’s code is.

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

/Kristian

#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)

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 !

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 🙂

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

Hi Steve

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

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

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.

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…

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.

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.

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);

}

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