井字遊戲(OOXX)的遊戲描述
Tic Tac Toe(井字遊戲)是經典的雙人棋盤遊戲,在一個3x3的方格中進行。
每回合兩個玩家輪流選一個位置,先讓自己的符號( X 或 O)在水平線、垂直線或對角線上連成一線的玩家宣告獲勝。
上層思考邏輯與框架
- 棋盤設置:
- 遊戲使用一個3x3的方格,總共有九個空格可供玩家選擇。
- 玩家:
- 兩名玩家交替進行,X代表AI , O代表真人玩家。
- 遊戲進行:
- 兩位玩家輪流在空格中放置自己的符號。每次放置後,
檢查是否有任何一方的符號在橫向、縱向或對角線上連成一直線。
- 兩位玩家輪流在空格中放置自己的符號。每次放置後,
- 勝利條件:
- 若某位玩家成功將三個相同的符號連成一線,則該玩家獲勝。
如果所有九個格子都被填滿而沒有任何一方獲勝,則遊戲結束,結果為平局。
- 若某位玩家成功將三個相同的符號連成一線,則該玩家獲勝。
中層功能元件分析
1.怎麼建立井字遊戲的遊戲版?
透過list comprehension建立長度為9的陣列,並且標上1~9的數字,提示玩家每個位置所代表的數字。
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| 7 | 8 | 9 |
1 對應到左上方的格子。2 對應到正上方的格子。3 對應到右上方的格子。
4 對應到正左方的格子。5 對應到正中央的格子。6 對應到正右方的格子。
7 對應到左下方的格子。8 對應到正下方的格子。9 對應到右下方的格子。
# 初始化棋盤
board = [str(i+1) for i in range(9)]
2.怎麼輸出遊戲狀態?
透過雙層迴圈,輸出3x3的遊戲版,並且以|當作間隔符號。
如圖所示
| 1 | 2 | 3 |
| 4 | X | 6 |
| 7 | 8 | 9 |
# 輸出棋盤
def print_board():
for row in [board[i*3:(i+1)*3] for i in range(3)]:
print('| ' + ' | '.join(row) + ' |')
3.人類玩家怎麼選擇位置?
從鍵盤接收1~9的數字,依照數字在遊戲版上對應的位置放置O的符號。
# 玩家回合
player_move = int(input("選擇一個位置 (1-9): ")) - 1
if board[player_move].isnumeric():
board[player_move] = 'O'
4.AI玩家怎麼選擇位置?
AI根據board遊戲版當前的棋盤狀態,
透過mixMax演算法遞迴展開所有可能的分枝情況。
最小化人類玩家勝率,最大化AI玩家勝率。
選擇對AI玩家最有利的位置,讓X連成一線的機會最大化。
# minMax演算法
# O 代表玩家
# X 代表AI
# AI選擇位置時,會尋找勝率最大的位置
def minimax(board, depth, is_maximizing):
if check_winner(board, 'O'):
return -1
elif check_winner(board, 'X'):
return 1
elif is_draw(board):
return 0
if is_maximizing:
best_score = -math.inf
for i in range(9):
if board[i].isnumeric():
original = board[i]
board[i] = 'X'
score = minimax(board, depth + 1, False)
board[i] = original
best_score = max(score, best_score)
return best_score
else:
best_score = math.inf
for i in range(9):
if board[i].isnumeric():
original = board[i]
board[i] = 'O'
score = minimax(board, depth + 1, True)
board[i] = original
best_score = min(score, best_score)
return best_score
# AI透過mixMax演算法,選擇最好的位置
def best_move():
best_score = -math.inf
move = 0
for i in range(9):
#if board[i] == ' ':
if board[i].isnumeric():
original = board[i]
board[i] = 'X'
score = minimax(board, 0, False)
board[i] = original
if score > best_score:
best_score = score
move = i
return move
5.怎麼檢查是否已經有玩家連成一線,宣告獲勝?
檢查遊戲盤上所有水平線、垂直線、大對角線的位置,
看看是否已經有O連成一線或者X連成一線的情況。
如果O連成一線,代表人類玩家宣告獲勝。
如果X連成一線,代表AI玩家宣告獲勝。
# 檢查是否有玩家獲勝
def check_winner(board, player):
win_conditions = [(0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)]
return any(board[a] == board[b] == board[c] == player for a, b, c in win_conditions)
while 遊戲循環:
...
if check_winner(board, 'O'):
print_board()
print("你贏了!")
break
if check_winner(board, 'X'):
print_board()
print("你輸了!")
break
6.如何判斷雙方平手?
檢查是有所有位置都已經被填滿O或X的符號?
如果已經沒有剩餘的空位,代表雙方平手。
如果還有空位,則井字遊戲繼續進行。
# 檢查是否平局
def is_draw(board):
space_count = 0
for row in board:
for cell in row:
if cell.isnumeric() == True:
return False
return True
7.怎麼建立遊戲循環機制?
外層以while True迴圈建立遊戲循環機制。
每回合玩家和AI輪流選一個位置放下O或X。
在每回合的末端,判斷是否已經有玩家獲勝或者平手?
如果是,則遊戲結束,break離開遊戲循環機制的while迴圈。
# AI先手
# AI在正中央位置放一個X
ai_move = 4
board[ai_move] = 'X'
print_board()
while True:
# 玩家回合
player_move = int(input("選擇一個位置 (1-9): ")) - 1
if board[player_move].isnumeric():
board[player_move] = 'O'
if check_winner(board, 'O'):
print_board()
print("你贏了!")
break
elif is_draw(board):
print_board()
print("平局!")
break
# AI回合
ai_move = best_move()
board[ai_move] = 'X'
if check_winner(board, 'X'):
print_board()
print("你輸了!")
break
elif is_draw(board):
print_board()
print("平局!")
break
else:
print("位置已被佔用,請選擇其他位置。")
print_board()
底層的完整實作: 井字遊戲(OOXX)
import math
# 初始化棋盤
board = [str(i+1) for i in range(9)]
# 輸出棋盤
def print_board():
for row in [board[i*3:(i+1)*3] for i in range(3)]:
print('| ' + ' | '.join(row) + ' |')
# 檢查是否有玩家獲勝
def check_winner(board, player):
win_conditions = [(0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)]
return any(board[a] == board[b] == board[c] == player for a, b, c in win_conditions)
# 檢查是否平局
def is_draw(board):
space_count = 0
for row in board:
for cell in row:
if cell.isnumeric() == True:
return False
return True
# Minimax演算法
# O 代表玩家
# X 代表AI
# AI選擇位置時,會尋找勝率最大的位置
def minimax(board, depth, is_maximizing):
if check_winner(board, 'O'):
return -1
elif check_winner(board, 'X'):
return 1
elif is_draw(board):
return 0
if is_maximizing:
best_score = -math.inf
for i in range(9):
if board[i].isnumeric():
original = board[i]
board[i] = 'X'
score = minimax(board, depth + 1, False)
board[i] = original
best_score = max(score, best_score)
return best_score
else:
best_score = math.inf
for i in range(9):
if board[i].isnumeric():
original = board[i]
board[i] = 'O'
score = minimax(board, depth + 1, True)
board[i] = original
best_score = min(score, best_score)
return best_score
# AI透過mixMax演算法,選擇最好的位置
def best_move():
best_score = -math.inf
move = 0
for i in range(9):
#if board[i] == ' ':
if board[i].isnumeric():
original = board[i]
board[i] = 'X'
score = minimax(board, 0, False)
board[i] = original
if score > best_score:
best_score = score
move = i
return move
# 遊戲主體
def play_game():
# AI先手
# AI在正中央位置放一個X
ai_move = 4
board[ai_move] = 'X'
print_board()
while True:
# 玩家回合
player_move = int(input("選擇一個位置 (1-9): ")) - 1
if board[player_move].isnumeric():
board[player_move] = 'O'
if check_winner(board, 'O'):
print_board()
print("你贏了!")
break
elif is_draw(board):
print_board()
print("平局!")
break
# AI回合
ai_move = best_move()
board[ai_move] = 'X'
if check_winner(board, 'X'):
print_board()
print("你輸了!")
break
elif is_draw(board):
print_board()
print("平局!")
break
else:
print("位置已被佔用,請選擇其他位置。")
print_board()
if __name__ == '__main__':
play_game()
試玩畫面

點這行 線上執行 與 試玩(在新的頁面按Run開始玩)
延伸思考:
如果對程式或者演算法有興趣的同學,
可以試著觀察OOXX井字遊戲的規律,
開發更強的連線演算法,嘗試讓兩個AI玩家對戰。
很有趣喔~