ポーカーの役判定(第10回オフラインリアルタイムどう書くの参考問題)

haskellグループなのにF#のエントリ。大目に見てね。

F#の練習に http://qiita.com/items/d819d1e5f2378317511e の問題を解いてみた。 (横浜へなちょこプログラミング勉強会には参加したことないですが……横からすみません)

http://qiita.com/items/80c2c22d3224cb7c448c

以下、頭の中をトレース出力。

型を用意

手は5枚のカードから構成され、カードはスート(絵柄マーク)とランク(番号)を持っている。

スートは列挙型でもいいんだけど、整数値を使うことはなさそうなので値を指定しない。こうするとSuit型は判別共用体になるのかな。

type Suit = Spade | Heart | Diamond | Club

ランクを数値型や列挙型にするかどうかかなり悩んだが、今回はそうしなかった。なぜかというとAの存在。 Aって1にも14にもなるカードなのだが、そのロジックをどこに入れるかが悩ましい。 とりあえず Rank -> int list という変換関数があれば何とかなるかなと目星をつけ、こちらも判別共用体に。

type Rank = R2 | R3 | R4 | R5 | R6 | R7 | R8 | R9 | R10 | RJ | RQ | RK | RA

カードはタプルの別名。 そして、さっきの変換関数はカードに対して定義することにした。 Aの特別扱いはここに反映される。

type Card = Rank * Suit
let cardToNums (c:Card) =
  match fst c with
  | R2  -> [2]
  | R3  -> [3]
  | R4  -> [4]
  | R5  -> [5]
  | R6  -> [6]
  | R7  -> [7]
  | R8  -> [8]
  | R9  -> [9]
  | R10 -> [10]
  | RJ  -> [11]
  | RQ  -> [12]
  | RK  -> [13]
  | RA  -> [1;14]

手はカード5枚。リストにするかどうか迷った(ロジック上はリストの方が扱いやすい)が、5枚というのを明確にしたかったのでタプルにしてみた。 なんだか無駄なような気はするが。

type Hand = Card * Card * Card * Card * Card
let toList (c0,c1,c2,c3,c4) = [c0;c1;c2;c3;c4]

役の判定ロジック

最初は、役をCategory型として判別共用体で定義した上で、Card -> Category list という判定関数を書こうかなと考えた。 次に、if文を並べて Card -> Category にしてもいいやと考え、実際に実装してみた。 しかし、最後に求められる出力は "RF" とか "-" といった文字列なので、Category -> string も必要になった。 なんだかばからしくなったので、Category型を排除して、 その代わりにアクティブパターンで役を判定し、文字列に変換するようにした。

let (|Flush|_|)             : Hand -> unit option = ????
let (|Straight|_|)          : Hand -> unit option = ????
let (|StraightFlush|_|)     : Hand -> unit option = ????
let (|RoyalFlush|_|)        : Hand -> unit option = ????
let (|FourFlush|_|)         : Hand -> unit option = ????
let (|FourStraight|_|)      : Hand -> unit option = ????
let (|FourStraightFlush|_|) : Hand -> unit option = ????

let show (h:Hand) =
  match h with
  | RoyalFlush        -> "RF"
  | StraightFlush     -> "SF"
  | Flush             -> "FL"
  | Straight          -> "ST"
  | FourStraightFlush -> "4SF"
  | FourFlush         -> "4F"
  | FourStraight      -> "4S"
  | _                 -> "-"

役の強さはこの show 関数に埋め込まれている(上から順に強い)。

(書き途中)