Last active
May 25, 2017 15:46
-
-
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 😄
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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