在学习算法的过程中,game theory的算法中,棋局的算法是其中之一。而这篇文章将是一个系列文章,用来记录黑白棋AI的开发学习以及我对黑白棋的思考。
一、黑白棋是什么?
黑白棋,又叫翻转棋(Reversi)、奥赛罗棋(Othello)、苹果棋或反棋(Anti reversi)。黑白棋在西方和日本很流行。
黑白棋是19世纪末英国人发明的。直到上个世纪70年代日本人长谷川五郎将其进行发展和推广,借用莎士比亚名剧奥赛罗(othello)为这个游戏重新命名(日语“オセロ”),也就是现在大家玩的黑白棋。为何借用莎士比亚名剧呢?是因为奥赛罗是莎士比亚一个名剧的男主角。他是一个黑人,妻子是白人,因受小人挑拨,怀疑妻子不忠一直情海翻波,最终亲手把妻子杀死。后来真相大白,奥赛罗懊悔不已,自杀而死。黑白棋就是借用这个黑人白人斗争的故事而命名。
黑白棋游戏规则简单,开局在棋盘中间存在4子,黑白棋分先后下棋,并翻转所下棋子与其横纵斜方向上其余夹在自己棋子中间的对方棋子,最后以棋盘上谁的棋子多来判断胜负。它的游戏规则简单,因此上手很容易,但是它的变化又非常复杂。有一种说法是:只需要几分钟学会它,却需要一生的时间去精通它。
二、初学者的大食策略(Maximum disc strategy)及AI思考
黑白棋规则规定对局结束时子多一方为胜方,于是初学者通常会一开始使劲吃子,认为在下棋过程中只要自己在每一步上尽可能多的掌控更多的棋子,而尽可能让对方掌控更多的棋子,这样就能够保证自己最终的胜利。
而对于这种策略来说,AI的开发是比较简单的,如果要把程序想写的简单一点,可以去搜寻当下的可以获得的最大盘面分数的点。而复杂一点,当然也可以通过最大最小搜索的方式通过综合评估下棋。这里给出python的AI代码:
#encoding=utf-8
'''AI思路:按照分数选择走法,优先选择翻转后分数最多的走法'''
import random
# 电脑走法,AI
def getComputerMove(board, computerTile):
# 获取所以合法走法
possibleMoves = getValidMoves(board, computerTile)
# 打乱所有合法走法
random.shuffle(possibleMoves)
# [x, y]在角上,则优先走,因为角上的不会被再次翻转
for x, y in possibleMoves:
if isOnCorner(x, y):
return [x, y]
bestScore = -1
for x, y in possibleMoves:
dupeBoard = getBoardCopy(board)#复制棋盘
makeMove(dupeBoard, computerTile, x, y)#在复制棋盘上下棋
# 按照分数选择走法,优先选择翻转后分数最多的走法
score = getScoreOfBoard(dupeBoard)[computerTile]#获取当下分数
if score > bestScore:
bestMove = [x, y]
bestScore = score
return bestMove
# 获取可落子的位置
def getValidMoves(board, tile):
validMoves = []
for x in range(8):
for y in range(8):
if isValidMove(board, tile, x, y) != False:
validMoves.append([x, y])
return validMoves
# 是否在角上
def isOnCorner(x, y):
return (x == 0 and y == 0) or (x == 7 and y == 0) or (x == 0 and y == 7) or (x == 7 and y == 7)
# 是否是合法走法
def isValidMove(board, tile, xstart, ystart):
# 如果该位置已经有棋子或者出界了,返回False
if not isOnBoard(xstart, ystart) or board[xstart][ystart] != 'none':
return False
# 临时将tile 放到指定的位置
board[xstart][ystart] = tile
if tile == 'black':
otherTile = 'white'
else:
otherTile = 'black'
# 要被翻转的棋子
tilesToFlip = []
for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:
x, y = xstart, ystart
x += xdirection
y += ydirection
if isOnBoard(x, y) and board[x][y] == otherTile:
x += xdirection
y += ydirection
if not isOnBoard(x, y):
continue
# 一直走到出界或不是对方棋子的位置
while board[x][y] == otherTile:
x += xdirection
y += ydirection
if not isOnBoard(x, y):
break
# 出界了,则没有棋子要翻转OXXXXX
if not isOnBoard(x, y):
continue
# 是自己的棋子OXXXXXXO
if board[x][y] == tile:
while True:
x -= xdirection
y -= ydirection
# 回到了起点则结束
if x == xstart and y == ystart:
break
# 需要翻转的棋子
tilesToFlip.append([x, y])
# 将前面临时放上的棋子去掉,即还原棋盘
board[xstart][ystart] = 'none' # restore the empty space
# 没有要被翻转的棋子,则走法非法。翻转棋的规则。
if len(tilesToFlip) == 0: # If no tiles were flipped, this is not a valid move.
return False
return tilesToFlip
# 是否出界
def isOnBoard(x, y):
return x >= 0 and x <= 7 and y >= 0 and y <= 7
# 复制棋盘
def getBoardCopy(board):
dupeBoard = getNewBoard()
for x in range(8):
for y in range(8):
dupeBoard[x][y] = board[x][y]
return dupeBoard
# 将一个tile棋子放到(xstart, ystart)
def makeMove(board, tile, xstart, ystart):
tilesToFlip = isValidMove(board, tile, xstart, ystart)
if tilesToFlip == False:
return False
board[xstart][ystart] = tile
for x, y in tilesToFlip:
board[x][y] = tile
return True
# 获取棋盘上黑白双方的棋子数
def getScoreOfBoard(board):
xscore = 0
oscore = 0
for x in range(8):
for y in range(8):
if board[x][y] == 'black':
xscore += 1
if board[x][y] == 'white':
oscore += 1
return {'black': xscore, 'white': oscore}
# 开局时建立新棋盘
def getNewBoard():
board = []
for i in range(8):
board.append(['none'] * 8)
return board
三、大食策略的AI缺点
但大食策略实际上是一个较差的策略。这个策略犯了短视的错误。初学者下了几盘棋以后,就很容易发现这种下法并不是很好,我们接下来会进行举例说明。
在图1中,黑棋只有一个棋子,以及四步棋可下。白棋肯定能赢吗?
图1 黑先
黑棋可以下a1或h8,并且照此下去,他可以下完所有剩下的棋步(因为下每步棋之后白棋都得跳步),最后比分将是黑棋40:24!因此很明显地,即使是在对局十分临近结束时,拥用大量棋子也不能绝对保证最终的胜利。在图1的实例中,白棋确实拥有许多棋子,但它们极易受到攻击:它们仍会被对手翻转回去。我们可以思考一下,如果是能够把四个边都占到,至少就有30个点位,对于64个点位的棋盘来说,中间很容易再多3子,很容易获胜。而进一步的改进策略,就会用到稳定子(stable disk)的概念,这个概念与策略也将在后续的博客中谈到。
文末给出我的Git:AshZh/Othello-AI,算法使用js进行写作方便大家一起学习,如果想要挑战一下,欢迎随时打开下面网页,随时打开随时游戏:javascript黑白棋人机博弈游戏ashzh.github.io
为了更快的学习黑白棋的算法,及代码编写方法,界面UI和部分代码在学习的时候从网络中直接扒来的,现在已经不太找得到那个UI的作者是谁,在博客中说明。UI的不同的算法以及黑白棋方面算法的思考也会在未来不断地完善。一方面会放在博客中,一方面作为学习资料放在Git中与大家一起分享。
参考文献: