# Python實作課程 ## 第五堂課 : Tic Tac Toe(AI) ## 2022 / 5 / 27 ### Tony --- ## 重點回顧 ---- 我們前兩堂課寫完了基本的TicTacToe,玩家可以是真人也可以是電腦,但此時的電腦並不像真人用腦子玩遊戲,他只是隨便挑一格放符號而已 可不可以讓電腦具備判斷盤面的能力,讓他下的每一步都是對他具有最大效益的? --- ## minimax ---- MiniMax算法常用於棋類等由兩方較量的遊戲和程序。該算法是一個零總和算法,<font color='FFF300'>即一方要在可選的選項中選擇將其優勢最大化的選擇,另一方則選擇令對手優勢最小化的方法。</font>而開始的時候總和為0。 本Code當中的minimax思維:當換到AI的回合時,挑選對他而言優勢最大的選擇;當另一個玩家的回合時,挑選對他而言傷害最小的選擇 ---- 我們希望以最短的步數贏下比賽,所以: 1. 如果最後AI贏, 則該盤面的value = 1*(現在盤面所剩空格+1) 2. 如果最後AI輸掉, 則該盤面的value = -1*(現在盤面所剩空格+1) 3. 平局的盤面,其value=0 4. 每個最終盤面的value往回推,如果該回合換AI,則選擇他的子盤面中value最大者,反之則選最小者 5. 最後根據當前盤面以及玩家做最好的選擇 ---- ![](https://i.imgur.com/6RR9DvE.png) --- ## 開始寫Code ---- 請打開上次寫完的TicTacToe If 檔案丟失:[點我拿完整Code](https://hackmd.io/@Tony041010/TicTacToe_FullCode) ---- 在TicTacToe補上這個函式(如果沒有) ```python= def num_empty_squares(self): return self.board.count(' ') ``` ---- 我們要做的: 1. 建一個GeniusComputerPlayer Class 2. 寫minimax函式 ---- 1. 建立GeniusComputerPlayer Class 建在`player.py`裡面,構造仿照前兩個寫過的player 並多一個minimax函式,函式最後會回傳一個字典Dictionary ```python= class GeniusComputerPlayer(Player): def __init__(self, letter): super().__init__(letter) def get_move(self, game): if len(game.available_moves()) == 9: square = random.choice(game.available_moves()) else: square = self.minimax(game, self.letter)['score'] return square def minimax(self, state, player): #state:遊戲本體,但因為我們指的是"當前盤面" #所以用state較佳 pass ``` ---- 2. 寫minimax函式 先設點變數 ```python= def minimax(self, state, player): max_player = self.letter # AI自己 other_player = 'O' if player == 'X' else 'X' ``` ---- 接著,我們check目前盤面是否為一個最終盤面,即上一個玩家已經贏了或是平手 我們需要用到一個資料型態叫做**dictionary字典**,他是一個集合,每一個元素由一個鍵(Key)和值(Value)組成 ```python= if state.current_winner == other_player: if other_player == self.letter: #最終盤面是AI贏 return {'position':None, 'score':1*(state.num_empty_squares()+1)} elif not state.empty_squares(): return {'position':None, 'score':0} else: #最終盤面是AI輸 return {'position':None, 'score':-1*(state.num_empty_squares()+1)} ``` ---- 再來,我們就要進入預測盤面的環節,先初始化環境 ```python= if player == max_player:#如股現在玩家是AI就要選最大優勢的 best = {'position':None, 'score':-math.inf} #負無限大,即保證他的score會越來越大 else:#反之則選傷害最小的 best = {'position':None, 'score':math.inf}#正無限大,即保證他的score會越來越小 ``` ---- 預測盤面:針對目前盤面的下一層所有可能結果 1. 先做一次該結果 2. 利用遞迴去找他的子平面的所有狀況,因此可以模擬所有盤面 3. 回復原狀 4. 如果有模擬盤面有更好的字典就更新 ---- ```python= for possible_move in state.available_moves(): # step 1:make a move, try that spot state.make_move(possible_move, player) # step 2:recurse using minimax to simulate a game after making that move sim_score = self.minimax(state, other_player) #now, we alternate player # step 3:undo the move state.board[possible_move] = ' ' state.current_winner = None sim_score['position'] = possible_move # step 4: update the dictionaries if necessary if player == max_player: # to maximize the max_player if sim_score['score'] > best['score']: best = sim_score # replace that else: # to minimize the other_player if sim_score['score'] < best['score']: best = sim_score # replace that return best ```
{"metaMigratedAt":"2023-06-17T01:42:29.246Z","metaMigratedFrom":"YAML","title":"Python實作三點五:TicTacToe(AI)","breaks":true,"slideOptions":"{\"transition\":\"slide\",\"theme\":null}","contributors":"[{\"id\":\"4f731eff-9d88-41f4-af56-2e3e02f20cfc\",\"add\":3627,\"del\":222}]"}
    746 views