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

更新於 發佈於 閱讀時間約 1 分鐘

井字遊戲(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()

試玩畫面

raw-image



點這行 線上執行 與 試玩(在新的頁面按Run開始玩)


延伸思考:


如果對程式或者演算法有興趣的同學,

可以試著觀察OOXX井字遊戲的規律,

開發更強的連線演算法,嘗試讓兩個AI玩家對戰。

很有趣喔~

留言
avatar-img
留言分享你的想法!
小松鼠-avatar-img
發文者
2024/09/27
林燃(創作小說家)
小松鼠-avatar-img
發文者
2024/10/10
動手學Python/資料結構/演算法 的目錄提及了這篇文章,趕快過去看看吧!
avatar-img
小松鼠的演算法樂園
95會員
427內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
2024/10/10
從Python 內建deque資料結構的角度切入, 同時了解deque 與 FIFO Queue相關的function用法。 collections.deque是一種兩端點皆可進出的雙端佇列 在兩端點高效地在O(1)常數時間內添加和刪除元素。 這使得deque非常適合實現FIFO Queue
Thumbnail
2024/10/10
從Python 內建deque資料結構的角度切入, 同時了解deque 與 FIFO Queue相關的function用法。 collections.deque是一種兩端點皆可進出的雙端佇列 在兩端點高效地在O(1)常數時間內添加和刪除元素。 這使得deque非常適合實現FIFO Queue
Thumbnail
2024/09/23
深入探討圖(Graph)的基本概念 及 最短路徑Shortest Path的尋找。 我們專注於廣度優先搜尋(BFS)演算法,以等權圖的最短路徑為例, 詳細說明如何利用BFS從起點擴散到終點,並且提供詳細的程式碼範例。 透過實作,讀者能夠更清楚理解圖論及BFS的應用,並體會水波紋擴散模型的重要性。
Thumbnail
2024/09/23
深入探討圖(Graph)的基本概念 及 最短路徑Shortest Path的尋找。 我們專注於廣度優先搜尋(BFS)演算法,以等權圖的最短路徑為例, 詳細說明如何利用BFS從起點擴散到終點,並且提供詳細的程式碼範例。 透過實作,讀者能夠更清楚理解圖論及BFS的應用,並體會水波紋擴散模型的重要性。
Thumbnail
2024/09/15
定義 圖Graph: 由節點和邊所組成的一個網狀資料結構。 圖的表達方式Graph representation: 常見的有相鄰串列adjacency list或相鄰矩陣adjacency matrix。 本文以adjacenct list作為示範。 節點Vertex: 節點
Thumbnail
2024/09/15
定義 圖Graph: 由節點和邊所組成的一個網狀資料結構。 圖的表達方式Graph representation: 常見的有相鄰串列adjacency list或相鄰矩陣adjacency matrix。 本文以adjacenct list作為示範。 節點Vertex: 節點
Thumbnail
看更多
你可能也想看
Thumbnail
TOMICA第一波推出吉伊卡哇聯名小車車的時候馬上就被搶購一空,一直很扼腕當時沒有趕緊入手。前陣子閒來無事逛蝦皮,突然發現幾家商場都又開始重新上架,價格也都回到正常水準,估計是官方又再補了一批貨,想都沒想就立刻下單! 同文也跟大家分享近期蝦皮購物紀錄、好用推薦、蝦皮分潤計畫的聯盟行銷!
Thumbnail
TOMICA第一波推出吉伊卡哇聯名小車車的時候馬上就被搶購一空,一直很扼腕當時沒有趕緊入手。前陣子閒來無事逛蝦皮,突然發現幾家商場都又開始重新上架,價格也都回到正常水準,估計是官方又再補了一批貨,想都沒想就立刻下單! 同文也跟大家分享近期蝦皮購物紀錄、好用推薦、蝦皮分潤計畫的聯盟行銷!
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
井字遊戲(OOXX)的遊戲描述 Tic Tac Toe(井字遊戲)是經典的雙人棋盤遊戲,在一個3x3的方格中進行。 每回合兩個玩家輪流選一個位置,先讓自己的符號(是 X 或 O)在 水平線、垂直線或對角線上連成一線的玩家宣告獲勝。
Thumbnail
井字遊戲(OOXX)的遊戲描述 Tic Tac Toe(井字遊戲)是經典的雙人棋盤遊戲,在一個3x3的方格中進行。 每回合兩個玩家輪流選一個位置,先讓自己的符號(是 X 或 O)在 水平線、垂直線或對角線上連成一線的玩家宣告獲勝。
Thumbnail
賓果的遊戲描述 在一個5x5的方陣上隨機填充1~25的數字。 玩家(使用者) 和 電腦(AI)輪流叫一個號碼,最先占據一整條直線連線的獲勝。 就像小時候玩的bingo 賓果連線遊戲一樣! (可以是占據兩條對角線,可以是占據水平直線,可以是占據垂直直線)
Thumbnail
賓果的遊戲描述 在一個5x5的方陣上隨機填充1~25的數字。 玩家(使用者) 和 電腦(AI)輪流叫一個號碼,最先占據一整條直線連線的獲勝。 就像小時候玩的bingo 賓果連線遊戲一樣! (可以是占據兩條對角線,可以是占據水平直線,可以是占據垂直直線)
Thumbnail
河內塔的遊戲描述 有三個柱子A柱,B柱,C柱。 A柱上有 N 個 (N>1) 穿孔圓盤,盤的尺寸由下到上依次變小。 要求按下列規則透過合法移動,將所有圓盤移至 C 柱: 1. 每次只能移動頂端的一個圓盤; 2. 大圓盤不能疊在小圓盤上面。
Thumbnail
河內塔的遊戲描述 有三個柱子A柱,B柱,C柱。 A柱上有 N 個 (N>1) 穿孔圓盤,盤的尺寸由下到上依次變小。 要求按下列規則透過合法移動,將所有圓盤移至 C 柱: 1. 每次只能移動頂端的一個圓盤; 2. 大圓盤不能疊在小圓盤上面。
Thumbnail
賓果這款遊戲我想大家應該都不陌生才對,常常會出現在各種活動場合,看誰最快達到指定的連線數量,誰就能得到獎品 賓果遊戲基本概念 這種賓果遊戲使用的卡片由一個5x5的方格組成,每個方格中填入1到25之間的數字,每張卡片上的數字排列是隨機的。 🕹️遊戲規則 準備工作:每個玩家獲得一張或多
Thumbnail
賓果這款遊戲我想大家應該都不陌生才對,常常會出現在各種活動場合,看誰最快達到指定的連線數量,誰就能得到獎品 賓果遊戲基本概念 這種賓果遊戲使用的卡片由一個5x5的方格組成,每個方格中填入1到25之間的數字,每張卡片上的數字排列是隨機的。 🕹️遊戲規則 準備工作:每個玩家獲得一張或多
Thumbnail
你一定有玩過猜拳遊戲,但你知道怎麼用Python寫一個猜拳遊戲嗎?今天我要分享一個簡單又好玩的程式碼,讓你可以和電腦對戰! 首先,我們要導入random模組,這個模組可以讓我們隨機生成一個數字,代表電腦出的拳。 然後,我們要用input函數讓使用者輸入自己出的拳,0代表剪刀,1代表石頭,2代表
Thumbnail
你一定有玩過猜拳遊戲,但你知道怎麼用Python寫一個猜拳遊戲嗎?今天我要分享一個簡單又好玩的程式碼,讓你可以和電腦對戰! 首先,我們要導入random模組,這個模組可以讓我們隨機生成一個數字,代表電腦出的拳。 然後,我們要用input函數讓使用者輸入自己出的拳,0代表剪刀,1代表石頭,2代表
Thumbnail
入門系列篇主要講解基本規則,適合初學者,一起輕鬆學圍棋吧🌟
Thumbnail
入門系列篇主要講解基本規則,適合初學者,一起輕鬆學圍棋吧🌟
Thumbnail
這篇文章將利用之前所學過的一些東西,包括if敘述、串列、while迴圈、函數等等的觀念,來實作一個撲克牌的小遊戲-21點。
Thumbnail
這篇文章將利用之前所學過的一些東西,包括if敘述、串列、while迴圈、函數等等的觀念,來實作一個撲克牌的小遊戲-21點。
Thumbnail
這次來介紹一款蠻簡單、輕鬆,卻又小心機的桌遊『搶尾刀』,其卡牌也不過單純只用了 3、4、5、6、7 這五種數字牌而已,就讓玩家們在玩的過程中,體驗到充滿刀光劍影的煙硝味喔 ^^ (背上被其它玩家補了很多刀的感覺,喔…難怪要取名叫『搶尾刀』,呵~) 在遊戲結束後擁有最多張數字牌的玩家獲勝。
Thumbnail
這次來介紹一款蠻簡單、輕鬆,卻又小心機的桌遊『搶尾刀』,其卡牌也不過單純只用了 3、4、5、6、7 這五種數字牌而已,就讓玩家們在玩的過程中,體驗到充滿刀光劍影的煙硝味喔 ^^ (背上被其它玩家補了很多刀的感覺,喔…難怪要取名叫『搶尾刀』,呵~) 在遊戲結束後擁有最多張數字牌的玩家獲勝。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News