poker
Table of Contents
リモコン
2020年に購入した亀山式テレビのリモコンが壊れた。自分の分身のように思っ ている、女房は大弱り。ネットをガサゴソやって、互換品をアマゾンと楽天で 見付けてきた。
価格的には、両者譲らずと言った所。楽天は、うざいメール攻撃をしてくるの で、却下だそうです。こういう点が評価されるんですよ、三木谷君(だったか な)。
火曜日に発注して金曜日に、黒猫が包みを咥えてやってきた。田舎ですから、 翌日の配達なんて、ありえない。地方は蚊帳の外か。
ああ、蚊帳が欲しいぞ。蚊の攻撃が堪らんわい。
ask ChatGPT
ChatGPTの限界を試す、どこまで難しい問題に対応できるか
「AIがRISC-Vを設計」というニュースを読んで、はや失業(!?)かと思ったら
もう、いやになる程記事や本が出ている。正しい使い道は、形式言語を生成さ せる事だろう。いくら流暢に説明してくれたって、それを手元で確認できなけ れば、嘘が一遍でバレるからね。
という事で、次なるお題は、前回ちょっと出てきた、egisonによるポーカーの 手を判定するプログラムをhaskellで記述せよって奴。ポーカーの手と言うか 役は、 ポーカー・ハンドの一覧 に説明がある。
ポーカーの手(ポーカー・ハンド)を判定するプログラムをhaskellで記述してください。 カードは、スート(C,D,H,S) + 番号(1 .. 13) で、表現します。 以下は、入出力例です。 Input: S5 D10 S7 C5 H1 ==> One pair Input:
軽微なコンパイルエラーが一度発生して、それの修正版がすぐに出来あがった。
import Data.List
-- ポーカーの手の種類を表すデータ型
data HandRank = HighCard | OnePair | TwoPair | ThreeOfAKind | Straight | Flush | FullHouse | FourOfAKind | StraightFlush | RoyalFlush deriving (Show, Eq)
-- カードのデータ型
data Card = Card Suit Int deriving (Show, Eq)
-- カードのスートを表すデータ型
data Suit = C | D | H | S deriving (Show, Eq)
-- カードの番号を取得する関数
getValue :: Card -> Int
getValue (Card _ num) = num
-- カードのスートを取得する関数
getSuit :: Card -> Suit
getSuit (Card suit _) = suit
-- カードの番号を文字列に変換する関数
valueToString :: Int -> String
valueToString 1 = "A"
valueToString 11 = "J"
valueToString 12 = "Q"
valueToString 13 = "K"
valueToString n = show n
-- カードの文字列表現を取得する関数
cardToString :: Card -> String
cardToString (Card suit num) = suitToString suit ++ valueToString num
-- カードのスートを文字列に変換する関数
suitToString :: Suit -> String
suitToString C = "C"
suitToString D = "D"
suitToString H = "H"
suitToString S = "S"
-- ハンドの文字列表現を取得する関数
handToString :: [Card] -> String
handToString = unwords . map cardToString
-- ハンドのカードの番号のリストを取得する関数
getValues :: [Card] -> [Int]
getValues = map getValue
-- ハンドのスートのリストを取得する関数
getSuits :: [Card] -> [Suit]
getSuits = map getSuit
-- ハンドがストレートか判定する関数
isStraight :: [Int] -> Bool
isStraight values = length (nub values) == 5 && maximum values - minimum values == 4
-- ハンドがフラッシュか判定する関数
isFlush :: [Suit] -> Bool
isFlush suits = length (nub suits) == 1
-- ハンドのランクを判定する関数
handRank :: [Card] -> HandRank
handRank hand
| isRoyalFlush = RoyalFlush
| isStraightFlush = StraightFlush
| isFourOfAKind = FourOfAKind
| isFullHouse = FullHouse
| isFlush (getSuits hand) = Flush
| isStraight (getValues hand) = Straight
| isThreeOfAKind = ThreeOfAKind
| isTwoPair = TwoPair
| isOnePair = OnePair
| otherwise = HighCard
where
sortedValues = sort (getValues hand)
isRoyalFlush = isFlush (getSuits hand) && sortedValues == [1, 10, 11, 12, 13]
isStraightFlush = isStraight (getValues hand) && isFlush (getSuits hand)
isFourOfAKind = any (\x -> length x == 4) (group sortedValues)
isFullHouse = (length (group sortedValues) == 2) && (any (\x -> length x == 3) (group sortedValues))
isThreeOfAKind = any (\x -> length x == 3) (group sortedValues)
isTwoPair = (length (filter (\x -> length x == 2) (group sortedValues))) == 2
isOnePair = any (\x -> length x == 2) (group sortedValues)
-- 入力の文字列をカードに変換する関数
parseCard :: String -> Card
parseCard (s:v) = Card (charToSuit s) (charToValue v)
where
charToSuit 'C' = C
charToSuit 'D' = D
charToSuit 'H' = H
charToSuit 'S' = S
charToValue "A" = 1
charToValue "J" = 11
charToValue "Q" = 12
charToValue "K" = 13
charToValue v = read v :: Int
-- 入力の文字列をハンドに変換する関数
parseHand :: String -> [Card]
parseHand = map parseCard . words
main :: IO ()
main = do
putStrLn "Enter the hand:"
input <- getLine
let hand = parseHand input
putStrLn ("Input: " ++ handToString hand)
putStrLn (" ==> " ++ show (handRank hand))
ghci> :main Enter the hand: S11 D11 H12 C11 S1 Input: SJ DJ HQ CJ SA ==> ThreeOfAKind
分解して実行してみる。
λ> my = parseHand "C1 C2 D12 S13 H1" λ> my [Card C 1,Card C 2,Card D 12,Card S 13,Card H 1] λ> handToString my "CA C2 DQ SK HA" λ> show $ handRank my "OnePair"
次は日本の伝統芸 花札・株札 の、役を判定する`プログラムを発注してみるかな。多分、そんなの知らない と言われそう。いや、世界のNintendoが札を出しているぐらいだから案外、認 知度が高かったりして。間口を広げるのもいいけど、もう少し西洋かぶれして みれ。
添削
一応、家庭教師に添削をお願いしてみる。
[sakae@fb /tmp/cgpoker]$ hlint app/Main.hs
app/Main.hs:79:61-108: Suggestion: Redundant bracket
Found:
(length (group sortedValues) == 2)
&& (any (\ x -> length x == 3) (group sortedValues))
Perhaps:
(length (group sortedValues) == 2)
&& any (\ x -> length x == 3) (group sortedValues)
app/Main.hs:81:21-80: Suggestion: Redundant bracket
Found:
(length (filter (\ x -> length x == 2) (group sortedValues))) == 2
Perhaps:
length (filter (\ x -> length x == 2) (group sortedValues)) == 2
2 hints
見栄え命って人は、 haskell / stylish-haskell こういうのを御愛用してるのかな。
people thinking …
15年も前の作だから、そのままでは当然コンパイルエラーになる。 ちょいと補修工事を実施したよ。
import System.Environment import Data.Maybe import Data.List
importしているモジュール名に階層を付けて整理(リファクタリング)しましたって事です。 テストデータは、こちら。ちょっと目移りしちゃうな。
% ./poker SQSJSASKST Royal flush % ./poker D9D7D6D5D8 Straight flush % ./poker C2D2S2H3H2 Four of a kind % ./poker C2D3S2H3H2 Full house % ./poker S9S4S8STSJ Flush % ./poker C4H7D5S6H3 Straight % ./poker S6H6C5DQC6 Three of a kind % ./poker S6HQC5DQC6 Two pair % ./poker S6H4C5DQC6 One pair % ./poker SJSQSKSAC2 No pair
Chat.GPTはnub関数が好きみたいだった。こちらの方は、満遍なく関数を繰り 出しているようにみうけられるけど、 !! が、新鮮だな。
ghci> :i (!!)
(!!) :: GHC.Stack.Types.HasCallStack => [a] -> Int -> a
-- Defined in ‘GHC.List’
infixl 9 !!
リストを配列とみなして、インデックス番号で、エレメントをアクセスするんか。 なんか、rubyとかpythonみたいだな。
主張 haskellは命令型(でもあるんです)
コンパイルしないでも使えるhaskell(熱いのでCPUを労る)
[sakae@arch tmp]$ runghc poker.hs SQSJSASKST Royal flush [sakae@arch tmp]$ runghc poker.hs C2D3S2H3H2 Full house [sakae@arch tmp]$ runghc poker.hs S9S4S8STSJ Flush
irb = ghci と言うのは、当然で、ruby = runghc も、真実。
スピードが欲しいなら、ruby jit じゃなくて、ghcでコンパイルすれば宜しい。
runghc vs. ghc
fib 32 を使って、スピード比べしてみる。(on FreeBSD(32Bit)
module Main where import System.Environment fib :: Int -> Int fib 0 = 0 fib 1 = 1 fib n = fib (n - 1) + fib (n - 2) main :: IO () main = do (h:_) <- getArgs print $ fib (read h :: Int)
素朴な定義のものです。
[sakae@fb /tmp/myfib]$ time myfib 32
2178309
real 0m0.067s
user 0m0.057s
sys 0m0.000s
[sakae@fb /tmp/myfib]$ runghc app/Main.hs 32
app/Main.hs:1:8: error:
Could not load module ‘Prelude’
It is a member of the hidden package ‘base-4.16.4.0’.
You can run ‘:set -package base’ to expose it.
:
linuxでは動いたのに、BSDでは、臍をまげられた。代替えでghciします。
ghci> :set +s ghci> fib 32 2178309 (7.49 secs, 648,856,876 bytes)
100倍もスピードが遅いですねぇ。それからメモリーも贅沢に使ってますよ。 ちなみに、:set +sは、
+s print timing/memory stats after each evaluation
実行時間とメモリー消費を確認するスイッチ。時間を取るか空間を取るかのせ めぎ合いになった時の頼もしい奴です。+で有効、-で無効になります。
Haskellでフィボナッチ数列 〜Haskellで非実用的なコードを書いて悦に入るのはやめろ〜
こういう人がおられました。人の褌で相撲を取ってみるって事で、メモリー消 費も一緒に出してみます。
λ> fib 32 2178309 (0.01 secs, 342,356 bytes) λ> fib 40 102334155 (0.01 secs, 335,824 bytes)
いろいろ出てたけど、一例でメモ化版です。劇的です。
所で、
[sakae@deb myfib]$ cabal repl : [1 of 1] Compiling Main ( app/Main.hs, interpreted ) Ok, one module loaded.
Compiling Main なんて言われるものだから、とっても速く実行できるとばか り、思っていたんよ。じゃ、このコンパイルって、まやかしなのか? こうい う細部に疑問をもつ人はいないみたい。ふと、考察する。
多分、半分コンパイルしましたって事だろう。ソースコードに矛盾がないか、 コンパイルする。内部的には、parsecして、構文木を作る。これだって、りっ ぱなコンパイル。 ghciは、この構文木を忠実に実行する。
[sakae@deb myfib]$ cabal build : [1 of 1] Compiling Main ( app/Main.hs, /tmp/myfib/dist-newstyle/build/i386-linux/ghc-9.2.8/myfib-0.1.0.0/x/myfib/build/myfib/myfib-tmp/Main.o ) Linking /tmp/myfib/dist-newstyle/build/i386-linux/ghc-9.2.8/myfib-0.1.0.0/x/myfib/build/myfib/myfib ...
こちらは、ghciな環境から離れて、独立独歩で実行できるように、ランタイム システム(RTS)を抱えこむようにする、コンパイルの後半部分の作業だ。 正確に言うなら、Main.hs -> Main.o にするのがGHC の狭義のコンパイル。その後、実行に必要なRTSライブラリィーやら、色々な 物をリンカーを使ってリンクする。このリンカーを呼出したりするのもGHCが 担っているんだな。
poker game
日本のhaskellアプリ設計100恵に選ばれた素晴しい講義です。
Haskellでポーカーを作ろう〜第一回 リストのシャッフルとカードの定義〜
このサイトは昔訪問した記憶が有るぞ。むずかしくて撤退したな。今では、楽 しめるようになった。トランプが話題というのは、身近でよいな。
ふと、トランプって、戦争のシュミレーション用カードと思ったぞ。クラブ国、 ダイヤ国、ハート国、スペード国が有りました。それぞれの国には、エース、 国王、女王、王子、以下序列のついた家来たちがいました。 国王より、エースというか将軍さまが偉いのは、軍事国家だから。 ああ、国家と言うより、高崎山のA群猿、B群猿とかと考えた方がいいかも。
で、ポーカーゲームは、革命のシュミレーションです。よその国の同位の兵隊 とかを沢山集めた人が勝ち。でも、本当に強いのは一国の兵士で固める事。腕 と度胸に自信が有ったら挑戦するが良い。