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群猿とかと考えた方がいいかも。
で、ポーカーゲームは、革命のシュミレーションです。よその国の同位の兵隊 とかを沢山集めた人が勝ち。でも、本当に強いのは一国の兵士で固める事。腕 と度胸に自信が有ったら挑戦するが良い。