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

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

前回: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 += 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)ですので、ここでループ終了となります。

なお、処理部分の先頭4マス分の空白があると思います。この字下げのことをインデントと言います。インデントされている部分はそれがwhile文の処理部分であることを示しています。これがないとインタプリタは処理部分だと理解できませんので、処理部分にインデントは必須です。

ここまでで、ループの大枠が完成しました。以下、ループ内の具体的な実装をしていきましょう。

2. 次のプレイヤを表示

  • ループが始まりましたので、まずは準備編で用意しておいたcurrent_playerを活用して、次がどちらの番なのかをユーザに教えてあげましょう。そのためには
print(f"次は {current_player} の番です")

このように上に登場したf-stringを使うとわかりやすいです。

3. input関数 & int関数でユーザの入力を受け取る

  • 次はユーザの入力を受け取ります。Pythonでは入力はとても簡単に実装できます。input関数を使って
index = input(f"{current_player} の手 : ")

このように()の中に表示させたい文字列を書いておけばいいのです。

これを実行するとどうなるかというと、

変数cuurent_player"○"が格納されている場合

このように○ の手 :という、input()の()の中に書いた文字列が表示され、さらに、四角の中に実際に何かを入力できるようになります。
そして、入力した内容は文字列として変数indexに代入されるのです。

ただし、今回はプレイヤの入力を文字列ではなく整数値として受け取りたいです。boardリストの要素を指し示すためのインデックス整数値でなければならないからです。そのためには、

index = int(input(f"{current_player} の手 : "))

このようにint関数を使い、文字列整数値に変換します。なお、intというのはinteger(整数値)の略です。

このコードでは、例えばユーザが"4"と入力したらint("4")によって文字列"4"整数値4に変換され、index = 4となり、変数index4が格納されることになります。

クイズ❶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文の活用

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("あなたは長生きです")

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("引き分け")                                     # 引き分け

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

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

コメントを残す

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