0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ポーカーの役ができる確率を計算してみた(ジョーカー無し)

Last updated at Posted at 2024-02-03

ポーカーの役ができる確率を計算してみました。但し、ジョーカーを除いています。


  • C言語で作成するので、インクルード
poker.c
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>

  • 全カードの枚数と手札の枚数を宣言します。
/* カードの枚数 */
const int N_CARDS=52;

/* カード(C[i][0]はマーク、C[i][1]は数字(A⇒0,2⇒1,...,10⇒9,J⇒10,Q⇒11,K⇒12)を表します) */
int CARDS[N_CARDS][2];

/* 手札の枚数 */
const int N_HANDS=5;

  • 初期設定

グローバル変数CARDSにおいて、
$0\le i<j<52$ならば$C_{i,1}\le C_{j,1}$
0<=i<j<52ならばCARDS[i][1]<=CARDS[j][1]
となるようにします。

/* 初期設定 */
void init(){
    for(int i=0;i<N_CARDS;i++){
        //マーク
        CARDS[i][0]=i%4;
        
        //数字
        CARDS[i][1]=i/4;

        //j<iに対して、数字部分CARDS[j][1]<=CARDS[i][1]が保証される。
        if(i>0)assert(CARDS[i-1][1]<=CARDS[i][1]);
    }
}

  • 役のできるパターン数を初期化
/* 全パターン数 */
int nPatterns=0;

/* ロイヤルストレートフラッシュのパターン数 */
int cntRoyalStraightFlush=0;

/* ストレートフラッシュのパターン数 */
int cntStraightFlush=0;

/* フォーカードのパターン数 */
int cntFourOfAKind=0;

/* フルハウスのパターン数 */
int cntFullHouse=0;

/* フラッシュのパターン数 */
int cntFlush=0;

/* ストレートのパターン数 */
int cntStraight=0;

/* スリーカードのパターン数 */
int cntThreeOfAKind=0;

/* ツーペアのパターン数 */
int cntTwoPair=0;

/* ワンペアのパターン数 */
int cntOnePair=0;

/* ノーペア(ハイカード)のパターン数 */
int cntNoPair=0;

  • ロイヤルストレートフラッシュの判定

以下、引数handにおいて、
$0\le i<j\le4$ならば$H_{i,1}\le H_{j,1}$
0<=i<j<=4ならばhand[i][1]<=hand[j][1]
を前提とします。

/* ロイヤルストレートフラッシュの判定
 * ・5枚すべて同じスート
 *  かつ
 * ・0枚目がA、1枚目が10、2枚目がJ、3枚目がQ、4枚目がK(内部的には{0,9,10,11,12})
 * ならばtrue。
 */
bool isRoyalStraightFlush(int hands[N_HANDS][2]){
    if(hands[0][1]!=0){//0番目のカードがエース(0)でなければ
        //ロイヤルストレートフラッシュでない
        return false;
    }
    for(int i=1;i<N_HANDS;i++){
        if(hands[i][0]!=hands[0][0] || hands[i][1]!=i+8){//i枚目のスートが0枚目と異なる、またはi枚目の数字がi+9(内部的にはi+8)でなければ
            //ロイヤルストレートフラッシュでない
            return false;
        }
    }
    return true;
}

  • ストレートフラッシュの判定
/* ストレートフラッシュの判定
 * ・5枚すべて同じスート
 *  かつ
 * ・i枚目の数字が0枚目の数字+i(または、i枚目の数字がi-1枚目の数字+1)
 * ならばtrue。
 */
bool isStraightFlush(int hands[N_HANDS][2]){
    for(int i=1;i<N_HANDS;i++){
        if(hands[i][0]!=hands[0][0] || hands[i][1]!=hands[0][1]+i){//i枚目のスートが0枚目と異なる、またはi枚目の数字が0枚目の数字+iでなければ
            //ストレートフラッシュでない
            return false;
        }
    }
    return true;
}

  • フォーカードの判定
/* フォーカードの判定
 * ・先頭4枚が同じ数字
 *  または
 * ・末尾4枚が同じ数字
 * ならばtrue。
 */
bool isFourOfAKind(int hands[N_HANDS][2]){
    if(hands[0][1]==hands[1][1] && hands[1][1]==hands[2][1] && hands[2][1]==hands[3][1]){//先頭4枚が同じ数字ならば
        //フォーカードである
        return true;
    }else if(hands[1][1]==hands[2][1] && hands[2][1]==hands[3][1] && hands[3][1]==hands[4][1]){//末尾4枚が同じ数字ならば
        //フォーカードである
        return true;
    }
    return false;
}

  • フルハウスの判定
/* フルハウスの判定
 * ・先頭3枚が同じ数字 かつ 末尾2枚が同じ数字
 *  または
 * ・先頭2枚が同じ数字 かつ 末尾3枚が同じ数字
 * ならばtrue。
 */
bool isFullHouse(int hands[N_HANDS][2]){
    if(hands[0][1]==hands[1][1] && hands[1][1]==hands[2][1] && hands[3][1]==hands[4][1]){//先頭3枚が同じ数字 かつ 末尾2枚が同じ数字ならば
        //フルハウスである
        return true;
    }else if(hands[0][1]==hands[1][1] && hands[2][1]==hands[3][1] && hands[3][1]==hands[4][1]){//先頭2枚が同じ数字 かつ 末尾3枚が同じ数字ならば
        //フルハウスである
        return true;
    }
    return false;
}

  • フラッシュの判定

ロイヤルストレートフラッシュ、ストレートフラッシュが成り立つ場合もtrueを返却します。後の判定順序で制御します。

/* フラッシュの判定
 * ・5枚すべて同じスート
 * ならばtrue。
 */
 bool isFlush(int hands[N_HANDS][2]){
    for(int i=1;i<N_HANDS;i++){
        if(hands[i][0]!=hands[0][0]){//異なるスートがあれば
            //フラッシュでない
            return false;
        }
    }
    return true;
}

  • ストレートの判定

ロイヤルストレートフラッシュ、ストレートフラッシュが成り立つ場合もtrueを返却します。後の判定順序で制御します。

/* ストレート(10-J-Q-K-A型)の判定
 * ・0枚目がA、1枚目が10、2枚目がJ、3枚目がQ、4枚目がK(内部的には{0,9,10,11,12})
 * ならばtrue。
 */
bool isRoyalStraight(int hands[N_HANDS][2]){
    if(hands[0][1]!=0){//0枚目がAでない
        //ロイヤルストレートでない
        return false;
    }
    for(int i=1;i<N_HANDS;i++){
        if(hands[i][1]!=i+8){//i枚目の数字がi+9(内部的にはi+8)でなければ
            //ロイヤルストレートでない
            return false;
        }
    }
    return true;
}

/* ストレートフラッシュの判定
 * ・10-J-Q-K-A型のストレート
 *  または
 * ・i枚目の数字が0枚目の数字+i(または、i枚目の数字がi-1枚目の数字+1)
 * ならばtrue。
 */
bool isStraight(int hands[N_HANDS][2]){
    if(isRoyalStraight(hands)){//10-J-Q-K-A型のストレートならば
        //ストレートである
        return true;
    }
    for(int i=1;i<N_HANDS;i++){
        if(hands[i][1]!=hands[0][1]+i){//i枚目の数字が0枚目の数字+iでなければ
            //ストレートでない
            return false;
        }
    }
    return true;
}

  • スリーカードの判定

フォーカード、フルハウスが成り立つ場合もtrueを返却します。後の判定順序で制御します。

/* スリーカードの判定
 * ・先頭3枚が同じ数字
 *  または
 * ・真ん中3枚が同じ数字
 *  または
 * ・末尾3枚が同じ数字
 * ならばtrue。
 */
 bool isThreeOfAKind(int hands[N_HANDS][2]){
    if(hands[0][1]==hands[1][1] && hands[1][1]==hands[2][1]){//先頭3枚が同じ数字ならば
        //スリーカードである
        return true;
    }else if(hands[1][1]==hands[2][1] && hands[2][1]==hands[3][1]){//真ん中3枚が同じ数字ならば
        //スリーカードである
        return true;
    }else if(hands[2][1]==hands[3][1] && hands[3][1]==hands[4][1]){//末尾3枚が同じ数字ならば
        //スリーカードである
        return true;
    }
    return false;
}

  • ツーペアの判定

フォーカード、フルハウスが成り立つ場合もtrueを返却します。後の判定順序で制御します。

/* ツーペアの判定
 * ・0枚目と1枚目が同じ数字、かつ、2枚目と3枚目が同じ数字
 *  または
 * ・0枚目と1枚目が同じ数字、かつ、3枚目と4枚目が同じ数字
 *  または
 * ・1枚目と2枚目が同じ数字、かつ、3枚目と4枚目が同じ数字
 * ならばtrue。
 */
 bool isTwoPair(int hands[N_HANDS][2]){
    if(hands[0][1]==hands[1][1] && hands[2][1]==hands[3][1]){//0枚目と1枚目が同じ数字、かつ、2枚目と3枚目が同じ数字ならば
        //ツーペアである
        return true;
    }else if(hands[0][1]==hands[1][1] && hands[3][1]==hands[4][1]){//0枚目と1枚目が同じ数字、かつ、3枚目と4枚目が同じ数字ならば
        //ツーペアである
        return true;
    }else if(hands[1][1]==hands[2][1] && hands[3][1]==hands[4][1]){//1枚目と2枚目が同じ数字、かつ、3枚目と4枚目が同じ数字ならば
        //ツーペアである
        return true;
    }
    return false;
}

  • ワンペアの判定

今までtrueと判定された役が成り立つ場合もtrueを返却します。後の判定順序で制御します。

/* ワンペアの判定
 * ・隣り合う2枚が同じ数字
 * ならばtrue。
 */
bool isOnePair(int hands[N_HANDS][2]){
    for(int i=1;i<N_HANDS;i++){
        if(hands[i-1][1]==hands[i][1]){//隣り合う2枚が同じ数字ならば
            //ワンペアである。
            return true;
        }
    }
    return false;
}

  • カードを配る&役判定
/*
 * 五重ループを再帰処理で実施します。
 */
void deal(int m, int hands[N_HANDS][2], int k){
    if(m<N_HANDS){//まだ5枚配られていなければ
        for(int i=k;i<N_CARDS;i++){//(m+1)重ループ目
            //手札セット
            memcpy(hands[m],CARDS[i],sizeof(CARDS[i]));
            //次のカードを配る
            deal(m+1,hands,i+1);
        }
    }else{//5枚配られたら
        //i<jに対してhands[i][1]<=hands[j][1]が保証されている
        for(int i=1;i<N_HANDS;i++){
            //隣り合う2枚で確認
            assert(hands[i-1][1]<=hands[i][1]);
        }
        //パターン数インクリメント
        nPatterns++;
        
        //役判定(処理順を間違えると、ツーペアなのにワンペアと判定されたりします!!)
        if(isRoyalStraightFlush(hands)){//ロイヤルストレートフラッシュ
            cntRoyalStraightFlush++;
        }else if(isStraightFlush(hands)){//ストレートフラッシュ
            cntStraightFlush++;
        }else if(isFourOfAKind(hands)){//フォーカード
            cntFourOfAKind++;
        }else if(isFullHouse(hands)){//フルハウス
            cntFullHouse++;
        }else if(isFlush(hands)){//フラッシュ
            cntFlush++;
        }else if(isStraight(hands)){//ストレート
            cntStraight++;
        }else if(isThreeOfAKind(hands)){//スリーカード
            cntThreeOfAKind++;
        }else if(isTwoPair(hands)){//ツーペア
            cntTwoPair++;
        }else if(isOnePair(hands)){//ワンペア
            cntOnePair++;
        }else{//ノーペア(ハイカード)
            cntNoPair++;
        }
    }
}

  • 出力形式
/* 出力形式を定義します
 * 役名:左寄せ21文字分
 * パターン数:右寄せ8桁分
 * 確率:整数部分3桁、小数部分6桁
 * return : 出力文字数
 */
int print(const char* str, int cnt){
    return printf("%-21s:%8d(%10.6f%%)\n",str,cnt,100.0*cnt/nPatterns);
}

  • メイン処理
int main(int argc, char* argv[]){
    //初期処理
    init();
    
    //手札を宣言
    int hands[N_HANDS][2];

    //2,598,960通りの手札の役を判定
    deal(0,hands,0);

    //それぞれの役のパターン数と確率を出力します。
    print("Royal Straight Flush",cntRoyalStraightFlush);
    print("Straight Flush",cntStraightFlush);
    print("Four of a Kind",cntFourOfAKind);
    print("Full House",cntFullHouse);
    print("Flush",cntFlush);
    print("Straight",cntStraight);
    print("Three of a Kind",cntThreeOfAKind);
    print("Two Pair",cntTwoPair);
    print("One Pair",cntOnePair);
    int len=print("No Pair",cntNoPair);//出力文字数を取得しておく
    for(int i=0;i<len;i++){
        //合計出力前の横棒を出力する
        putchar('-');
    }
    putchar('\n');
    //合計を出力
    print("Total",nPatterns);
    return 0;
}

  • コンパイル&実行
gcc poker.c -o poker
poker

  • 出力結果
Royal Straight Flush :       4(  0.000154%)
Straight Flush       :      36(  0.001385%)
Four of a Kind       :     624(  0.024010%)
Full House           :    3744(  0.144058%)
Flush                :    5108(  0.196540%)
Straight             :   10200(  0.392465%)
Three of a Kind      :   54912(  2.112845%)
Two Pair             :  123552(  4.753902%)
One Pair             : 1098240( 42.256903%)
No Pair              : 1302540( 50.117739%)
--------------------------------------------
Total                : 2598960(100.000000%)

こちらの記事と出力結果が一致しました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?