ポーカーの役判定
http://d.hatena.ne.jp/argius/20080117/1200590510 を見ておもしろそうだなと思ったので書いてみた。そしてどう書く?org(http://ja.doukaku.org/121/)にも久し振りの投稿。
haskellではデータ構造が簡単に定義できるので、これくらいでも使いたくなる。コードの半分くらいがデータ構造の定義と文字列とのインターフェイスになってしまった。役までdataを使って書いてるのは、テストコードをちゃんと書こうと思ってやめたなごり。
入力チェックは全くやってないので、変な入力がはいるとエラーで落ちるか変な結果がでます。
testを実行するとこんなん。
("SQSJSASKST",Royal flush) ("D9D7D6D5D8",Straight flush) ("C2D2S2H3H2",Four of a kind) ("C2D3S2H3H2",Full House) ("S9S4S8STSJ",Flush) ("C4H7D5S6H3",Straight) ("S6H6C5DQC6",Three of a kind) ("S6HQC5DQC6",Two pair) ("S6H4C5DQC6",One pair) ("SJSQSKSAC2",No pair)
確定した役と可能性のある役を列挙するオンライン判定版、というのもおもしろいかもしれない。
以下ソース。
import System import List(sort,group,elemIndex) import Maybe(fromJust) data Card = Card {suits::Suits,rank::Int} deriving Show data Suits = S | D | H | C deriving (Read,Eq,Show) data Hand = RF | STF | FK | FH | F | ST | TK | TP | OP | NO read_cards :: String -> [Card] read_cards str = map read_card $ split_2 str where read_card [s,r] = Card (read [s]) (fromJust (elemIndex r "DD23456789TJQKA")) split_2 [] = [] split_2 list = let (hd,rest) = splitAt 2 list in hd : split_2 rest instance Show Hand where show h = case h of RF -> "Royal flush" STF -> "Straight flush" FK -> "Four of a kind" FH -> "Full House" F -> "Flush" ST -> "Straight" TK -> "Three of a kind" TP -> "Two pair" OP -> "One pair" NO -> "No pair" hand :: [Card] -> Hand hand cards | check_st && check_flush && head rank_seq == 10 = RF | check_st && check_flush = STF | max_group == 4 = FK | sort group_count == [2,3] = FH | check_flush = F | check_st = ST | max_group == 3 = TK | pair_count == 2 = TP | pair_count == 1 = OP | otherwise = NO where rank_seq = sort $ map rank cards groups = group rank_seq group_count = map length groups max_group = foldl1 max group_count pair_count = length (filter (\l->length l == 2) groups) check_flush = all (\c->suits c==suits (head cards)) cards check_st = normal || rank_seq == [2,3,4,5,14] where normal = and $ zipWith (\a b->a-b == head rank_seq) rank_seq [0..] main = getArgs >>= print . hand . read_cards . head -- Test test = mapM_ print $ zip testlist (map (hand .read_cards) testlist) testlist = ["SQSJSASKST", "D9D7D6D5D8", "C2D2S2H3H2", "C2D3S2H3H2", "S9S4S8STSJ", "C4H7D5S6H3", "S6H6C5DQC6", "S6HQC5DQC6", "S6H4C5DQC6", "SJSQSKSAC2"]