蒙特卡洛树搜索-黑白棋(一):黑白棋介绍及棋盘类
这是关于蒙特卡洛树搜索解决黑白棋问题的文章,如果你不了解蒙特卡洛树搜索,参看蒙特卡洛树搜索
文章目录
- 1. 黑白棋简介
- 2. 游戏规则
- 3. 棋盘类
- 4.函数具体实现
- 5. 测试
1. 黑白棋简介
黑白棋(Reversi),也叫翻转棋,是一款经典的策略游戏。
一般棋子双面为黑白两色,故称“黑白棋”。因为行棋之时将对方棋子翻转,则变为己方棋子,故又称“翻转棋” (Reversi) 。
它使用 8x8 的棋盘,由两人执黑子和白子轮流下棋,最后子多方为胜方。
2. 游戏规则
- 开始时,黑棋位于D5和E4,白棋位于D4和E5.
- 一个合法的落子:
i) 在空处落子、并翻转对手一个或多个棋子
ii) 新落子位置必须在可以夹住对方的位置上、对方被夹住的棋子翻转。可以是横着夹、竖着夹、对角线夹
iii) 任何被夹住的棋子必须被反过来 - 如果一方没有合法棋步,也就是无论他下在哪里,都无法翻转对方的棋子了,这一轮只能弃权
- 棋局持续知道棋盘填满或双方都没有合法棋步可下
- 如果一方落子时间超过1min,或者连续三次落子不合法,则判断该方失败
3. 棋盘类
board棋盘,规格时8*8,黑棋用X表示、白棋用O表示、空用.
表示。
类中有如下的函数:
class Board(object)def __init__(self): #初始化函数...def __getitem__(self,index): #获取索引处的落子状态...def display(self,step_time=None,total_time=None):#显示棋盘...def count(self,color):#统计color一方的棋子数量...def get_winnner(self):#判断哪一方获胜...def _move(self,action,color):#落子...def backpropagation(self,action,flipped_pos,color):#回溯...def is_on_board(self,x,y):#判断是否越界...def _can_fliped(self,action,color):#判断落子是否合法...def get_legal_actions(self,color):#获得合法的走法...def board_num(self,action):#棋盘左边转化为索引...def num_board(self,action):#索引转化为棋盘坐标...
4.函数具体实现
__init__
def __init__(self):"""初始化棋盘状态"""self.empty = '.' # 未落子状态self._board = [[self.empty for _ in range(8)] for _ in range(8)] # 规格:8*8self._board[3][4] = 'X' # 黑棋棋子self._board[4][3] = 'X' # 黑棋棋子self._board[3][3], self._board[4][4] = 'O', 'O' # 白棋棋子
__getitem__(self,index)
def __getitem__(self, index):"""添加Board[][] 索引语法:param index: 下标索引:return:"""return self._board[index]
diaplay
def display(self, step_time=None, total_time=None):"""打印棋盘:param step_time: 每一步的耗时, 比如:{"X":1,"O":0},默认值是None:param total_time: 总耗时, 比如:{"X":1,"O":0},默认值是None:return:"""board = self._board# print(step_time,total_time)# 打印列名print(' ', ' '.join(list('ABCDEFGH')))# 打印行名和棋盘for i in range(8):# print(board)print(str(i + 1), ' '.join(board[i]))if (not step_time) or (not total_time):# 棋盘初始化时展示的时间step_time = {"X": 0, "O": 0}total_time = {"X": 0, "O": 0}print("统计棋局: 棋子总数 / 每一步耗时 / 总时间 ")print("黑 棋: " + str(self.count('X')) + ' / ' + str(step_time['X']) + ' / ' + str(total_time['X']))print("白 棋: " + str(self.count('O')) + ' / ' + str(step_time['O']) + ' / ' + str(total_time['O']) + '\n')else:# 比赛时展示时间print("统计棋局: 棋子总数 / 每一步耗时 / 总时间 ")print("黑 棋: " + str(self.count('X')) + ' / ' + str(step_time['X']) + ' / ' + str(total_time['X']))print("白 棋: " + str(self.count('O')) + ' / ' + str(step_time['O']) + ' / ' + str(total_time['O']) + '\n')
count
def count(self, color):"""统计 color 一方棋子的数量。(O:白棋, X:黑棋, .:未落子状态):param color: [O,X,.] 表示棋盘上不同的棋子:return: 返回 color 棋子在棋盘上的总数"""count = 0for y in range(8):for x in range(8):if self._board[x][y] == color:count += 1return count
get_winner
def get_winner(self):"""判断黑棋和白旗的输赢,通过棋子的个数进行判断:return: 0-黑棋赢,1-白旗赢,2-表示平局,黑棋个数和白旗个数相等"""# 定义黑白棋子初始的个数black_count, white_count = 0, 0for i in range(8):for j in range(8):# 统计黑棋棋子的个数if self._board[i][j] == 'X':black_count += 1# 统计白旗棋子的个数if self._board[i][j] == 'O':white_count += 1if black_count > white_count:# 黑棋胜return 0, black_count - white_countelif black_count < white_count:# 白棋胜return 1, white_count - black_countelif black_count == white_count:# 表示平局,黑棋个数和白旗个数相等return 2, 0
board_num
def board_num(self, action):"""棋盘坐标转化为数字坐标:param action:棋盘坐标,比如A1:return:数字坐标,比如 A1 --->(0,0)"""row, col = str(action[1]).upper(), str(action[0]).upper()if row in '12345678' and col in 'ABCDEFGH':# 坐标正确x, y = '12345678'.index(row), 'ABCDEFGH'.index(col)return x, y
num_board
def num_board(self, action):"""数字坐标转化为棋盘坐标:param action:数字坐标 ,比如(0,0):return:棋盘坐标,比如 (0,0)---> A1"""row, col = actionl = [0, 1, 2, 3, 4, 5, 6, 7]if col in l and row in l:return chr(ord('A') + col) + str(row + 1)
is_on_board
def is_on_board(self, x, y):"""判断坐标是否出界:param x: row 行坐标:param y: col 列坐标:return: True or False"""return x >= 0 and x <= 7 and y >= 0 and y <= 7
_can_fliped
def _can_fliped(self, action, color):"""检测落子是否合法,如果不合法,返回 False,否则返回反转子的坐标列表:param action: 下子位置:param color: [X,0,.] 棋子状态:return: False or 反转对方棋子的坐标列表"""# 判断action 是不是字符串,如果是则转化为数字坐标if isinstance(action, str):action = self.board_num(action)xstart, ystart = action# 如果该位置已经有棋子或者出界,返回 Falseif not self.is_on_board(xstart, ystart) or self._board[xstart][ystart] != self.empty:return False# 临时将color放到指定位置self._board[xstart][ystart] = color# 棋手op_color = "O" if color == "X" else "X"# 要被翻转的棋子flipped_pos = []flipped_pos_board = []for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0],[-1, 1]]:x, y = xstart, ystartx += xdirectiony += ydirection# 如果(x,y)在棋盘上,而且为对方棋子,则在这个方向上继续前进,否则循环下一个角度。if self.is_on_board(x, y) and self._board[x][y] == op_color:x += xdirectiony += ydirection# 进一步判断点(x,y)是否在棋盘上,如果不在棋盘上,继续循环下一个角度,如果在棋盘上,则进行while循环。if not self.is_on_board(x, y):continue# 一直走到出界或不是对方棋子的位置while self._board[x][y] == op_color:# 如果一直是对方的棋子,则点(x,y)一直循环,直至点(x,y)出界或者不是对方的棋子。x += xdirectiony += ydirection# 点(x,y)出界了和不是对方棋子if not self.is_on_board(x, y):break# 出界了,则没有棋子要翻转OXXXXXif not self.is_on_board(x, y):continue# 是自己的棋子OXXXXXXOif self._board[x][y] == color:while True:x -= xdirectiony -= ydirection# 回到了起点则结束if x == xstart and y == ystart:break# 需要翻转的棋子flipped_pos.append([x, y])# 将前面临时放上的棋子去掉,即还原棋盘self._board[xstart][ystart] = self.empty # restore the empty space# 没有要被翻转的棋子,则走法非法。返回 Falseif len(flipped_pos) == 0:return Falsefor fp in flipped_pos:flipped_pos_board.append(self.num_board(fp))# 走法正常,返回翻转棋子的棋盘坐标return flipped_pos_board
_move
def _move(self, action, color):"""落子并获取反转棋子的坐标:param action: 落子的坐标 可以是 D3 也可以是(2,3):param color: [O,X,.] 表示棋盘上不同的棋子:return: 返回反转棋子的坐标列表,落子失败则返回False"""# 判断action 是不是字符串,如果是则转化为数字坐标if isinstance(action, str):action = self.board_num(action)fliped = self._can_fliped(action, color)if fliped:# 有就反转对方棋子坐标for flip in fliped:x, y = self.board_num(flip)self._board[x][y] = color# 落子坐标x, y = action# 更改棋盘上 action 坐标处的状态,修改之后该位置属于 color[X,O,.]等三状态self._board[x][y] = colorreturn flipedelse:# 没有反转子则落子失败return False
get_legal_actions
为简化枚举所有的空位置,考虑合法的落子必须位于对手的子的边上。
def get_legal_actions(self, color):"""按照黑白棋的规则获取棋子的合法走法:param color: 不同颜色的棋子,X-黑棋,O-白棋:return: 生成合法的落子坐标,用list()方法可以获取所有的合法坐标"""# 表示棋盘坐标点的8个不同方向坐标,比如方向坐标[0][1]则表示坐标点的正上方。direction = [(-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1)]op_color = "O" if color == "X" else "X"# 统计 op_color 一方邻近的未落子状态的位置op_color_near_points = []board = self._boardfor i in range(8):# i 是行数,从0开始,j是列数,也是从0开始for j in range(8):# 判断棋盘[i][j]位子棋子的属性,如果是op_color,则继续进行下一步操作,# 否则继续循环获取下一个坐标棋子的属性if board[i][j] == op_color:# dx,dy 分别表示[i][j]坐标在行、列方向上的步长,direction 表示方向坐标for dx, dy in direction:x, y = i + dx, j + dy# 表示x、y坐标值在合理范围,棋盘坐标点board[x][y]为未落子状态,# 而且(x,y)不在op_color_near_points 中,统计对方未落子状态位置的列表才可以添加该坐标点if 0 <= x <= 7 and 0 <= y <= 7 and board[x][y] == self.empty and (x, y) not in op_color_near_points:op_color_near_points.append((x, y))l = [0, 1, 2, 3, 4, 5, 6, 7]for p in op_color_near_points:if self._can_fliped(p, color):# 判断p是不是数字坐标,如果是则返回棋盘坐标# p = self.board_num(p)if p[0] in l and p[1] in l:p = self.num_board(p)yield p
backpropagation
回退
def backpropagation(self, action, flipped_pos, color):"""回溯:param action: 落子点的坐标:param flipped_pos: 反转棋子坐标列表:param color: 棋子的属性,[X,0,.]三种情况:return:"""# 判断action 是不是字符串,如果是则转化为数字坐标if isinstance(action, str):action = self.board_num(action)self._board[action[0]][action[1]] = self.empty# 如果 color == 'X',则 op_color = 'O';否则 op_color = 'X'op_color = "O" if color == "X" else "X"for p in flipped_pos:# 判断action 是不是字符串,如果是则转化为数字坐标if isinstance(p, str):p = self.board_num(p)self._board[p[0]][p[1]] = op_color
5. 测试
if __name__ == '__main__':board = Board()board.display()print("X可选动作:\n",list(board.get_legal_actions('X')))print("X错误的下D2 返回值是",board._move('D2','X'))print("\n落子在D3\n")board._move('D3','X')board.display()print("O可选动作:\n", list(board.get_legal_actions('O')))
A B C D E F G H
1 . . . . . . . .
2 . . . . . . . .
3 . . . . . . . .
4 . . . O X . . .
5 . . . X O . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
统计棋局: 棋子总数 / 每一步耗时 / 总时间
黑 棋: 2 / 0 / 0
白 棋: 2 / 0 / 0X可选动作:['D3', 'C4', 'F5', 'E6']
X错误的下D2 返回值是 False落子在D3A B C D E F G H
1 . . . . . . . .
2 . . . . . . . .
3 . . . X . . . .
4 . . . X X . . .
5 . . . X O . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
统计棋局: 棋子总数 / 每一步耗时 / 总时间
黑 棋: 4 / 0 / 0
白 棋: 1 / 0 / 0O可选动作:['E3', 'C3', 'C5']Process finished with exit code 0
蒙特卡洛树搜索-黑白棋(一):黑白棋介绍及棋盘类相关推荐
- AlphaGo的制胜秘诀:蒙特卡洛树搜索初学者指南
编译 | reason_W 出品 | AI科技大本营(公众号ID:rgznai100) 长久以来,计算机在围棋领域不可能达到人类专家的水平一直是学术界的主流观点.围棋,被认为是人工智能的" ...
- 五子棋AI - 蒙特卡洛树搜索
动机 自高中时代做了一个带简单AI的五子棋游戏后,一直以来实现一个更加厉害的五子棋AI算是我的小目标.之前也尝试过使用 MinMax 算法,最终结果不甚理想.当然并不是算法问题,而是搭配这个算法需要许 ...
- 蒙特卡洛树搜索 棋_蒙特卡罗树搜索赢得黑白棋
蒙特卡洛树搜索 棋 With the COVID-19 pandemic still wreaking havoc around the world, many of us have been stu ...
- 那么蒙特卡洛树搜索(Monte Calro Tree Search, MCTS)究竟是啥
同时发布于:http://www.longgaming.com/archives/214 Intro 最近阿法狗和李师师的人机大战着实火了一把,还顺带捧红了柯杰,古力等一干九段.虽然我从小学的是象棋, ...
- 蒙特卡洛方法、蒙特卡洛树搜索(Monte Carlo Tree Search, MCTS) 学习
文章目录 1. 从多臂赌博机说起 2. UCB 3. 蒙特卡洛树搜索 4. 伪代码 提出一个问题: 假设你当前有n个币,面前有k个赌博机.每个赌博机投一个币后摇动会产生随机的产出,你会怎么摇? 1. ...
- 面向初学者的蒙特卡洛树搜索MCTS详解及其实现
目录 0. 序言 1. 蒙特卡洛算法的前身今世 2. 蒙特卡洛搜索算法的原理 2.1 Exploration and Exploitation(探索与利用) 2.2 Upper Confidence ...
- 围棋AI,蒙特卡洛树搜索
目录 1 蒙特卡罗方法(Monte Carlo method) 2. 蒙特卡洛树搜索(Monte Carlo Tree Search,MCTS) 3 Upper Confidence Bounds(U ...
- AlphaGo制胜绝招:蒙特卡洛树搜索入门指南
作者 | int8.io 编译 | 张健欣 编辑 | Emily Chen AI 前线导读:本文是一篇关于蒙特卡洛树搜索的入门指南,介绍什么是蒙特卡洛树搜索及其各个细节的基本概念,然后通过一个简单的例 ...
- AlphaGo背后的力量:蒙特卡洛树搜索入门指南
我们都知道 DeepMind 的围棋程序 AlphaGo,以及它超越人类的强大能力,也经常会听到「蒙特卡洛树搜索」这个概念.事实上,蒙特卡洛树搜索是在完美信息博弈场景中进行决策的一种通用技术,除游戏之 ...
最新文章
- Fedora 23 将默认使用 Wayland – 多监视器支持
- windows下codelite的使用
- HTML基础标签入门
- 0.项目运行环境和项目经理
- 归并排序的分析与Java实现
- Workbox CLI v3.x 中文版
- memcached搭建缓存系统
- 用Java搭建一套访问redis的API
- 从零开始通过 Artifactory 搭建公网的 maven 仓库
- 在SQL中用正则表达式替换html标签
- C语言调用多元函数,遗传算法C语言源代码(一元函数和二元函数)
- 加密的m3u8、ts文件合并
- youtube下载助手 firefox插件
- 2019网易校招笔试题 瞌睡
- 襄阳2021年高考成绩查询,2021湖北襄阳高考成绩公布时间
- idea重装后打不开,点起来没反应(难受)
- 皕杰报表web应用服务器的差异
- Mina中的Pasta(Pallas和Vesta)曲线
- 计算机格式化磁盘6,装机必学:硬盘分区、格式化通用方法大全
- 关于解压 tar.gz的问题