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 …

Haskell でポーカーの役判定処理

ポーカーの役判定 (改良版)

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入門(初級)

コンパイルしないでも使える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群猿とかと考えた方がいいかも。

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

etc