Python3.10.0のパターンマッチでポーカーの役を判定してみた

この記事は「[ESM Advent Calendar 2021](https://adventar.org/calendars/6972)」の12/18の記事です。

https://adventar.org/calendars/6972

Python 3.10で新しく入った[パターンマッチ](https://www.python.org/dev/peps/pep-0636/)でポーカーの役を判定してみました。

コードはこんな感じ。

```Python
def judge_poker(cards):
    match sorted(cards, key=lambda card: card[1]):
        case [(v, a), (w, b), (x, c), (y, d), (z, e)]  if v == w == x == y == z and all([(x[1]- x[0]) == 1 for x in zip([a, b, c, d], [b, c, d, e])]):
            result = 'straight_flash'
        case [(v, 1), (w, 10), (x, 11), (y, 12), (z, 13)]  if v == w == x == y == z:
            result = 'straight_flash'
        case [(_, a), (_, b), (_, c), (_, d), (_, _)]  if a == b == c == d:
            result = 'four_of_kind'
        case [(_, _), (_, a), (_, b), (_, c), (_, d)]  if a == b == c == d:
            result = 'four_of_kind'
        case [(_, a), (_, b), (_, c), (_, d), (_, e)]  if all([(x[1]- x[0]) == 1 for x in zip([a, b, c, d], [b, c, d, e])]):
            result = 'straight'
        case [(_, 1), (_, 10), (_, 11), (_, 12), (_, 13)]:
            result = 'straight'
        case [(v, _), (w, _), (x, _), (y, _), (z, _)]  if v == w == x == y == z:
            result = 'flash'
        case [(_, a), (_, b), (_, c), (_, d), (_, e)]  if a == b == c and d == e:
            result = 'full_house'
        case [(_, a), (_, b), (_, c), (_, d), (_, e)]  if a == b and c == d == e:
            result = 'full_house'
        case [(_, a), (_, b), (_, c), (_, _), (_, _)]  if a == b == c:
            result = 'three_of_kind'
        case [(_, _), (_, a), (_, b), (_, c), (_, _)]  if a == b == c:
            result = 'three_of_kind'
        case [(_, _), (_, _), (_, a), (_, b), (_, c)]  if a == b == c:
            result = 'three_of_kind'
        case [(_, a), (_, b), (_, c), (_, d), (_, _)]  if a == b and c == d:
            result = 'two_pair'
        case [(_, _), (_, a), (_, b), (_, c), (_, d)]  if a == b and c == d:
            result = 'two_pair'
        case [(_, a), (_, b), (_, _), (_, c), (_, d)]  if a == b and c == d:
            result = 'two_pair'
        case [(_, a), (_, b), (_, _), (_, _), (_, _)]  if a == b:
            result = 'one_pair'
        case [(_, _), (_, a), (_, b), (_, _), (_, _)]  if a == b:
            result = 'one_pair'
        case [(_, _), (_, _), (_, a), (_, b), (_, _)]  if a == b:
            result = 'one_pair'
        case [(_, _), (_, _), (_, _), (_, a), (_, b)]  if a == b:
            result = 'one_pair'
        case _:
            result = 'buta'
```

カードは2要素のタプルで表現していて、それぞれのタプルの1つ目の要素がスートで2つ目の要素が数です。 ``judge_poker([('S', 1), ('D', 2), ('H', 3), ('C', 4), ('S', 1)])`` のように使います。

[Ruby版](https://qiita.com/t-kunou/items/f79bfd615133d3b414ac)と比べると、値をキャプチャしつつそのパターンの中で使うことが出来ず、同じ数か判定する部分などがガード条件まみれになってしまいましたが、同じ形の繰り返しなので理解はしやすいかと思います。ストレート系のガード条件が長くなってしまうのはご愛嬌ということで。
ガード条件をできるだけシンプルにするように書きましたが、ガード条件でもっと頑張れば行数を減らすことができます。ただし、ガード条件で頑張りだすとパターンマッチっぽさが薄れるのが悩ましいところです。

```Python
def judge_poker(cards):
    match sorted(cards, key=lambda card: card[1]):
        case [(v, a), (w, b), (x, c), (y, d), (z, e)]  if v == w == x == y == z and all([(x[1]- x[0]) == 1 for x in zip([a, b, c, d], [b, c, d, e])]):
            result = 'straight_flash'
        case [(v, 1), (w, 10), (x, 11), (y, 12), (z, 13)]  if v == w == x == y == z:
            result = 'straight_flash'
        case [(_, a), (_, b), (_, c), (_, d), (_, e)]  if (a == b == c == d) or (b == c == d == e):
            result = 'four_of_kind'
        case [(_, a), (_, b), (_, c), (_, d), (_, e)]  if all([(x[1]- x[0]) == 1 for x in zip([a, b, c, d], [b, c, d, e])]):
            result = 'straight'
        case [(_, 1), (_, 10), (_, 11), (_, 12), (_, 13)]:
            result = 'straight'
        case [(v, _), (w, _), (x, _), (y, _), (z, _)]  if v == w == x == y == z:
            result = 'flash'
        case [(_, a), (_, b), (_, c), (_, d), (_, e)]  if (a == b == c and d == e) or (a == b and c == d == e):
            result = 'full_house'
        case [(_, a), (_, b), (_, c), (_, d), (_, e)]  if (a == b == c) or (b == c == d) or (c == d == e):
            result = 'three_of_kind'
        case [(_, a), (_, b), (_, c), (_, d), (_, e)]  if (a == b and c == d) or (a == b and d == e) or (b == c and d == e):
            result = 'two_pair'
        case [(_, a), (_, b), (_, c), (_, d), (_, e)]  if a == b or b == c or c == d or d == e:
            result = 'one_pair'
        case _:
            result = 'buta'

    return result
```