Skip to content

Instantly share code, notes, and snippets.

@bencoveney
Created August 4, 2014 13:57
Show Gist options
  • Save bencoveney/eb09ec79cd1e6ea907e9 to your computer and use it in GitHub Desktop.
Save bencoveney/eb09ec79cd1e6ea907e9 to your computer and use it in GitHub Desktop.
Poker hands
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;
using System.Threading.Tasks;
namespace EulerProject
{
class Program
{
#region Card
/// <summary>
/// A playing card
/// </summary>
class Card
{
/// <summary>
/// Initializes a new instance of the <see cref="Card"/> struct.
/// </summary>
/// <param name="suit">The suit.</param>
/// <param name="value">The value.</param>
public Card(string cardValue)
{
char[] cardComponents = cardValue.ToCharArray();
// Parse the card value
Value = Value.None;
switch (cardComponents[0])
{
case '2':
Value = Value.Two;
break;
case '3':
Value = Value.Three;
break;
case '4':
Value = Value.Four;
break;
case '5':
Value = Value.Five;
break;
case '6':
Value = Value.Six;
break;
case '7':
Value = Value.Seven;
break;
case '8':
Value = Value.Eight;
break;
case '9':
Value = Value.Nine;
break;
case 'T':
Value = Value.Ten;
break;
case 'J':
Value = Value.Jack;
break;
case 'Q':
Value = Value.Queen;
break;
case 'K':
Value = Value.King;
break;
case 'A':
Value = Value.Ace;
break;
default :
throw new ArgumentException("Invalid Card Value");
}
// Parse the card's suit
Suit = Suit.None;
switch (cardComponents[1])
{
case 'C' :
Suit = Suit.Club;
break;
case 'S':
Suit = Suit.Spade;
break;
case 'H':
Suit = Suit.Heart;
break;
case 'D':
Suit = Suit.Diamond;
break;
default:
throw new ArgumentException("Invalid Card Suit");
}
}
/// <summary>
/// Gets or sets the suit.
/// </summary>
/// <value>
/// The suit.
/// </value>
public Suit Suit { get; private set; }
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>
/// The value.
/// </value>
public Value Value { get; private set; }
}
/// <summary>
/// A card's suit
/// </summary>
enum Suit
{
None,
Club,
Spade,
Heart,
Diamond,
}
/// <summary>
/// A card's value
/// </summary>
enum Value
{
None,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
Ten = 10,
Jack = 11,
Queen = 12,
King = 13,
Ace = 14,
}
#endregion
#region Hand
class Hand
{
public Hand(List<Card> cards)
{
Cards = cards;
}
public List<Card> Cards { get; private set; }
public List<Value> CardValues {
get
{
List<Value> values = new List<Value>();
Cards.ForEach(x => values.Add(x.Value));
return values;
}
}
public bool isBetterThan(Hand otherHand)
{
// check for royal flushes
if (this.hasRoyalFlush() && !otherHand.hasRoyalFlush()) return true;
if (!this.hasRoyalFlush() && otherHand.hasRoyalFlush()) return false;
if (this.hasRoyalFlush() && otherHand.hasRoyalFlush()) return this.beatsHandOnHighValues(otherHand);
// If one hand has a straight flush and the other doesnt
if (this.hasStraightFlush() && !otherHand.hasStraightFlush()) return true;
if (!this.hasStraightFlush() && otherHand.hasStraightFlush()) return false;
if (this.hasStraightFlush() && otherHand.hasStraightFlush()) return this.beatsHandOnHighValues(otherHand);
// If one hand has a four of a kind and the other doesnt
if (this.hasFourOfAKind() && !otherHand.hasFourOfAKind()) return true;
if (!this.hasFourOfAKind() && otherHand.hasFourOfAKind()) return false;
if (this.hasFourOfAKind() && otherHand.hasFourOfAKind()) return this.beatsHandOnHighValues(otherHand);
// If one hand has a full house and the other doesnt
if (this.hasFullHouse() && !otherHand.hasFullHouse()) return true;
if (!this.hasFullHouse() && otherHand.hasFullHouse()) return false;
if (this.hasFullHouse() && otherHand.hasFullHouse()) return this.beatsHandOnHighValues(otherHand);
// If one hand has a flush and the other doesnt
if (this.hasFlush() && !otherHand.hasFlush()) return true;
if (!this.hasFlush() && otherHand.hasFlush()) return false;
if (this.hasFlush() && otherHand.hasFlush()) return this.beatsHandOnHighValues(otherHand);
// If one hand has a _ and the other doesnt
if (this.hasStraight() && !otherHand.hasStraight()) return true;
if (!this.hasStraight() && otherHand.hasStraight()) return false;
if (this.hasStraight() && otherHand.hasStraight()) return this.beatsHandOnHighValues(otherHand);
// If one hand has a _ and the other doesnt
if (this.hasThreeOfAKind() && !otherHand.hasThreeOfAKind()) return true;
if (!this.hasThreeOfAKind() && otherHand.hasThreeOfAKind()) return false;
if (this.hasThreeOfAKind() && otherHand.hasThreeOfAKind()) return this.beatsHandOnHighValues(otherHand);
// If one hand has a _ and the other doesnt
if (this.hasTwoPair() && !otherHand.hasTwoPair()) return true;
if (!this.hasTwoPair() && otherHand.hasTwoPair()) return false;
if (this.hasTwoPair() && otherHand.hasTwoPair()) return this.beatsHandOnHighValues(otherHand);
// If one hand has a _ and the other doesnt
if (this.hasOnePair() && !otherHand.hasOnePair()) return true;
if (!this.hasOnePair() && otherHand.hasOnePair()) return false;
if (this.hasOnePair() && otherHand.hasOnePair()) return this.beatsHandOnHighValues(otherHand);
return this.beatsHandOnHighValues(otherHand);
}
/// <summary>
/// </summary>
/// <param name="hand"></param>
/// <returns></returns>
private bool beatsHandOnHighValues(Hand hand)
{
// relying on high values
// Console.WriteLine("Hand 1: {0} | Hand 2: {1}", this.ToString(), hand.ToString());
// Get sorted values
List<Value> sortedValues = CardValues;
sortedValues.Sort();
sortedValues.Reverse();
List<Value> otherHandSortedValues = hand.CardValues;
otherHandSortedValues.Sort();
otherHandSortedValues.Reverse();
// if theres 4 of a kind
if (this.getQuadValues().Count() > 0)
{
// Compare them
if (this.getQuadValues()[0] > hand.getQuadValues()[0]) return true;
if (this.getQuadValues()[0] < hand.getQuadValues()[0]) return false;
}
// if theres 3 of a kind
if (this.getTripleValues().Count() > 0)
{
// Compare them
if (this.getTripleValues()[0] > hand.getTripleValues()[0]) return true;
if (this.getTripleValues()[0] < hand.getTripleValues()[0]) return false;
}
// if there are pairs
if (this.getPairedValues().Count() > 0)
{
// get the values where pairs occur
List<Value> pairs = getPairedValues();
pairs.Sort();
pairs.Reverse();
List<Value> otherPairs = hand.getPairedValues();
otherPairs.Sort();
otherPairs.Reverse();
// for each value
for (int i = 0; i < pairs.Count(); i++)
{
// Compare them
if (pairs[i] > otherPairs[i]) return true;
if (pairs[i] < otherPairs[i]) return false;
}
}
for (int i = 0; i < Cards.Count(); i++)
{
// If this hand's card is higher then succeed
if (sortedValues[i] > otherHandSortedValues[i])
return true;
// If the other hand's card is higher then fail
if (otherHandSortedValues[i] > sortedValues[i])
return false;
}
return false;
}
/// <summary>
/// determines whether the had is a consecutive sequence of 1 type going from ten to ace
/// </summary>
/// <returns></returns>
private bool hasRoyalFlush()
{
// if not a straight flush this cant be a royal flush
if (!hasStraightFlush()) return false;
// Get a list of all card values
List<int> values = new List<int>();
Cards.ForEach(x => values.Add((int)x.Value));
// Check the lowest card is a 10
if (values.Min() != 10) return false;
// Check the highest card is an ace
if (values.Max() != 14) return false;
return true;
}
/// <summary>
/// determines whether the hand is a consecutive sequence of 1 type
/// </summary>
/// <returns></returns>
private bool hasStraightFlush()
{
return (hasStraight() && hasFlush());
}
/// <summary>
/// determines whether the hand has a 4 of a kind
/// </summary>
/// <returns></returns>
private bool hasFourOfAKind()
{
return getQuadValues().Count() == 1 ? true : false;
}
private List<Value> getQuadValues()
{
// Get a list of each card's value
List<Value> Values = new List<Value>();
Cards.ForEach(x => Values.Add(x.Value));
// Foreach possible card value find out if it is a quad
List<Value> quads = new List<Value>();
foreach (Value value in (Value[])Enum.GetValues(typeof(Value)))
{
// test for quad
// quit after finding one, there wont be any more
if (Values.Where(x => x == value).Count() == 4) quads.Add(value);
}
return quads;
}
/// <summary>
/// determines whether the hand has 1 pair and 1 triple
/// </summary>
/// <returns></returns>
private bool hasFullHouse()
{
return (hasOnePair() && hasThreeOfAKind());
}
/// <summary>
/// determines whether the cards are all the same type
/// </summary>
/// <returns></returns>
private bool hasFlush()
{
// Get the suit of a card
Suit suit = Cards.First().Suit;
// If the cards dont all match that suit then fail
if (Cards.Where(x => x.Suit == suit).Count() != Cards.Count) return false;
return true;
}
/// <summary>
/// determines whether the cards are a consecutive sequence
/// </summary>
/// <returns></returns>
private bool hasStraight()
{
// Get a list of card values
List<int> values = new List<int>();
Cards.ForEach(x => values.Add((int)x.Value));
// If the cards arent all distinct then fail
if (values.Distinct().Count() != Cards.Count) return false;
// If the cards arent consecutive then fail
if (values.Max() - values.Min() != (Cards.Count-1)) return false;
return true;
}
/// <summary>
/// determines whether the hand has a 3 of a kind
/// </summary>
/// <returns></returns>
private bool hasThreeOfAKind()
{
return getTripleValues().Count() == 1 ? true : false;
}
private List<Value> getTripleValues()
{
// Get a list of each card's value
List<Value> Values = new List<Value>();
Cards.ForEach(x => Values.Add(x.Value));
// Foreach possible card value find out if it is a quad
List<Value> triples = new List<Value>();
foreach (Value value in (Value[])Enum.GetValues(typeof(Value)))
{
// test for quad
// quit after finding one, there wont be any more
if (Values.Where(x => x == value).Count() == 3) triples.Add(value);
}
return triples;
}
/// <summary>
/// Determines whether the hand has exactly 1 pair.
/// </summary>
/// <returns></returns>
private bool hasOnePair()
{
return getPairedValues().Count() == 1 ? true : false;
}
/// <summary>
/// Determines whether the hand has exactly 2 pairs.
/// </summary>
/// <returns></returns>
private bool hasTwoPair()
{
return getPairedValues().Count() == 2 ? true : false;
}
/// <summary>
/// Returns the number of pairs in a hand
/// </summary>
/// <returns></returns>
private List<Value> getPairedValues()
{
// Get a list of each card's value
List<Value> Values = new List<Value>();
Cards.ForEach(x => Values.Add(x.Value));
// Foreach possible card value find out if it is a pair
List<Value> pairs = new List<Value>();
foreach (Value value in (Value[])Enum.GetValues(typeof(Value)))
{
// test for pair
if (Values.Where(x => x == value).Count() == 2) pairs.Add(value);
}
return pairs;
}
public override string ToString()
{
StringBuilder result = new StringBuilder();
foreach (Card c in Cards)
{
switch (c.Suit)
{
case Suit.None:
default :
result.Append("ERROR");
break;
case Suit.Club:
result.Append("C");
break;
case Suit.Spade:
result.Append("S");
break;
case Suit.Heart:
result.Append("H");
break;
case Suit.Diamond:
result.Append("D");
break;
}
switch (c.Value)
{
default:
case Value.None:
result.Append("ERROR");
break;
case Value.Two:
result.Append("2");
break;
case Value.Three:
result.Append("3");
break;
case Value.Four:
result.Append("4");
break;
case Value.Five:
result.Append("5");
break;
case Value.Six:
result.Append("6");
break;
case Value.Seven:
result.Append("7");
break;
case Value.Eight:
result.Append("8");
break;
case Value.Nine:
result.Append("9");
break;
case Value.Ten:
result.Append("T");
break;
case Value.Jack:
result.Append("J");
break;
case Value.Queen:
result.Append("Q");
break;
case Value.King:
result.Append("K");
break;
case Value.Ace:
result.Append("A");
break;
}
result.Append(",");
}
return result.ToString();
}
}
#endregion
/// <summary>
/// Mains the specified arguments.
/// </summary>
/// <param name="args">The arguments.</param>
[STAThread]
static void Main(string[] args)
{
// Read all lines
List<string> lines = File.ReadAllLines("poker.txt").ToList();
// For each line read the hands
int numOfWinsForPlayerOne = 0;
int numOfWinsForPlayerTwo = 0;
foreach (string line in lines)
{
// Split into parts
List<string> lineParts = line.Split(' ').ToList();
// Parse parts as cards
List<Card> cards = new List<Card>();
lineParts.ForEach(x => cards.Add(new Card(x)));
// create the hands
Hand PlayerOne = new Hand(cards.GetRange(0, 5));
Hand PlayerTwo = new Hand(cards.GetRange(5, 5));
// Compare the hands
if (PlayerOne.isBetterThan(PlayerTwo))
{
numOfWinsForPlayerOne++;
}
if (PlayerTwo.isBetterThan(PlayerOne))
{
numOfWinsForPlayerTwo++;
}
}
Console.WriteLine("Player 1 wins: {0}", numOfWinsForPlayerOne);
Console.WriteLine("Player 2 wins: {0}", numOfWinsForPlayerTwo);
Console.WriteLine(numOfWinsForPlayerOne + numOfWinsForPlayerTwo);
Tests();
Console.Read();
}
/// <summary>
/// Testses this instance.
/// </summary>
static void Tests()
{
// All same but 5
RunTest("9C 8C 6D 5C 3C", "9C 8C 6D 5C 2C", true);
// All same but 4
RunTest("9C 8C 6D 5C 3C", "9C 8C 6D 4C 3C", true);
// All same but 3
RunTest("9C 8C 7D 5C 3C", "9C 8C 6D 5C 3C", true);
// All same but 2
RunTest("9C 8C 6D 5C 3C", "9C 7C 6D 5C 3C", true);
// All same but 1
RunTest("9C 7C 6D 5C 3C", "8C 7C 6D 5C 3C", true);
// Quads above diff
RunTest("AC AC AD AC KC", "AC AC AD AC QC", true);
// Quads below diff
RunTest("QC QC QD QC AC", "QC QC QD QC KC", true);
// Triples above diff highest high
RunTest("AC AC AD KC QC", "AC AC AD KC JC", true);
// Triples above diff lowest high
RunTest("AC AC AD KC QC", "AC AC AD KC JC", true);
// Triples between diff highest high
RunTest("QC QC QD AC JC", "QC QC QD KC JC", true);
// Triples between diff lowest high
RunTest("QC QC QD AC JC", "QC QC QD AC TC", true);
// Triples below diff highest high
RunTest("9C 9C 9D AC JC", "9C 9C 9D KC JC", true);
// Triples below diff lowest high
RunTest("9C 9C 9D AC QC", "9C 9C 9D AC JC", true);
// Full House
RunTest("9C 9C 9D JC JC", "9C 9C 9D TC TC", true);
// Full House
RunTest("9C 9C 9D TC TC", "9C 9C 9D 8C 8C", true);
// Full House
RunTest("9C 9C 9D 8C 8C", "9C 9C 9D 7C 7C", true);
}
static void RunTest(string hand1, string hand2, bool isHand1Winner)
{
// Split into parts
List<string> hand1Parts = hand1.Split(' ').ToList();
List<string> hand2Parts = hand2.Split(' ').ToList();
// Parse parts as cards
List<Card> hand1cards = new List<Card>();
hand1Parts.ForEach(x => hand1cards.Add(new Card(x)));
List<Card> hand2cards = new List<Card>();
hand2Parts.ForEach(x => hand2cards.Add(new Card(x)));
// create the hands
Hand PlayerOne = new Hand(hand1cards);
Hand PlayerTwo = new Hand(hand2cards);
if (PlayerOne.isBetterThan(PlayerTwo) != isHand1Winner || PlayerTwo.isBetterThan(PlayerOne) == isHand1Winner)
{
Console.WriteLine("Hand1: {0} Hand2: {1}", hand1.ToString(), hand2.ToString());
}
else
{
Console.WriteLine("Tests Passed");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment