对于那些不知道5张牌扑克直线是什么的人:http://en.wikipedia.org/wiki/List_of_poker_hands#Straight
我正在Scala中编写一个小型扑克模拟器来帮助我学习该语言,并且我已经创建了一个 Hand 类,其中包含5个有序卡。每个卡都有 Rank 和 Suit ,两者都定义为 Enumerations 。 Hand 类具有评估手牌等级的方法,其中一个检查手牌是否包含直线(我们暂时可以忽略直线冲洗)。我知道有一些很好的算法可以确定一个Straight,但是我想知道我是否可以用Scala的模式匹配来设计一些东西,所以我想出了以下内容:
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)
}
工作正常并且相当可读,但我想知道是否有任何方法可以摆脱 if 。我想像下面这样的东西,虽然我无法让它起作用:
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)
}
有什么想法吗?另外,作为一个附带问题,内部 matchesStraight 定义的一般意见是什么?这应该是私人的还是以不同的方式完成?
答案 0 :(得分:3)
您不能将信息传递给提取器,并且您不能使用另一个值中返回的值的信息,除了if
语句 - 这是涵盖所有这些情况。
你能做的就是创建自己的提取器来测试这些东西,但如果没有重用,它将不会给你带来太多的好处。
例如:
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)
然后你可以像这样使用它:
listOfCards match {
case Straight(cards) => true
case _ => false
}
但是,当然,你真正想要的只是if
中的SeqExtractor
陈述。所以,不要太喜欢解决方案,因为你可能会错过更简单的做事方式。
答案 1 :(得分:2)
您可以执行以下操作:
val ids = ranks.map(_.id)
ids.max - ids.min == 4 && ids.distinct.length == 5
正确处理aces需要做一些工作。
更新:这是一个更好的解决方案:
(ids zip ids.tail).forall{case (p,q) => q%13==(p+1)%13}
比较中的% 13
处理aces为1级和14级。
答案 2 :(得分:1)
如下:
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
答案 3 :(得分:1)
这是一个完全不同的方法,但确实使用模式匹配。它在match子句中产生警告,似乎表明它不应该工作。但它实际上产生了正确的结果:
Straight !!! 34567
Straight !!! 34567
Sorry no straight this time
我现在忽略了套房,我也忽略了在2下的ace的可能性。
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")
}
}
}
如果你对像谷歌'教堂数字scala type system'这样的更多东西感兴趣
答案 4 :(得分:0)
这样的事情怎么样?
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
}
}
你仍然坚持使用if
(实际上更大)但是没有递归或自定义提取器(我相信你在next
使用不正确,所以你的第二个尝试不起作用。)
答案 5 :(得分:0)
如果您正在撰写扑克计划,那么您已经在检查是否属于独一无二的。当一只手没有n种(n> 1)并且最小面额和最大值之间的差异恰好为4时,它是直的。
答案 6 :(得分:0)
几天前,我正在做类似这样的事情,因为Project Euler问题54.和你一样,我把Rank和Suit作为枚举。
我的卡类看起来像这样:
case class Card(rank: Rank.Value, suit: Suit.Value) extends Ordered[Card] {
def compare(that: Card) = that.rank compare this.rank
}
注意我给了它Ordered
特性,以便我们以后可以轻松比较卡片。此外,在解析手时,我使用sorted
将它们从高到低排序,这使评估值更加容易。
这是我的straight
测试,它会返回Option
值,具体取决于它是否为直线。实际返回值(Ints列表)用于确定手的强度,第一个表示手牌类型从0(无对)到9(同花顺),其他是其他任何牌的等级计算其价值的手。对于直道,我们只担心排名最高的牌。
另外,请注意,您可以使用Ace作为低音,“滚轮”或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
}
}
答案 7 :(得分:0)
这是一个完整的惯用Scala手分类器,适用于所有手(处理5高直道):
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)}
}