Skip to content

Instantly share code, notes, and snippets.

@sorashi
Last active May 25, 2017 15:46
Show Gist options
  • Save sorashi/858a6bd8f723da0f2fb31af0aaefb18a to your computer and use it in GitHub Desktop.
Save sorashi/858a6bd8f723da0f2fb31af0aaefb18a to your computer and use it in GitHub Desktop.
This is the solution to problem 54 of Project Euler. Don't look at this if you don't want to see spoilers. The hand ranking uses a lot of LINQ, enjoy 😄
#r "System.Numerics"
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.IO;
using System.Diagnostics;
/*
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.
*/
enum Suit
{
Hearts, Clubs, Spades, Diamonds
}
enum Kind
{
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
}
class Card : IComparable<Card>
{
public override string ToString()
{
return Kind.ToString() + " of " + Suit.ToString();
}
public static Card Parse(string s)
{
if (s.Length != 2) throw new FormatException();
int kind;
if (!int.TryParse(s[0].ToString(), out kind))
{
switch (s[0])
{
case 'T':
kind = 10;
break;
case 'J':
kind = 11;
break;
case 'Q':
kind = 12;
break;
case 'K':
kind = 13;
break;
case 'A':
kind = 14;
break;
default:
throw new FormatException("Unexpected KIND");
}
}
Suit suit;
switch (s[1])
{
case 'H':
suit = Suit.Hearts;
break;
case 'C':
suit = Suit.Clubs;
break;
case 'S':
suit = Suit.Spades;
break;
case 'D':
suit = Suit.Diamonds;
break;
default:
throw new FormatException("Unexpected SUIT");
}
return new Card(suit, (Kind)kind);
}
public Suit Suit { get; set; }
public Kind Kind { get; set; }
public Card(Suit s, Kind k)
{
this.Suit = s;
this.Kind = k;
}
public bool IsSameSuit(Card other)
{
return other?.Suit == this?.Suit;
}
public int CompareTo(Card other)
{
if (this == null && other == null) return 0;
if (other == null) return 1;
if (this == null) return -1;
if (this.Kind > other.Kind) return 1;
if (this.Kind < other.Kind) return -1;
return 0;
}
public static int operator -(Card first, Card second)
{
return (int)first.Kind - (int)second.Kind;
}
}
enum Rank
{
HighCard = 1,
OnePair = 2,
TwoPairs = 3,
ThreeOfKind = 4,
Straight = 5,
Flush = 6,
FullHouse = 7,
FourOfKind = 8,
StraightFlush = 9,
RoyalFlush = 10
};
class Hand
{
public Rank Rank { get; set; }
public Card HighCard { get; set; }
// This is just for full house
public Card HighCard2 { get; set; }
public List<Card> OtherCards = new List<Card>();
public static Hand ResolveHand(Card[] hand)
{
var h = new Hand();
var handList = hand.ToList();
// Royal Flush
if (handList.GroupBy(x => x.Suit).Count() == 1 && handList[0].Kind == Kind.Ace && handList[1].Kind == Kind.King &&
handList[2].Kind == Kind.Queen && handList[3].Kind == Kind.Jack &&
handList[4].Kind == Kind.Ten)
{
h.Rank = Rank.RoyalFlush;
h.HighCard = handList.First();
// no other cards
return h;
}
// Straight Flush
if (handList.GroupBy(x => x.Suit).Count() == 1 && handList.Zip(handList.Skip(1), (a, b) => a - b).All(x => x == 1))
{
h.Rank = Rank.StraightFlush;
h.HighCard = handList.First();
return h;
}
// Four of Kind
var fourOfKind = handList.GroupBy(x => x.Kind).OrderByDescending(x => x.Count()).First();
if (fourOfKind.Count() == 4)
{
h.Rank = Rank.FourOfKind;
h.HighCard = fourOfKind.First();
h.OtherCards.AddRange(handList.Except(fourOfKind));
return h;
}
// Full House
var fullHouse = handList.GroupBy(x => x.Kind).OrderByDescending(x => x.Count());
if (fullHouse.First().Count() == 3 && fullHouse.Skip(1).First().Count() == 2)
{
h.Rank = Rank.FullHouse;
h.HighCard = fullHouse.First().First();
h.HighCard2 = fullHouse.Skip(1).First().First();
return h;
}
// Flush
if (handList.GroupBy(x => x.Suit).Count() == 1)
{
h.Rank = Rank.Flush;
h.HighCard = handList.First();
return h;
}
// Straight
if (handList.Zip(handList.Skip(1), (a, b) => a - b).All(x => x == 1))
{
h.Rank = Rank.Straight;
h.HighCard = handList.First();
return h;
}
// Three of a Kind
var threeOfKind = handList.GroupBy(x => x.Kind).FirstOrDefault(x => x.Count() == 3);
if (threeOfKind != null)
{
h.Rank = Rank.ThreeOfKind;
h.HighCard = threeOfKind.First();
h.OtherCards.AddRange(handList.Except(threeOfKind));
return h;
}
// Two Pairs
var twoPairs = handList.GroupBy(x => x.Kind).OrderByDescending(x => x.Count()).Take(2);
if (twoPairs.All(x => x.Count() == 2))
{
h.Rank = Rank.TwoPairs;
h.HighCard = twoPairs.OrderByDescending(x => x.Key).First().First();
h.OtherCards.AddRange(handList.Except(twoPairs.SelectMany(x => x)));
return h;
}
// One Pair
var onePair = handList.GroupBy(x => x.Kind).OrderByDescending(x => x.Count()).First();
if (onePair.Count() == 2)
{
h.Rank = Rank.OnePair;
h.HighCard = onePair.First();
h.OtherCards.AddRange(handList.Except(onePair));
return h;
}
// High Card
h.Rank = Rank.HighCard;
h.HighCard = handList.OrderByDescending(x => x.Kind).First();
h.OtherCards.AddRange(handList.Except(new[] { h.HighCard }));
return h;
}
}
var sw = Stopwatch.StartNew();
var input = File.ReadAllLines("p054_poker.txt").Select(
x => new Card[][]
{
x.Split(' ').Take(5).Select(c=>Card.Parse(c)).OrderByDescending(c=>c).ThenBy(a=>a.Suit).ToArray(),
x.Split(' ').Skip(5).Select(c=>Card.Parse(c)).OrderByDescending(c=>c).ThenBy(a=>a.Suit).ToArray()
});
Console.WriteLine("Parsing:\t" + sw.Elapsed);
sw.Restart();
var hands = input.Select(x => x.Select(y => Hand.ResolveHand(y)).ToArray()).ToArray();
Console.WriteLine("Hand resolved:\t" + sw.Elapsed);
sw.Stop();
bool PlayerOneWon(Hand p1, Hand p2)
{
if (p1.Rank > p2.Rank)
{
return true;
}
if(p1.Rank == Rank.FullHouse && p2.Rank == Rank.FullHouse){
if(p1.HighCard.Kind == p2.HighCard.Kind) return p1.HighCard2.Kind > p2.HighCard2.Kind;
return p1.HighCard.Kind > p2.HighCard.Kind;
}
if (p1.Rank == p2.Rank && p1.HighCard.Kind > p2.HighCard.Kind)
{
return true;
}
if (p1.Rank == p2.Rank && p1.HighCard.Kind == p2.HighCard.Kind)
{
for (var i = 0; i < Math.Min(p1.OtherCards.Count, p2.OtherCards.Count); i++)
{
if (p1.OtherCards[i].Kind > p2.OtherCards[i].Kind)
{
return true;
}
if (p1.OtherCards[i].Kind < p2.OtherCards[i].Kind) return false; ;
}
}
return false;
}
Console.WriteLine("Running tests:");
var assertions = new[] {
"5H 5C 6S 7S KD\t2C 3S 8S 8D TD\tfalse",
"5D 8C 9S JS AC\t2C 5C 7D 8S QH\ttrue",
"2D 9C AS AH AC\t3D 6D 7D TD QD\tfalse",
"4D 6S 9H QH QC\t3D 6D 7H QD QS\ttrue",
"2H 2D 4C 4D 4S\t3C 3D 3S 9S 9D\ttrue"
};
foreach (var a in assertions)
{
var b = a.Split('\t');
var p1 = Hand.ResolveHand(b[0].Split(' ').Select(x => Card.Parse(x)).ToArray());
var p2 = Hand.ResolveHand(b[1].Split(' ').Select(x => Card.Parse(x)).ToArray());
var r = b[2] == "true";
Console.WriteLine((PlayerOneWon(p1, p2) != r ? "Fail" : "Pass") + "\n\t" + a + "\n\t" + p1.Rank.ToString() + "|" + p1.HighCard.ToString() + "\t" + p2.Rank.ToString() + "|" + p2.HighCard.ToString());
}
sw.Restart();
Console.WriteLine("Result:\t" + hands.Sum(x=>PlayerOneWon(x[0], x[1]) ? 1 : 0));
sw.Stop();
Console.WriteLine("Time:\t" + sw.Elapsed);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment