エンジニアの備忘録

学んだ事をアウトプットしていきます

PHP:ポーカーの役判定アルゴリズム

PHP ポーカー でググっても既に学習済みの人じゃないと分からないような難しいコードしか出てこなかったので、このメモが同じ段階で躓いている初心者の人の為になれば幸いです。

準備

まずはロイヤルストレートフラッシュの手持ちからスタート。

ここのマークと数字を書き換えても、ちゃんと役が判定出来るアルゴリズムを作る。

$cards = [

["suite" => "club" , "number" => 4]  // 1枚目

["suite" => "club" , "number" => 3] // 2枚目

["suite" => "club" , "number" => 2] // 3枚目

["suite" => "club" , "number" => 5] // 4枚目

["suite" => "club" , "number" => 1] // 5枚目

]

 

このままでは扱いずらいので、数字とマークを分けておき数字はソートしておく(ストレート等を判定する為)

$hand_num = array_column($cards,"number");

$hand_suit = array_column($cards,"number");

sort($hand_num);

出力結果→

Array (

[0] => 1

[1] => 2

[2] => 3

[3] => 4

[4] => 5 ) です。

 

下準備が出来たので、ここから役を判定するアルゴリズムを考えていきます。

フラッシュ

条件:手札5枚のマークが全て同じマーク

1枚目と2枚目が同じマークかつ・・・4枚目と5枚目が同じマークなら。

  if ($hand_suit[0] == $hand_suit[1] &&
        $hand_suit[1] == $hand_suit[2] &&
        $hand_suit[2] == $hand_suit[3] &&
        $hand_suit[3] == $hand_suit[4] ) {
        $bool_flush = true;
    }

ストレート

条件:1~13の数字内で連番5回続く

もっとスマートに判定する方法があるだろうけど、真っ先に思いついたのが

1~5 2~6 3~7・・・というように全試行していく方法。

if ($str15 ||$str26 ||$str37 ||$str48 ||$str59 ||$str610||$str711||$str812||$str913||$str113) {
        $bool_straight = true;
    }

ストレートフラッシュとロイヤルストレートフラッシュ

条件:ストレートとフラッシュ両方を満たした場合かつ、1.10.11.12.13の連番だった場合

この2つに関してはストレートとフラッシュを組み合わせるだけ。

 

// ストレートフラッシュ

  if ($bool_flush && $bool_straight) {
        $bool_str_flush = true;
    }

// ロイヤルストレートフラッシュ
    if ($bool_str_flush == true && $str113 != true) {  // $str113 = 1.10.11.12.13の連番 
        $bool_royal = true;  
    }

 

フォーカードとスリーカード

条件:同じ数字が3枚、4枚ある場合

例えば手持ちの数字が [5.5.5.5.9]の場合、

  • 1枚目と2枚目のカードが同じ
  • 2枚目と3枚目のカードが同じ
  • 3枚目と4枚目のカードが同じ

この条件を満たせばフォーカードになる。

しかし、[1.5.5.5.5]の場合だとフォーカードなのに判定することが出来なくなるのでボツ。

カードの前後を比較することで判定するのを辞めて、

手札の中の数字とその数字が何枚あるか?を集計して判定する。

 for ($i=1; $i <= 13; $i++) {   
        $count_num[$i] = 0; // 配列の初期化 

    }
    for ($i=0; $i <= 4; $i++) { 
        $count_num[$hand_num[$i]]++; 
    }

出力結果→

1が1枚、5が4枚とカウント出来ているので、下記のコードで判定が可能になる。

(補足:ストレートも最適化可能)

 

for ($i=1; $i <= 13; $i++) { 
        $count_num[$hand_num[$i]]++;

if ($count_num[$i] == 4) {
            $bool_four = true;

if ($count_num[$i] == 3) {
            $bool_three = true;
        }

}

フルハウスとツーペアとワンペア

フルハウス = スリーカード+ワンペア

ツーペア = ワンペアが2つ

なのでワンペアのカウントが出来れば、さっき用意したスリーカードの判定を組み合わせてフルハウスが判定可能になり、ツーペアとワンペアも判定可能になる。

if ($count_num[$i] == 2) {
            $bool_one_pair++;  // ワンペアがあれば+1カウント
        }

if ($bool_three == true && $bool_one_pair == 1) {
            return "フルハウスです";
        }

if ($bool_one_pair == 2) {
            return "ツーペアです";
        }

if ($bool_one_pair == 1) {
        }

 

チェック

ちゃんと判定出来ていました

全ての役判定結果

追記:JOKERが1枚あるとき

まずジョーカーが手札にあった場合カウントする。

// ジョーカーをカウント
        $joker = false;
        if ($hand_num[0] == 0) {
            $joker = true;
        }

例えば、手札の数字に0(=joker)があるときにワンペアが成立してしまうとスリーカードが成立することになるので、ジョーカーが1枚あるときは一部の役に昇格が起きるので、追加しないといけない事が増える。

  • 手札が(0,1,10,11,12)(0,1,11,12,13)(0,1,10,12,13)(0,1,10,11,13)(0,10,11,12,13)だったらロイヤルストレートフラッシュ
  • 0,2,4,5,6 → ストレート
  • 同じマークが4枚+joker → フラッシュ
  • フォーカード+joker → ファイブカード
  • スリーカード+joker → フォーカード
  • ツーペア+joker → フルハウス
  • ワンペア+joker → スリーカード
  • ノーペア+joker → ワンペア

フラッシュ

club・diamond・heart・joker・spadeのいずれかでフラッシュが成立し、アルファベットでソートしたときに下記どちらかを満たしていれば、フラッシュが判定できる。

・1枚目から4枚目はclub・diamond・heartかつ5枚目はjoker

・1枚目はjokerかつ2枚目から5枚目はspade

ストレート

全通りの組み合わせを配列に格納し(面倒)、手持ちがどれかに当てはまっていればジョーカー入りのストレートと判定。

ロイヤルストレートフラッシュとストレートフラッシュ

フラッシュとストレートを組み合わせるだけ。


それ以外の役

特に難しい要素はなく、この通りに当てはめて書くだけ。

  • フォーカード+joker → ファイブカード
  • スリーカード+joker → フォーカード
  • ツーペア+joker → フルハウス
  • ワンペア+joker → スリーカード
  • ノーペア+joker → ワンペア

 

出力結果

無事判定出来てました