Created
November 29, 2012 04:31
-
-
Save dholbrook/4166809 to your computer and use it in GitHub Desktop.
euler 54
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
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