Pythonでマルバツゲームをつくろう #2 完成編

今回は、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文でループを表す

  • ここまででゲームに使う道具は揃ったので、いよいよゲームの開始です。
    マルバツゲームは囲碁将棋と同様に二人のプレイヤーのターンが交互に繰り返されます。この、繰り返すターンを表現するのに、今回は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 += 1turn = turn + 1と同じ意味で、turn1を加算する処理です。この処理を経て、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

while文に付け加え

  • さて、上のコードでwhile文はちゃんと動くのですが、何かをprintしているわけではないので実行しても何も見えません。せっかくなので少し処理を付け加えてみましょう。
current_player = "○"
turn = 0
while turn < 9:                                             # 条件
    turn += 1                                               # 処理
    print(f"次は {current_player} の番です")                  # (例)次は ○ の番です
    current_player = "○" if current_player == "×" else "×"  # 手番交代
  • 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となり、変数index4が格納されることになります。

boardのインデックスを指定し手を適用

  • このおかげで変数indexboardリストのインデックスとして使えるようになりました。そこで、以下のように盤面に手を適用します。具体的には、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文は単体で使われたのですが、ifelseと使われることも多いです。elseそうではないならという意味のキーワードです。

手番交代のコード

  • 上で出てきた手番交代のコードは、まさにif-else文として以下のように書けるものでした。
if current_player == "×":  # もし現在のプレイヤが×ならば、
    current_player = "○"   # ○に手番を交代する。
else:                      # そうでないならば、
    current_player = "×"   # ×に手番を交代する。

三項演算子

  • そして、上では、全く同じことを示す、下の書き方が採用されていたのです。
current_player = "○" if current_player == "×" else "×"  # ○に手番を交代する、もし現在のプレイヤが×ならば。 そうでないならば、×(に手番を交代する)。
  • これは三項演算子を使った書き方で、4行が1行になり、スッキリしているのがわかると思います。意味はおよそコメントの通りです。

クイズ❷if文の活用

ifelseを組み合わせることで二択の条件分岐を表現できますが、三択以上の分岐を表現したい場合は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("引き分け")                                     # 引き分け

これで、マルバツゲームを遊べるコードの完成です!

次回:Pythonでマルバツゲームをつくろう #3 人対人編

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です