こんにちは、みやびのです。

本講座ではPythonistaでポーカーゲームを作る方法を紹介しています。

前回「Pythonistaのポーカーゲームにカード交換機能を追加する~Pythonista+sceneポーカー作成講座3~」ではカードを交換する機能を追加しました。

これでようやくゲームっぽくなりましたね。

そして第4回となる今回はPythonistaでジョーカーを追加する処理について紹介します。

本記事の内容は以下の通り。

・Pythonistaポーカーにジョーカーを追加する方法
・Pythonistaポーカーにジョーカーの実装例と実行例

ジョーカーはどんなカードにでもなれる最強のカードです。
ゲームとしては強力なカードですが、判定はなかなか複雑になります。

Pythonistaポーカーにジョーカーを追加する方法

ジョーカーが加わることによるルール

ジョーカーはどんなカードにでもなれるカードです。

例えばKが2枚がある時にジョーカーが加われば3カードに、ハートが4枚ある時にジョーカーが加わればフラッシュになります。

ジョーカーが加わった時の役に昇格は以下の通り。

ジョーカーが加わった時の役
ぶたフラッシュ、ストレート、ストレートフラッシュ、ロイヤルストレートフラッシュ、ジョーカーのいずれか
1ペア3カード
2ペアフルハウス
3カード4カード
4カード5カード(ローカルルール)
上記以外役に5枚使うので昇格なし

5カードという役はローカルルールなので具体的な強さはルールによってまちまちですが、出る確率はロイヤルストレートフラッシュより低いです。

ジョーカー+4カードなので確率的にまず出ないと思いますが、一応判定は追加しておきましょう。
2ペア以上の役が成立しない場合の役は「ジョーカー」とします。

ジョーカーの判定を考える

以上のようにゲームとしては非常に強力なカードですが、プログラムで判定を作ろうと思うとかなり複雑な処理が必要です。

ジョーカーについては以下のパターンについて考える必要があります。

・ジョーカーのパラメータ
・ペアがある場合
・3カード、4カードについて
・フラッシュについて
・ストレートについて

◆ジョーカーのパラメータ
ジョーカーは最後に計算したいのでnumberは99とします。

joker =  {
  'number' : 99,
  'symbol' : 'Joker',
  'string' : 'Joker'
}

◆ペアがある場合
ペアがある場合の処理は以下の2つの処理を行います。

・ペアカウントを一つ減らして
・マッチナンバーに3を代入

上記を行うことで、1ペアは3カード、2ペアはフルハウス(3カード+1ペア)にできます。

◆3カード、4カードについて
マッチナンバーを+1するだけでOKです。

◆フラッシュについて
フラッシュフラグを廃止してフラッシュカウントに変更します。

フラッシュフラグの仕様は以下の通り。

・前後の数字を比較してマークが一致したらフラッシュカウントを+1
・フラッシュカウントが4の場合、フラッシュ成立
・ジョーカーがある場合はフラッシュカウントを+1

ジョーカーがある場合は他のマークと同様+1にするだけなのでそこまで複雑ではありませんね。

◆ストレートについて
ストレートフラグを廃止してストレートカウントを追加します。また、10~Aのストレートの成立した場合にロイヤルフラグというフラグを新たに立てます。

ストレートフラグの仕様は以下の通り。

・前後の数字が連続した数字の場合+1
・ストレートカウントが4の場合はストレート成立

ストレートカウントが3+ジョーカーの場合必ずストレートが成立しますが、ストレートカウント2でも成立する場合があります。

2,3,4,5→連続は3か所なのでストレートカウント3。ストレートが成立。
A,2,4,5→連続は2か所なのでストレートカウント2。このパターンもストレートが成立。
A,2,5,6→連続は2か所なのでストレートカウント2。このパターンはジョーカーが加わってもストレートが成立しない。

さらに10~Aのストレートもあるので判定がかなりカオスです。

◆ストレートカウントが3の場合の処理

・ストレートカウントが3なら+1
・10~Aが並んでいる場合はロイヤルフラグもTrueにする

3の場合はストレートカウントを+1すればOKです。
ロイヤルフラグが成立するかもチェックしましょう。

◆ストレートカウントが2の場合

・[3]と[0]の差が4以下の場合はストレートカウントを4にする
・差が5以上でなくても10~Aが並んでいる場合はカウントを4にする。ロイヤルフラグもTrueにする
・10がない場合も考慮

ストレートカウントが2の状態でストレートを成立させるためには、手札の最大値と最小値の差が4以下である必要があります。
これだけだとペアがある場合にもストレートが成立してしまう可能性があるので、ペアの判定を先に行いましょう。

差が5以上でも10~Aの場合があります。
この場合もカウントを4にして、さらにロイヤルフラグを立てます。

Pythonistaポーカーにジョーカーの実装例と実行例

make_card_list()の修正

カードリストを更新する処理「self.card_list = card_list」の直前に以下の処理を追加します。

# ジョーカーの追加		
joker =  {
	'number' : 99,
	'symbol' : 'Joker',
	'string' : 'Joker'
}
card_list.append(joker)

check_poker_hand()の修正

フラッシュフラグ・ストレートフラグをそれぞれフラッシュカウント・ストレートカウントに変更します。
また、ロイヤルフラグを追加します。

◆修正前

# フラッシュの有無フラグ
flash_flag = True
# ストレートの有無フラグ
straight_flag = True

◆修正後

# フラッシュカウント
flash_count = 0
# ストレートカウント
straight_count = 0

# ロイヤルフラグ
royal_flg = False

一致しない場合にフラグを落としていましたが、一致した場合にカウントアップするように処理を修正します。

◆修正前

# 同じマークが続いているかチェック
if flash_flag == True and cards[i]['symbol'] != cards[i - 1]['symbol']:
  flash_flag = False
# 数字が連続しているかチェック
if straight_flag == True and cards[i]['number'] != cards[i - 1]['number'] + 1:
  if cards[i]['number'] != 10 or cards[i-1]['number'] != 1:
    straight_flag = False 

◆修正後

# 同じマークが続いているかチェック
if cards[i]['symbol'] == cards[i - 1]['symbol']:
  flash_count += 1
# 数字が連続しているかチェック
if cards[i]['number'] == cards[i - 1]['number'] + 1:
  straight_count += 1
else:
  if cards[i]['number'] == 10 and cards[i-1]['number'] == 1:
    straight_count += 1

最終手札チェックの前にジョーカーチェックを追加します。

# ジョーカーチェック
joker_flg = cards[4]['string'] == 'Joker'
  if joker_flg:
    # フラッシュは一律カウントアップ
    flash_count += 1
  # 3カード,4カードの場合
  if match_number >= 3:
    # 3カードを4カード、4カードを5カードへ
    match_number += 1
  # ペアのチェック(ストレートより先にチェックする)
  elif pair_count > 0:
    # ペアを3カードに昇格
    pair_count -= 1
    match_number = 3
  # ストレートチェック1
  elif straight_count == 3:
    straight_count += 1
    # このパターンは必ず10,J,Q,Kなのでジョーカーを加えるとロイヤルフラグ確定
    if cards[0]['number'] == 10:
      straight_count = 4
      royal_flg = True
    # 13がないケース
    elif cards[0]['number'] == 1 and cards[1]['number'] == 10 and cards[3]['number'] == 12:
      straight_count = 4
      royal_flg = True
    # ストレートチェック2
    elif straight_count == 2:
      # 4以下ならストレートが成立
      if cards[3]['number'] - cards[0]['number'] <= 4:
        straight_count = 4

      # ロイヤルの並びのチェック
      elif cards[0]['number'] == 1 and cards[1]['number'] == 10 and cards[3]['number'] == 13:
        straight_count = 4
        royal_flg = True
      # 10がない場合
      elif cards[0]['number'] == 1 and cards[1]['number'] == 11 and cards[3]['number'] == 13:
        straight_count = 4
        royal_flg = True

最終手札チェックはフラグ判定の修正と5カード・ジョーカーの役の追加です。

# 最終手札チェック
if straight_count == 4 and flash_count == 4:
  if cards[0]['number'] == 1 and cards[4]['number'] == 13 or royal_flg == True:
    # ロイヤルストレートフラッシュ
    hand = 'ロイヤルnストレートフラッシュ'
  else:
    # ストレートフラッシュ
    hand = 'ストレートフラッシュ'
elif match_number > 2:			
  if match_number == 5:
    # 5カード
    hand = '5カード'
  elif match_number == 4:
    # 4カード
    hand = '4カード'
  else:
    if pair_count > 0:
      # フルハウス
      hand = 'フルハウス'
    else:
      # 3カード
      hand = '3カード'
elif flash_count == 4:
  # フラッシュ
  hand = 'フラッシュ'
elif straight_count == 4:
  # ストレート
  hand = 'ストレート'
elif pair_count > 0:
  if pair_count > 1:
    # 2ペア
    hand = '2ペア'
  else:
    # 1ペア
    hand = '1ペア'
elif joker_flg:
  hand = 'ジョーカー'
else:
  # なし
  hand = 'ぶた'

◆修正コードと実行例
修正後コード全文は以下の通りです。

※長いので非表示にしています。MySceneは修正がないので今回は割愛。
MySceneのコード第3回「Pythonistaのポーカーゲームにカード交換機能を追加する~Pythonista+sceneポーカー作成講座3~」をお読みください。

+マークをクリックするとソースコードを表示できます。

ソースコードの表示・非表示切り替え
class TrumpGame:
	def make_card_list(self):	
		# マークのリスト
		symbol_list = ['Clubs', 'Hearts', 'Spades', 'Diamonds']
		# カードリスト
		card_list = []

		# カードのデータを作成
		for symbol in symbol_list:
			for number in range(1, 14):
				card = {
					'number' : number,
					'symbol' : symbol
				}
				# マークと数字を合体させる
				# 11以上と1は置き換え
				if number == 1:
					card['string'] = symbol + 'A'
				elif number == 11:
					card['string'] = symbol + 'J'
				elif number == 12:
					card['string'] = symbol + 'Q'
				elif number == 13:
					card['string'] = symbol + 'K'
				else:
					# 10以下ならそのまま
					card['string'] = symbol + str(number)

				# カードをリストに追加
				card_list.append(card)
				
		# ジョーカーの追加		
		joker =  {
					'number' : 99,
					'symbol' : 'Joker',
					'string' : 'Joker'
		}
		card_list.append(joker)
			
		self.card_list = card_list
	def shuffle(self):
			# カードをシャッフルする
			random.shuffle(self.card_list)
			
	# 手札を作成する
	def reset_draw_cards(self, number):
		card_list = self.make_card_list()
		self.shuffle()
		self.draw_cards = []

		for i in range(0, number):
			self.draw_cards.append(
				self.card_list.pop(0)
			)	
	
	# カード交換
	def change_card(self, id):
		self.draw_cards[id] = self.card_list.pop(0)
	
	# 役のチェック処理
	def check_poker_hand(self):
		# ペア数
		pair_count = 0
		# 同じ数字のカウント
		match_count = 0
		# 同じ数字の枚数(3カード,4カードチェック用)
		match_number = 0
		# フラッシュカウント
		flash_count = 0
		# ストレートカウント
		straight_count = 0
		
		royal_flg = False
		
		# 数字の昇順に並び替える
		cards = sorted(self.draw_cards, key=lambda x:x['number'])
		
		# チェックループ
		for i in range(1,5):
				# 前の数字が同じかチェック
				if cards[i]['number'] == cards[i - 1]['number']:
					match_count += 1
					# 最終ループチェック
					if i == 4:
						if match_count == 1:
							pair_count += 1
						# 3カード以上の場合
						elif match_count > 1:
							match_number = match_count + 1
				else:
					# 違う数字の場合
					if match_count == 1:
						pair_count += 1
					# 3カード以上の場合
					elif match_count > 1:
						match_number = match_count + 1
					match_count = 0
				# 同じマークが続いているかチェック
				if cards[i]['symbol'] == cards[i - 1]['symbol']:
					flash_count += 1
				# 数字が連続しているかチェック
				if cards[i]['number'] == cards[i - 1]['number'] + 1:
					straight_count += 1
				else:
					if cards[i]['number'] == 10 and cards[i-1]['number'] == 1:
						straight_count += 1
		
		# ジョーカーチェック
		joker_flg = cards[4]['string'] == 'Joker'
		if joker_flg:
			# フラッシュは一律カウントアップ
			flash_count += 1
			# 3カード,4カードの場合
			if match_number >= 3:
				# 3カードを4カード、4カードを5カードへ
				match_number += 1
			# ペアのチェック(ストレートより先にチェックする)
			elif pair_count > 0:
				# ペアを3カードに昇格
				pair_count -= 1
				match_number = 3
			# ストレートチェック1
			elif straight_count == 3:
				straight_count += 1
				# このパターンは必ず10,J,Q,Kなのでジョーカーを加えるとロイヤルフラグ確定
				if cards[0]['number'] == 10:
					straight_count = 4
					royal_flg = True
				# 13がないケース
				elif cards[0]['number'] == 1 and cards[1]['number'] == 10 and cards[3]['number'] == 12:
					straight_count = 4
					royal_flg = True
			# ストレートチェック2
			elif straight_count == 2:
				# 4以下ならストレートが成立
				if cards[3]['number'] - cards[0]['number'] <= 4:
					straight_count = 4

				# ロイヤルの並びのチェック
				elif cards[0]['number'] == 1 and cards[1]['number'] == 10 and cards[3]['number'] == 13:
					straight_count = 4
					royal_flg = True
				# 10がない場合
				elif cards[0]['number'] == 1 and cards[1]['number'] == 11 and cards[3]['number'] == 13:
					straight_count = 4
					royal_flg = True

		# 最終手札チェック
		if straight_count == 4 and flash_count == 4:
			if cards[0]['number'] == 1 and cards[4]['number'] == 13 or royal_flg == True:
				# ロイヤルストレートフラッシュ
				hand = 'ロイヤルnストレートフラッシュ'
			else:
				# ストレートフラッシュ
				hand = 'ストレートフラッシュ'
		elif match_number > 2:			
			if match_number == 5:
				# 5カード
				hand = '5カード'
			elif match_number == 4:
				# 4カード
				hand = '4カード'
			else:
				if pair_count > 0:
					# フルハウス
					hand = 'フルハウス'
				else:
					# 3カード
					hand = '3カード'
		elif flash_count == 4:
			# フラッシュ
			hand = 'フラッシュ'
		elif straight_count == 4:
			# ストレート
			hand = 'ストレート'
		elif pair_count > 0:
			if pair_count > 1:
				# 2ペア
				hand = '2ペア'
			else:
				# 1ペア
				hand = '1ペア'
		elif joker_flg:
			hand = 'ジョーカー'
		else:
			# なし
			hand = 'ぶた'
		
		return hand

>>ソースコードをGitHubで見る

◆実行例

以上ポーカーにジョーカーを追加する方法でした。
講座はいよいよ次回で最後です。

次回>>第5回:Pythonistaのポーカーゲームにベット機能を追加する

ポーカー講座TOP>>Pythonista+sceneポーカー作成講座