I'm working with below stuff as a way to learn functional programming and scala, I came from a python background.
case class Point(x: Int, y:Int)
object Operation extends Enumeration {
type Operation = Value
val TurnOn, TurnOff, Toggle = Value
}
object Status extends Enumeration {
type Status = Value
val On, Off = Value
}
val inputs: List[String]
def parseInputs(s: String): (Point, Point, Operation)
Idea is that we have a light matrix(Point), each Point can be either On or Off as describe in Status.
My inputs is a series of command, asking to either TurnOn, TurnOff or Toggle all the lights from one Point to another Point (The rectangular area defined using two points are bottom-left corner and upper-right corner).
My original solution is like this:
type LightStatus = mutable.Map[Point, Status]
val lightStatus = mutable.Map[Point, Status]()
def updateStatus(p1: Point, p2: Point, op: Operation): Unit = {
(p1, p2) match {
case (Point(x1, y1), Point(x2, y2)) =>
for (x <- x1 to x2)
for (y <- y1 to y2) {
val p = Point(x, y)
val currentStatus = lightStatus.getOrElse(p, Off)
(op, currentStatus) match {
case (TurnOn, _) => lightStatus.update(p, On)
case (TurnOff, _) => lightStatus.update(p, Off)
case (Toggle, On) => lightStatus.update(p, Off)
case (Toggle, Off) => lightStatus.update(p, On)
}
}
}
}
for ((p1, p2, op) <- inputs.map(parseInputs)) {
updateStatus(p1, p2, op)
}
Now I have lightStatus as a map to describe the end status of the entire matrix. This works, but seems less functional to me as I was using a mutable Map instead of an immutable object, so I tried to re-factor this into a more functional way, I ended up with this:
inputs.flatMap(s => parseInputs(s) match {
case (Point(x1, y1), Point(x2, y2), op) =>
for (x <- x1 to x2;
y <- y1 to y2)
yield (Point(x, y), op)
}).foldLeft(Map[Point, Status]())((m, item) => {
item match {
case (p, op) =>
val currentStatus = m.getOrElse(p, Off)
(op, currentStatus) match {
case (TurnOn, _) => m.updated(p, On)
case (TurnOff, _) => m.updated(p, Off)
case (Toggle, On) => m.updated(p, Off)
case (Toggle, Off) => m.updated(p, On)
}
}
})
I have couple questions regarding this process:
My second version doesn't seem as clean and straightforward as the first version to me, I'm not sure if this is because I'm not that familiar with functional programming or I just wrote bad functional code.
Is there someway to simplify the syntax on second piece? Especially the (m, item) => ??? function in the foldLeft part? Something like (m, (point, operation)) => ??? gives me syntax error
The second piece of code takes significantly longer to run, which surprise me a bit as these two code essentially is doing the same thing, As I don't have too much Java background, Any idea what might be causing the performance issue?
Many thanks!
From a Functional Programming perspective, your code suffers from the fact that...
The lightStatus Map "maintains state" and thus requires mutation.
A large "area" of status changes == a large number of data updates.
If you can accept each light status as a Boolean value, here's a design that requires no mutation and has fast status updates even over very large areas.
case class Point(x: Int, y:Int)
class LightGrid private (status: Point => Boolean) {
def apply(p: Point): Boolean = status(p)
private def isWithin(p:Point, ll:Point, ur:Point) =
ll.x <= p.x && ll.y <= p.y && p.x <= ur.x && p.y <= ur.y
//each light op returns a new LightGrid
def turnOn(lowerLeft: Point, upperRight: Point): LightGrid =
new LightGrid(point =>
isWithin(point, lowerLeft, upperRight) || status(point))
def turnOff(lowerLeft: Point, upperRight: Point): LightGrid =
new LightGrid(point =>
!isWithin(point, lowerLeft, upperRight) && status(point))
def toggle(lowerLeft: Point, upperRight: Point): LightGrid =
new LightGrid(point =>
isWithin(point, lowerLeft, upperRight) ^ status(point))
}
object LightGrid { //the public constructor
def apply(): LightGrid = new LightGrid(_ => false)
}
usage:
val ON = true
val OFF = false
val lg = LightGrid().turnOn(Point(2,2), Point(11,11)) //easy numbers
.turnOff(Point(8,8), Point(10,10))
.toggle(Point(1,1), Point(9,9))
lg(Point(1,1)) //ON
lg(Point(7,7)) //OFF
lg(Point(8,8)) //ON
lg(Point(9,9)) //ON
lg(Point(10,10)) //OFF
lg(Point(11,11)) //ON
lg(Point(12,12)) //OFF
Related
I'm using pattern matching in scala a lot. Many times I need to do some calculations in guard part and sometimes they are pretty expensive. Is there any way to bind calculated values to separate value?
//i wan't to use result of prettyExpensiveFunc in body safely
people.collect {
case ...
case Some(Right((x, y))) if prettyExpensiveFunc(x, y) > 0 => prettyExpensiveFunc(x)
}
//ideally something like that could be helpful, but it doesn't compile:
people.collect {
case ...
case Some(Right((x, y))) if {val z = prettyExpensiveFunc(x, y); y > 0} => z
}
//this sollution works but it isn't safe for some `Seq` types and is risky when more cases are used.
var cache:Int = 0
people.collect {
case ...
case Some(Right((x, y))) if {cache = prettyExpensiveFunc(x, y); cache > 0} => cache
}
Is there any better solution?
ps: Example is simplified and I don't expect anwers that shows that I don't need pattern matching here.
You can use cats.Eval to make expensive calculations lazy and memoizable, create Evals using .map and extract .value (calculated at most once - if needed) in .collect
values.map { value =>
val expensiveCheck1 = Eval.later { prettyExpensiveFunc(value) }
val expensiveCheck2 = Eval.later { anotherExpensiveFunc(value) }
(value, expensiveCheck1, expensiveCheck2)
}.collect {
case (value, lazyResult1, _) if lazyResult1.value > 0 => ...
case (value, _, lazyResult2) if lazyResult2.value > 0 => ...
case (value, lazyResult1, lazyResult2) if lazyResult1.value > lazyResult2.value => ...
...
}
I don't see a way of doing what you want without creating some implementation of lazy evaluation, and if you have to use one, you might as well use existing one instead of rolling one yourself.
EDIT. Just in case you haven't noticed - you aren't losing the ability to pattern match by using tuple here:
values.map {
// originial value -> lazily evaluated memoized expensive calculation
case a # Some(Right((x, y)) => a -> Some(Eval.later(prettyExpensiveFunc(x, y)))
case a => a -> None
}.collect {
// match type and calculation
...
case (Some(Right((x, y))), Some(lazyResult)) if lazyResult.value > 0 => ...
...
}
Why not run the function first for every element and then work with a tuple?
Seq(1,2,3,4,5).map(e => (e, prettyExpensiveFunc(e))).collect {
case ...
case (x, y) if y => y
}
I tried own matchers and effect is somehow OK, but not perfect. My matcher is untyped, and it is bit ugly to make it fully typed.
class Matcher[T,E](f:PartialFunction[T, E]) {
def unapply(z: T): Option[E] = if (f.isDefinedAt(z)) Some(f(z)) else None
}
def newMatcherAny[E](f:PartialFunction[Any, E]) = new Matcher(f)
def newMatcher[T,E](f:PartialFunction[T, E]) = new Matcher(f)
def prettyExpensiveFunc(x:Int) = {println(s"-- prettyExpensiveFunc($x)"); x%2+x*x}
val x = Seq(
Some(Right(22)),
Some(Right(10)),
Some(Left("Oh now")),
None
)
val PersonAgeRank = newMatcherAny { case Some(Right(x:Int)) => (x, prettyExpensiveFunc(x)) }
x.collect {
case PersonAgeRank(age, rank) if rank > 100 => println("age:"+age + " rank:" + rank)
}
https://scalafiddle.io/sf/hFbcAqH/3
I have a custom class, A, and I have defined some operations within the class as follows:
def +(that: A) = ...
def -(that: A) = ...
def *(that: A) = ...
def +(that: Double) = ...
def -(that: Double) = ...
def *(that: Double) = ...
In order to have something like 2.0 + x make sense when x is of type A, I have defined the following implicit class:
object A {
implicit class Ops (lhs: Double) {
def +(rhs: A) = ...
def -(rhs: A) = ...
def *(rhs: A) = ...
}
}
This all works fine normally. Now I introduce a compiler plugin with a TypingTransformer that performs some optimizations. Specifically, let's say I have a ValDef:
val x = y + a * z
where x, y, and z are of type A, and a is a Double. Normally, this compiles fine. I put it through the optimizer, which uses quasiquotes to change y + a * z into something else. BUT in this particular example, the expression is unchanged (there are no optimizations to perform). Suddenly, the compiler no longer does an implicit conversion for a * z.
To summarize, I have a compiler plugin that takes an expression that would normally have implicit conversions applied to it. It creates a new expression via quasiquotes, which syntactically appears the same as the old expression. But for this new expression, the compiler fails to perform implicit conversion.
So my question — how does the compiler determine that an implicit conversion must take place? Is there a specific flag or something that needs to be set in the AST that quasiquotes are failing to set?
UPDATE
The plugin phase looks something like this:
override def transform(tree: Tree) = tree match {
case ClassDef(classmods, classname, classtparams, impl) if classname.toString == "Module" => {
var implStatements: List[Tree] = List()
for (node <- impl.body) node match {
case DefDef(mods, name, tparams, vparamss, tpt, body) if name.toString == "loop" => {
var statements: List[Tree] = List()
for (statement <- body.children.dropRight(1)) statement match {
case Assign(opd, rhs) => {
val optimizedRHS = optimizeStatement(rhs)
statements = statements ++ List(Assign(opd, optimizedRHS))
}
case ValDef(mods, opd, tpt, rhs) => {
val optimizedRHS = optimizeStatement(rhs)
statements = statements ++
List(ValDef(mods, opd, tpt, optimizedRHS))
}
case Apply(Select(src1, op), List(src2)) if op.toString == "push" => {
val optimizedSrc2 = optimizeStatement(src2)
statements = statements ++
List(Apply(Select(src1, op), List(optimizedSrc2)))
}
case _ => statements = statements ++ List(statement)
}
val newBody = Block(statements, body.children.last)
implStatements = implStatements ++
List(DefDef(mods, name, tparams, vparamss, tpt, newBody))
}
case _ => implStatements = implStatements ++ List(node)
}
val newImpl = Template(impl.parents, impl.self, implStatements)
ClassDef(classmods, classname, classtparams, newImpl)
}
case _ => super.transform(tree)
}
def optimizeStatement(tree: Tree): Tree = {
// some logic that transforms
// 1.0 * x + 2.0 * (x + y)
// into
// 3.0 * x + 2.0 * y
// (i.e. distribute multiplication & collect like terms)
//
// returned trees are always newly created
// returned trees are create w/ quasiquotes
// something like
// 1.0 * x + 2.0 * y
// will return
// 1.0 * x + 2.0 * y
// (i.e. syntactically unchanged)
}
UPDATE 2
Please refer to this GitHub repo for a minimum working example: https://github.com/darsnack/compiler-plugin-demo
The issue is that a * z turns into a.<$times: error>(z) after I optimize the statement.
The issue is related to the pos field associated with trees. Even though everything is happening before the namer, and the tree with and without the compiler plugin is syntactically the same, the compiler will not be able to infer implicit conversion due to this pesky line in the compiler source:
val retry = typeErrors.forall(_.errPos != null) && (errorInResult(fun) || errorInResult(tree) || args.exists(errorInResult))
(credit to hrhino for finding this).
The solution is to always use treeCopy when creating a new tree so that all the internal flags/fields are copied:
case Assign(opd, rhs) => {
val optimizedRHS = optimizeStatement(rhs)
statements = statements ++ List(treeCopy.Assign(statement, opd, optimizedRHS))
}
And when generating a tree using quasiquotes, remember to set the position:
var optimizedNode = atPos(statement.pos.focus)(q"$optimizedSrc1.$newOp")
I updated my MWP Github repo with the fixed solution: https://github.com/darsnack/compiler-plugin-demo
Sorry for blurry description, but I can't describe better. So, the problem -
I have hierarchy of classes
sealed trait GameEvent
case object RoundStarted extends GameEvent
case object MessageSent extends GameEvent
....
After parsing game data, I have List[GameEvent]. By business logic, I need provide ability to "view" game at particular round. Start of each round determined by RoundStartedEvent. API method have following signature:
def load(id:Int, round:Int) = {
val game = repo.load(id)
val view = game.dropToRound(round)
view
}
case class Game(id:Int, events:List[GameEvent]){
def dropToRound(round:Int) = {
val newEvents = //events.filter() How? I need find index of "round"-th RoundStarted event and get all elements before it
this.copy(events = newEvents)
}
}
val testData = Game(1, List(RoundStarted//round 0, MessageSent, MessageSent, RoundStarted//round 1, MessageSent, RoundStarted//round 2))
//To retrieve all events before round 2 we calling load(1, 1)
assert(load(1, 1) shouldBe (Game(1,List(RoundStarted//round 0, MessageSent, MessageSent)))
I know how to do it imperatively, but what is the better way to acomplish it functionally? Preferrably without libs like scalaz, but if it really concise - I'll accept it as well:)
You can't actually use a filter since you want to collect events only until a specific condition occurs.
This function will collect the events:
def collectEvents(round: Int, events: List[GameEvent]): List[GameEvent] = {
def collectEventsList(r: Int, eventList: List[GameEvent], collectedEvents: List[GameEvent]): List[GameEvent] = {
eventList match {
case RoundStarted :: _ if r == 0 =>
collectedEvents
case RoundStarted :: y if r > 0 =>
collectEventsList(r - 1, y, RoundStarted :: collectedEvents)
case x :: y =>
collectEventsList(r, y, x :: collectedEvents)
case List() =>
collectedEvents
}
}
collectEventsList(round, events, List()).reverse
}
This is probably the worst of both worlds: it uses takeWhile together with mutable var. And it is pretty much equivalent to imperative solution. But at least it is short:
case class Game(id: Int, events: List[GameEvent]) {
def dropToRound(round: Int): Game = {
var cnt = 0
val newEvents = events.takeWhile({
case RoundStarted => cnt += 1
cnt <= round
case _ => true
})
this.copy(events = newEvents)
}
}
Update
Here is a more pure translation of the imperative code:
case class Game(id: Int, events: List[GameEvent]) {
def dropToRound(round: Int): Game = {
val newEvents = events.foldLeft((List.empty[GameEvent], 0))((t, ev) => (t, ev) match {
case ((out, cnt), _) if cnt > round => t
case ((out, cnt), RoundStarted) if cnt == round => t
case ((out, cnt), RoundStarted) => (ev :: out, cnt + 1)
case ((out, cnt), _) => (ev :: out, cnt)
})._1.reverse
this.copy(events = newEvents)
}
}
The obvious drawbacks are:
You can't stop foldLeft so it goes through the whole list of events anyway
Additional reverse at the end.
I'd like to model a function which would return a set of values with weighed probability. Something like this:
25% => return "a"
25% => return "b"
50% => return "c"
Most of the documentation I've seen so far is rather heavy and delves quickly into scientific depths without examples, thus the question:
What's the easiest way to achieve this?
EDIT: I am using Gatling DSL to write a load test with weighed actions. The built-in weighed distribution has a limitation (won't work in loops), which I would like to avoid by having an own implementation. The snippet looks like this:
override def scenarioBuilder = scenario(getClass.getSimpleName)
.exec(Account.create)
.exec(Account.login)
.exec(Account.activate)
.exec(Loop.create)
.forever(getAction)
def getAction = {
// Here is where I lost my wits
// 27.6% => Log.putEvents
// 18.2% => Log.putBinary
// 17.1% => Loop.list
// 14.8% => Key.listIncomingRequests
// rest => Account.get
}
Here's the shortest version of a generic function to choose based on probabilities:
val probabilityMap = List(0.25 -> "a", 0.25 -> "b")
val otherwise = "c"
def getWithProbability[T](probs: List[(Double, T)], otherwise: T): T = {
// some input validations:
assert(probs.map(_._1).sum <= 1.0)
assert(probs.map(_._1).forall(_ > 0))
// get random in (0, 1) range
val rand = Random.nextDouble()
// choose by probability:
probs.foldLeft((rand, otherwise)) {
case ((r, _), (prob, value)) if prob > r => (1.0, value)
case ((r, result), (prob, _)) => (r-prob, result)
}._2
}
foldLeft keeps advancing over the probabilities until it finds one where r is smaller; If we haven't found it, we move onto the next probability with r-prob to "remove" the part of the range we've already covered.
EDIT: an equivalent but perhaps easier to read version can use scanLeft to create cumulative ranges before "searching" for the range within which rand has landed:
def getWithProbability[T](probs: List[(Double, T)], otherwise: T): T = {
// same validations..
// get random in (0, 1) range
val rand = Random.nextDouble()
// create cumulative values, in this case (0.25, a), (0,5, b)
val ranges = probs.tail.scanLeft(probs.head) {
case ((prob1, _), (prob2, value)) => (prob1+prob2, value)
}
ranges.dropWhile(_._1 < rand).map(_._2).headOption.getOrElse(otherwise)
}
There, clean and ready:
randomSwitchOrElse(
27.6 -> exec(Log.putEvents),
18.2 -> exec(Log.putBinary))
For those who don't know what a 5-card Poker Straight is: http://en.wikipedia.org/wiki/List_of_poker_hands#Straight
I'm writing a small Poker simulator in Scala to help me learn the language, and I've created a Hand class with 5 ordered Cards in it. Each Card has a Rank and Suit, both defined as Enumerations. The Hand class has methods to evaluate the hand rank, and one of them checks whether the hand contains a Straight (we can ignore Straight Flushes for the moment). I know there are a few nice algorithms for determining a Straight, but I wanted to see whether I could design something with Scala's pattern matching, so I came up with the following:
def isStraight() = {
def matchesStraight(ranks: List[Rank.Value]): Boolean = ranks match {
case head :: Nil => true
case head :: tail if (Rank(head.id + 1) == tail.head) => matchesStraight(tail)
case _ => false
}
matchesStraight(cards.map(_.rank).toList)
}
That works fine and is fairly readable, but I was wondering if there is any way to get rid of that if. I'd imagine something like the following, though I can't get it to work:
private def isStraight() = {
def matchesStraight(ranks: List[Rank.Value]): Boolean = ranks match {
case head :: Nil => true
case head :: next(head.id + 1) :: tail => matchesStraight(next :: tail)
case _ => false
}
matchesStraight(cards.map(_.rank).toList)
}
Any ideas? Also, as a side question, what is the general opinion on the inner matchesStraight definition? Should this rather be private or perhaps done in a different way?
You can't pass information to an extractor, and you can't use information from one value returned in another, except on the if statement -- which is there to cover all these cases.
What you can do is create your own extractors to test these things, but it won't gain you much if there isn't any reuse.
For example:
class SeqExtractor[A, B](f: A => B) {
def unapplySeq(s: Seq[A]): Option[Seq[A]] =
if (s map f sliding 2 forall { case Seq(a, b) => a == b } ) Some(s)
else None
}
val Straight = new SeqExtractor((_: Card).rank)
Then you can use it like this:
listOfCards match {
case Straight(cards) => true
case _ => false
}
But, of course, all that you really want is that if statement in SeqExtractor. So, don't get too much in love with a solution, as you may miss simpler ways of doing stuff.
You could do something like:
val ids = ranks.map(_.id)
ids.max - ids.min == 4 && ids.distinct.length == 5
Handling aces correctly requires a bit of work, though.
Update: Here's a much better solution:
(ids zip ids.tail).forall{case (p,q) => q%13==(p+1)%13}
The % 13 in the comparison handles aces being both rank 1 and rank 14.
How about something like:
def isStraight(cards:List[Card]) = (cards zip cards.tail) forall { case (c1,c2) => c1.rank+1 == c2.rank}
val cards = List(Card(1),Card(2),Card(3),Card(4))
scala> isStraight(cards)
res2: Boolean = true
This is a completely different approache, but it does use pattern matching. It produces warnings in the match clause which seem to indicate that it shouldn't work. But it actually produces the correct results:
Straight !!! 34567
Straight !!! 34567
Sorry no straight this time
I ignored the Suites for now and I also ignored the possibility of an ace under a 2.
abstract class Rank {
def value : Int
}
case class Next[A <: Rank](a : A) extends Rank {
def value = a.value + 1
}
case class Two() extends Rank {
def value = 2
}
class Hand(a : Rank, b : Rank, c : Rank, d : Rank, e : Rank) {
val cards = List(a, b, c, d, e).sortWith(_.value < _.value)
}
object Hand{
def unapply(h : Hand) : Option[(Rank, Rank, Rank, Rank, Rank)] = Some((h.cards(0), h.cards(1), h.cards(2), h.cards(3), h.cards(4)))
}
object Poker {
val two = Two()
val three = Next(two)
val four = Next(three)
val five = Next(four)
val six = Next(five)
val seven = Next(six)
val eight = Next(seven)
val nine = Next(eight)
val ten = Next(nine)
val jack = Next(ten)
val queen = Next(jack)
val king = Next(queen)
val ace = Next(king)
def main(args : Array[String]) {
val simpleStraight = new Hand(three, four, five, six, seven)
val unsortedStraight = new Hand(four, seven, three, six, five)
val notStraight = new Hand (two, two, five, five, ace)
printIfStraight(simpleStraight)
printIfStraight(unsortedStraight)
printIfStraight(notStraight)
}
def printIfStraight[A](h : Hand) {
h match {
case Hand(a: A , b : Next[A], c : Next[Next[A]], d : Next[Next[Next[A]]], e : Next[Next[Next[Next[A]]]]) => println("Straight !!! " + a.value + b.value + c.value + d.value + e.value)
case Hand(a,b,c,d,e) => println("Sorry no straight this time")
}
}
}
If you are interested in more stuff like this google 'church numerals scala type system'
How about something like this?
def isStraight = {
cards.map(_.rank).toList match {
case first :: second :: third :: fourth :: fifth :: Nil if
first.id == second.id - 1 &&
second.id == third.id - 1 &&
third.id == fourth.id - 1 &&
fourth.id == fifth.id - 1 => true
case _ => false
}
}
You're still stuck with the if (which is in fact larger) but there's no recursion or custom extractors (which I believe you're using incorrectly with next and so is why your second attempt doesn't work).
If you're writing a poker program, you are already check for n-of-a-kind. A hand is a straight when it has no n-of-a-kinds (n > 1) and the different between the minimum denomination and the maximum is exactly four.
I was doing something like this a few days ago, for Project Euler problem 54. Like you, I had Rank and Suit as enumerations.
My Card class looks like this:
case class Card(rank: Rank.Value, suit: Suit.Value) extends Ordered[Card] {
def compare(that: Card) = that.rank compare this.rank
}
Note I gave it the Ordered trait so that we can easily compare cards later. Also, when parsing the hands, I sorted them from high to low using sorted, which makes assessing values much easier.
Here is my straight test which returns an Option value depending on whether it's a straight or not. The actual return value (a list of Ints) is used to determine the strength of the hand, the first representing the hand type from 0 (no pair) to 9 (straight flush), and the others being the ranks of any other cards in the hand that count towards its value. For straights, we're only worried about the highest ranking card.
Also, note that you can make a straight with Ace as low, the "wheel", or A2345.
case class Hand(cards: Array[Card]) {
...
def straight: Option[List[Int]] = {
if( cards.sliding(2).forall { case Array(x, y) => (y compare x) == 1 } )
Some(5 :: cards(0).rank.id :: 0 :: 0 :: 0 :: 0 :: Nil)
else if ( cards.map(_.rank.id).toList == List(12, 3, 2, 1, 0) )
Some(5 :: cards(1).rank.id :: 0 :: 0 :: 0 :: 0 :: Nil)
else None
}
}
Here is a complete idiomatic Scala hand classifier for all hands (handles 5-high straights):
case class Card(rank: Int, suit: Int) { override def toString = s"${"23456789TJQKA" rank}${"♣♠♦♥" suit}" }
object HandType extends Enumeration {
val HighCard, OnePair, TwoPair, ThreeOfAKind, Straight, Flush, FullHouse, FourOfAKind, StraightFlush = Value
}
case class Hand(hand: Set[Card]) {
val (handType, sorted) = {
def rankMatches(card: Card) = hand count (_.rank == card.rank)
val groups = hand groupBy rankMatches mapValues {_.toList.sorted}
val isFlush = (hand groupBy {_.suit}).size == 1
val isWheel = "A2345" forall {r => hand exists (_.rank == Card.ranks.indexOf(r))} // A,2,3,4,5 straight
val isStraight = groups.size == 1 && (hand.max.rank - hand.min.rank) == 4 || isWheel
val (isThreeOfAKind, isOnePair) = (groups contains 3, groups contains 2)
val handType = if (isStraight && isFlush) HandType.StraightFlush
else if (groups contains 4) HandType.FourOfAKind
else if (isThreeOfAKind && isOnePair) HandType.FullHouse
else if (isFlush) HandType.Flush
else if (isStraight) HandType.Straight
else if (isThreeOfAKind) HandType.ThreeOfAKind
else if (isOnePair && groups(2).size == 4) HandType.TwoPair
else if (isOnePair) HandType.OnePair
else HandType.HighCard
val kickers = ((1 until 5) flatMap groups.get).flatten.reverse
require(hand.size == 5 && kickers.size == 5)
(handType, if (isWheel) (kickers takeRight 4) :+ kickers.head else kickers)
}
}
object Hand {
import scala.math.Ordering.Implicits._
implicit val rankOrdering = Ordering by {hand: Hand => (hand.handType, hand.sorted)}
}