今回は、マルバツゲームを完成させましょう。
前回:Pythonでマルバツゲームをつくろう #1 最初の盤面を表示するまで
current_player = "○" # 現在のプレイヤー
turn = 0 # 今何ターン目か
board = [ # 盤面(リスト)
"𝟶", "𝟷", "𝟸",
"𝟹", "𝟺", "𝟻",
"𝟼", "𝟽", "𝟾"
]
print(f" {board[0]} │ {board[1]} │ {board[2]} ") # 最初の盤面を表示
print("───┼───┼───")
print(f" {board[3]} │ {board[4]} │ {board[5]} ")
print("───┼───┼───")
print(f" {board[6]} │ {board[7]} │ {board[8]} ")
1. while文でループの実装
- ここまででゲームに使う道具は揃ったので、いよいよゲームの開始です。
マルバツゲームは囲碁や将棋と同様に二人のプレイヤーのターンが交互に繰り返されます。繰り返すターンを表現するのに、今回はwhile
文を使うことにします。 - 例えば、9回まわったら抜けるループは
while
文を使うと
turn = 0
while turn < 9: # while 条件:
turn += 1 # 処理
このように書きます。このコードの流れを見てみましょう。
1. 最初に変数turn
を用意し、0
を格納します。この変数は何回ループしたかを管理するために使います。
2. 次に、while
文の条件をチェックします。条件というのはこの場合turn < 9
です。turn
の中身は最初は0
であり、0 < 9
はもちろん真(True
)なので条件は満たされ、処理に進みます。
3. 処理のturn += 1
はturn = turn + 1
と同じ意味で、turn
に1を加算する処理です。この処理を経て、turn
の中身は0
から1
になりました。
4. 処理が完了したので、再び条件turn < 9
のチェックに戻ります。1 < 9
は真(True
)なので、今回もまた処理に進みます。
…このように、条件チェック→処理を繰り返します。変数turn
の中身はこのループを繰り返すたびに0
1
2
3
4
5
6
7
8
と増えていきます。
5. 9回目の処理を経て、turn
の中身は8
から9
になりました。
6. 処理が完了したので、再び条件をチェックすると、9 < 9
になりました。これは偽(False
)ですので、ここでループ終了となります。
なお、処理部分の先頭に4マス分の空白があると思います。この字下げのことをインデントと言います。インデントされている部分はそれがwhile
文の処理部分であることを示しています。これがないとインタプリタは処理部分だと理解できませんので、処理部分にインデントは必須です。
ここまでで、ループの大枠が完成しました。以下、ループ内の具体的な実装をしていきましょう。
2. 次のプレイヤを表示
- ループが始まりましたので、まずは準備編で用意しておいた
current_player
を活用して、次がどちらの番なのかをユーザに教えてあげましょう。そのためには
print(f"次は {current_player} の番です")
このように上に登場したf-stringを使うとわかりやすいです。
3. input関数 & int関数でユーザの入力を受け取る
- 次はユーザの入力を受け取ります。Pythonでは入力はとても簡単に実装できます。
input
関数を使って
index = input(f"{current_player} の手 : ")
このように()
の中に表示させたい文字列を書いておけばいいのです。
これを実行するとどうなるかというと、
このように○ の手 :
という、input()の()の中に書いた文字列が表示され、さらに、四角の中に実際に何かを入力できるようになります。
そして、入力した内容は文字列として変数index
に代入されるのです。
ただし、今回はプレイヤの入力を文字列ではなく整数値として受け取りたいです。boardリストの要素を指し示すためのインデックスは整数値でなければならないからです。そのためには、
index = int(input(f"{current_player} の手 : "))
このようにint
関数を使い、文字列を整数値に変換します。なお、intというのはinteger(整数値)の略です。
このコードでは、例えばユーザが"4"
と入力したらint("4")
によって文字列の"4"
が整数値の4
に変換され、index = 4
となり、変数index
に4
が格納されることになります。
クイズ❶input関数とint関数の活用
西暦を入力すると令和に変換して出力するプログラムを書いてください。(令和1年は西暦2019年)
(例)
コンピュータ出力「西暦を入力:」
ユーザ入力「2024」
コンピュータ出力「西暦2024年は令和6年です。」
(例)
seireki = int(input("西暦を入力:"))
reiwa = seireki - 2018
print(f"西暦{seireki}年は令和{reiwa}年です。")
他の元号でも遊んでみてください:
平成1年 1989年
昭和1年 1926年
大正1年 1912年
明治1年 1868年
4. 入力手をboardリストに適用
- さて、変数
index
内に整数値を受け取りましたので、次はboard
リストに"o"
を適用しましょう。これは、
board[index] = current_player
このように書きます。
5. 現在の盤面を表示
- 入力を盤面に適用しましたので、それをユーザが見れるように表示しましょう。1-4のコードをそのまま使うことができます。
print(f" {board[0]} │ {board[1]} │ {board[2]} ")
print("───┼───┼───")
print(f" {board[3]} │ {board[4]} │ {board[5]} ")
print("───┼───┼───")
print(f" {board[6]} │ {board[7]} │ {board[8]} ")
6. if-else文で勝利判定
- ここまででユーザの入力、およびその結果の出力が完了しましたので、次は勝利判定です。毎ターン、コンピュータに盤面上に○か×が三つ並んだかどうかを判定してもらいます。もし三つ並んでいた場合は、勝者を表示し、ゲームを終了(ループを脱出)します。並んでいなければ何もしません。このように条件分岐(2つ以上のルートに分かれること)を表現するには
if-else
文を使います。
# \ は複数の行が繋がっていることを示すための記号
if (board[0] == board[1] == board[2] == current_player) or \
(board[3] == board[4] == board[5] == current_player) or \
(board[6] == board[7] == board[8] == current_player) or \
(board[0] == board[3] == board[6] == current_player) or \
(board[1] == board[4] == board[7] == current_player) or \
(board[2] == board[5] == board[8] == current_player) or \
(board[0] == board[4] == board[8] == current_player) or \
(board[2] == board[4] == board[6] == current_player):
print(f"勝者: {current_player}")
break
if
というのはもし〜ならばという意味で、or
というのはまたはという意味です。break
はループを無条件で抜ける呪文です。なので、上のコードは
# もし 一行目の横三つが全て同じ(current_player) または
# 二行目の横三つが 〃 または
# 三行目の横三つが 〃 または
# 一列目の縦三つが 〃 または
# 二列目の縦三つが 〃 または
# 三列目の縦三つが 〃 または
# 右下がりの斜め三つが 〃 または
# 右上がりの斜め三つが 〃 ならば:
# 勝者を表示
# ループを抜ける
という意味になります。
7. 三項演算子で手番交代
- ターンの最後に、手番交代をしましょう。これは、
if current_player == "×":
current_player = "○"
else:
current_player = "×"
このように書きます。else
はそうではないなら
という意味で、if
とセットで使われます。なお、これは簡潔に
current_player = "○" if current_player == "×" else "×"
こう書くこともできます。この書き方を三項演算子といいます。4行が1行になり、スッキリしているのがわかると思います。
クイズ❷if文の活用
if
とelse
を組み合わせることで二択の条件分岐を表現できますが、三択以上の分岐を表現したい場合はelif
(そうではなくもし)を使います。
if temperature >= 30: # もし temperatureが30以上ならば:
print("Hot") # Hot と表示
elif temperature >= 20: # そうではなくもし temperatureが20以上ならば:
print("Warm") # Warm と表示
elif temperature >= 10: # そうではなくもし temperatureが10以上ならば:
print("Cool") # Cool と表示
else: # そうでないなら:
print("Cold") # Cold と表示
これを踏まえて、ユーザの入力した誕生年に応じて表示が以下の4段階に変化するプログラムを作ってください。
2019〜:「あなたは令和◯年生まれです」
1989〜:「あなたは平成◯年生まれです」
1926〜:「あなたは昭和◯年生まれです」
それ以外:「あなたは長生きです」
(例)
コンピュータ「誕生年を入力:」
ユーザ「1964」
コンピュータ「あなたは昭和39年生まれです」
birth_year = int(input("誕生年を入力:"))
if birth_year >= 2019:
print(f"あなたは令和{birth_year - 2018}年生まれです")
elif birth_year >= 1989:
print(f"あなたは平成{birth_year - 1988}年生まれです")
elif birth_year >= 1926:
print(f"あなたは昭和{birth_year - 1925}年生まれです")
else:
print("あなたは長生きです")
8. while-else文で引き分けを表示
- マルバツゲームの途中で片方の勝利が決まればそこで
break
してゲームは終了しますが、どちらも勝つこともなく9回のループが終わることもあります(引き分け)。while
文の後ろにelse
文を設置することで、途中でbreak
することなく全部回りきった場合のみ行う処理を書けます。
while turn < 9:
# 省 略
if (board[0] == ...): # 勝利決定
break
else: # 途中でbreakせず9回まわり切った場合のみelse内の処理に進む
print("引き分け")
9. 出力を消去
- ここまでですでにマルバツゲームのロジックは完成しているのですが、最後に少しだけ付け加えます。
- 現時点のコードでは、ループのたびに新たな盤面が出力され、出力結果が溜まっていくようになっています。これでもいいのですが、見やすさのため新しい盤面が表示されるタイミングで前の盤面が消去されるようにしたいと思います。
- そのためには
IPython
という外部ライブラリを使います。外部ライブラリというのは、他の人が作って公開してくれている、関数などの便利な機能のセットのことです。今回はその中のclear_output
という出力消去用の関数を使います。
from IPython.display import clear_output
これをコードの一番先頭に書くと、インタプリタに「Ipython
ライブラリのdisplay
モジュール内にあるclear_output
関数をあとで使うから用意しておいてね!」と伝えていることになります。
- さて、実際にどこで
clear_output
関数を使うかというと、以下のようにinput
の直前に書きます。
clear_output(wait=True) # ← ココ
index = int(input(... # input部分、以下略
wait = True
と書いておくことで、すぐに出力を消去するのではなく、次の出力があったタイミングで表示済みの出力を消す設定になります。この場合、具体的には、ユーザが例えば4
と入力して○ の手 : 4
と出力される直前に、それまでの出力結果が画面から消去されるのです。
組み合わせて完成!
from IPython.display import clear_output
current_player = "○" # 現在のプレイヤー
turn = 0 # 今何ターン目か
board = [ # 盤面(リスト)
"𝟶", "𝟷", "𝟸",
"𝟹", "𝟺", "𝟻",
"𝟼", "𝟽", "𝟾"
]
print(f" {board[0]} │ {board[1]} │ {board[2]} ") # 最初の盤面を表示
print("───┼───┼───")
print(f" {board[3]} │ {board[4]} │ {board[5]} ")
print("───┼───┼───")
print(f" {board[6]} │ {board[7]} │ {board[8]} ")
while turn < 9:
turn += 1 # ターンを進める
print(f"次は {current_player} の番です")
clear_output(wait=True) # 新たに出力され次第、表示済みの出力を消去
index = int(input(f"{current_player} の手 : ")) # ユーザの入力を受け取る
board[index] = current_player # 入力手を盤面に適用
print(f" {board[0]} │ {board[1]} │ {board[2]} ") # 現在の盤面を表示
print("───┼───┼───")
print(f" {board[3]} │ {board[4]} │ {board[5]} ")
print("───┼───┼───")
print(f" {board[6]} │ {board[7]} │ {board[8]} ")
# 勝利判定
if (board[0] == board[1] == board[2] == current_player) or \
(board[3] == board[4] == board[5] == current_player) or \
(board[6] == board[7] == board[8] == current_player) or \
(board[0] == board[3] == board[6] == current_player) or \
(board[1] == board[4] == board[7] == current_player) or \
(board[2] == board[5] == board[8] == current_player) or \
(board[0] == board[4] == board[8] == current_player) or \
(board[2] == board[4] == board[6] == current_player):
print(f"勝者: {current_player}")
break
current_player="○" if current_player=="×" else "×" # 手番交代
else:
print("引き分け") # 引き分け
これで、マルバツゲームを遊べるコードの完成です!