2024-09-27|閱讀時間 ‧ 約 19 分鐘

⭕⭕❌❌回憶殺 python實現 井字遊戲 可線上玩+AI對戰

井字遊戲(OOXX)的遊戲描述


Tic Tac Toe(井字遊戲)是經典的雙人棋盤遊戲,在一個3x3的方格中進行。

每回合兩個玩家輪流選一個位置,讓自己的符號( X 或 O)在
水平線、垂直線或對角線上連成一線的玩家宣告獲勝


上層思考邏輯與框架

  1. 棋盤設置:
    • 遊戲使用一個3x3的方格,總共有九個空格可供玩家選擇。
  2. 玩家:
    • 兩名玩家交替進行,X代表AI , O代表真人玩家。
  3. 遊戲進行:
    • 兩位玩家輪流在空格中放置自己的符號。每次放置後,
      檢查是否有任何一方的符號在橫向、縱向或對角線上連成一直線
  4. 勝利條件:
    • 若某位玩家成功將三個相同的符號連成一線,則該玩家獲勝
      如果所有九個格子都被填滿而沒有任何一方獲勝,則遊戲結束,結果為平局。

中層功能元件分析


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玩家對戰。

很有趣喔~

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.