前回で一応じゃんけんゲームのアプリが完成したけど、色々機能を追加したらプログラムが複雑になっちゃったよね
そうだよねー
どこからどこまでがボタンの機能か分かりにくいし、なんか更新するプログラムが何箇所もあって見にくいし
ということで、今回はプログラムをもう少し簡潔に整えていこう!
まずは変数名について
「hands」「hand_images」「win_rules」ってこのプログラムにおいては絶対に変更しない要素だよね
確かに、じゃんけんの手の種類が増えたり勝ち方が変わっちゃうことってないもんね
そうだね!こういう、プログラムの動作中に絶対変更することがないものを「定数」っていうんだ
他のプログラムではちゃんと区別を付けられるんだけど、Pythonは変数と定数で扱いの違いは無いんだよね
じゃあ別にわざわざ改めなくても良いんじゃない??
まぁ命名の仕方はなんでも良いんだけど、「こうやって名前をつけた方が皆が分かりやすくなるから良いよ」っていう暗黙の作法みたいなのがあるんだ
ほうほう
じゃあその作法を覚えておけば、パッと見て定数か変数かってのが分かるようになるんだね
そゆこと
ちなみに「定数」は全部の文字を大文字にして、単語ごとをアンダーバーで繋いだ形にすれば良いよ!
# 定数
HANDS = ["グー", "チョキ", "パー"]
HAND_IMAGES = {
"グー": "janken_gu.png",
"チョキ": "janken_choki.png",
"パー": "janken_pa.png"
}
WIN_RULES = {
"グー": "チョキ",
"チョキ": "パー",
"パー": "グー"
}
定数を使ってる場所もちゃんと変更するのを忘れないようにしないとだね!!
次は各ボタンの処理とスコアの管理についての改善方法を考えようか
ボタンの処理の部分だと、if文の中にたくさんプログラムを書いてるから分かりにくくなってるよね
スコアの管理についても、「global」を使うのはあまり良くなさそうだったし、使わないでうまく出来る方法はないのかなぁ
まさにその2つが今回の改善ポイントだね!
その問題を一気に解決してくれるのが「class」なんだよ
そういえばじゃんけんゲームを作る前に紹介してたね!?
今回の場合、スコアを管理する変数をゲーム全体で扱えるのが良くて、「各ボタンにそれぞれ機能を持たせるイメージ」が重要になるよね
オブジェクト指向...!!
今回はプログラム全体を「class」にすることで、葵が言った改善ポイントをクリアできるようになるんだ
だけど、1つずつ説明すると混乱するかもしれないから、今回は改修後の全体のプログラムを先に出しちゃうね
import TkEasyGUI as eg
import random
# 定数
HANDS = ["グー", "チョキ", "パー"]
HAND_IMAGES = {
"グー": "janken_gu.png",
"チョキ": "janken_choki.png",
"パー": "janken_pa.png"
}
WIN_RULES = {
"グー": "チョキ",
"チョキ": "パー",
"パー": "グー"
}
class JankenGame:
def __init__(self):
# スコア変数
self.win_count = 0
self.lose_count = 0
self.draw_count = 0
self.window = None
def check_hands(self,player_hand, cpu_hand):
if player_hand == cpu_hand:
self.draw_count += 1
return "あいこだよ"
elif WIN_RULES[player_hand] == cpu_hand:
self.win_count += 1
return "勝ち!!"
else:
self.lose_count += 1
return "負け..."
def update_hand_image(self):
# 処理後のじゃんけんの手の画像の更新
self.window["win_score"].update(f"勝ち: {self.win_count}")
self.window["lose_score"].update(f"負け: {self.lose_count}")
self.window["draw_score"].update(f"あいこ: {self.draw_count}")
def play_janken(self, player):
# じゃんけんの手のボタンを押した時の処理
cpu = random.choice(HANDS)
self.window["player_hand"].update(HAND_IMAGES[player])
self.window["cpu_hand"].update(HAND_IMAGES[cpu])
self.window["result"].update(f"プレイヤーの手は「{player}」、CPUの手は「{cpu}」です")
eg.popup(self.check_hands(player, cpu))
self.update_hand_image()
def score_reset(self):
# リセットボタンを押した時の処理
self.window["player_hand"].update(data=b"")
self.window["cpu_hand"].update(data=b"")
self.window["result"].update("")
self.win_count = 0
self.lose_count = 0
self.draw_count = 0
self.update_hand_image()
eg.popup("スコアをリセットしました!")
def create_layout(self):
# レイアウト(画面構成)
col1 = eg.Column([[eg.Image(HAND_IMAGES["グー"], size=(100, 100))], [eg.Button("グー")]])
col2 = eg.Column([[eg.Image(HAND_IMAGES["チョキ"], size=(100, 100))], [eg.Button("チョキ")]])
col3 = eg.Column([[eg.Image(HAND_IMAGES["パー"], size=(100, 100))], [eg.Button("パー")]])
layout = [
[eg.Text("じゃんけんの手を選ぼう!")],
[col1, col2, col3],
[eg.Text("プレイヤーの手"),
eg.Image(None, size=(100, 100),key="player_hand"),
eg.Text("vs"),
eg.Image(None, size=(100, 100), key="cpu_hand"),
eg.Text("CPUの手")],
[eg.Text("", key="result")],
[eg.Text("勝ち: 0", key="win_score"),
eg.Text("負け: 0", key="lose_score"),
eg.Text("あいこ: 0", key="draw_score")],
[eg.Button("リセット")]
]
return layout
def run(self):
# ウィンドウを作成
self.window = eg.Window("じゃんけんアプリ", self.create_layout())
# イベントループ(閉じるまで表示)
while True:
event, values = self.window.read()
if event in HANDS:
self.play_janken(event)
if event == "リセット":
self.score_reset()
if event is eg.WINDOW_CLOSED:
break
# ウィンドウを閉じる
self.window.close()
# ゲームを実行
game = JankenGame()
game.run()
Oh...なんかガラッと変わった...
大丈夫、落ち着いて見てみようか
まず、メインでプログラムが動く場所は「def run(self):」のところだよ!
あ、確かに
じゃんけんの手のボタンには「self.play_janken(event)」
リセットボタンには「self.score_reset()」
って関数だけが入ってるね
そうだね!ボタンに対して1つの機能が付いているって形になってるから、その機能を変更したい時に何を見たら良いか分かりやすいでしょ
クラスの中の関数も、元からあったプログラムをほぼそのまま関数名を付けただけなんだね!
今回は元の形を出来るだけキープした方が分かりやすいと思ったからね!
あとは、クラスの中で扱いたいインスタンス変数や関数に対しては「self」が付いてるって違いくらいかな
この「self」を付けるのが面倒そうだけどね...
まぁプログラミング中はそう感じるかもだけど、クラスを使うことで新しい機能を追加する時や機能を変更したい時に便利になるから慣れることをオススメするよ
1から考えるのは難しいから、今回みたいに変数の管理が難しいって感じ始めたら考えてみるようにする!
最後にデザインの部分だけど、オブジェクトが全体的に左に寄ってて綺麗じゃないでしょ
確かに、気になる人は気になるかもね
そこで、オブジェクトを中央揃えにする方法を教えるよ!
...といっても、これはすごく簡単で、「eg.Window()」の引数に「element_justification="center"」を追加するだけで良いよ!!
self.window = eg.Window("じゃんけんアプリ", self.create_layout(), element_justification="center")
それだけでこんなに綺麗になるんだ!
てか、クラスにしたからプログラムのどこを変えたら良いのか分かりやすくなってる!!
それなら良かった
見つけられない人はクラス内の関数「def run(self):」に注目してみてね!
