Haskellでポーカー?
長いコードになりましたので続きから
import System.Random import Data.List import System.IO data Suit = Dia | Clover | Heart | Spade | Wild deriving (Show,Eq,Ord) data Card = NumCard Suit Rank | Joker deriving (Eq) instance Show Card where show (NumCard x y) = (show x)++" "++(show (fromEnum y)) show (Joker) = "Joker" instance Ord Card where compare (Joker) _ = GT compare _ (Joker) = LT compare (NumCard x y) (NumCard v w) | y == w = EQ | y < w = LT | otherwise = GT data PrizeR = PR Prize Rank deriving(Eq) instance Show PrizeR where show (PR x y) = show x data Rank = None | One | Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King deriving(Show,Eq,Ord,Enum,Bounded) type Hand = [Card] data Prize = High | OnePair | TwoPair | ThreeCards | Straight | Flush | FullHouse| FourCards | StraightFlush | RoyalStraightFlush | FiveCards deriving(Show,Eq,Ord,Enum) maxhand [] = NumCard Dia None maxhand (x:xs) = let y = maxhand xs in if x > y then x else y handstosetsuit ((NumCard a _):(NumCard b _):(NumCard c _):(NumCard d _):(NumCard e _):[]) = (a,b,c,d,e) handstosetsuit ((NumCard a _):(NumCard b _):(NumCard c _):(NumCard d _):(Joker):[]) = (a,b,c,d,Wild) handstosetnum ((NumCard _ a):(NumCard _ b):(NumCard _ c):(NumCard _ d):(NumCard _ e):[]) = (a,b,c,d,e) handstosetnum ((NumCard _ a):(NumCard _ b):(NumCard _ c):(NumCard _ d):(Joker):[]) = (a,b,c,d,None) countlist [] x = 0 countlist (x:xs) y = if x == y then 1 + countlist xs y else countlist xs y ismaxdup (a,b,c,d,e) = let maxdupi [] l = l maxdupi (x:xs) l = maxdupi (drop (countlist xs x) xs ) (max (1 + countlist xs x) l) in maxdupi (a:b:c:d:e:[]) 0 culcPrize card = let suitset = handstosetsuit card numset = handstosetnum card in if (isFiveCards numset) then PR FiveCards (ismaxnum numset) else if (isRoyalStraight numset) && (isFlush suitset) then PR RoyalStraightFlush (ismaxnum numset) else if (isStraight numset) && (isFlush suitset) then PR StraightFlush (ismaxnum numset) else if (isFourCards numset) then PR FourCards (ismaxnum numset) else if (isFullHouse numset) then PR FullHouse (ismaxnum numset) else if (isFlush suitset) then PR Flush (ismaxnum numset) else if (isStraight numset) then PR Straight (ismaxnums numset) else if (isThreeCards numset) then PR ThreeCards (ismaxnums numset) else if (isTwoPair numset) then PR TwoPair (ismaxnums numset) else if (isOnePair numset) then PR OnePair (ismaxnums numset) else PR High (ismaxnums numset) ismaxnums (_,_,_,a,b) = if b /= None then b else a isOnePair (a,b,c,d,None) = True isOnePair (a,b,c,d,e) = if (ismaxdup (a,b,c,d,e) == 2) then True else False isTwoPair (a,b,c,d,None) = a == b || b == c || c == d isTwoPair (a,b,c,d,e) = a == b && c == d || a == b && d == e || b == c && d == e isThreeCards (a,b,c,d,None) = if (ismaxdup (a,b,c,d,None) == 2) then True else False isThreeCards (a,b,c,d,e) = if (ismaxdup (a,b,c,d,e) == 3) then True else False isFourCards (a,b,c,d,None) = if (ismaxdup (a,b,c,d,None)) == 3 then True else False isFourCards (a,b,c,d,e) = if (ismaxdup (a,b,c,d,e)) == 4 then True else False isFullHouse (a,b,c,d,None) = a == b && c == d isFullHouse (a,b,c,d,e) = (a == b && b == c && d == e) || (a == b && c == d && d == e) isFlush (a,b,c,d,Wild) = a == b && b == c && c == d isFlush (a,b,c,d,e) = a == b && b == c && c == d && d == e isStraight (a,b,c,d,None) = if d <= Three then False else (pred d == c && pred c == b && pred b == a) || (pred (pred d) == c && pred c == b && pred b == a) || (pred d == c && pred (pred c) == b && pred b == a )|| (pred d == c && pred c == b && pred (pred b) == a ) isStraight (a,b,c,d,e) = pred e == d && pred d == c && pred c == b && pred b == a isFiveCards (a,b,c,d,None) = a == b && b == c && c == d isFiveCards (a,b,c,d,e) = a == b && b == c && c == d && d == e isRoyalStraight (One,Ten,Jack,Queen,None) = True isRoyalStraight (One,Ten,Jack,King,None) = True isRoyalStraight (One,Ten,Queen,King,None) = True isRoyalStraight (One,Jack,Queen,King,None) = True isRoyalStraight (Ten,Jack,Queen,King,None) = True isRoyalStraight (One,Ten,Jack,Queen,King) = True isRoyalStraight _ = False ismaxnum (a,b,c,d,e) = if a == One then a else e elemdS [] _ = [] elemdS (x:xs) s = if x <= s then (x:(elemdS xs s)) else elemdS xs s elemuS [] _ = [] elemuS (x:xs) s = if x > s then (x:(elemuS xs s)) else elemuS xs s suitsort [] = [] suitsort (x:xs) = suitsort((elemdS xs x))++[x]++suitsort((elemuS xs x)) deck = (Joker):[NumCard x y | x <- [Dia,Clover,Heart,Spade] , y <- [(One)..(King)]] randM f = do x <- randomRIO (0,f) :: IO Int return x remD _ [] = [] remD 0 (y:ys) = ys remD x (y:ys) = y:(remD (x-1) ys) distribute5i d xs = if length xs == 5 then return xs else do x <- randM (52-(length xs)) distribute5i (remD x d) ((d !! x ):xs) distribute5 = do xs <- distribute5i deck ([]) return (suitsort xs) repeatpoker s x = do y <- distribute5 let (PR pr _) = culcPrize y if pr == s then do print x print y else repeatpoker s (x+1) main = do repeatpoker (RoyalStraightFlush) 0
実行例
$ ./poker 56274 [Spade 1,Spade 11,Spade 12,Spade 13,Joker] $ ./poker 50725 [Dia 10,Dia 11,Dia 12,Dia 13,Joker] $ ./poker 173559 [Heart 1,Heart 10,Heart 11,Heart 13,Joker]
ポーカーといいつつ上のコードはロイヤルストレートフラッシュが出るまでカードを混ぜて5枚ひくのを繰り返すプログラムになってます。mainをちょっと改変すれば5枚ひいて役を表示するものにできます。
ここで作ったSuitやらCardやらの型はトランプゲームを作る機会があれば再利用できそうです。けれども、Jokerは数値としてみるかスートとしてみるか、スート数値のセットと同列に見るかなど、なかなか扱いに困りました。結果、スートもカードも数値にもジョーカーの場所を作りました。数値だけ見たいときやスートだけ見たいときにジョーカー特有の場所があると便利です。もっとうまい実装がありそうですが。
カードのデータをとって役の条件を満たすかどうかを判定する関数の実装がなかなか辛かったです...。うまい方法が思い浮かばなかったので、引数のソートを前提にして虱潰しのようなことを書いています。
Haskellの型クラスや型、インスタンスやI/Oモナドなどいろいろなことがこれを通して学べました。
いきあたりばったりで長いコード書くとコーディングのセンスが試されてるような感じがします。