今回は、マルバツゲームで、ランダムに手を選ぶボット🤖と対戦できるようにしましょう。
前回: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ブロックに
- これに対するロボットの処理は
else
文に書きます。 - その前に
random
モジュールとtime
モジュールをコードの冒頭でimport
しておきます。
import time, random
- それでは、
else
文内の処理について見ていきましょう。
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秒待つだけのコード
- 最初の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 = []
の部分でlst
という名前の空のリストを作っています。このリストには、次打てる場所、すなわち空きマスのインデックスが格納されます。
for文で実際に次に打てる手を一つ一つ格納していく
- その下の
for
文というのは、while
文と同じくループを表現する書き方で、for i in [リスト]
と書くと、ループのたびにリスト
から一つずつ順番に要素を取り出して変数i
に入れる、という意味になります。
board = [
"𝟶", "𝟷", "𝟸", ...
]
for item in board: # boardリスト内の要素を一つずつ順番に取り出していく
print(item) # 処理部分
- 例えばこのコードなら、
for
文はboard
リスト内の一つ一つの要素を順番取り出してitem
に格納し、処理部分でitem
をprint
しているので、
𝟶
𝟷
𝟸
と表示されます。
要素とインデックスをペアで返してくれるenumerate関数
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
に追加するということです。
random.choice関数
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:
に変えます。
完成!
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("引き分け") # 引き分け
※clear_output
の位置は、表示が適切に異なるようなタイミングに移動してあります。