この記事は「ESM Advent Calendar 2021」の12/18の記事です。
Python 3.10で新しく入ったパターンマッチでポーカーの役を判定してみました。
コードはこんな感じ。
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版と比べると、値をキャプチャしつつそのパターンの中で使うことが出来ず、同じ数か判定する部分などがガード条件まみれになってしまいましたが、同じ形の繰り返しなので理解はしやすいかと思います。ストレート系のガード条件が長くなってしまうのはご愛嬌ということで。
ガード条件をできるだけシンプルにするように書きましたが、ガード条件でもっと頑張れば行数を減らすことができます。ただし、ガード条件で頑張りだすとパターンマッチっぽさが薄れるのが悩ましいところです。
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