ポーカーの手札から役を特定する

問題

第一回 オフラインリアルタイムどう書くの参考問題 #勉強会 #Ruby #C - Qiita

5枚のカードを示す文字列を入力とし、ポーカーの役を出力せよ。
ただし:

  • 一枚のカードは、スートを表す文字+ランクを表す文字列 で構成される。
  • スートを表す文字は、S, H, D, C のいずれか
  • ランクを表す文字列は、2, 3, ..., 9, 10, J, Q, K, A のいずれか
  • 下表の役に対応すること。下表の役に該当しない場合は '--' を出力すること。
  • カードはジョーカーを含まない52枚から5枚が選ばれる。
  • 不正な入力への対応は不要。

対応すべき役と、その役だった場合に出力する文字列は以下のとおり:

  • フォーカード : 4K
  • フルハウス : FH
  • スリーカード : 3K
  • ツーペア : 2P
  • ワンペア : 1P
  • 上記のいずれにも該当しない : --

例えば、入力が「D3C3C10D10S3」ならフルハウスなので「FH」と出力する。
入力が「S8D10HJS10CJ」ならツーペアなので「2P」と出力する。

解答

ふたことポイント

  • itertools.groupbyで同じランクをまとめている。
  • dictはtupleをkeyにできる。listはkeyにできないのよ〜

辞書型のキーは ほぼ 任意の値です。ハッシュ可能(hashable)でない、つまり、リストや辞書型を含む、変更可能な型 (値ではなく、オブジェクトの同一性で比較されます) はキーとして使用できません。

http://www.python.jp/doc/release/library/stdtypes.html#typesmapping
import re
from itertools import groupby

r = re.compile(".([\dJQKA]+)"*5)

def pat(cards):
    g = r.match(cards).groups()
    pats = {
            (2, 3): "FH",
            (1, 1, 3): "3K",
            (1, 2, 2): "2P",
            (1, 1, 1, 2): "1P",
            (1, 4): "4K",
            (1, 1, 1, 1, 1): "--"
            }
    trumps = tuple(sorted(len(list(g)) for k, g in groupby(sorted(g))))
    return pats[trumps]

if __name__ == "__main__":
    games = ["D3C3C10D10S3", "S8D10HJS10CJ", "S8D8H8S2H3"]
    for cards in games:
        print pat(cards)
# Output:
# FH
# 2P
# 3K

Pythonの解はすでにあったのでより短くなるようには意識した。patは辞書を引いたら出てきたんだけどトランプの役を表す単語これでいいのかしら。

感想

というかパソコン甲子園2011の出力形式を変えただけ?
http://web-ext.u-aizu.ac.jp/pc-concours/2012/03/03_p_kakomon.html
微妙にトランプゲーム作りたくなってきた。