黑白棋 AI 算法

2024-02-19 05:59
文章标签 算法 ai 黑白棋

本文主要是介绍黑白棋 AI 算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        本篇文章是博主在人工智能等领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在Pytorch

       Pytorch(4)---黑白棋 AI 算法》

黑白棋 AI 算法

目录

1. 实验介绍

1.1 实验内容

1.2 实验要求

1.3 注意事项

2.实验内容

2.1 棋盘介绍

2.2 创建随机玩家

2.3 创建人类玩家

2.4 创建 Game 类

2.5 创建 AI 玩家

3.运行结果


1. 实验介绍

1.1 实验内容

黑白棋 (Reversi),也叫苹果棋,翻转棋,是一个经典的策略性游戏。

        一般棋子双面为黑白两色,故称“黑白棋”。因为行棋之时将对方棋子翻转,则变为己方棋子,故又称“翻转棋” (Reversi) 。
        棋子双面为红、绿色的称为“苹果棋”。它使用 8x8 的棋盘,由两人执黑子和白子轮流下棋,最后子多方为胜方。
        随着网络的普及,黑白棋作为一种最适合在网上玩的棋类游戏正在逐渐流行起来。
中国主要的黑白棋游戏站点有 Yahoo 游戏、中国游戏网、联众游戏等。

黑白棋示范视频icon-default.png?t=N7T8https://v.youku.com/v_show/id_XMjYyMzc1Mjcy.html?spm=a2h0k.11417342.soresults.dtitle 可以从4分钟开始观看 

游戏规则
棋局开始时黑棋位于 E4 和 D5 ,白棋位于 D4 和 E5,如图所示。

  1. 黑方先行,双方交替下棋。
  2. 一步合法的棋步包括:
    • 在一个空格处落下一个棋子,并且翻转对手一个或多个棋子;
    • 新落下的棋子必须落在可夹住对方棋子的位置上,对方被夹住的所有棋子都要翻转过来,
      可以是横着夹,竖着夹,或是斜着夹。夹住的位置上必须全部是对手的棋子,不能有空格;
    • 一步棋可以在数个(横向,纵向,对角线)方向上翻棋,任何被夹住的棋子都必须被翻转过来,棋手无权选择不去翻某个棋子。
  3. 如果一方没有合法棋步,也就是说不管他下到哪里,都不能至少翻转对手的一个棋子,那他这一轮只能弃权,而由他的对手继续落子直到他有合法棋步可下。
  4. 如果一方至少有一步合法棋步可下,他就必须落子,不得弃权。
  5. 棋局持续下去,直到棋盘填满或者双方都无合法棋步可下。
  6. 如果某一方落子时间超过 1 分钟 或者 连续落子 3 次不合法,则判该方失败。

1.2 实验要求

  • 使用 『蒙特卡洛树搜索算法』 实现 miniAlphaGo for Reversi。
  • 使用 Python 语言。

1.3 注意事项

  • 在与人类玩家对奕时,运行环境将等待用户输入座标,此时代码将处于 While..Loop 回圈中,请务必输入'Q'离开,否则将持续系统将等待(hold)。
  • 当长时间指示为运行中的时候,造成代码无法执行时,可以重新启动

2.实验内容

2.1 棋盘介绍

2.1.1 初始化棋盘

棋盘规格是 8x8,'X' 代表黑棋,'O' 代表白棋,'.' 代表未落子状态。

棋盘初始化 - 利用 Board 类(board.py)中的 display() 方法展示棋盘:

# 导入棋盘文件
from board import Board# 初始化棋盘
board = Board()# 打印初始化棋盘
board.display()

 2.1.2 棋盘与坐标之间的关系

棋盘坐标 E4, 转化为坐标形式就是 (3, 4), 坐标数值大小是从 0 开始,到 7 结束。

Board 类中,提供以上两种坐标的转化方法:

  • board_num(action): 棋盘坐标转化为数字坐标。
    • action: 棋盘坐标,e.g. 'G6'
    • 返回值: 数字坐标,e.g. (5, 6)
  • num_board(action): 数字坐标转化为棋盘坐标。
    • action: 数字坐标,e.g. (2, 7)
    • 返回值: 棋盘坐标,e.g. 'H3'
# 查看坐标 (4,3) 在棋盘上的位置 
position = (4, 3)
print(board.num_board(position))# 查看棋盘位置 'G2' 的坐标
position = 'G2'
print(board.board_num(position))

2.1.3 Board 类中比较重要的方法

  • get_legal_actions(color): 根据黑白棋的规则获取 color 方棋子的合法落子坐标,用 list() 方法可以获取所有的合法坐标。
    • color: 下棋方,'X' - 黑棋,'O' - 白棋
    • 返回值: 合法的落子坐标列表
# 棋盘初始化后,黑方可以落子的位置
print(list(board.get_legal_actions('X')))

  • _move(action, color): 根据 color 落子坐标 action 获取翻转棋子的坐标。
    • action: 落子的坐标,e.g. 'C4'
    • color: 下棋方,'X' - 黑棋,'O' - 白棋
    • 返回值: 反转棋子棋盘坐标列表
# 打印初始化后的棋盘
board.display()# 假设现在黑棋下棋,可以落子的位置有:['D3', 'C4', 'F5', 'E6'],
# 黑棋落子 D3 , 则白棋被翻转的棋子是 D4。 # 表示黑棋
color = 'X' # 落子坐标
action = 'D3' # 打印白方被翻转的棋子位置
print(board._move(action,color))# 打印棋盘
board.display() 

 2.2 创建随机玩家

# 导入随机包
import randomclass RandomPlayer:"""随机玩家, 随机返回一个合法落子位置"""def __init__(self, color):"""玩家初始化:param color: 下棋方,'X' - 黑棋,'O' - 白棋"""self.color = colordef random_choice(self, board):"""从合法落子位置中随机选一个落子位置:param board: 棋盘:return: 随机合法落子位置, e.g. 'A1' """# 用 list() 方法获取所有合法落子位置坐标列表action_list = list(board.get_legal_actions(self.color))# 如果 action_list 为空,则返回 None,否则从中选取一个随机元素,即合法的落子坐标if len(action_list) == 0:return Noneelse:return random.choice(action_list)def get_move(self, board):"""根据当前棋盘状态获取最佳落子位置:param board: 棋盘:return: action 最佳落子位置, e.g. 'A1'"""if self.color == 'X':player_name = '黑棋'else:player_name = '白棋'print("请等一会,对方 {}-{} 正在思考中...".format(player_name, self.color))action = self.random_choice(board)return action

随机玩家 RandomPlayer 主要是随机获取一个合法落子位置。后续随机玩家可以跟人类玩家、AI 玩家等进行对弈。

随机玩家 get_move() 方法, 主要思路:

  • 随机玩家的 get_move() 方法主要调用了 random_choice() 方法。
  • random_choice() 方法是:先用 list() 方法获取合法落子位置坐标列表,然后用 random.choice() 方法随机获取合法落子位置中的一个。

# 导入棋盘文件
from board import Board# 棋盘初始化
board = Board() # 打印初始化棋盘
board.display() # 玩家初始化,输入黑棋玩家
black_player = RandomPlayer("X") # 黑棋玩家的随机落子位置
black_action = black_player.get_move(board)  print("黑棋玩家落子位置: %s"%(black_action))# 打印白方被翻转的棋子位置
print("黑棋落子后反转白棋的棋子坐标:",board._move(black_action,black_player.color))# 打印黑棋随机落子后的棋盘
board.display() # 玩家初始化,输入白棋玩家
white_player = RandomPlayer("O") # 白棋玩家的随机落子位置
white_action = white_player.get_move(board) print("白棋玩家落子位置:%s"%(white_action))# 打印黑棋方被翻转的棋子位置
print("白棋落子后反转黑棋的棋子坐标:",board._move(white_action,white_player.color))# 打印白棋随机落子后的棋盘
board.display() 

2.3 创建人类玩家

人类玩家 HumanPlayer 主要实现 get_move() 方法。

class HumanPlayer:"""人类玩家"""def __init__(self, color):"""玩家初始化:param color: 下棋方,'X' - 黑棋,'O' - 白棋"""self.color = colordef get_move(self, board):"""根据当前棋盘输入人类合法落子位置:param board: 棋盘:return: 人类下棋落子位置"""# 如果 self.color 是黑棋 "X",则 player 是 "黑棋",否则是 "白棋"if self.color == "X":player = "黑棋"else:player = "白棋"# 人类玩家输入落子位置,如果输入 'Q', 则返回 'Q'并结束比赛。# 如果人类玩家输入棋盘位置,e.g. 'A1',# 首先判断输入是否正确,然后再判断是否符合黑白棋规则的落子位置while True:action = input("请'{}-{}'方输入一个合法的坐标(e.g. 'D3',若不想进行,请务必输入'Q'结束游戏。): ".format(player,self.color))# 如果人类玩家输入 Q 则表示想结束比赛if action == "Q" or action == 'q':return "Q"else:row, col = action[1].upper(), action[0].upper()# 检查人类输入是否正确if row in '12345678' and col in 'ABCDEFGH':# 检查人类输入是否为符合规则的可落子位置if action in board.get_legal_actions(self.color):return actionelse:print("你的输入不合法,请重新输入!")

人类玩家 get_move() 方法主要思路是:

  • 人类玩家输入落子位置,如果输入'Q', 则返回 'Q' 并结束比赛。
  • 如果人类玩家输入棋盘位置,e.g. 'A1',首先判断输入是否正确,然后再判断是否符合黑白棋规则的落子位置。
# 导入棋盘文件
from board import Board# 棋盘初始化
board = Board() # 打印初始化后棋盘
board.display() # 人类玩家黑棋初始化
black_player = HumanPlayer("X") # 人类玩家黑棋落子位置
action = black_player.get_move(board)# 如果人类玩家输入 'Q',则表示想结束比赛,
# 现在只展示人类玩家的输入结果。
if action == "Q":print("结束游戏:",action)
else:# 打印白方被翻转的棋子位置print("黑棋落子后反转白棋的棋子坐标:", board._move(action,black_player.color))# 打印人类玩家黑棋落子后的棋盘
board.display() 

2.4 创建 Game 类

该类主要实现黑白棋的对弈,已经实现随机玩家和人类玩家,现在可以来对弈一下。
Game 类(game.py)的主要方法和属性:

  • 属性:
    • self.board:棋盘
    • self.current_player:定义当前的下棋一方,考虑游戏还未开始我们定义为 None
    • self.black_player:定义黑棋玩家 black_player
    • self.white_player:定义白棋玩家 white_player
  • 方法:
    • switch_player():下棋时切换玩家
    • run():黑白棋游戏的主程序
!pip install func-timeout
# 导入黑白棋文件
from game import Game  # 人类玩家黑棋初始化
black_player = HumanPlayer("X")# 随机玩家白棋初始化
white_player = RandomPlayer("O")# 游戏初始化,第一个玩家是黑棋,第二个玩家是白棋
game = Game(black_player, white_player)# 开始下棋
game.run()

考虑到人类下棋比较慢,我们直接采用随机玩家与随机玩家下棋,效果如下:

# 导入黑白棋文件
from game import Game  # 随机玩家黑棋初始化
black_player = RandomPlayer("X")# 随机玩家白棋初始化
white_player = RandomPlayer("O")# 游戏初始化,第一个玩家是黑棋,第二个玩家是白棋
game = Game(black_player, white_player)# 开始下棋
game.run()

2.5 创建 AI 玩家

通过以上流程的介绍或者学习,相信大家一定很熟悉如何玩这个游戏。
现在 AI 玩家需要大家来完善!
该部分主要是需要大家使用 『蒙特卡洛树搜索算法』 来实现 miniAlphaGo for Reversi。

import math
import random
import sys
from copy import deepcopyclass Node:def __init__(self, now_board, parent=None, action=None, color=""):self.visits = 0  # 访问次数self.reward = 0.0  # 期望值self.now_board = now_board  # 棋盘状态self.children = []  # 孩子节点self.parent = parent  # 父节点self.action = action  # 对应动作self.color = color  # 该节点玩家颜色def get_ucb(self, ucb_param):if self.visits == 0:return sys.maxsize  # 未访问的节点ucb为无穷大# UCB公式explore = math.sqrt(2.0 * math.log(self.parent.visits) / float(self.visits))now_ucb = self.reward/self.visits + ucb_param * explorereturn now_ucb# 生个孩子def add_child(self, child_now_board, action, color):child_node = Node(child_now_board, parent=self, action=action, color=color)self.children.append(child_node)# 判断是否完全扩展def full_expanded(self):# 有孩子并且所有孩子都访问过了就是完全扩展if len(self.children) == 0:return Falsefor kid in self.children:if kid.visits == 0:return Falsereturn Trueclass AIPlayer:"""AI 玩家"""def __init__(self, color):"""玩家初始化:param color: 下棋方,'X' - 黑棋,'O' - 白棋"""self.max_times = 50  # 最大迭代次数self.ucb_param = 1  # ucb的参数Cself.color = colordef uct(self, max_times, root):"""根据当前棋盘状态获取最佳落子位置:param max_times: 最大搜索次数:param root: 根节点:return: action 最佳落子位置"""for i in range(max_times):  # 最多模拟max次selected_node = self.select(root)leaf_node = self.extend(selected_node)reward = self.stimulate(leaf_node)self.backup(leaf_node, reward)max_node = None     # 搜索完成,然后找出最适合的下一步max_ucb = -sys.maxsizefor child in root.children:child_ucb = child.get_ucb(self.ucb_param)if max_ucb < child_ucb:max_ucb = child_ucbmax_node = child  # max_node指向ucb最大的孩子return max_node.actiondef select(self, node):""":param node:某个节点:return: ucb值最大的叶子"""# print(len(node.children))if len(node.children) == 0:   # 叶子,需要扩展return nodeif node.full_expanded():    # 完全扩展,递归选择ucb最大的孩子max_node = Nonemax_ucb = -sys.maxsizefor child in node.children:child_ucb = child.get_ucb(self.ucb_param)if max_ucb < child_ucb:max_ucb = child_ucbmax_node = child    # max_node指向ucb最大的孩子return self.select(max_node)else:   # 没有完全扩展就选访问次数为0的孩子for kid in node.children:   # 从左开始遍历if kid.visits == 0:return kiddef extend(self, node):if node.visits == 0:    # 自身还没有被访问过,不扩展,直接模拟return nodeelse:   # 需要扩展,先确定颜色if node.color == 'X':new_color = 'O'else:new_color = 'X'for action in list(node.now_board.get_legal_actions(node.color)):  # 把所有可行节点加入孩子列表,并初始化new_board = deepcopy(node.now_board)new_board._move(action, node.color)# 新建节点node.add_child(new_board, action=action, color=new_color)if len(node.children) == 0:return nodereturn node.children[0]     # 返回新的孩子列表的第一个,以供下一步模拟def stimulate(self, node):""":param node:模拟起始点:return: 模拟结果rewardboard.get_winner()会返回胜负关系和获胜子数考虑胜负关系和获胜的子数,定义获胜积10分,每多赢一个棋子多1分"""board = deepcopy(node.now_board)color = node.colorcount = 0while (not self.game_over(board)) and count < 50:   # 游戏没有结束,就模拟下棋action_list = list(node.now_board.get_legal_actions(color))if not len(action_list) == 0:   # 可以下,就随机下棋action = random.choice(action_list)board._move(action, color)if color == 'X':color = 'O'else:color = 'X'else:   # 不能下,就交换选手if color == 'X':color = 'O'else:color = 'X'action_list = list(node.now_board.get_legal_actions(color))action = random.choice(action_list)board._move(action, color)if color == 'X':color = 'O'else:color = 'X'count = count + 1# winner:0-黑棋赢,1-白旗赢,2-表示平局# diff:赢家领先棋子数winner, diff = board.get_winner()if winner == 2:reward = 0elif winner == 0:    # 这里逻辑是反的,写出了bug...应该是其他地方逻辑也反了一次,负负得正了...实在不想找bug了对不住reward = 10 + diffelse:reward = -(10 + diff)if self.color == 'X':reward = - rewardreturn rewarddef backup(self, node, reward):"""反向传播函数"""while node is not None:node.visits += 1if node.color == self.color:node.reward += rewardelse:node.reward -= rewardnode = node.parentreturn 0def game_over(self, board):"""判断游戏是否结束:return: True/False 游戏结束/游戏没有结束"""# 根据当前棋盘,双方都无处可落子,则终止b_list = list(board.get_legal_actions('X'))w_list = list(board.get_legal_actions('O'))is_over = (len(b_list) == 0 and len(w_list) == 0)  # 返回值 True/Falsereturn is_overdef get_move(self, board):"""根据当前棋盘状态获取最佳落子位置:param board: 棋盘:return: action 最佳落子位置, e.g. 'A1'"""if self.color == 'X':player_name = '黑棋'else:player_name = '白棋'print("请等一会,对方 {}-{} 正在思考中...".format(player_name, self.color))root = Node(now_board=deepcopy(board), color=self.color)action = self.uct(self.max_times, root)return action

        如果是要提交mian.py文件用于运行和测试,直接将上面创建 AI 玩家这部分代码全部复制到mian.py中,运行测试就好

以上就是 AI 玩家的初步代码,其中特别注意:

  1. 请不要修改get_move方法的输入和输出
  2. 可以添加 AIPlayer 的属性和方法。
  3. 完善算法时请注意落子时间:落子需要在 60s 之内!
  4. 落子 3 次不在合法范围内即判断该方失败, 故落子前请检查棋子的合法性。

2.5.1 测试 AI 玩家

如果您已经实现 AIPlayer,你可以选人类玩家、随机玩家与 AIPlayer 算法对战,甚至 AIPlayer 与 AIPlayer 自己对战!

# 导入黑白棋文件
from game import Game  # 人类玩家黑棋初始化
black_player = HumanPlayer("X")# AI玩家白棋初始化
white_player =AIPlayer("O")# 游戏初始化,第一个玩家是黑棋,第二个玩家是白棋
game = Game(black_player, white_player)# 开始下棋
game.run()

3.运行结果


        参考资料来源:B站

        文章若有不当和不正确之处,还望理解与指出。由于部分文字、图片等来源于互联网,无法核实真实出处,如涉及相关争议,请联系博主删除。如有错误、疑问和侵权,欢迎评论留言联系作者,或者关注VX公众号:Rain21321,联系作者。

这篇关于黑白棋 AI 算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/723705

相关文章

Ubuntu系统怎么安装Warp? 新一代AI 终端神器安装使用方法

《Ubuntu系统怎么安装Warp?新一代AI终端神器安装使用方法》Warp是一款使用Rust开发的现代化AI终端工具,该怎么再Ubuntu系统中安装使用呢?下面我们就来看看详细教程... Warp Terminal 是一款使用 Rust 开发的现代化「AI 终端」工具。最初它只支持 MACOS,但在 20

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

AI绘图怎么变现?想做点副业的小白必看!

在科技飞速发展的今天,AI绘图作为一种新兴技术,不仅改变了艺术创作的方式,也为创作者提供了多种变现途径。本文将详细探讨几种常见的AI绘图变现方式,帮助创作者更好地利用这一技术实现经济收益。 更多实操教程和AI绘画工具,可以扫描下方,免费获取 定制服务:个性化的创意商机 个性化定制 AI绘图技术能够根据用户需求生成个性化的头像、壁纸、插画等作品。例如,姓氏头像在电商平台上非常受欢迎,

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G