项目作者: skozlov

项目描述 :
Scala library for recognition and comparison of Texas holdem hands
高级语言: Scala
项目地址: git://github.com/skozlov/holdem4s.git
创建时间: 2017-12-23T06:55:05Z
项目社区:https://github.com/skozlov/holdem4s

开源协议:Apache License 2.0

下载


Holdem4s

Scala library for recognition and comparison of Texas holdem hands.

Code sample

  1. import com.github.skozlov.holdem4s._
  2. import Suit._
  3. import Rank._
  4. Hand(2♠, 2♥, A♠, K♠, Q♠, J♥, 9♥) match {
  5. case Pair(`2`, highestKicker :: _) =>
  6. println(s"A pair of Twos with ${highestKicker.name} kicker and so on") //A pair of Twos with Ace kicker and so on
  7. }

Quick start guide

It is assumed in the code samples below that the following imports are included:

  1. import com.github.skozlov.holdem4s._
  2. import Suit._
  3. import Rank._

Suits

There is a constant for each of 4 suits, named after the corresponding well-known unicode character:

  1. val suits: List[Suit] = List(♠, ♥, ♦, ♣)
  2. println(suits mkString " ") //♠ ♥ ♦ ♣

They have aliases coinciding with their English names:

  1. println(List(♠, ♥, ♦, ♣) == List(Spades, Hearts, Diamonds, Clubs)) //true

Ranks

There are intuitive constants for all 13 ranks:

  1. val ranks: List[Rank] = List(A, K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, 2)
  2. println(ranks mkString " ") //A K Q J T 9 8 7 6 5 4 3 2

Ranks can be compared:

  1. println(A > K) //true

… and sorted:

  1. val unorderedRanks = scala.util.Random.shuffle(ranks)
  2. println(unorderedRanks mkString " ") //"Q 8 2 J 7 4 K 9 3 A 5 T 6" or in another order
  3. println(unorderedRanks.sorted mkString " ") //2 3 4 5 6 7 8 9 T J Q K A

They also have aliases:

  1. require(List[Rank](A, K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, 2)
  2. == List(Ace, King, Queen, Jack, Ten, Nine, Eight, Seven, Six, Five, Four, Three, Two))

Cards

Creating a card is pretty easy:

  1. val aceSpades: Card = A

Card is a case class with arguments rank and suit, so it has properties of the same names:

  1. println(aceSpades.rank) //A
  2. println(aceSpades.suit) //♠

But its own toString() is overloaded:

  1. println(aceSpades) // A♠

Of course, you can use aliases of ranks and suits:

  1. println((Ace spades) == (A♠)) //true

Class Card extends Ordered[Card]. Cards are ordered by ranks:

  1. println((K♦) < (A♠)) // true

Hands and Combinations

A hand is a set of cards available to the player.
It can include up to 7 cards: 2 in the pocket and 3 to 5 on the board.

We support hands consisting of any number of cards from 1 to 7 (both inclusive).
An unusual number of cards (1, 3 or 4) in the hand can be used, for instance, in assumptions.

So the shortest hand contains just 1 card:

  1. val shortestHand: Hand = Hand(A♠)

… and the longest one consists of 7 cards:

  1. val longestHand = Hand(K♥, Q♥, J♥, T♥, 9♥, A♣, A♠)

Hands can be compared with each other:

  1. println(Hand(A♠, A♥, A♦, A♣, K♠, K♥, K♦) < Hand(A♥, K♥, Q♥, J♥, T♥)) //true

Hand(A♠, A♥, A♦, A♣, K♠, K♥, K♦) is longer and contains cards of higher ranks,
but Hand(A♥, K♥, Q♥, J♥, T♥) beats it,
because the rank of a hand is determined by the highest 5-card combination it contains:

  1. val combination: Combination = Hand(A ♠, A ♥, A ♦, A ♣, K ♠, K ♥, K ♦).toCombination
  2. println(combination) //Quad Aces with King kicker
  3. println(Hand(A♥, K♥, Q♥, J♥, T♥).toCombination) //Royal flush

Note that toString() of Combination returns the human-readable name of the combination.

How hands of different lengths are compared?
As we saw above, Hand(A♥, K♥, Q♥, J♥, T♥) beats Hand(A♠, A♥, A♦, A♣, K♠, K♥, K♦),
because royal flush always beats four of a kind.
But what if both hands are, for example, threes of a kind?
Then the general rule is applied: all alse being equal, the longer combination beats the shorter one:

  1. println(Hand(A♠, A♥, A♦, K♣, Q♠, J♥) > Hand(A♠, A♥, A♦, K♣)) //true

In this example, we have 2 “Three aces” with King as the highest kicker,
but Hand(A♠, A♥, A♦, K♣, Q♠, J♥) has 2nd kicker, while Hand(A♠, A♥, A♦, K♣) has only one.

Another example:

  1. println(Hand(A♠, A♥, A♦, Q♠, J♥) < Hand(A♠, A♥, A♦, K♣)) //true

Again, we have 2 “Three aces”, but the 1st kickers are different, so the condition “all alse being equal” does not hold.

Remember that a combination cannot contain more than 5 cards, so the following hands are considered equivalent:

  1. println(Hand(A♠, A♥, A♦, K♣, Q♠, J♥).toCombination == Hand(A♠, A♥, A♦, K♣, Q♠).toCombination) //true
  2. println(!(
  3. Hand(A♠, A♥, A♦, K♣, Q♠, J♥) > Hand(A♠, A♥, A♦, K♣, Q♠)
  4. || Hand(A♠, A♥, A♦, K♣, Q♠, J♥) < Hand(A♠, A♥, A♦, K♣, Q♠)
  5. )) //true

Finally, pattern matching:

  1. Hand(2♠, 2♥, A♠, K♠, Q♠, J♥, 9♥) match {
  2. case Pair(`2`, highestKicker :: _) =>
  3. println(s"A pair of Twos with ${highestKicker.name} kicker and so on") //A pair of Twos with Ace kicker and so on
  4. }