### Poker Logic

Posted on: June 7, 2016 1:31:50 AM

Another programming challenge that I decided to do was a poker challenge The goal of this challenge is to parse 1000 random poker hands of 2 players and determine how many times the first player wins. This was actually quite a fun challenge and I suggest you try to do it on your own even though I'm posting my code.

I decided to do this in a more TDD way than I normally do. So just as I started, I'll start by posting all of my unit tests. Granted, I didn't have them all at the time of development, but I don't think it's necessary to post what I did every step of the way.

PokerTests.cs
```using NUnit.Framework;
using Poker_Kata;

namespace Poker_Tests
{
[TestFixture]
public class PokerTests
{
private Player P1;
private Player P2;

[Test]
public void P13OfAKind()
{
P1.SetHand("3D", "3H", "3C", "4D", "7D");
P2.SetHand("2S", "2D", "2C", "AS", "KS");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P1));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.ThreeKind));
}

[Test]
public void P1FourKind()
{
P1.SetHand("2D", "2S", "2C", "2H", "TD");
P2.SetHand("2S", "AS", "4S", "5S", "6S");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P1));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.FourKind));
}

[Test]
public void P1HighCard()
{
P1.SetHand("2D", "3D", "4D", "5D", "8C");
P2.SetHand("2C", "3S", "4S", "5S", "7S");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P1));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.HighCard));
}

[Test]
public void P1HighPair()
{
P1.SetHand("3D", "3H", "4D", "5D", "6D");
P2.SetHand("2S", "2C", "QS", "KD", "AS");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P1));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.Pair));
}

[Test]
public void P1Pair()
{
P1.SetHand("2D", "2H", "4D", "5D", "7D");
P2.SetHand("2C", "3S", "4S", "5S", "7S");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P1));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.Pair));
}

[Test]
public void P1RoyalFlush()
{
P1.SetHand("AD", "TD", "KD", "QD", "JD");
P2.SetHand("AS", "2S", "3S", "4S", "5S");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P1));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.StraightFlush));
}

[Test]
public void P1Straight()
{
P1.SetHand("2D", "3H", "4C", "5D", "6D");
P2.SetHand("2S", "2D", "2C", "AS", "AS");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P1));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.Straight));
}

[Test]
public void P1TwoPair()
{
P1.SetHand("2D", "2H", "3D", "3D", "7D");
P2.SetHand("2S", "3S", "4S", "AS", "AS");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P1));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.TwoPair));
}

[Test]
public void P1TwoPairHighCard()
{
P1.SetHand("4C", "4S", "2D", "AD", "2H");
P2.SetHand("4S", "4D", "2S", "KS", "2C");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P1));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.TwoPair));
}

[Test]
public void P2AceHighCard()
{
P1.SetHand("2D", "3D", "4S", "5D", "7D");
P2.SetHand("2S", "3S", "6D", "5S", "AS");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.HighCard));
}

[Test]
public void P2Flush()
{
P1.SetHand("AD", "KH", "QC", "JD", "TD");
P2.SetHand("2S", "TS", "4S", "5S", "6S");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.Flush));
}

[Test]
public void P2FullHouse()
{
P1.SetHand("AD", "AH", "AC", "3D", "7D");
P2.SetHand("2S", "2D", "2C", "3S", "3D");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.FullHouse));
}

[Test]
public void P2FullHouse3CardHigher()
{
P2.SetHand("4S", "4D", "4C", "2S", "2D");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.FullHouse));
}

[Test]
public void P2HighCard2()
{
P1.SetHand("8C", "TS", "KC", "9H", "4S");
P2.SetHand("7D", "2S", "5D", "3S", "AC");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.HighCard));
}

[Test]
public void P2HighCard3()
{
P1.SetHand("4D", "5H", "TH", "QS", "KS");
P2.SetHand("2H", "3D", "4S", "6C", "AD");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.HighCard));
}

[Test]
public void P2HighFlush()
{
P1.SetHand("4D", "KD", "QD", "JD", "TD");
P2.SetHand("2S", "AS", "4S", "5S", "6S");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.Flush));
}

[Test]
public void P2LowStraight()
{
P1.SetHand("AD", "2H", "3C", "4D", "5C");
P2.SetHand("2D", "3H", "4C", "5D", "6D");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.Straight));
}

[Test]
public void P2StraightFlush()
{
P1.SetHand("AD", "2H", "3C", "4D", "5C");
P2.SetHand("AS", "2S", "3S", "4S", "5S");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.StraightFlush));
}

[Test]
public void P2TwoPair()
{
P1.SetHand("2D", "2H", "3D", "3D", "7D");
P2.SetHand("4S", "4D", "2S", "AS", "2C");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(1));
Assert.That(winners[0].Player, Is.EqualTo(P2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.TwoPair));
}

[Test]
public void PairTie()
{
P1.SetHand("2D", "2H", "4D", "5D", "6D");
P2.SetHand("2S", "2C", "4S", "5S", "6S");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.Pair));
}

[OneTimeSetUp]
public void TestsSetup()
{
P1 = new Player { Name = "P1" };
P2 = new Player { Name = "P2" };
}

[Test]
public void Tie()
{
P1.SetHand("2D", "3D", "4D", "5D", "6D");
P2.SetHand("2S", "3S", "4S", "5S", "6S");

var winners = Poker.DetermineWinner(P1, P2);

Assert.That(winners.Length, Is.EqualTo(2));
Assert.That(winners[0].Hand, Is.EqualTo(Hand.StraightFlush));
}
}
}
```

Now we'll need the Card, Player, and PokerHand helper classes. These are simple POCO helpers just to hold some information for whatever is utilizing the code.

Card.cs
```namespace Poker_Kata
{
public enum Faces
{
Club,
Diamond,
Heart
}

public class Card
{
public Card()
{
}

public Card(int value, Faces face)
{
Value = value;
Face = face;
}

public Card(string card)
{
switch (card[0])
{
case '2':
Value = 2;
break;

case '3':
Value = 3;
break;

case '4':
Value = 4;
break;

case '5':
Value = 5;
break;

case '6':
Value = 6;
break;

case '7':
Value = 7;
break;

case '8':
Value = 8;
break;

case '9':
Value = 9;
break;

case 'T':
Value = 10;
break;

case 'J':
Value = 11;
break;

case 'Q':
Value = 12;
break;

case 'K':
Value = 13;
break;

case 'A':
Value = 14;
break;
}

switch (card[1])
{
case 'H':
Face = Faces.Heart;
break;

case 'D':
Face = Faces.Diamond;
break;

case 'C':
Face = Faces.Club;
break;

case 'S':
break;
}
}

public Faces Face { get; set; }

public int Value { get; set; }

public override string ToString()
{
return Value + " " + Face.ToString();
}
}
}
```
Player.cs
```using System.Collections.Generic;

namespace Poker_Kata
{
public class Player
{
public Card[] Hand { get; set; }

public string Name { get; set; }

public void SetHand(params string[] cards)
{
List hand = new List();

foreach (string card in cards)
{
}

Hand = hand.ToArray();
}
}
}
```
PokerHand.cs
```namespace Poker_Kata
{
public class PokerHand
{
public Card[] Cards { get; set; }

public Hand Hand { get; set; }

public Player Player { get; set; }
}
}
```

Next is the heart of the project, the Poker class. This class will hold all the of the logic and rules of poker and allow you to pass in a number of players with their hands and will determine what hands they hold along with who won the hand. I didn't make this work with just any poker game, although it wouldn't take much work to do so, I only did enough to handle each player having 5 cards.

Poker.cs
```using System.Linq;

namespace Poker_Kata
{
public enum Hand
{
HighCard,
Pair,
TwoPair,
ThreeKind,
FullHouse,
Straight,
Flush,
FourKind,
StraightFlush
}

public static class Poker
{
public static PokerHand[] DetermineWinner(params Player[] players)
{
PokerHand[] pokerHands = new PokerHand[players.Length];

for (int i = 0; i < players.Length; i++)
{
var hand = GetHand(players[i].Hand);
hand.Player = players[i];

pokerHands[i] = hand;
}

pokerHands = pokerHands.Where(p => p.Hand == pokerHands.Max(p1 => p1.Hand)).ToArray();

if (pokerHands.Length > 1)
{
pokerHands = FilterByHigherHand(pokerHands);

if (pokerHands.Length > 1)
{
pokerHands = FilterByHighCard(pokerHands);
}
}

return pokerHands;
}

public static PokerHand GetHand(Card[] hand)
{
PokerHand pokerHand = new PokerHand();
pokerHand.Cards = hand = hand.OrderByDescending(c => c.Value).ToArray();
pokerHand.Hand = (hand.CountBy(c => c.Face == hand[0].Face) >= 5) ? Hand.Flush : Hand.HighCard;

if (CheckIsStraight(hand))
{
pokerHand.Hand = pokerHand.Hand == Hand.Flush ? Hand.StraightFlush : Hand.Straight;
}

int previousCard = 0;

foreach (var card in hand)
{
if (previousCard == card.Value) continue;

int valueCount = hand.CountBy(c => c.Value == card.Value);

if (valueCount == 2)
{
if (pokerHand.Hand == Hand.Pair)
{
pokerHand.Hand = Hand.TwoPair;
}
else if (pokerHand.Hand == Hand.ThreeKind)
{
pokerHand.Hand = Hand.FullHouse;
}
else
{
pokerHand.Hand = Hand.Pair;
}
}
else if (valueCount == 3)
{
if (pokerHand.Hand == Hand.Pair)
{
pokerHand.Hand = Hand.FullHouse;
}
else
{
pokerHand.Hand = Hand.ThreeKind;
}
}
else if (valueCount == 4)
{
pokerHand.Hand = Hand.FourKind;
}

previousCard = card.Value;
}

return pokerHand;
}

private static bool CheckIsStraight(Card[] hand)
{
int previousValue = 0;

foreach (var card in hand.OrderBy(c => c.Value))
{
if (previousValue > 0)
{
if (card.Value == 14 && previousValue == 5) break;

if (card.Value != previousValue + 1)
{
return false;
}
}

previousValue = card.Value;
}

return true;
}

private static PokerHand[] FilterByHighCard(PokerHand[] pokerHands)
{
foreach (var hand in pokerHands)
{
hand.Cards = hand.Cards.OrderByDescending(c => c.Value).ToArray();

if ((hand.Hand == Hand.Straight || hand.Hand == Hand.StraightFlush) &&
(hand.Cards.Max(c => c.Value) == 14 && hand.Cards.Min(c => c.Value) == 2))
{
hand.Cards[0].Value = 1;
hand.Cards = hand.Cards.OrderByDescending(c => c.Value).ToArray();
}
}

for (int i = 0; i < pokerHands[0].Cards.Length; i++)
{
int highCard = pokerHands.Select(p => p.Cards[i]).Max(c => c.Value);

pokerHands = pokerHands.Where(p => p.Cards[i].Value == highCard).ToArray();

if (pokerHands.Length == 1) break;
}

return pokerHands;
}

private static PokerHand[] FilterByHigherHand(PokerHand[] pokerHands)
{
switch (pokerHands[0].Hand)
{
case Hand.Pair:
pokerHands = FilterBySet(pokerHands, 2);
break;

case Hand.TwoPair:
pokerHands = FilterBySet(pokerHands, 2);

if (pokerHands.Length > 1)
{
pokerHands = FilterBySet(pokerHands, 2, false);
}
break;

case Hand.ThreeKind:
case Hand.FullHouse:
pokerHands = FilterBySet(pokerHands, 3);
break;

case Hand.FourKind:
pokerHands = FilterBySet(pokerHands, 4);
break;
}

return pokerHands;
}

private static PokerHand[] FilterBySet(PokerHand[] pokerHands, int setCount, bool checkMax = true)
{
var groupedCards = pokerHands.Select(p => p.Cards.GroupBy(c => c.Value).Where(g => g.Count() == setCount));
int checkDigit;

if (checkMax)
{
checkDigit = groupedCards.SelectMany(g => g).Max(g => g.Key);
}
else
{
checkDigit = groupedCards.SelectMany(g => g).Min(g => g.Key);
}

pokerHands = pokerHands.Where(p => p.Cards.CountBy(c => c.Value == checkDigit) == setCount).ToArray();

return pokerHands;
}
}
}
```

In order for this code to compile, you'll need one more helper class that holds the extension CountBy.

Extensions.cs
```using System;
using System.Collections.Generic;

namespace Poker_Kata
{
public static class Extensions
{
public static int CountBy(this IEnumerable source, Func comparer)
{
int count = 0;

foreach (var s in source)
{
if (comparer(s))
{
count++;
}
}

return count;
}
}
}
```

I believe this logic should be able to handle any 5 card poker hand, if not, please post a comment on what hand it fails on and I will happily take a look.

Now that we have all the logic and the unit tests passing, we need to read in the .txt document that the challenge provides that holds all the hands. I did this in the Program class, so when the program executes, it completes the challenge assuming you give the argument of the .txt document.

Program.cs

```using System;
using System.IO;
using System.Linq;

namespace Poker_Kata
{
internal class Program
{
private static string[] LoadFile(string file)
{
string[] contents;

using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read))
using (var sr = new StreamReader(fs))
{
contents = sr.ReadToEnd().Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
}

return contents;
}

private static void Main(string[] args)
{
if (args.Length > 0)
{
int p1Wins = 0;
int p2Wins = 0;
int ties = 0;

var cards = LoadFile(args[0]);

Player p1 = new Player() { Name = "P1" };
Player p2 = new Player() { Name = "P2" };

foreach (var hand in cards)
{
var game = hand.Split(' ');

p1.SetHand(game.Take(5).ToArray());
p2.SetHand(game.Skip(5).Take(5).ToArray());

var winners = Poker.DetermineWinner(p1, p2);

p1.Hand = p1.Hand.OrderBy(c => c.Value).ToArray();
p2.Hand = p2.Hand.OrderBy(c => c.Value).ToArray();

if (winners.Length == 2)
{
ties++;
}
else if (winners[0].Player.Equals(p1))
{
p1Wins++;
}
else
{
p2Wins++;
}
}

Console.WriteLine("P1: {0}\r\nP2: {1}\r\nTies: {2}", p1Wins, p2Wins, ties);
}
}
}
}
```

I do hope, once again, that you attempt this challenge properly without just using my code to get the answer. I know that my code could be optimized further, but I see no need to do so at this time. Thanks for reading and happy coding.

No comments yet, be the first to leave one.

Contact Me
Recent Blog Activity
Blog Archive