ポーカーの組み合わせは何通りあるか

# はじめに
[前回](https://qiita.com/devmynote/items/4867fd52b452fd467fbc)に紹介した、組み合わせ処理を使って、ポーカー・ハンド（役）の組み合わせが全部で何通りあるかを検証します。

# ソースコード
検証用のソースコードを実装しました。

``` d:poker.d
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について](https://tour.dlang.org/tour/ja/basics/ranges)

`struct Combinations(T)`を[UFCS](https://tour.dlang.org/tour/ja/gems/uniform-function-call-syntax-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枚を選んだ最善のポーカーの役の組み合わせ（セブンカード・スタッド）

```txt:実行結果
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
```

# 参考情報
実行結果が正しいかどうかを以下のサイトで確認しています。

[ポーカー・ハンドの一覧](https://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%BC%E3%82%AB%E3%83%BC%E3%83%BB%E3%83%8F%E3%83%B3%E3%83%89%E3%81%AE%E4%B8%80%E8%A6%A7)

[D言語 組み合わせの実装例](http://rosettacode.org/wiki/Combinations#D)