我是一个F#新手,我想分享我的扑克手卡塔问题实现,以获得一些反馈。
module PokerHands
open System.Text.RegularExpressions
type Figure =
| Two | Three | Four | Five
| Six | Seven | Eight | Nine
| Ten | Jack | Queen | King | Ace
type Suit = Diamonds | Spades | Hearts | Clubs
type Card = Figure * Suit
type Rank =
| HighCard of Figure * Rank option
| Pair of Figure * Rank
| TwoPair of Figure * Figure * Rank
| ThreeOfKind of Figure
| Straight of Figure
| Flush of Rank
| FullHouse of Figure
| FourOfKind of Figure
| StraightFlush of Rank
type Player = {
Name: string;
Cards: Card list;
Hand: Rank;
}
let SortDesc sequence = sequence |> Seq.toList |> List.sortWith (fun x y -> compare y x)
let SortDescBy f sequence = sequence |> List.sortWith (fun x y -> compare (f y) (f x))
let rec BuildHighCard (figures : Figure list) =
match SortDesc figures with
| [h] -> HighCard(h, None)
| h :: t -> HighCard(h, Some(BuildHighCard(t)))
| _ -> failwith "empty"
let isFlush (cards : Card list) =
cards |> Seq.distinctBy snd |> Seq.length = 1
let isStraight (cards : Card list) =
let figs = cards |> List.map fst |> List.sort
let flag = ref true
for i in {1 .. List.length figs - 1} do
if (compare figs.[i-1] figs.[i]) <> -1 then flag.Value <- false
flag.Value
let (|IsStraightFlush|_|) cards =
if isFlush cards && isStraight cards then Some(StraightFlush(BuildHighCard(cards |> List.map fst))) else None
let (|IsFlush|_|) cards =
if isFlush cards then Some(Flush(BuildHighCard(cards |> List.map fst))) else None
let (|IsStraight|_|) cards =
if isStraight cards then Some(Straight(cards |> List.minBy fst |> fst)) else None
let (|IsGrouped|_|) (counts : int list) cards =
let groups = cards |> Seq.countBy fst |> Seq.toList |> SortDescBy snd
if groups |> List.map snd = counts then Some(groups |> List.map fst) else None
let DetermineRank (cards : Card list) =
match cards with
| IsStraightFlush straightFlush -> straightFlush
| IsGrouped [4;1] [f;_] -> FourOfKind(f)
| IsGrouped [3;2] [f;_] -> FullHouse(f)
| IsFlush flush -> flush
| IsStraight straight -> straight
| IsGrouped [3;1;1] [f;_;_] -> ThreeOfKind(f)
| IsGrouped [2;2;1] [f1;f2;rest]-> TwoPair(List.max [f1;f2], List.min [f1;f2], BuildHighCard([rest]))
| IsGrouped [2;1;1;1] (h :: t) -> Pair(h, BuildHighCard(t))
| _ -> BuildHighCard(cards |> List.map fst)
let ParsePlayers input =
let r = Regex("(\w+):( [2-9,T,J,Q,K,A][H,C,D,S]){5}")
let matches = input |> r.Matches
let parseCard (capture : Capture) =
let literal = capture.Value.Trim()
let figure =
match literal.[0] with
| '2' -> Two
| '3' -> Three
| '4' -> Four
| '5' -> Five
| '6' -> Six
| '7' -> Seven
| '8' -> Eight
| '9' -> Nine
| 'T' -> Ten
| 'J' -> Jack
| 'Q' -> Queen
| 'K' -> King
| 'A' -> Ace
| _ -> failwith "unrecognized figure"
let suit =
match literal.[1] with
| 'D' -> Diamonds
| 'S' -> Spades
| 'H' -> Hearts
| 'C' -> Clubs
| _ -> failwith "unrecognized suit"
(figure, suit)
let parsePlayer (input : Match) =
let name = input.Groups.[1].Value
let cards =
input.Groups.[2].Captures
|> Seq.cast
|> Seq.map parseCard
|> Seq.toList
{Name = name; Cards = cards; Hand = DetermineRank cards}
matches
|> Seq.cast
|> Seq.map parsePlayer
|> Seq.toArray
type Score =
| Win of string * string
| Tie
let rec Rationale wonHand lostHand =
let (|RanksEq|_|) (wonHand,lostHand) =
match wonHand, lostHand with
| StraightFlush(w), StraightFlush(l) -> Some (Rationale w l)
| Flush(w), Flush(l) -> Some (Rationale w l)
| TwoPair(w1,w2,r1), TwoPair(l1,l2,r2) when w1 = l1 && w2 = l2 -> Some (Rationale r1 r2)
| Pair(f1,r1), Pair(f2,r2) when f1 = f2 -> Some (Rationale r1 r2)
| HighCard(w,r1), HighCard(l,r2) when w = l -> Some (Rationale (Option.get r1) (Option.get r2))
| _ -> None
match wonHand, lostHand with
| RanksEq(res) -> res
| StraightFlush(_), _ -> "straight flush"
| FourOfKind(f), _ -> sprintf "four of kind: %A" f
| FullHouse(f), _ -> "full house"
| Flush(_), _ -> "flush"
| Straight(f), _ -> "straight"
| ThreeOfKind(f), _ -> sprintf "three of kind: %A" f
| TwoPair(f1,f2,_), _ -> sprintf "two pairs: %A + %A" f1 f2
| Pair(f,_), _ -> sprintf "pair of: %A" f
| HighCard(w,_), _ -> sprintf "high card: %A" w
| _ -> failwith "unhandled rationale"
let DetermineScore (players : Player array) =
let c = compare (players.[0].Hand) (players.[1].Hand)
if c = 0
then Tie
else
let winner = players |> Array.maxBy (fun p -> p.Hand)
let looser = players |> Array.minBy (fun p -> p.Hand)
Win(winner.Name, Rationale winner.Hand looser.Hand)
let FormatScore score =
match score with
| Win(winner, rationale) -> sprintf "%s wins - %s" winner rationale
| Tie -> "Tie"
let CompareHands input =
input
|> ParsePlayers
|> DetermineScore
|> FormatScore乍一看,让人感到奇怪的是Rank歧视联盟。我决定用这种方式来实现它,这样我就可以比较一下Ranks了。
需要解释的另一件事是基本原理函数:它解决了获胜级别的字符串逻辑,内部RanksEq活动模式解决了两个级别都成对的情况--在这里,逻辑应该返回“高卡”。
以下是我所关心的问题:
isStraight函数-如何实现一个纯函数外观的函数来检查序列中的所有元素是否是连续的(对于受歧视的联合类型)?IsStraight、IsFlush、IsStraightFlush活动模式,这三种模式都有共同之处--它们能以某种方式合并成一个活动模式吗?DetermineScore函数--我不喜欢使用Array.min和max从一对值中得到一个越低越高的值--是否有一个一行呢?比如返回元组Player * Player的东西?对代码的任何其他评论都将非常欢迎。
发布于 2014-03-14 22:32:08
我喜欢这个!我认为,你使用受歧视的结合和积极的模式是相当优雅的。(尽管利用受歧视工会的compare来做这件事,似乎有点不对--尽管我很难为这种情绪辩护。)
至于你的关切:
let flag ... flag.Value替换为此):让rec check = function x ::y ::zs -> compare x y <> -1 & check (y : zs) X -> true _ ->虚假检查图查看有关此样式的更多信息,请参见这里。或者,您可以尝试使用组合子。如果你只想比较五张牌的顺序,那就像这样吧?List.toSeq卡|> Seq.distinct |> Seq.toList |>函数x X;_;y ->比较x= -4 false _ -> falseBuildHighCard(cards |> List.map fst)。我认为如果你这样做了,你会把你的程序弄乱,而不是让它更易读。事实上,很清楚发生了什么事,是吗?但是,也许ParsePlayers已经返回一个元组而不是数组?
小建议:您可以将SortDesc和SortDescBy实现为:
let SortDescBy f = List.sortWith (fun x y -> compare (f y) (f x)) 一般来说,当您编写x |> f时,它的意思只是f x。所以当你定义
let f x = x |> g ...你可以把中间的人裁掉
let f = g ...发布于 2014-03-18 20:39:22
以函数方式实现isStraight的方法是使用Seq.zip接收所有对i-th和(i+1)-th卡,然后确保所有对都满足使用Seq.forall的条件:
let isStraight (cards : Card list) =
let figs = cards |> Seq.map fst |> Seq.sort
let pairs = Seq.zip figs (Seq.skip 1 figs)
Seq.forall (fun pair -> compare (fst pair) (snd pair) = -1) pairs(我使用Seq而不是List,因为List.zip的行为略有不同,而且没有List.skip。)
发布于 2014-03-21 08:05:05
结果是,有一个Seq.pairwise函数在本例中将与Seq.zip figs (Seq.skip 1 figs)一样工作。
因此,将@svick和@soren的建议放在一起,使用Seq.pairwise,我们可以编写如下isStraight函数:
let isStraight (cards : Card list) =
cards
|> Seq.map fst
|> Seq.sort
|> Seq.pairwise
|> Seq.forall ((<||) compare >> (=) -1)https://codereview.stackexchange.com/questions/43831
复制相似问题