今回は、ランダムに手を選ぶボット🤖と対戦できるようにしましょう。
前回:Pythonでマルバツゲームをつくろう #3.5 見た目を色々変えてみよう
from IPython.display import clear_output
O = "○"
X = "×"
current_player = O # 現在のプレイヤー
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) # 新たに出力され次第、表示済みの出力を消去
while True: # ユーザの入力を受け取る
try:
index = int(input(f"{current_player} の手 : "))
if board[index] != O and board[index] != X:
break
else:
print("𝟶 ~ 𝟾 の空いているマスを選んでください")
except ValueError:
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]} ")
clear_output(wait=True)
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 = O if current_player == X else X # 手番交代
else:
print("引き分け") # 引き分け
1. ランダムボットが打つターンを追加
- まずは○の番は人間のターン、×の番はボットのターンということにしましょう。
- ○の番に人間が打つ、という条件に変更するのは簡単です。
if current_player == O: # これを付け加えるだけ
while True: # ユーザ入力部分
(省略)
- while文による入力部分を、
current_player
がO
("○"
)である時のみ実行するよう、if
文で囲めばいいのです。
- これに対するロボットの処理は
else
文に書きますが、その前にrandom
モジュールとtime
モジュールをコードの冒頭でimport
しておきます。
import time, random
- それでは、
else
文内の処理について見ていきましょう。
if current_player == O:
(省略)
else: # もしcurrent_playerがXなら
print("ボット考え中...")
time.sleep(2)
index = random.choice([index for index, value in enumerate(board) if value not in ("○", "×")])
print(f"ボットの手: {index}")
- 最初の2行は、ボット考え中…と
print
関数によって表示してから、time.sleep(2)
によって2秒間何もしないで待つ、というコードです。これは、ユーザが打った瞬間にボットが打ち返してこないようにするためです。
index = random.choice([index for index, value in enumerate(board) if value not in ("○", "×")])
- この部分は、
lst = []
for index, value in enumerate(board):
if value not in ("○", "×"):
lst.append(index)
index = random.choice(lst)
このように書いているのと同じ意味なので、まずはこちらを見ていきます。
- まず、
lst = []
の部分でlst
という名前の空のリストを作っています。
- その下の
for
文というのは、while
文と同じくループを表現する書き方で、for i in [リスト]
と書くと、ループのたびにリスト
から一つずつ順番に要素を取り出して変数i
に入れる、という意味になります。
board = [
"𝟶", "𝟷", "𝟸", ...
]
for item in board: # boardリスト内の要素を一つずつ順番に取り出していく
print(item) # 処理部分
例えばこのコードなら、for
文はboard
リスト内の一つ一つの要素を順番取り出してitem
に格納し、処理部分でitem
をprint
しているので、
𝟶
𝟷
𝟸
…
と表示されます。
- boardリストはインデックスによって要素を指定しますが、実際に格納しているのは要素だけです。今回使われている
enumerate
は、要素に加えてインデックスも一緒に返してくれる関数なのです。
for index, value in enumerate(board): # enumerate(board)とすることで、要素だけでなくインデックスも取り出せる
print(index, value)
これを実行すると、
0 𝟶
1 𝟷
2 𝟸
… …
このように、インデックスとそれに対応する要素が一緒に返されます。なお、これはリストではなくタプルという形式で返されます(タプルはリストと同じように複数のアイテムをまとめる機能ですが、リストと違って要素を変更できないという性質があります)。
さて、実際にはfor index, value in enumerate(board):
の後の処理は以下のようになっていました。
if value not in ("○", "×"):
lst.append(index)
これは、もしvalue
すなわちboard
内の要素( "𝟶"
, "𝟷"
, …) が("○", "×")
というタプルに含まれていなければ、lst
リストにindex
を追加する、という意味です。つまり、まだ打たれていないインデックスであればlstに追加するということです。
index = random.choice(lst)
そして、randomモジュールのchoice関数によって、lstリスト内のどれかの要素がランダムに選ばれ、変数indexに格納されるのです。
これでユーザはボットと対戦できるようになりました。
from IPython.display import clear_output
O = "○"
X = "×"
current_player = O # 現在のプレイヤー
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) # 新たに出力され次第、表示済みの出力を消去
if current_player == O: # もしcurrent_playerがOなら
while True: # ユーザの入力を受け取る
try:
index = int(input(f"{current_player} の手 : "))
if board[index] != O and board[index] != X:
break
else:
print("𝟶 ~ 𝟾 の空いているマスを選んでください")
except ValueError:
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]} ")
clear_output(wait=True)
else: # もしcurrent_playerがXなら
print("ボット考え中...")
time.sleep(2)
index = random.choice([index for index, value in enumerate(board) if value not in ("○", "×")])
print(f"ボットの手: {index}")
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 = O if current_player == X else X # 手番交代
else:
print("引き分け") # 引き分け
2. ユーザが○か×かランダムに決まるようにする
次は、ユーザが○か×か、ランダムに決まるようにしましょう。
そのためには、現在○の番か×の番かを管理するための変数current_player
に加えて、ユーザが○か×かを格納する変数USER
を用意し、○か×が半々の確率で格納されるようにします。
USER = O if random.choice([True, False]) else X
print(f"あなたは {USER} です")
ここでもrandomモジュールのchoice関数を使うことにします。リストの中からランダムで要素を選ぶので、1/2の確率でTrueが選ばれUSERは○になるか、もしくは1/2の確率でFalseが選ばれてUSERは×になります。
if current_player == O:
となっていた部分は、
if current_player == USER:
に変えます。
完成!
※clear_output
の位置は、表示が適切に異なるようなタイミングに移動してあります。
from IPython.display import clear_output
O = "○"
X = "×"
USER = O if random.choice([True, False]) else X
current_player = O # 現在のプレイヤー
turn = 0 # 今何ターン目か
board = [ # 盤面(リスト)
"𝟶", "𝟷", "𝟸",
"𝟹", "𝟺", "𝟻",
"𝟼", "𝟽", "𝟾"
]
print(f"あなたは {USER} です")
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} の番です")
if current_player == USER: # もしcurrent_playerがOなら
clear_output(wait=True) # ⭐️
while True: # ユーザの入力を受け取る
try:
index = int(input(f"{current_player} の手 : "))
if board[index] != O and board[index] != X:
break
else:
print("𝟶 ~ 𝟾 の空いているマスを選んでください")
except ValueError:
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]} ")
clear_output(wait=True) # ⭐️
else: # もしcurrent_playerがXなら
print("ボット考え中...")
clear_output(wait=True) # ⭐️
time.sleep(2)
index = random.choice([index for index, value in enumerate(board) if value not in ("○", "×")])
print(f"ボットの手: {index}")
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 = O if current_player == X else X # 手番交代
else:
print("引き分け") # 引き分け