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

本講座はPythonistaを活用してポーカーゲームを作成する方法について解説するものです。(全5回を予定)

第1回の前回「Pythonでポーカーゲームを作成する」ではPythonでポーカーの基本となる処理を作成しました。

第2回となる今回は、Pythonでポーカーの基本となる処理を作成します。

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

・Pythonistaでポーカーを画面表示する方法
・Pythonistaでポーカーを画面表示する場合の実装例と実行例

Pythonistaでポーカーを画面表示する方法

ポーカーの画面表示に必要な処理は以下の通りです。

・背景色の変更
・カード画像の追加
・ポーカー役の表示
・手札の更新

背景色の変更

デフォルトだと黒なので背景色の変更を行います。
背景色はなんでもOKです。
トランプのカラーに合わせて決めましょう。

背景色は「background_color」で指定できます。

self.background_color = '#004f82'

カード画像の追加

5枚のトランプカードを画面に追加します。
Pythonistaでは予めトランプ画像が同梱されているので画像を用意する必要はありません。

Texture()関数に’card’ + ‘symbol’ + ‘number’を渡すことで画面表示できます。

item = SpriteNode()
self.items.append(item)
item.position = (50 + i * 70, 350)
item.texture = Texture('card:' + card['string'])
item.anchor_point = (0.5, 0)
self.add_child(item)

画像の追加方法については「Pythonistaのsceneライブラリ画像の使い方」もあわせてお読みください。

ポーカー役の表示

ただ5枚カードを出すだけだと味気ないのでポーカーの役を上部に表示しましょう。
文字列を画面に表示する場合は、「LabelNode()」を使用します。

記述例は以下の通り。

# 役の表示
hand_font = ('Futura', 30)
self.hand_label = LabelNode('', hand_font, parent=self)
self.hand_label.position = (self.size.w/2, self.size.h - 70)
self.hand_label.z_position = 1

関連記事>>【Pythonista+scene】文字を画面に表示する方法

手札の更新

手札の更新処理を作成するためには「カードを引く処理」と「役の更新処理」の2つが必要です。

◆カードを引く処理
前回作成したreset_draw_cards()を呼び出せば5枚のカードのリストが作成できます。
あとは5枚の画像に対応したカードを割り当てるだけです。

# カードを5枚引く処理
def reset_draw_cards(self):
  # カードを5枚引く
  self.tg.reset_draw_cards(5)
  # カード画像の割り当て
  for i,card in enumerate(self.tg.draw_cards):
    self.set_item(i)

カードを引く処理はタッチした時に動かします。
タッチした時の処理はtouch_began()メソッドに定義可能です。

def touch_began(self, touch):
  self.reset_game()

touch_began()の下に処理を書くだけだと画面のどこをタッチした場合でも処理が動きますが、ボタンなどを設置することでタッチの範囲を絞ることができます。

ボタンの上に文字を挿入することも可能です。

# ボタン
self.button = SpriteNode('pzl:Button1', position=(self.size.w/2,300))
self.add_child(self.button)

# ボタンの文字列
button_font = ('Futura', 30)
self.button_label = LabelNode('Reset', button_font, parent=self, color='black')
self.button_label.position = (self.size.w/2,305)
self.button_label.z_position = 1

タッチの位置はself.point_from_scene()にtouch.locationを渡すことで取得できます。
画像の範囲はframeに格納されているのでこれと比較します。

def touch_began(self, touch):
  touch_loc = self.point_from_scene(touch.location)
		
  if touch_loc in self.button.frame:
    # 画像を変えるとクリックしたように見える
    self.button.texture = Texture('pzl:Button2')
    self.reset_game()
					
    # 役の更新
    self.hand_label.text = self.tg.check_poker_hand()

◆役の更新
役の更新は前回作成した「check_poker_hand()」を呼び出して役を設定します。

# 役の更新
self.hand_label.text = self.tg.check_poker_hand()

Pythonistaでポーカーを画面表示する場合の実装例と実行例

具体的な実装例と実行例を紹介します。

初期設定の処理実装

◆setup()の修正
setup()メソッドには初期設定メソッドの「setup_object()」と「reset_game()」の2つのメソッドを呼び出す処理を追加します。

def setup(self):
	self.setup_object()
	self.reset_game()

◆setup_object()
背景や画像・文字をセットするメソッドです。
以下の4つの処理を行います。

・バックグラウンド(背景)の設定
・役の表示の追加
・Restボタンの追加
・カードの画像の追加

def setup_object(self):
	# バックグラウンド設定
	self.background_color = '#004f82'
	ground = Node(parent=self)
		
	# 役の表示
	hand_font = ('Futura', 30)
	self.hand_label = LabelNode('', hand_font, parent=self)
	self.hand_label.position = (self.size.w/2, self.size.h - 70)
	self.hand_label.z_position = 1
		
	# ボタン
	self.button = SpriteNode('pzl:Button1', position=(self.size.w/2,300))
	self.add_child(self.button)
		
	button_font = ('Futura', 30)
	self.button_label = LabelNode('Reset', button_font, parent=self, color='black')
	self.button_label.position = (self.size.w/2,305)
	self.button_label.z_position = 1
		
	self.items = []
	self.tg = TrumpGame()
	# カードの初期設定
	for i in range(0,5):
		item = SpriteNode()
		self.items.append(item)
		item.position = (50 + i * 70, 350)
		self.add_child(item)

◆reset_game()
山札をシャッフルして5枚引き直すメソッドです。
前回作成した「reset_draw_cards()」を呼び出して役の表示を更新します。

def reset_game(self):
	self.reset_draw_cards()
	# 役の更新
	self.hand_label.text = self.tg.check_poker_hand()

◆reset_draw_cards()
カードを5枚引く処理です。
前回作成したTrumpクラスの「reset_draw_cards()」を呼び出し1枚ずつカードをセットします。

# カードを5枚引く処理
def reset_draw_cards(self):
	self.tg.reset_draw_cards(5)
	for i,card in enumerate(self.tg.draw_cards):
		self.set_item(i)

◆set_item()
カードを1枚単位で設定するメソッドです。
カード画像の変更と位置のanchor_pointの調整を行います。

トランプの画像はそのままのサイズで5枚を並べてしまうと画面に収まりきらないので、サイズ調整が必要です。

# カードを画面表示するためのセットアップ
def set_item(self, id):
	item = self.items[id]
	card = self.tg.draw_cards[id]
	item.texture = Texture('card:' + card['string'])
	item.anchor_point = (0.5, 0)
	# サイズ調整
	self.resize_item(item)

サイズ調整の処理はメソッドを作成しました。
画像サイズはsizeに代入することで変更できます。

# サイズ調整
def resize_item(self, item):
	item.size = (65, 87)

画面タッチ時の処理実装

◆touch_began()、touch_ended()の修正
ボタンをタッチした時に役を更新する処理を追加します。

def touch_began(self, touch):
	touch_loc = self.point_from_scene(touch.location)
		
	if touch_loc in self.button.frame:
		self.button.texture = Texture('pzl:Button2')
		self.reset_game()
					
		# 役の更新
		self.hand_label.text = self.tg.check_poker_hand()

ボタンの画像をtouch_began()で「pzl:Button2」に変え、touch_ended()で「pzl:Button1」に戻すことでボタンを押したように見せることができます。

def touch_ended(self, touch):
	self.button.texture = Texture('pzl:Button1')

実装例と実行例

上記コードをまとめた実装例は以下の通りです。
※TrumpGameクラスの処理は前回からの変更はありません。
※長いので非表示にしています。

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

ソースコードの表示・非表示切り替え
from scene import *
from scene import *
import random

class MyScene (Scene):
	def setup(self):
		self.setup_object()
		self.reset_game()
		
	def touch_began(self, touch):
		touch_loc = self.point_from_scene(touch.location)
		
		if touch_loc in self.button.frame:
			self.button.texture = Texture('pzl:Button2')
			self.reset_game()
					
			# 役の更新
			self.hand_label.text = self.tg.check_poker_hand()
	
	def touch_ended(self, touch):
		self.button.texture = Texture('pzl:Button1')
			
	# カードを画面表示するためのセットアップ
	def set_item(self, id):
		item = self.items[id]
		card = self.tg.draw_cards[id]
		item.texture = Texture('card:' + card['string'])
		item.anchor_point = (0.5, 0)
		# サイズ調整
		self.resize_item(item)
		
	
	def setup_object(self):
		# バックグラウンド設定
		self.background_color = '#004f82'
		ground = Node(parent=self)
		
		# 役の表示
		hand_font = ('Futura', 30)
		self.hand_label = LabelNode('', hand_font, parent=self)
		self.hand_label.position = (self.size.w/2, self.size.h - 70)
		self.hand_label.z_position = 1
		
		# ボタン
		self.button = SpriteNode('pzl:Button1', position=(self.size.w/2,300))
		self.add_child(self.button)
		
		button_font = ('Futura', 30)
		self.button_label = LabelNode('Reset', button_font, parent=self, color='black')
		self.button_label.position = (self.size.w/2,305)
		self.button_label.z_position = 1
		
		self.items = []
		self.tg = TrumpGame()
		# カードの初期設定
		for i in range(0,5):
			item = SpriteNode()
			self.items.append(item)
			item.position = (50 + i * 70, 350)
			self.add_child(item)
	
	def reset_game(self):
		self.reset_draw_cards()
		# 役の更新
		self.hand_label.text = self.tg.check_poker_hand()
												
	# カードを5枚引く処理
	def reset_draw_cards(self):
		self.tg.reset_draw_cards(5)
		for i,card in enumerate(self.tg.draw_cards):
				self.set_item(i)
	
	# サイズ調整
	def resize_item(self, item):
		item.size = (65, 87)


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)
			
		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 check_poker_hand(self):
		# ペア数
		pair_count = 0
		# 同じ数字のカウント
		match_count = 0
		# 同じ数字の枚数(3カード,4カードチェック用)
		match_number = 0
		# フラッシュの有無フラグ
		flash_flag = True
		# ストレートの有無フラグ
		straight_flag = True
		
		# 数字の昇順に並び替える
		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 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 straight_flag == True and flash_flag == True:
			if cards[0]['number'] == 1 and cards[4]['number'] == 13:
				# ロイヤルストレートフラッシュ
				hand = 'ロイヤルnストレートフラッシュ'
			else:
				# ストレートフラッシュ
				hand = 'ストレートフラッシュ'
		elif match_number > 2:
			if match_number == 4:
				# 4カード
				hand = '4カード'
			else:
				if pair_count > 0:
					# フルハウス
					hand = 'フルハウス'
				else:
					# 3カード
					hand = '3カード'
		elif flash_flag == True:
			# フラッシュ
			hand = 'フラッシュ'
		elif straight_flag == True:
			# ストレート
			hand = 'ストレート'
		elif pair_count > 0:
			if pair_count > 1:
				# 2ペア
				hand = '2ペア'
			else:
				# 1ペア
				hand = '1ペア'
		else:
			# なし
			hand = 'ぶた'
		
		return hand

if __name__ == '__main__':
	run(MyScene(), show_fps=False)

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

◆実行例

画面をタッチするとカードが切り替わります。

以上、Pythonistaでポーカーを画面表示する方法でした。
次回はカードの交換処理を追加する方法を紹介します。

次回>>第3回:Pythonistaのポーカーゲームにカード交換機能を追加する

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