今回は、Pythonによるマルバツゲームを完成させましょう。

前回: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文でループを表す
- ここまででゲームに使う道具は揃ったので、いよいよゲームの開始です。
マルバツゲームは囲碁や将棋と同様に二人のプレイヤーのターンが交互に繰り返されます。この、繰り返すターンを表現するのに、今回はwhile
文を使います。 - 例えば、9回まわったら抜けるループは
while
文を使うと
turn = 0
while turn < 9: # 条件
turn += 1 # 処理
このように書きます。このコードの流れを見てみましょう。
while文の流れ
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
)ですので、ここでループ終了となります。
インデント
- 処理部分の先頭にある空白をインデントと言います。インデントによって
while
文の処理部分であることを示しますので、忘れずに空白を開けます。インデントはwhile
文以外にもif
文や関数、クラスなど、まとまりを表現する時に使われます。
PEP8
- なお、PEP8というPythonを綺麗に書くためのルールによればインデントはスペース4つ開けることが推奨されています。参考:
[Pythonコーディング規約]PEP8を読み解く
PEP8日本語訳
while文に付け加え
- さて、上のコードで
while
文はちゃんと動くのですが、何かをprint
しているわけではないので実行しても何も見えません。せっかくなので少し処理を付け加えてみましょう。
current_player = "○"
turn = 0
while turn < 9: # 条件
turn += 1 # 処理
print(f"次は {current_player} の番です") # (例)次は ○ の番です
current_player = "○" if current_player == "×" else "×" # 手番交代
print(f"次は {current_player} の番です")
・・・
f-stringを利用して次の手番をprint
しています(復習:print関数 & f-stringで盤面を表示)。
current_player = "○" if current_player == "×" else "×"
・・・
手番交代をしています。詳しくはすぐ下のif
文のところで解説させていただきます。
コードを実行
- このコードを実行するとこのようになります。
次は ○ の番です
次は × の番です
次は ○ の番です
次は × の番です
次は ○ の番です
次は × の番です
次は ○ の番です
次は × の番です
次は ○ の番です
while
ループによって○と×の番が交互に繰り返されているのがわかりますね。
ループの大枠が完成
- これで、ループの大枠が完成しました。このままだと何もしないまま9ターンが過ぎるだけなので、ここからは手を入力したり、盤面を更新して表示したり、勝利判定をしたりなど、ループ内の具体的な実装をしていきましょう。なお、手番交代はターンの最後に行うので、以下の処理はその前に書き足すことにします。
2. ユーザの入力を受け取り、盤面に適用して表示

input関数:入力を受け取る
- ユーザがどの手を選んだのか、その入力を受け取ります。Pythonでは入力はとても簡単に実装できます。以下のように、
input
関数を使って()
の中に表示させたい文字列を書いておけばいいだけです。
index = input(f"{current_player} の手 : ")
- このコードを実行すると、
()
の中に書いておいた文字列が表示され、入力用の枠が表示されます。入力した内容は文字列として変数index
に代入されます。

int関数:文字列を整数値に変換
- さて、今回はこれに加えて以下のように
input
関数をint
関数で囲みます。int
関数は文字列を整数値に変換してくれる関数です。
index = int(input(f"{current_player} の手 : "))
- このように書くことによって、例えばユーザが
"4"
と入力したらint("4")
によって文字列の"4"
が整数値の4
に変換され、index = 4
となり、変数index
に4
が格納されることになります。
boardのインデックスを指定し手を適用
- このおかげで変数
index
をboard
リストのインデックスとして使えるようになりました。そこで、以下のように盤面に手を適用します。具体的には、board
リストの指定したインデックスにある要素を"𝟺"
のような数字から"○"
(変数current_player
の中身)に入れ替えます。
board[index] = current_player
printして表示
- 入力を盤面に適用しましたので、続けて、それをユーザが見れるように表示しましょう。前回の盤面表示コードをそのまま使います。
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]} ")
ここまでで、例えばユーザが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年
3. if-else文で勝利判定

- 残す処理は終局の判定です。
勝利の判定
- 毎ターン、コンピュータに盤面上に○か×が三つ並んだかどうかを判定してもらいます。もし三つ並んでいた場合は勝利と判定し、勝者を表示したのちゲーム終了(ループを脱出)します。並んでいなければ何もせずスルーします。
if -else文
- このように条件分岐を表現するには
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) または
# 二行目の横三つが 〃 または
# 三行目の横三つが 〃 または
# 一列目の縦三つが 〃 または
# 二列目の縦三つが 〃 または
# 三列目の縦三つが 〃 または
# 右下がりの斜め三つが 〃 または
# 右上がりの斜め三つが 〃 ならば:
# 勝者を表示
# ループを抜ける
という意味になります。
- なお、今の例では
if
文は単体で使われたのですが、if
はelse
と使われることも多いです。else
はそうではないなら
という意味のキーワードです。
手番交代のコード
- 上で出てきた手番交代のコードは、まさに
if-else
文として以下のように書けるものでした。
if current_player == "×": # もし現在のプレイヤが×ならば、
current_player = "○" # ○に手番を交代する。
else: # そうでないならば、
current_player = "×" # ×に手番を交代する。
三項演算子
- そして、上では、全く同じことを示す、下の書き方が採用されていたのです。
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("あなたは長生きです")
4. while-else文で引き分けを表示

- マルバツゲームの途中で片方の勝利が決まればそこで
break
してゲームは終了しますが、どちらも勝つこともなく9回のループが終わると引き分けとなります。このような時のためにちょうどいい書き方があります。while
文の後ろにelse
文を設置することで、途中でbreak
することなく全部回りきった場合のみ行う処理を書けるのです。
while turn < 9:
# 省 略
if (board[0] == ...): # 勝利判定
break
else: # 途中でbreakせず9回まわり切った場合のみelse内の処理に進む
print("引き分け")
5. 出力を消去

- ここまでですでにマルバツゲームのロジックは完成しているのですが、最後に少しだけ付け加えます。現時点のコードでは、ループのたびに新たな盤面が出力され、出力結果が溜まっていくようになっています。

- これでもいいのですが、見やすさのため新しい盤面が表示されるタイミングで前の盤面が消去されるようにしたいと思います。そのためには
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("引き分け") # 引き分け
これで、マルバツゲームを遊べるコードの完成です!