はじめに
前回に紹介した、組み合わせ処理を使って、ポーカー・ハンド(役)の組み合わせが全部で何通りあるかを検証します。
ソースコード
検証用のソースコードを実装しました。
struct Combinations(T) {
import std.traits: Unqual;
Unqual!T[] pool, front;
size_t[] index;
size_t n, r;
bool empty = false;
this(T[] pool_, in size_t r_) pure nothrow @safe {
this.pool = pool_.dup;
this.n = pool.length;
this.r = r_;
if (r > n){
empty = true;
}
front.length = r;
index.length = r;
foreach ( i; 0 .. r){
front[i] = pool[i];
index[i] = i;
}
}
void popFront() pure nothrow @safe {
if (!empty) {
bool broken = false;
size_t pos;
foreach_reverse (immutable i; 0 .. r) {
pos = i;
if (index[i] != i + n - r) {
broken = true;
break;
}
}
if (!broken) {
empty = true;
return;
}
index[pos]++;
foreach (immutable i; pos + 1 .. r){
index[i] = index[i - 1] + 1;
front[i] = pool[index[i]];
}
front[pos] = pool[index[pos]];
}
}
}
Combinations!(T) combinations(T)(T[] items, in size_t k)
{
return typeof(return)(items, k);
}
int analyzeHand(int[] card) pure
{
int[5] num; // 2,3,4...Q,K,A
int[13] sheet;
int[5] kind;
bool same = true;
for ( int j = 0; j < card.length; j++ ){
num[j] = card[j] >> 2;
sheet[num[j]]++;
if ( (card[0] & 0x03) != (card[j] & 0x03) ){
same = false;
}
}
for ( int j = 0; j < sheet.length; j++ ){
kind[sheet[j]]++;
}
switch ( kind[0] ){
case 9:
return( 8 ); // 8(One pair)
case 10:
return( kind[3] ? 6 : 7 ); // 6(Three of a kind) 7(Two pair)
case 11:
return( kind[4] ? 2 : 3 ); // 2(Four of a kind) 3(Full house)
default:
int x = num[0];
if ( (num[1] == x + 1 && num[2] == x + 2 && num[3] == x + 3)
&& (num[4] == x + 4 || (num[0] == 0 && num[4] == 12)) ){
// 0(Royal flush) 1(Straight flush) 5(Straight)
return( same ? ((num[0] == 8) ? 0 : 1) : 5 );
} else {
return( same ? 4 : 9 ); // 4(Flush) 9(High Cards)
}
}
}
int analyzeSevenHand(int[] card) pure
{
import std.algorithm.comparison: min;
int hand = 9; // 9(High Cards)
foreach ( c; card.combinations(5) ){
hand = min(hand, c.analyzeHand);
}
return ( hand );
}
string[] name = [
"Royal flush","Straight flush","Four of a kind","Full house","Flush",
"Straight","Three of a kind","Two pair","One pair","High Cards"
];
void calculate(string msg, int n, int function(int[]) fp)
{
import std.stdio;
import std.range: array, iota;
writeln(msg);
int[10] hand;
foreach ( card; 52.iota.array.combinations(n) ){
hand[fp(card)]++;
}
for ( int j = 0; j < hand.length; j++ ){
writefln("%-20s %10d", name[j], hand[j]);
}
}
void main() {
calculate("Poker hand", 5, &analyzeHand);
calculate("Seven card stud", 7, &analyzeSevenHand);
}
ソースコード補足説明
struct Combinations(T)
は、組み合わせを取得するための構造体です。
empty
、front
、popFront
を用意することで、Range
の仕組みを利用しています。
D言語 Rangeについて
struct Combinations(T)
をUFCSで使いやすくするために、関数Combinations!(T) combinations(T)(T[] items, in size_t k)
を用意しています。
analyzeHand
は、ポーカーの役を判定するための関数です。
引数のcard
は大きさ5の配列で、トランプのカードを表す数値(0~51)が入っている前提です。
カードを表す数値(0~51)は、
- 下位2ビットでトランプのマーク(♠、♦、♥、♣)を表現
- 残りのビットでトランプの数字(0~12を2, 3, 4 ... Q, K, Aに割り当て)を表現
戻り値は、ポーカーの役を表す数値(0~9)です。定数定義をさぼって、コメントで補足しています。
関数中のswitch ( kind[0] )
を使って、ペアになっていないカードの数を判定しています。
kind[3]
が1
であれば同じ数字が3枚、kind[4]
が1
であれば同じ数字が4枚そろっていることになります。
実行結果
ポーカーの役の組み合わせを2パターン算出しました。
- 最初に配られた5枚でのポーカーの役の組み合わせ(ポーカー・ハンド)
- 最初に配られた7枚から5枚を選んだ最善のポーカーの役の組み合わせ(セブンカード・スタッド)
Poker hand
Royal flush 4
Straight flush 36
Four of a kind 624
Full house 3744
Flush 5108
Straight 10200
Three of a kind 54912
Two pair 123552
One pair 1098240
High Cards 1302540
Seven card stud
Royal flush 4324
Straight flush 37260
Four of a kind 224848
Full house 3473184
Flush 4047644
Straight 6180020
Three of a kind 6461620
Two pair 31433400
One pair 58627800
High Cards 23294460
参考情報
実行結果が正しいかどうかを以下のサイトで確認しています。