目次
- 手役判定
手役判定
この章では、枚数2枚~7枚の与えられたカード配列を引数とし、それらから作ることのできる最強の役を判定する check_hand(v) の説明を行う。
下図はショーダウンにおいて、手札をさらしている状態だが、プレイヤーパネル下部にポーカー役名 (フルハウス・3オブアカインド)が表示されている。この役判定が check_hand() で行われている。

ショーダウンにおいて、どのプレイヤーが勝ったのかを決めるために、 手持ちカード・共有カードからポーカーの役を判定する必要があるのはもちろんのこと、 共有カードが0,3,4枚である途中のラウンドでも、その時点での手役を表示するために、引数のカード枚数は可変となっている。
また、同じ役であった場合にも強弱を判定可能にする必要があるので、check_hand() が返す値は単なる数値ではなく、 [手役値, ランク1,ランク2,…] という配列にしている。ランクとはカードの数字のことで、 手役により意味のある数値が付加的に追加される。
例えば、5, 5, 5, K, K のフルハウスの場合、[FULL_HOUSE, RANK_5, RANK_K] が返る。
こうしておけば、配列の先頭から順に値を比較することで役の強弱を判定できる。
check_hand() の実装を下記に示す。
enum { # 手役
HIGH_CARD = 0,
ONE_PAIR,
TWO_PAIR,
THREE_OF_A_KIND,
STRAIGHT,
FLUSH,
FULL_HOUSE,
FOUR_OF_A_KIND,
STRAIGHT_FLUSH,
ROYAL_FLUSH,
N_KIND_HAND,
};
# 手役判定
# return: [手役値, ランク1,ランク2,...]
func check_hand(v : Array) -> Array:
var rcnt = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] # 各ランク枚数
var scnt = [0, 0, 0, 0] # 各スート枚数
for i in range(v.size()): # 手札のランク・スートの数を数える
rcnt[card_to_rank(v[i])] += 1
scnt[card_to_suit(v[i])] += 1
var s = -1 # フラッシュの場合のスート
if scnt[CLUBS] >= 5: s = CLUBS # スートが5枚以上あればフラッシュ
elif scnt[DIAMONDS] >= 5: s = DIAMONDS
elif scnt[HEARTS] >= 5: s = HEARTS
elif scnt[SPADES] >= 5: s = SPADES
if s >= CLUBS: # フラッシュ確定の場合
var bitmap = 0 # ランクをビット値に変換したものの合計
for i in v.size():
if( card_to_suit(v[i]) == s ): # 同一スートの場合
bitmap |= 1 << card_to_rank(v[i])
var mask = 0x1f00 # AKQJT
for i in range(9):
if( (bitmap & mask) == mask ):
return [STRAIGHT_FLUSH, mask] # ストレートフラッシュ
mask >>= 1
if( bitmap == 0x100f ): # 1 0000 00000 1111 = 5432A
return [STRAIGHT_FLUSH, 0x0f] # 5432A よりも 65432 の方が強い
return add_rank(v, s, FLUSH) # 単なるフラッシュ
.....
まずは enum で役名を宣言している。強い役ほど大きい数字になっている。
関数の実装では、まず各ランク・スートの数を数える。どれかのスート枚数が5を超えていればフラッシュが確定となる。
ストレートフラッシュの場合もあるので、再度すべてのカードを調べ、当該スートの場合は、ランクをビットマップにした総和を計算する。 それが 0x1f00, 0x0f80, …, 0x001f, 0x100f のいずれかであればストレートフラッシュ、そうでなければ単なるフラッシュとなる。
add_rank() はフラッシュの場合に、そのスートの数字を大きい順に結果配列に追加する関数だ。実装を下記に示す。
func add_rank(v, s, hand): # フラッシュの場合に、そのスートの数字を大きい順に結果配列に追加
var rnk = []
for i in range(v.size()):
if( card_to_suit(v[i]) == s ): # 同一スートの場合
rnk.push_back(card_to_rank(v[i]))
rnk.sort() # 昇順ソート
var t = [hand]
for i in range(5): # 大きいランクから5枚を配列に追加
t.push_back(rnk[-1-i]) # ランクを降順に格納
return t
以下は check_hand() 実装の続きだ。
func check_hand(v : Array) -> Array:
.....
var threeOfAKindRank1 = -1 # 3 of a Kind の rcnt インデックス
var threeOfAKindRank2 = -1 # 3 of a Kind の rcnt インデックス その2
var pairRank1 = -1 # ペアの場合の rcnt インデックス
var pairRank2 = -1 # ペアの場合の rcnt インデックス その2、pairRank1 > pairRank2 とする
for r in range(13):
if( rcnt[r] == 4):
return [FOUR_OF_A_KIND, r] # 4 of a kind は他のプレイヤーと同じ数字になることはない
if( rcnt[r] == 3):
if( threeOfAKindRank1 < 0 ):
threeOfAKindRank1 = r
else:
threeOfAKindRank2 = r
elif( rcnt[r] == 2):
if pairRank1 < 0:
pairRank1 = r
elif pairRank2 < 0:
if pairRank1 > r:
pairRank2 = r
else:
pairRank2 = pairRank1
pairRank1 = r
else:
if r > pairRank1:
pairRank2 = pairRank1
pairRank1 = r
if r > pairRank2:
pairRank2 = r
# 3カード*2 もフルハウス
if( threeOfAKindRank1 >= 0 && (pairRank1 >= 0 || threeOfAKindRank2 >= 0) ):
return [FULL_HOUSE, threeOfAKindRank1] # 3 of a kind は他のプレイヤーと同じ数字になることはない
.....
次にランクごとの枚数を調べ、ペア系の役を探す。
同じランクが4枚揃っていれば、4オブアカインド確定なので、[FOUR_OF_A_KIND, r] を返す。r はランクだ。 まずありえないことだが、複数人が4オブアカインドの場合に、優劣をつけるための情報だ。 もしそうなったら、壮絶なベット合戦が繰り広げられることだろう。
同じランクが3枚、2枚の場合もチェックし、そうであればそのランクを threeOfAKindRank1, 2, pairRank1, pairRank2 に保持する。 そして、3枚が2種類、または3枚と2枚があればフルハウスとなる。
以下が、check_hand() の残りの実装コードだ。
func check_hand(v : Array) -> Array:
.....
var bitmap = 0
var mask = 1
for i in range(13):
if( rcnt[i] != 0 ):
bitmap |= mask
mask <<= 1
mask = 0x1f00 # AKQJT
for i in range(9):
if( (bitmap & mask) == mask ):
return [STRAIGHT, mask]
mask >>= 1
if( (bitmap & 0x100f) == 0x100f ): # 5432A
return [STRAIGHT, 0x0f] # 5432A より 65432 の方が強い
if( threeOfAKindRank >= 0 ):
return [THREE_OF_A_KIND, threeOfAKindRank] # 3 of a kind は他のプレイヤーと同じ数字になることはない
if( pairRank2 >= 0 ):
#return [TWO_PAIR]
return add_rank_pair(v, pairRank1, pairRank2, TWO_PAIR)
if( pairRank1 >= 0 ):
return add_rank_pair(v, pairRank1, -1, ONE_PAIR)
#return [ONE_PAIR]
return add_rank_pair(v, -1, -1, HIGH_CARD)
ストレート、3オブアカインド、2ペア、ワンペアをチェックし、そうであればその役情報を返す。
add_rank_pair() はペアがある場合に、同じ役での強弱判定のための情報を付加する関数だ。
以下にそのコードを示す。
func add_rank_pair(v, p1, p2, hand): # ペアの場合に、ペア以外の数字を大きい順に結果配列に追加
var rnk = []
for i in range(v.size()):
var r = card_to_rank(v[i])
if r != p1 && r != p2: # ペアの数字でない場合
rnk.push_back(r)
rnk.sort() # 昇順ソート
var t = [hand]
var n = 5 # 配列に追加する枚数
if p2 >= 0: # 2ペアの場合
t.push_back(p1)
t.push_back(p2)
n = 1
elif p1 >= 0: # 1ペアの場合
t.push_back(p1)
n = 3
n = min(rnk.size(), n)
for i in range(n):
t.push_back(rnk[rnk.size()-1-i]) # ランクを降順に格納
return t
TechProjin Godot入門 関連連載リンク
Godotで学ぶゲーム制作
さくさく理解するGodot入門 連載目次
標準C++ライブラリの活用でコーディング力UP!
「競技プログラミング風」標準C++ライブラリ 連載目次