今回は、Python言語を使って、ゼロから三目並べを作る手順を紹介します。
チックタックトーのゲームプレイ
ゲームについて
三目並べは2人用のゲームで、3×3の正方形のマスでプレイします。
各プレイヤーは交互にマスを占有し、3つのマークを水平、垂直、斜めに配置することを目的とします。
一方のプレーヤーは十字の「X」を、もう一方のプレーヤーは無の「O」をマーカーとして使用します。
Step 1: 三目並べのデザイン
三目並べをコマンドラインで行うので、まず最初に三目並べのデザインを作成する必要があります。
# Function to print Tic Tac Toedef print_tic_tac_toe(values):
print(")
print(" | |")
print(" {} | {} | {}".format(values[0], values[1], values[2]))
print(' _____|_____|_____')
print(" | |")
print(" {} | {} | {}".format(values[3], values[4], values[5]))
print(' _____|_____|_____')
print(" | |")
print(" {} | {} | {}".format(values[6], values[7], values[8]))
print(" | |")
print(")
|
もしプレイヤーが特定の箱をマークしなければならないなら、グリッドに示された対応する数字を入力しなければならない。
例えば、中央のブロックを占めたい場合、端末に「5」と入力します。
このグリッドは次のようにして生成することができます。
# Function for a single game of Tic Tac Toedef single_game(cur_player):
# Represents the Tic Tac Toe
values = [' ' for x in range(9)]
# Stores the positions occupied by X and O
player_pos = {'X':[], 'O':[]}
|
上のコードでは、引数として渡された値に従って三目並べゲームを生成している。
ここで、引数 values は、グリッドの各セルの状態を含むリストです。
この記事もチェック:Pythonのコマンドライン引数でargparseやgetoptを使う方法
ステップ2:データ構造を使った情報の保存
どんなゲームでも、その核となるのはその背後にあるゲームメカニズムです。
このゲームはかなり簡単に作れるので、その仕組みも簡単です。
どの瞬間にも、2つの重要な情報が必要です。
- グリッドの状態 – 各セルの状態、つまり、占有されているか空いているかを保存するデータ構造を持っている必要があります。
- 各プレイヤーの動き – 各プレイヤーの過去と現在の動き、つまり
'X'と'O'が占める位置の知識を何らかの形で持っていなければならない。
注:どちらの情報もグリッドの状態を用いてアクセスすることも可能ですが、プレイヤーの位置が必要になるたびにグリッドを走査する必要がありました。
これは、時間対空間の複雑さのトレードオフと呼ぶことができます。
これは、時間を節約するための一般的なテクニックです。
# Game Loop for a single game of Tic Tac Toewhile True:
print_tic_tac_toe(values)
|
グリッドの状態は、3つの可能な値を持つ文字のリストによって管理される。
- 空いているセル
- X’
- プレイヤーXが占有しているセル‘O’- プレイヤーOが占有しているセル *‘ ‘` – プレイヤーXが占有しているセル
各プレイヤーの手は、整数のリストの辞書として格納される。
キーはそれぞれのプレイヤーの 'X' と 'O' です。
対応するリストには、各プレイヤーが占有しているマス目に与えられた数字が格納される。
注: 変数 cur_player は、'X' または 'O' のように、現在移動しているプレイヤーを格納します。
ステップ3: ゲームループ
すべてのゲームには、ある種のゲームループがあり、あるプレイヤーが勝つか、ゲームが引き分けに終わるまで実行される。
三目並べでは、各ループの繰り返しが、プレイヤーの一手を意味します。
# Try exception block for MOVE inputtry:
print("Player ", cur_player, " turn. Which box? : ", end="")
move = int(input())
except ValueError:
print("Wrong Input!!! Try Again")
continue
# Sanity check for MOVE inoutif move < 1 or move > 9:
print("Wrong Input!!! Try Again")
continue
# Check if the box is not occupied alreadyif values[move-1] != ' ':
print("Place already filled. Try again!!")
continue
|
ステップ4: プレイヤーの入力を処理する
ゲームの各反復において、プレイヤーは自分の手を入力しなければならない。
# Update game information# Updating grid status values[move-1] = cur_player
# Updating player positionsplayer_pos[cur_player].append(move) |
プレイヤーが意図しない値を入力した場合に備えて、tryブロックを作成します。
このようなイベントでゲームが止まってはいけないので、ValueErrorの例外を処理してゲームを続行します。
入力された値が有効な位置かどうか、有効な位置であれば既に占有されていないかなど、いくつかのサニティチェックを行う必要があります。
ステップ5:情報の更新
プレイヤーの入力を受けて、ゲームを円滑に進めるために情報を更新する必要があります。
# Function call for checking winif check_win(player_pos, cur_player):
print_tic_tac_toe(values)
print("Player ", cur_player, " has won the game!!")
print(")
return cur_player
# Function call for checking draw gameif check_draw(player_pos):
print_tic_tac_toe(values)
print("Game Drawn")
print(")
return 'D'
|
値リスト(values` list)は現在のプレイヤーによって占有されているマスを更新します。
プレーヤーの位置は、現在のプレーヤーが取ったばかりの位置を追加します。
Valueリストを更新し、print_tic_tac_toe()` 関数を呼び出すと、グリッドは次のようになります。
# Function to check if any player has wondef check_win(player_pos, cur_player):
# All possible winning combinations
soln = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7], [2, 5, 8], [3, 6, 9], [1, 5, 9], [3, 5, 7]]
# Loop to check if any winning combination is satisfied
for x in soln:
if all(y in player_pos[cur_player] for y in x):
# Return True if any winning combination satisfies
return True
# Return False if no combination is satisfied
return False # Function to check if the game is drawndef check_draw(player_pos):
if len(player_pos['X']) + len(player_pos['O']) == 9:
return True
return False |
ステップ6: 勝つか負けるかのチェック
各手番の後、いずれかのプレイヤーがゲームに勝ったか、またはゲームが引き分けになったかを確認する必要があります。
これは次のようにして確認することができる。
関数呼び出し。
# Switch player movesif cur_player == 'X':
cur_player = 'O'
else:
cur_player = 'X'
|
いずれかのプレイヤーが勝った場合、single_game()関数はその手を打った現在のプレイヤーを返す。
引き分けになった場合は 'D' を返します。
機能
if __name__ == "__main__":
print("Player 1")
player1 = input("Enter the name : ")
print(")
print("Player 2")
player2 = input("Enter the name : ")
print(")
|
check_win() – この関数は、すべての勝ちの組み合わせを持っている。
この関数が行うのは、現在のプレイヤーのポジションによって、いずれかの勝利の組み合わせが満たされるかどうかをチェックすることだけです。
もし満たすなら、Trueを返す。
もし、どの組み合わせも満たされない場合、この関数は False を返す。
check_draw() – 引き分け条件は非常に単純で、すべての「9」のポジションが取られたときにゲームが引き分けとなる。
ステップ7:現在のプレイヤーを入れ替える
各プレイヤーは一度に一回しか動けないので、一回動くごとに現在のプレイヤーを入れ替えなければならない。
# Stores the player who chooses X and Ocur_player = player1
# Stores the choice of playersplayer_choice = {'X' : "", 'O' : ""}
# Stores the optionsoptions = ['X', 'O']
# Stores the scoreboardscore_board = {player1: 0, player2: 0}
|
一回の対局では、これだけでよい。
しかし、この記事では、複数回の対局を行うためのスコアボードシステムも紹介しています。
ステップ8:選手名の入力
スコアボードには、各選手の名前を表示することが義務付けられています。
# Function to print the score-boarddef print_scoreboard(score_board):
print("--------------------------------")
print(" SCOREBOARD ")
print("--------------------------------")
players = list(score_board.keys())
print(" ", players[0], " ", score_board[players[0]])
print(" ", players[1], " ", score_board[players[1]])
print("--------------------------------)
|
ステップ9:ゲーム関連情報の保存
現在のプレイヤー、プレイヤーの選択(クロスを取るか、ナオトを取るか)、利用可能な選択肢(クロスとナオト)、スコアボードなどの情報を保存する必要があります。
# Game Loop for a series of Tic Tac Toe# The loop runs until the players quit while True:
# Player choice Menu
print("Turn to choose for", cur_player)
print("Enter 1 for X")
print("Enter 2 for O")
print("Enter 3 to Quit")
|
デフォルトでは、カレントプレーヤーは最初に名前を入力したプレーヤーです。
Step 10: スコアボードの設計
スコアボードは辞書として格納され、キーはプレーヤー名、値は勝利数です。
# Try exception for CHOICE inputtry:
choice = int(input())
except ValueError:
print("Wrong Input!!! Try Again)
continue
# Conditions for player choice if choice == 1:
player_choice['X'] = cur_player
if cur_player == player1:
player_choice['O'] = player2
else:
player_choice['O'] = player1
elif choice == 2:
player_choice['O'] = cur_player
if cur_player == player1:
player_choice['X'] = player2
else:
player_choice['X'] = player1
elif choice == 3:
print("Final Scores")
print_scoreboard(score_board)
break else:
print("Wrong Choice!!!! Try Again)
|
スコアボードを表示するためには、プレーヤーの名前が必要です。
キーは .keys() 関数で抽出され、リストに変換されて、スコアを表示する際にインデックスが付けられるようになります。
この記事もチェック:Pythonのoct関数を使って整数(10進数)を8進数に変換する方法
ステップ 11: 外側のゲームループ
三目並べの複数の対戦を管理するために、もう一つゲームループが必要です。
各マッチで、現在のプレイヤーは自分のマーク ('X' or 'O') を選択します。
選択するためのメニューは、ゲームの繰り返しごとに表示されなければならない。
# Stores the winner in a single game of Tic-tac-toewinner = single_game(options[choice-1])
|
スコアボードとメニューはこのようになっている。
# Updates the scoreboard according to the winnerif winner != 'D' :
player_won = player_choice[winner]
score_board[player_won] = score_board[player_won] + 1
print_scoreboard(score_board) |
ステップ12:プレイヤーの選択肢の処理と割り当て
各反復において、現在のプレイヤーの選択肢を処理し、保存する必要がある。
# Switch player who chooses X or Oif cur_player == player1:
cur_player = player2
else:
cur_player = player1
|
プレイヤーの選択に従って、データが保存されています。
これは、各ゲームが終了した後、どのプレイヤーが勝ったかを教えてくれるので、重要なことです。
ステップ13: 試合を実行する
必要な情報をすべて保存したら、いよいよ独立した試合を実行し、勝利したマークを保存します。
# Function to print Tic Tac Toedef print_tic_tac_toe(values):
print(")
print(" | |")
print(" {} | {} | {}".format(values[0], values[1], values[2]))
print(' _____|_____|_____')
print(" | |")
print(" {} | {} | {}".format(values[3], values[4], values[5]))
print(' _____|_____|_____')
print(" | |")
print(" {} | {} | {}".format(values[6], values[7], values[8]))
print(" | |")
print(")
# Function to print the score-boarddef print_scoreboard(score_board):
print(" --------------------------------")
print(" SCOREBOARD ")
print(" --------------------------------")
players = list(score_board.keys())
print(" ", players[0], " ", score_board[players[0]])
print(" ", players[1], " ", score_board[players[1]])
print(" --------------------------------)
# Function to check if any player has wondef check_win(player_pos, cur_player):
# All possible winning combinations
soln = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7], [2, 5, 8], [3, 6, 9], [1, 5, 9], [3, 5, 7]]
# Loop to check if any winning combination is satisfied
for x in soln:
if all(y in player_pos[cur_player] for y in x):
# Return True if any winning combination satisfies
return True
# Return False if no combination is satisfied
return False # Function to check if the game is drawndef check_draw(player_pos):
if len(player_pos['X']) + len(player_pos['O']) == 9:
return True
return False # Function for a single game of Tic Tac Toedef single_game(cur_player):
# Represents the Tic Tac Toe
values = [' ' for x in range(9)]
# Stores the positions occupied by X and O
player_pos = {'X':[], 'O':[]}
# Game Loop for a single game of Tic Tac Toe
while True:
print_tic_tac_toe(values)
# Try exception block for MOVE input
try:
print("Player ", cur_player, " turn. Which box? : ", end="")
move = int(input())
except ValueError:
print("Wrong Input!!! Try Again")
continue
# Sanity check for MOVE inout
if move < 1 or move > 9:
print("Wrong Input!!! Try Again")
continue
# Check if the box is not occupied already
if values[move-1] != ' ':
print("Place already filled. Try again!!")
continue
# Update game information
# Updating grid status
values[move-1] = cur_player
# Updating player positions
player_pos[cur_player].append(move)
# Function call for checking win
if check_win(player_pos, cur_player):
print_tic_tac_toe(values)
print("Player ", cur_player, " has won the game!!")
print(")
return cur_player
# Function call for checking draw game
if check_draw(player_pos):
print_tic_tac_toe(values)
print("Game Drawn")
print(")
return 'D'
# Switch player moves
if cur_player == 'X':
cur_player = 'O'
else:
cur_player = 'X'
if __name__ == "__main__":
print("Player 1")
player1 = input("Enter the name : ")
print(")
print("Player 2")
player2 = input("Enter the name : ")
print(")
# Stores the player who chooses X and O
cur_player = player1
# Stores the choice of players
player_choice = {'X' : "", 'O' : ""}
# Stores the options
options = ['X', 'O']
# Stores the scoreboard
score_board = {player1: 0, player2: 0}
print_scoreboard(score_board)
# Game Loop for a series of Tic Tac Toe
# The loop runs until the players quit
while True:
# Player choice Menu
print("Turn to choose for", cur_player)
print("Enter 1 for X")
print("Enter 2 for O")
print("Enter 3 to Quit")
# Try exception for CHOICE input
try:
choice = int(input())
except ValueError:
print("Wrong Input!!! Try Again)
continue
# Conditions for player choice
if choice == 1:
player_choice['X'] = cur_player
if cur_player == player1:
player_choice['O'] = player2
else:
player_choice['O'] = player1
elif choice == 2:
player_choice['O'] = cur_player
if cur_player == player1:
player_choice['X'] = player2
else:
player_choice['X'] = player1
elif choice == 3:
print("Final Scores")
print_scoreboard(score_board)
break else:
print("Wrong Choice!!!! Try Again)
# Stores the winner in a single game of Tic Tac Toe
winner = single_game(options[choice-1])
# Edits the scoreboard according to the winner
if winner != 'D' :
player_won = player_choice[winner]
score_board[player_won] = score_board[player_won] + 1
print_scoreboard(score_board)
# Switch player who chooses X or O
if cur_player == player1:
cur_player = player2
else:
cur_player = player1
|
Step 14: スコアボードを更新する
三目並べが終わるたびに、スコアボードを更新する必要があります。
Player 1Enter the name : LuffyPlayer 2Enter the name : Sanji --------------------------------
SCOREBOARD
--------------------------------
Luffy 0
Sanji 0
--------------------------------
Turn to choose for LuffyEnter 1 for XEnter 2 for OEnter 3 to Quit1 | |
| |
_____|_____|_____
| |
| |
_____|_____|_____
| |
| |
| |
Player X turn. Which box? : 5 | |
| |
_____|_____|_____
| |
| X |
_____|_____|_____
| |
| |
| |
Player O turn. Which box? : 1 | |
O | |
_____|_____|_____
| |
| X |
_____|_____|_____
| |
| |
| |
Player X turn. Which box? : 9 | |
O | |
_____|_____|_____
| |
| X |
_____|_____|_____
| |
| | X
| |
Player O turn. Which box? : 2 | |
O | O |
_____|_____|_____
| |
| X |
_____|_____|_____
| |
| | X
| |
Player X turn. Which box? : 3 | |
O | O | X
_____|_____|_____
| |
| X |
_____|_____|_____
| |
| | X
| |
Player O turn. Which box? : 7 | |
O | O | X
_____|_____|_____
| |
| X |
_____|_____|_____
| |
O | | X
| |
Player X turn. Which box? : 6 | |
O | O | X
_____|_____|_____
| |
| X | X
_____|_____|_____
| |
O | | X
| |
Player X has won the game!! --------------------------------
SCOREBOARD
--------------------------------
Luffy 1
Sanji 0
--------------------------------
Turn to choose for SanjiEnter 1 for XEnter 2 for OEnter 3 to Quit2 | |
| |
_____|_____|_____
| |
| |
_____|_____|_____
| |
| |
| |
Player O turn. Which box? : 5 | |
| |
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| |
| |
Player X turn. Which box? : 3 | |
| | X
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| |
| |
Player O turn. Which box? : 2 | |
| O | X
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| |
| |
Player X turn. Which box? : 8 | |
| O | X
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| X |
| |
Player O turn. Which box? : 1 | |
O | O | X
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| X |
| |
Player X turn. Which box? : 9 | |
O | O | X
_____|_____|_____
| |
| O |
_____|_____|_____
| |
| X | X
| |
Player O turn. Which box? : 6 | |
O | O | X
_____|_____|_____
| |
| O | O
_____|_____|_____
| |
| X | X
| |
Player X turn. Which box? : 7 | |
O | O | X
_____|_____|_____
| |
| O | O
_____|_____|_____
| |
X | X | X
| |
Player X has won the game!! --------------------------------
SCOREBOARD
--------------------------------
Luffy 2
Sanji 0
--------------------------------
Turn to choose for LuffyEnter 1 for XEnter 2 for OEnter 3 to Quit3Final Scores --------------------------------
SCOREBOARD
--------------------------------
Luffy 2
Sanji 0
--------------------------------
|
もしゲームが引き分けに終わっていなければ、スコアボードを更新します。
ステップ15:選択プレイヤーを交代させる
各プレイヤーはどのマークが欲しいかを選択する機会がなければならないというのは、寛大な考えです。
そのために、cur_playerの値を入れ替える。

完全な動作コード

Last move: ‘X’ at 2
ゲームプレイの時間だ!
ゲームを作るためのすべてのステップが終了しました。
さあ、いよいよゲームをプレイする時間です。

まとめ
今回の記事は、読者の皆様にとって、楽しいだけでなく、参考になるものであったかと思います。
また、Githubにコードをアップロードしておきました。
コードはこちらをご覧ください。
もし、このゲームについて何か提案があれば、気軽にコメントしてください。