ポーカーの役ができる確率を計算してみました。但し、ジョーカーを除いています。
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%)
こちらの記事と出力結果が一致しました。