W11 黑白棋程式開發

You might also like

Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 47

機器學習

W15_ 黑白棋程式開發
01
電腦對局程式製作
Game tree of Tic-Tac-Toe
(2-player, deterministic, turns)

3
Minimax

4
Minimax algorithm

5
Resource limits

6
Evaluation functions

7
1. 子力 象棋
2. 位置
子力
將 2000
士 40
象 40
車 200
馬 90
包 90
卒 10
Othello
• 最多子 (Maximum Pieces)
• 位置權重 (Weighted Square)
• 行動能力 (Mobility Consideration)
• 穩定子 (Stability Pieces)

13
Cutting off search

14
15
16
17
18
19
20
21
02
AlphaGo 程式製作
AlphaGo

• MCTS + PN + VN + Rollout
• MCTS(Monte Carlo Tree Search, 蒙地卡羅樹搜尋 )
• 思考推理的架構
• Policy Network( 策略網路 )
• 記憶學過的棋型,從中找出目前策略
• Value Network ( 評估網路 )& Rollout policy( 隨機策
略)
• 形勢判斷

NDHU CSIE AI lab. 23


樹狀搜尋範例

B C D

NDHU CSIE AI lab. 24


Tree Search for Go

B C D

E F G H I

NDHU CSIE AI Lab. 25


甚麼是 PN, VN 的 N?

• Deeper learning Network( 深度學習類神經網路 )


• Multi-class classifier( 分類器 )
• Input is pixel
• Outputs are classes' possibilities
• 我想知道圖中有甚麼?

NDHU CSIE AI lab. Edited from Wiki 26


PN( 策略網路 )
• 我想知道那個著手是好棋
• Training data: KGS 3 千萬個盤面

NDHU CSIE AI lab. 27


Value Network ( 評估網路 )
• 我想做形勢判斷
• Training data: 3 千萬個盤面,從 3 千萬盤自我對局取得

NDHU CSIE AI lab. 28


AlphaGo 運作圖解
蒙地卡羅樹搜尋
在想過的變化中 選擇在此變化最 判斷此變化結果 更新所有相關的
選擇其中一變化 後要多想哪一手 盤面良窳好壞 變化的良窳
UCB A 變化有
A ABE, ABF, A A
Q+u ABG, AC, ADH,
ADH,ADI
B C D C D
B 電腦在想變化圖時, B C D B C D
一次只會選擇其中一
個變化多想一手
E F G H I E F G H I E F G H I E F G H I

J J J
PN, 3ms VN
策略網路 0.1 0.1 0.5 0.2 0.03 評估網路
MC Rollout

29
03
黑白棋程式製作
套件規劃
• othello
• OthelloUtil,py :黑白棋運作邏輯
• OthelloGame.py :控制黑白棋遊戲進行
• bots
• Random.py :自動隨機下

01 棋盤遊戲製作 31
OthelloUtil.py
方法
getValidMoves(board: np.array, color: int) - Nx2 array
取得盤面上某方的合法位置有哪些
isValidMove(board: np.array, color: int, position: tuple) - boolean
檢查落子是否合法
executeMove(board: np.array, color: int, position: tuple) - void
執行落子

01 棋盤遊戲製作 32
OthelloUtil - getValidMoves
import numpy as np

def getValidMoves(board, color):


moves = set()
for y,x in zip(*np.where(board==color)): 從每個等於 color 的位置向 8 方延伸出去
for direction in [(1,1),(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1),(0,1)]: (HIDDEN CODE 在下一頁 )
flips = []
HIDDEN CODE, HIDDEN CODE
return np.array(list(moves))

01 棋盤遊戲製作 33
OthelloUtil - getValidMoves
for size in range(1, len(board)):
ydir = y + direction[1] * size
xdir = x + direction[0] * size
if xdir >= 0 and xdir < len(board) and ydir >= 0 and ydir < len(board):
如果延伸過程只有對手棋子
if board[ydir][xdir]==-color:
且已經延伸到空位
flips.append((ydir, xdir))
就代表該空位是合法步
elif board[ydir][xdir]==0:
if len(flips)!=0:
moves.add((ydir, xdir))
break
else:
break
else:
break

01 棋盤遊戲製作 34
OthelloUtil - isValidMove
def isValidMove(board, color, position):
valids=getValidMoves(board, color)
if len(valids)!=0 and (valids==np.array(position)).all(1).sum()!=0:
return True 落子位置存在於 getValidMoves 中
else: 則回傳 True
return False

01 棋盤遊戲製作 35
OthelloUtil - executeMove
def executeMove(board, color, position):
y, x = position
board[y][x] = color
for direction in [(1,1),(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1),(0,1)]: 先將落子位置改成自己棋子
flips = [] 接下來和 getValidMoves 一樣
掃描落子位置的 8 個方向
valid_route=False
HIDDEN CODE, HIDDEN CODE
if valid_route:
board[tuple(zip(*flips))]=color

01 棋盤遊戲製作 36
OthelloUtil - executeMove
for size in range(1, len(board)):
ydir = y + direction[1] * size
xdir = x + direction[0] * size
if xdir >= 0 and xdir < len(board) and ydir >= 0 and ydir < len(board): 延伸過程記錄對手棋子座標
if board[ydir][xdir]==-color: 如果遇到自己棋子
則將記錄中的位置翻棋
flips.append((ydir, xdir))
如果遇到空位則清空記錄
elif board[ydir][xdir]==color 代表該路徑無法翻棋
if len(flips)>0:
valid_route=True
break
else:
break
else:
break

01 棋盤遊戲製作 37
OthelloGame
屬性
n: int ( 棋盤大小 )
current_player: int OthelloGame.BLACK=1
OthelloGame.WHITE=-1
方法
move(position: tuple): void 未結束 : None
黑贏 : 1
isEndGame(): result 白贏 : -1
play(): void 平手 : 0
showBoard(): void

01 棋盤遊戲製作 38
OthelloGame
import numpy as np
class OthelloGame(np.ndarray):
BLACK = 1
WHITE = -1 OthelloGame 繼承 numpy 陣列
def __new__(cls, n): 將黑子定義為 1 、白子定義為 -1 、空的為 0
return super().__new__(cls, shape=(n,n), dtype='int')

def __init__(self, n):


self.n=n
self.current_player=OthelloGame.BLACK
self[np.where(self!=0)]=0
self[int(n/2)][int(n/2)]=OthelloGame.WHITE
self[int(n/2)-1][int(n/2)-1]=OthelloGame.WHITE 設定先手為黑子
self[int(n/2)-1][int(n/2)]=OthelloGame.BLACK 並初始化棋盤
self[int(n/2)][int(n/2)-1]=OthelloGame.BLACK

01 棋盤遊戲製作 39
OthelloGame
import numpy as np
from othello.OthelloUtil import getValidMoves, executeMove, isValidMove
class OthelloGame(np.ndarray):
...
...
... 如果該步合法則執行翻棋
def move(self, position): 並將 current_player 改成另一位玩家
if isValidMove(self, self.current_player, position):
executeMove(self, self.current_player, position)
self.current_player=-self.current_player
else:
raise Exception('invalid move')

01 棋盤遊戲製作 40
OthelloGame
class OthelloGame(np.ndarray):
...
def isEndGame(self):
white_valid_moves=len(getValidMoves(self, OthelloGame.WHITE))
black_valid_moves=len(getValidMoves(self, OthelloGame.BLACK))
if white_valid_moves==0 and black_valid_moves==0: 如果兩方都無合法步可走
v,c=np.unique(self, return_counts=True) 代表遊戲結束
white_count=c[np.where(v==OthelloGame.WHITE)] 計算雙方棋數
black_count=c[np.where(v==OthelloGame.BLACK)] 回傳勝方,平手則回傳 0
if white_count>black_count: 尚未結束則回傳 None
return OthelloGame.WHITE
elif black_count>white_count:
return OthelloGame.BLACK
else:
return 0
else:
return None

01 棋盤遊戲製作 41
OthelloGame
class OthelloGame(np.ndarray):
...
def showBoard(self):
corner_offset_format='{:^'+str(len(str(self.n))+1)+'}'
print(corner_offset_format.format(''), end='') 顯示棋盤
for i in range(self.n):
print('{:^3}'.format( chr(ord('A')+i) ), end='')
print()
print(corner_offset_format.format(''), end='')
for i in range(self.n):
print('{:^3}'.format('-'), end='')
print()
for i in range(self.n):
print(corner_offset_format.format(i+1), end='')
for j in range(self.n):
if isValidMove(self, self.current_player, (i,j)):
print('{:^3}'.format('∎'), end='')
else:
print('{:^3}'.format(self[i][j]), end='')
print()

01 棋盤遊戲製作 42
OthelloGame
class OthelloGame(np.ndarray):
...
def play(self, black, white):
while self.isEndGame() == None:
print('{:#^30}'.format( ' Player '+str(self.current_player)+' ' ))
black 及 white 為物件
self.showBoard() # 顯示棋盤
需要有回傳位置的函數 getAction
if len(getValidMoves(self, self.current_player))==0: # 無合法步 OthelloGame 會將棋盤及當前玩
self.current_player=-self.current_player 家
continue 傳入 getAction
if self.current_player==OthelloGame.WHITE:
position=white.getAction(self.clone(), self.current_player) 執行直到 isEndGame()!=None
else: 過程如果當前玩家無合法步
position=black.getAction(self.clone(), self.current_player) 則直接跳過
try:
self.move(position)
except:
print('invalid move', end='\n\n')
continue

01 棋盤遊戲製作 43
執行方法
from othello import OthelloGame

game=OthelloGame(n=8)
即可在 console 看到文字介面的棋盤
∎ 代表合法步
class Human: 輸入座標即可讓 getAction 方法
def getAction(self, game, color): 回傳位置給 OthelloGame
print('input coordinate:', end='')
coor=input()
return (int(coor[1])-1, ord(coor[0])-ord('A'))

game.play(black=Human(), white=Human())

01 棋盤遊戲製作 44
執行方法
from othello import OthelloGame
from othello.bots.Random import BOT

使用 othello.bots.Random.BOT
game=OthelloGame(n=8)

class Human:
def getAction(self, game, color):
print('input coordinate:', end='')
coor=input()
return (int(coor[1])-1, ord(coor[0])-ord('A'))

game.play(black=Human(), white=BOT())

01 棋盤遊戲製作 45
執行方法
import numpy as np
from othello.OthelloUtil import getValidMoves

othello.bots.Random.BOT 內容
class BOT():
隨機取一個合法步
def __init__(self, *args, **kargs):
pass

def getAction(self, game, color):


valids=getValidMoves(game, color)
position=np.random.choice(range(len(valids)), size=1)[0]
position=valids[position]
return position

01 棋盤遊戲製作 46
THE END.

You might also like