「大石泉すきポーカー」の役判定プログラムをPythonで書きました

# 概要

- よ一ど ♀([@yo_do_53](https://twitter.com/yo_do_53))さんが、[診断メーカー](https://shindanmaker.com/)で「[大石泉すき](https://shindanmaker.com/733464)」診断を作る
  - 「大石泉すき」の5種類の文字から重複順列がランダムに取り出される
  - 「大石泉すき」と並ぶ確率は`1 / (5 ** 5) = 1 / 3125`
- 桜花([@ikmesyk3](https://twitter.com/ikmesyk3))さんが、↑の並びをポーカーに見立てた「[大石泉すきポーカー](https://twitter.com/ikmesyk3/status/1043484395653328896)」を提案
- 私が、その役判定プログラムを書く　←New！

# 先にソースコードを載せておきます

[Wandboxへのリンク](https://wandbox.org/permlink/4U4pEDgIApK6Sezk)

```python
import itertools
import collections
from typing import List

def judge_hand(name : str) -> str:
    """手役を判定する

    Parameters
    ----------
    name : str
        「大石泉すき」から重複を許して5文字取って並べたもの

    Returns
    -------
    str
        判定された役
    """

    # 各文字をバラす
    name_char = list(name)

    # 各文字の出現回数を数え、出現回数部分だけ抽出したもの。
    # 例：「大大すすき」→[2, 2, 1]
    char_count = collections.Counter(name_char).values()

    # 手役の判定処理
    if name == '大石泉すき':
        # 説明不要
        return 'ロイヤルストレートフラッシュ'
    elif len(set(name_char)) == 1:
        # uniq処理を施して1つしか残らない＝ファイブカード
        return 'ファイブカード'
    elif 4 in char_count:
        # ある文字が4つある＝フォーカード
        return 'フォーカード'
    elif len(char_count) == 5:
        # 5種類ある＝元の文字列が5文字なので各文字ある＝ストレートフラッシュ
        return 'ストレートフラッシュ'
    elif 2 in char_count and 3 in char_count:
        # 出現回数に「2」と「3」がある＝フルハウス
        return 'フルハウス'
    elif 'すき' in name:
        # すき
        return 'すき'
    elif 3 in char_count:
        # 出現回数に「3」がある＝スリーカード
        return 'スリーカード'
    elif 2 in char_count and len(char_count) == 3:
        # 出現回数が3文字分あって「2」が含まれる＝消去法からツーペア
        return 'ツーペア'
    return 'ブタ'

# メインルーチン
if __name__ == '__main__':
    # 使用する文字の種類
    chars = '大石泉すき'

    # itertools.productを利用して、重複順列を作り出し、1セットづつname_charに取り出す
    for name_char in itertools.product(chars, repeat=5):
        # name_charの全文字を結合したものをnameとする
        name = ''.join(name_char)
      
        # 判定を行い、結果を表示する
        print(f"{name}　{judge_hand(name)}")
```

```result1.txt
大大大大大　ファイブカード
大大大大石　フォーカード
大大大大泉　フォーカード
(中略)
大石泉すす　ブタ
大石泉すき　ロイヤルストレートフラッシュ
大石泉き大　ブタ
(中略)
きききき泉　フォーカード
ききききす　フォーカード
ききききき　ファイブカード
```

# ポイント

- 文字列(`str`型)に対して`list()`を適用すると、1文字づつにバラけます
  - 例：`list('大石泉すき')　　# => ['大', '石', '泉', 'す', 'き']`
- `collections.Counter()`を使えば、配列の各要素における個数を数えられます
  - 例：`[0, 1, 1, 2, 2, 2, 3]　　# => {0 => 1, 1 => 2, 2 => 3, 3 => 1}`
- `set()`を使えば重複した文字を実質取り除けます。リストに戻したければ`list()`で更に囲みます
  - 例：`set([0, 1, 1, 2, 2, 2, 3])　　# => {0, 1, 2, 3}`
- `itertools.product()`を使えば、[重複順列](http://www.geisya.or.jp/~mwm48961/kou2/rep_permu.htm)を自動で生成してくれます
  - 本来`itertools.product([0, 1, 2], repeat=5)`のように使いますが、Pythonは`str`型もイテレーション可能なので……

# おまけ：確率を計算

後ろの部分をちょいちょいと書き換えるだけでOKです。

[Wandboxへのリンク](https://wandbox.org/permlink/zpq8hcRqd5FGVP4c)

```python
if __name__ == '__main__':
    # 使用する文字の種類
    chars = '大石泉すき'

    # itertools.productを利用して、重複順列を作り出し、1セットづつname_charに取り出す
    temp = []
    for name_char in itertools.product(chars, repeat=5):
        # name_charの全文字を結合したものをnameとする
        name = ''.join(name_char)
      
        # 判定を行い、結果を格納する
        temp.append(judge_hand(name))

    # カウントした結果を表示する
    from pprint import pprint
    pprint(collections.Counter(temp))
```

```result2.txt
Counter({'ブタ': 984,
         'ツーペア': 765,
         'スリーカード': 516,
         'すき': 435,
         'フルハウス': 200,
         'ストレートフラッシュ': 119,
         'フォーカード': 100,
         'ファイブカード': 5,
         'ロイヤルストレートフラッシュ': 1})
```