yopisan
@yopisan (kota)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

ポーカーの役判定でテスト

ポーカーの役判定で強い順から並べてテストした結果、うまく表示できない。

例)
phpでポーカーの役判定でプログラムは組めたのですが、判定がうまく出ないため困っています。アドバイスお願いいたします。
まず決まりとしてポーカープログラムにジョーカーを追加してください。
// ジョーカー1枚のみ、suitをjoker、numberを0と表す。
// 上記以外は不正として処理してください。

// 追加された役
// 「フォーカード」+ジョーカーは「ファイブカード」

// 判定は強い役を優先してください。組み合わせの強さ順は以下とする。
// ロイヤルストレートフラッシュ > ストレートフラッシュ > ファイブカード > フォーカード > フルハウス > フラッシュ > ストレート > スリーカード > ツーペア > ワンペア
// ジョーカーが出た時点で最低でも「ワンペア」となること
これらを前提とする

発生している問題・エラー

この場合だとジョーカーも入れて同じ数字が3枚なっているため
スリーカードの判定が出ないといけないがワンペアのままになっている

該当するソースコード

// 絵柄不正(joker)ok
// $cards = [
//     ['suit'=>'heart', 'number'=>7],
//     ['suit'=>'joker', 'number'=>0],
//     ['suit'=>'joker', 'number'=>1],
//     ['suit'=>'diamond', 'number'=>8],
//     ['suit'=>'club', 'number'=>6],
// ];
// 数字の0の不正 ok
//   $cards = [
//    ['suit'=>'heart', 'number'=>12],
//    ['suit'=>'joker', 'number'=>0],
//    ['suit'=>'heart', 'number'=>0],
//    ['suit'=>'heart', 'number'=>10],
//    ['suit'=>'heart', 'number'=>13],
//  ];

//ロイヤルストレートフラッシュ ×
// $cards = [
//    ['suit'=>'heart', 'number'=>12],
//    ['suit'=>'joker', 'number'=>0],
//    ['suit'=>'heart', 'number'=>1],
//    ['suit'=>'heart', 'number'=>10],
//    ['suit'=>'heart', 'number'=>13],
//  ];

// ストレートフラッシュ ×
//  $cards = [
//      ['suit'=>'heart', 'number'=>12],
//      ['suit'=>'joker', 'number'=>0],
//      ['suit'=>'heart', 'number'=>9],
//      ['suit'=>'heart', 'number'=>10],
//      ['suit'=>'heart', 'number'=>13],
//  ];

//ファイブカード ×
// $cards = [
//      ['suit'=>'heart', 'number'=>12],
//      ['suit'=>'joker', 'number'=>0],
//      ['suit'=>'spade', 'number'=>12],
//      ['suit'=>'diamond', 'number'=>12],
//      ['suit'=>'club', 'number'=>12],
//  ];

//フォーカード ○
//  $cards = [
//      ['suit'=>'heart', 'number'=>12],
//      ['suit'=>'joker', 'number'=>0],
//      ['suit'=>'spade', 'number'=>9],
//      ['suit'=>'diamond', 'number'=>12],
//      ['suit'=>'club', 'number'=>12],
//  ];

//フルハウス ○
//  $cards = [
//      ['suit'=>'heart', 'number'=>12],
//      ['suit'=>'joker', 'number'=>0],
//      ['suit'=>'spade', 'number'=>1],
//      ['suit'=>'diamond', 'number'=>12],
//      ['suit'=>'club', 'number'=>1],
//  ];

//ストレート ×
// $cards = [
//      ['suit'=>'heart', 'number'=>9],
//      ['suit'=>'joker', 'number'=>0],
//      ['suit'=>'heart', 'number'=>10],
//      ['suit'=>'spade', 'number'=>13],
//      ['suit'=>'heart', 'number'=>12],
//  ];

//フラッシュ ×
//  $cards = [
//      ['suit'=>'heart', 'number'=>12],
//      ['suit'=>'joker', 'number'=>0],
//      ['suit'=>'heart', 'number'=>9],
//      ['suit'=>'heart', 'number'=>2],
//      ['suit'=>'heart', 'number'=>13],
//  ];

//スリーカード ×
//  $cards = [
//      ['suit'=>'heart', 'number'=>10],
//      ['suit'=>'joker', 'number'=>0],
//      ['suit'=>'heart', 'number'=>9],
//      ['suit'=>'spade', 'number'=>10],
//      ['suit'=>'club', 'number'=>12],
//  ];
// // ツーペア ×
// $cards = [
//   ['suit'=>'heart', 'number'=>6],
//   ['suit'=>'joker', 'number'=>0],
//   ['suit'=>'heart', 'number'=>2],
//   ['suit'=>'spade', 'number'=>2],
//   ['suit'=>'club', 'number'=>12],
// ];

//ワンペア ×
//  $cards = [
//      ['suit'=>'heart', 'number'=>10],
//      ['suit'=>'joker', 'number'=>0],
//      ['suit'=>'heart', 'number'=>9],
//      ['suit'=>'spade', 'number'=>1],
//      ['suit'=>'club', 'number'=>12],
//  ];
function judge($cards) {
    // この関数内に処理を記述
  $cover = array_unique($cards,SORT_REGULAR);
  $unique = count($cover);

  foreach($cards as $card){
      if($card['number']> 13 || $card['number'] < 0){
          return "手札が不正";
        }elseif($card['suit'] !='heart' && $card['suit'] !='spade'&& $card['suit'] !='diamond'&& $card['suit'] !='club'&& $card['suit'] !='joker'){
            return "手札が不正";
          }elseif($unique<5){ //重複分が削除されていた場合不正を返す
            return "手札が不正";
          }elseif ($card['suit']=="joker" && $card['number']!= 0) {
            return '手札は不正';
          }elseif($card['suit'] !='joker' && $card['number']==0){
            return "手札が不正";
          }
        }
    
         // カードの並び替え
         $sortNum= array_column($cards,'number');
         sort($sortNum);
         $royal = [1,10,11,12,13];
         $suit_array = array_column($cards,'suit');
         $min = $sortNum[0];
         $rangeNum= range($min,$min + 4);
         $countNum = array_count_values($sortNum);//同じ数をカウント
    
         $serch ='joker';
         $j_check = in_array($serch,$suit_array);//'joker'がsuitに含まれるかチェック
    
         // ロイヤルストレートフラッシュ > ストレートフラッシュ > ファイブカード > フォーカード > フルハウス > フラッシュ > ストレート > スリーカード > ツーペア > ワンペ
         // 役判定
         if (($sortNum == $royal) && (count(array_unique($suit_array)) == 1)) {
           $result = 10; // ロイヤルストレートフラッシュ
         } elseif (($sortNum == $rangeNum) && (count(array_unique($suit_array)) == 1)) {
           $result = 9;// ストレートフラッシュ
         } elseif (((count($countNum) == 4) && (array_keys($countNum,4)))&&($j_check)) {
           $result = 8;//役はファイブカード
         } elseif (((count($countNum) == 4) || (array_keys($countNum,3)))&&($j_check)) {
           $result = 7;//役はフォーカード
         } elseif ((count($countNum) == 3)||(($j_check))) {
           $result = 6;//役はフルハウス
         } elseif ((count(array_unique($suit_array)) == 1)&& ($j_check)) {
           $result = 5;// フラッシュ
         } elseif ($sortNum == $rangeNum){
           $result = 4;// ストレート
         } elseif ((count($countNum) == 3) && (array_keys($countNum,3))|| (count($countNum) == 4) && ($j_check)) {
           $result = 3;//スリーカード
         } elseif (count($countNum) == 3|| (count($countNum) == 3) && ($j_check)) {
           $result = 2;//ツーペア
         } elseif ((count($countNum) == 1) || (count($countNum) == 0)&&($j_check)){//またはジョーカーを引いた時
           $result = 1;//ワンペア
         } else {
           $result = 0;//役なし
         }
         // ロイヤルストレートフラッシュ > ストレートフラッシュ > ファイブカード > フォーカード > フルハウス > フラッシュ > ストレート > スリーカード > ツーペア > ワンペ
         // 結果を返す
         switch ($result) {
           case 10:
             return "役はロイヤルストレートフラッシュ";
             break;
           case 9:
             return "役はストレートフラッシュ";
             break;
           case 8:
             return "役はファイブカード";
             break;
           case 7:
              return "役はフォーカード";
              break;
           case 6:
             return "役はフルハウス";
             break;
           case 5:
             return "役はフラッシュ";
             break;
           case 4:
             return "役はストレート";
             break;
           case 3:
             return "役はスリーカード";
             break;
           case 2:
             return "役はツーペア";
             break;
           case 1:
             return "役はワンペア";
             break;
           case 0:
             return "役はなし";
             break;
         }
    }
    ?>
    <!DOCTYPE html>
    <html lang="ja">
    <head>
    <meta charset="utf-8">
    <title>ポーカー役判定(ジョーカーあり)</title>
    </head>
    <body>
        <section>
            <p>手札は</p>
            <p><?php foreach($cards as $card): ?><?=$card['suit'].$card['number'] ?><?php endforeach; ?></p>
            <p><?=judge($cards) ?>です。</p>
        </section>
    </body>
    </html>

自分で試したこと

恥ずかしながらポーカーのルールも知らなかったのでルールから覚えました。
資料「【すぐ出来る】ポーカーのルールを世界一わかりやすくプロギャンブラーが解説します。【テキサスホールデム】」
世界のヨコサワ
https://www.youtube.com/watch?v=tGoA4OWzzAk
あとはプロゲートなどで基本から立ち返ってみましたが、なかなかわからず

今の段階で検証をするためにテストをしたが、(ロイヤルストレートフラッシュ ストレートフラッシュ ファイブカード ストレート フラッシュ スリーカード ツーペア ワンペア)が読み込めていない
ジョーカーはどのカードにでもなりえるのでやはり、ジョーカーのコードが悪いのかもはや、どこを直せばいいのかわからないので、どなたかアドバイスや考え方をお願いいたします

0

2Answer

全体的にジョーカーを引いた際の役の判定が不足してると思います。
現在発生してる問題の箇所は以下の部分の判定に不足があると思われます。

} elseif ((count($countNum) == 3) && (array_keys($countNum,3))) {
           $result = 3;//スリーカード
         } elseif (count($countNum) == 3) {
           $result = 2;//ツーペア
         } 

ポーカーのルールに則った場合、ジョーカーを引いている場合の処理が必要となるため以下のようなコードになるかと思います。

} elseif ((count($countNum) == 3) && (array_keys($countNum,3)) || (count($countNum) == 4) && ($j_check)) {
           $result = 3;//スリーカード
         } elseif (count($countNum) == 3 || (count($countNum) == 4) && ($j_check)) {
           $result = 2;//ツーペア
         } 

この場合、1番下の役からジョーカーを引いていた場合を考えると簡単に考えられると思います。
例えば、以下のような条件分岐が必要です。

  • 役なし AND ジョーカー AND 残りが同模様
    → フラッシュ

  • 役なし AND ジョーカー AND 残りが連番 OR 残りが1つだけ数字が飛んだ連番
    → ストレート

  • 役なし AND ジョーカー
    → ワンペア (これだけコードにある)

  • ワンペア AND ジョーカー 
    → スリーカード (ワンペアの時点でストレートやフラッシュ等の役にはなり得ない)

  • ツーペア AND ジョーカー
    → フルハウス

役なし, ワンペア, ツーペア AND ジョーカー の例示です。ジョーカーが手元にあることで任意の役になりうる条件は全ての役について確認してください。

ワンペアANDジョーカーはツーペアともスリーカードとしても扱えるので、その辺りはルール上強い方を採択します。この場合、ツーペア < スリーカード < ストレート < フラッシュ の順番なので強い方から判定します。
条件式としては、ジョーカー以外の役を先に判定してそのネスト内でジョーカーを引いてる場合に分ける、ジョーカーを引いてる場合のネストを設けるなどやり方はありますが、コードの見やすさとしては前者が良いかと思います。

phpは未経験なので、コードの部分に誤りがあったらすみません。。

0Like

Comments

  1. @yopisan

    Questioner

    ありがとうございます。
    なるほどですね、スリーカードまでは出たのですが残りはどうなるのでしょうか。
    また疑問なんですが、フラッシュやストレートフラッシュなど数字以外に同じマークが条件の場合このコードで表すなら、このコードにプラスジョーカーが出た時の条件式で表すといいんでしょうか。

  2. > フラッシュやストレートフラッシュなど数字以外に同じマークが条件の場合このコードで表すなら、このコードにプラスジョーカーが出た時の条件式で表すといいんでしょうか。


    このコードにプラスで、OR (||)で書ききってしまうこともできます。
    厳密にはこのコードの判定では、
    ロイヤルストレートフラッシュ、ストレートフラッシュ、ファイブカード、フォーカード、ストレート、フラッシュに関しては書き換える必要がありません。なぜならこれらは判定条件がジョーカー以外のカードが5枚あることが前提だからです。

    問題はフラッシュ、ストレート等ですが、フラッシュに関しては特に問題なく以下で判定できます。
    ```php
    // ジョーカーが絡んだ場合の判定
    ($j_check) && (count(array_unique($suit_array)) == 2
    ```
    ストレートの判定が少し難しいですね。phpでは実証環境を用意してないので、コードは置いておきます。
    ```php
    $rangeNum_j= range($min,$min + 3);
    $rangeNum_j_lack = range($min,$min + 4) から両端以外の1つずつ値の抜けた要素を作成
    // minが8なら、[8, 10, 11, 12], [8, 9, 11, 12], [8, 9, 10, 12]
    ($j_check) && ( ($sortNum == $rangeNum) || ($sortNum == $rangeNum_j_lackのどれかに当てはまる))
    ```

    ジョーカーの絡んだ際の役は以下のようになります。
    - 役なし AND ジョーカー AND 残りが(10,J,Q,K,Aのいずれかの)連番 OR 残りが1つだけ数字が飛んだ(10,J,Q,K,Aのいずれかの)連番 AND 残りが同模様
    → ロイヤルストレートフラッシュ

    - 役なし AND ジョーカー AND 残りが連番 OR 残りが1つだけ数字が飛んだ連番 AND 残りが同模様
    → ストレートフラッシュ

    - 役なし AND ジョーカー AND 残りが同模様
    → フラッシュ

    - 役なし AND ジョーカー AND 残りが連番 OR 残りが1つだけ数字が飛んだ連番
    → ストレート

    - 役なし AND ジョーカー
    → ワンペア (これだけコードにある)

    - ワンペア AND ジョーカー 
    → スリーカード (ワンペアの時点でストレートやフラッシュ等の役にはなり得ない)

    - ツーペア AND ジョーカー
    → フルハウス

    - スリーカード AND ジョーカー
    → フォーカード

    - フォーカード AND ジョーカー
    → ファイブカード

    できれば、 function is_royal_straight_flash() のような形でそれぞれの役を判定するメソッドに書き換えた方が良いかと。その方がわかりやすくて理解も進みます。

  3. @yopisan

    Questioner

    ありがとうございます。
    ここの難しいところはジョーカーがなんのカードにもなり得るというところですよね
    とにかく、一つずつ進んでいけるように頑張ります

いっぺんに考えようとせずに、それぞれの役を(強い順に)判定することを考えてみるのが考えやすいです。

function judge($cards) {
    if (!checkCards($cards)) return "イカサマしてます?";

    if (isRoyalStraightFlush($cards)) {
        return "ロイヤルストレートフラッシュ";
    } else if (isStraightFlush($cards)) {
        return "ストレートフラッシュ";
    }
    // 省略
}

// カードのスートや数値、ジョーカーの枚数などをチェックする。
function checkCards($cards) {
    // 処理
}

// $cards がロイヤルストレートフラッシュかどうかをチェックする
function isRoyalStraightFlush ($cards) {
    // 処理
}

// $cards がストレートフラッシュかどうかをチェックする
function isStraightFlush($cards) {
    // 処理
}

また、ジョーカーの判定が難しそうなので、処理をわけてしまうのも手です。
分けない場合はしっかり、特に複数の役になるようなケースを考えましょう。

興味があったので自分で考えて組んでみましたが、最適化は考えてないので、参考程度に。

0Like

Comments

  1. @yopisan

    Questioner

    ありがとうございます。
    強い役から組んでいく必要があるみたいですね。今もコードに取り組んではいますが
    今の段階でどこまでできているのか検証して詰めていけるようにしていきます。
    ポーカーの判定のコード参考にさせていただきます。
    今回の場合、ジョーカーがどのカードの代わりにもなり得るので、そこは確かに苦労しそうです。

Your answer might help someone💌