Skip to content

Instantly share code, notes, and snippets.

@dholbrook
Created November 29, 2012 04:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dholbrook/4166809 to your computer and use it in GitHub Desktop.
Save dholbrook/4166809 to your computer and use it in GitHub Desktop.
euler 54
package euler
import scala.annotation.tailrec
object Euler54 extends App {
sealed abstract class CardRank extends Ordered[CardRank] {
import CardRank._
private def toInt(r: CardRank): Int = r match {
case Deuce => 0
case Three => 1
case Four => 2
case Five => 3
case Six => 4
case Seven => 5
case Eight => 6
case Nine => 7
case Ten => 8
case Jack => 9
case Queen => 10
case King => 11
case Ace => 12
}
private def fromInt(n: Int): Option[CardRank] = n match {
case 0 => Some(Deuce)
case 1 => Some(Three)
case 2 => Some(Four)
case 3 => Some(Five)
case 4 => Some(Six)
case 5 => Some(Seven)
case 6 => Some(Eight)
case 7 => Some(Nine)
case 8 => Some(Ten)
case 9 => Some(Jack)
case 10 => Some(Queen)
case 11 => Some(King)
case 12 => Some(Ace)
case _ => None
}
def compare(that: CardRank): Int =
toInt(this) compare toInt(that)
def next: Option[CardRank] =
fromInt(toInt(this) + 1)
}
object CardRank {
case object Deuce extends CardRank
case object Three extends CardRank
case object Four extends CardRank
case object Five extends CardRank
case object Six extends CardRank
case object Seven extends CardRank
case object Eight extends CardRank
case object Nine extends CardRank
case object Ten extends CardRank
case object Jack extends CardRank
case object Queen extends CardRank
case object King extends CardRank
case object Ace extends CardRank
def apply(r: Char): CardRank = r match {
case '2' => Deuce
case '3' => Three
case '4' => Four
case '5' => Five
case '6' => Six
case '7' => Seven
case '8' => Eight
case '9' => Nine
case 'T' => Ten
case 'J' => Jack
case 'Q' => Queen
case 'K' => King
case 'A' => Ace
case _ => throw new IllegalArgumentException
}
}
sealed abstract class Suit
object Suit {
case object Diamonds extends Suit
case object Spades extends Suit
case object Hearts extends Suit
case object Clubs extends Suit
def apply(s: Char): Suit = s match {
case 'D' => Diamonds
case 'S' => Spades
case 'H' => Hearts
case 'C' => Clubs
case _ => throw new IllegalArgumentException
}
}
case class Card(cardRank: CardRank, suit: Suit) extends Ordered[Card] {
def compare(that: Card) = this.cardRank compare that.cardRank
def next: Option[Card] = cardRank.next match {
case None => None
case Some(cr) => Some(Card(cr, suit))
}
}
object Card {
def apply(c: String): Card = c.toUpperCase().toList match {
case r :: s :: Nil => Card(CardRank(r), Suit(s))
case _ => throw new IllegalArgumentException
}
}
sealed abstract class HandRank extends Ordered[HandRank] {
import HandRank._
private def toInt(r: HandRank): Int = r match {
case HighCard => 0
case OnePair => 1
case TwoPairs => 2
case ThreeOfAKind => 3
case Straight => 4
case Flush => 5
case FullHouse => 6
case FourOfAKind => 7
case StraightFlush => 8
case RoyalFlush => 9
}
private def fromInt(i: Int): Option[HandRank] = i match {
case 0 => Some(HighCard)
case 1 => Some(OnePair)
case 2 => Some(TwoPairs)
case 3 => Some(ThreeOfAKind)
case 4 => Some(Straight)
case 5 => Some(Flush)
case 6 => Some(FullHouse)
case 7 => Some(FourOfAKind)
case 8 => Some(StraightFlush)
case 9 => Some(RoyalFlush)
case 10 => None
}
def compare(that: HandRank): Int =
toInt(this) compare toInt(that)
def next: Option[HandRank] =
fromInt(toInt(this) + 1)
}
object HandRank {
import CardRank._
case object HighCard extends HandRank
case object OnePair extends HandRank
case object TwoPairs extends HandRank
case object ThreeOfAKind extends HandRank
case object Straight extends HandRank
case object Flush extends HandRank
case object FullHouse extends HandRank
case object FourOfAKind extends HandRank
case object StraightFlush extends HandRank
case object RoyalFlush extends HandRank
def apply(cards: List[Card]): HandRank = {
import CardRank._
def isRoyalFlush = cards match {
case Card(r, _) :: _ if r == Ten && isFlush && isStraight => true
case _ => false
}
def isStraightFlush = isFlush && isStraight
def isFourOfAKind =
cards.groupBy(_.cardRank).filter(_._2.size == 4).size == 1
def isFullHouse = isThreeOfAKind && isOnePair
def isFlush: Boolean =
cards.groupBy(_.suit).size == 1
def isStraight: Boolean = {
@tailrec
def loop(cs: List[Card], next: Option[CardRank]): Boolean = (cs, next) match {
case (Nil, _) => true
case (hd :: tl, Some(n)) if hd.cardRank == n => loop(tl, hd.cardRank.next)
case _ => false
}
loop(cards.tail, cards.head.cardRank.next)
}
def isThreeOfAKind =
cards.groupBy(_.cardRank).filter(_._2.size == 3).size == 1
def isTwoPairs =
cards.groupBy(_.cardRank).filter(_._2.size == 2).size == 2
def isOnePair =
cards.groupBy(_.cardRank).filter(_._2.size == 2).size == 1
require(cards.length == 5, "lenght of hand must be 5")
require(cards.sorted == cards, "cards in hand must be sorted")
if (isRoyalFlush) RoyalFlush
else if (isStraightFlush) StraightFlush
else if (isFourOfAKind) FourOfAKind
else if (isFullHouse) FullHouse
else if (isFlush) Flush
else if (isStraight) Straight
else if (isThreeOfAKind) ThreeOfAKind
else if (isTwoPairs) TwoPairs
else if (isOnePair) OnePair
else HighCard
}
}
case class Hand(cards: List[Card]) extends Ordered[Hand] {
import HandRank._
import CardRank._
require(cards.length == 5, "lenght of hand must be 5, found " + cards.toList)
require(cards.sorted == cards, "cards in hand must be sorted")
val handRank = HandRank(cards)
def rankHigh: CardRank = handRank match {
case RoyalFlush => handHigh
case StraightFlush => handHigh
case FourOfAKind => fourOfAKindHigh
case FullHouse => handHigh
case Flush => handHigh
case Straight => handHigh
case ThreeOfAKind => threeOfAKindHigh
case TwoPairs => twoPairsHigh
case OnePair => onePairHigh
case HighCard => handHigh
}
def handHigh = cards.last.cardRank
private def fourOfAKindHigh =
cards.groupBy(_.cardRank).filter(_._2.size == 4).head._1
private def threeOfAKindHigh =
cards.groupBy(_.cardRank).filter(_._2.size == 3).head._1
private def twoPairsHigh =
cards.groupBy(_.cardRank).filter(_._2.size == 2).keys.toList.sorted.last
private def onePairHigh =
cards.groupBy(_.cardRank).filter(_._2.size == 2).head._1
def compare(that: Hand): Int = {
this.handRank compare that.handRank match {
case n if n != 0 => n
case _ => this.rankHigh compare that.rankHigh match {
case rh if rh != 0 => rh
case _ => this.handHigh compare that.handHigh
}
}
}
}
lazy val games = scala.io.Source.fromURL("http://projecteuler.net/project/poker.txt").mkString.split("\n")
var p1Wins = 0
for (game <- games) {
val cards = for (c <- game.split(" ")) yield Card(c)
val h1 = Hand(cards.slice(0, 5).toList.sorted)
val h2 = Hand(cards.slice(5, 10).toList.sorted)
if (h1 > h2) p1Wins = p1Wins + 1
}
println(p1Wins)
assert(p1Wins == 376)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment