强化学习极简例子--井字棋

井字棋

3*3的棋盘上,白、黑子轮流下。如果某方棋子占满同一行、同一列、同一条斜线,则算赢。当棋盘占满还未分出胜负,则为和棋。

棋局状态编码

由于每个位置有三种可能状态:空(0)、白(1)、黑(2),可用3进制来表示棋局当前状态,其中0行0列表示最低位,0行1列表示次低位。
在这里插入图片描述

图1. 棋局例, 其状态为1236

图1所示棋局,其状态用3进制表示为 ( 001200210 ) 3 (001200210)_3 (001200210)3, 转化为十进制即
0 × 3 8 + 0 × 3 7 + 1 × 3 6 + 2 × 3 5 + 0 × 3 4 + 0 × 3 3 + 2 × 3 2 + 1 × 3 1 + 0 × 3 0 = 1236 0 \times 3^8 + 0 \times 3^7 + 1 \times 3^6 + 2 \times 3^5 + 0 \times 3^4 + 0 \times 3^3 + 2 \times 3^2 + 1 \times 3^1 + 0 \times 3^0 = 1236 0×38+0×37+1×36+2×35+0×34+0×33+2×32+1×31+0×30=1236.
易知所有棋局(状态)数为 3 9 = 19683 3^9 = 19683 39=19683,但根据井字棋规则, 并非所有状态(如全1)为可达的。没有关系,这个值不大,我们可以使用暴力。

建模

本问题涉及三类实体: 棋盘、双方队员、裁判。

  1. 棋盘负责记录棋局,并判断当前状态(这不是裁判干的事情吗?我不管,这是智能棋盘。)
  2. 双方队员负责落子。
  3. 裁判负责组织训练和比赛。

学习过程

队员学习的成果,就是获得一个长度为19683的价值(Value)向量。其中各分量记录了相应状态的价值,价值越高,对该队员越有利。例如, v [ 1236 ] v[1236] v[1236]就是图1所示棋局状态的价值。
每个队员有自己的v向量。
v向量的初始化如下:

  1. 如果棋局状态为自己赢,则值为1.0;
  2. 如果棋局状态为对方赢, 则值为0.0;
  3. 如果棋局状态为平局, 则值为0.5;
  4. 如果棋局尚未结束, 则值为0.5.

训练阶段有很多(如10000轮)。
每一轮开始,双方队员从空棋盘状态瞎走…直到棋盘发现已经到达“白胜、黑胜、平局”三种终局状态之一,并及时让他们stop。

图2从下到上描述了第0轮棋局的进展过程. 如左下方所示,棋局初始没有子,所有位置均为0. 这时的状态为 ( 000000000 ) 3 = 0 (000000000)_3 = 0 (000000000)3=0. 它的下一个状态有9个选项, 依次是 ( 000000001 ) 3 = 1 (000000001)_3 = 1 (000000001)3=1, ( 000000010 ) 3 = 3 (000000010)_3 = 3 (000000010)3=3, …, ( 100000000 ) 3 = 6561 (100000000)_3 = 6561 (100000000)3=6561.
由于 v [ 0 ] = v [ 1 ] = v [ 3 ] = ⋯ = v [ 6561 ] = 0.5 v[0] = v[1] = v[3] = \dots = v[6561] = 0.5 v[0]=v[1]=v[3]==v[6561]=0.5, 下在任何地方的奖励都相同,所以随机选一个.
图2所示, 白棋选择了位置6, 这时状态变为 ( 001000000 ) 3 = 729 (001000000)_3 = 729 (001000000)3=729.
接下来该黑棋. 它剩下8个选项, 对应的状态依次是 ( 001000002 ) 3 = 731 (001000002)_3 = 731 (001000002)3=731 ( 201000000 ) 3 (201000000)_3 (201000000)3, 这些状态对应的奖励值也全是0.5, 所以它随机选择了位置2, 新的状态为 ( 001000200 ) 3 = 747 (001000200)_3 = 747 (001000200)3=747.
这样一路到了状态 ( 011200210 ) 3 = 3423 (011200210)_3 = 3423 (011200210)3=3423, 黑棋发现自己的下一个可选状态包括了 ( 211200210 ) 3 = 16545 (211200210)_3 = 16545 (211200210)3=16545而且它的 v [ 16545 ] = 1.0 v[16545] = 1.0 v[16545]=1.0. 这真是个好消息!所以它果断地选择了位置8并把白棋打败.
需要注意:

  1. 某状态的下一个状态,通常不与它的编号相连. 如状态0的下一个状态可以是6561.
  2. 选手在选择下一个状态时,既可以选择奖励值最大的(如黑棋的最后一步选择), 也可以按照一定概率随机选择. 前面对应于开发exploit,后者对应于探索explore. 可以有一个系数 ϵ \epsilon ϵ控制它.

这时, 两位队员各自总结经验. 方法是从棋局结束状态往开始状态逆向分析, 并更新自己的v矩阵.
在这里插入图片描述

图2 第0轮经验总结

图2从上到下则描述了第0轮的经验总结过程。由于最终状态是Player 2获胜,他将自已一路经过的状态奖励值增加,当然,有个衰减,由系数 α \alpha α控制。
Player 1输了,痛定思痛,将一路经过的状态奖励值减少,也用衰减系数 α \alpha α控制。
更新式子可以写成:
v [ s t a t e ] = v [ s t a t e ] + α ( v [ n e x t S t a t e ] − v [ s t a t e ] ) v[state] = v[state] + \alpha(v[nextState] - v[state]) v[state]=v[state]+α(v[nextState]v[state])
这里 s t a t e = 0 state = 0 state=0时, n e x t S t a t e = 729 nextState = 729 nextState=729, 以此类推。
对于Player 1而言 v [ 3423 ] = v [ 3423 ] + α × ( v [ 16545 ] − v [ 3423 ] ) = 0.5 + 0.1 × ( 0 − 0.5 ) = 0.45 v[3423] = v[3423] + \alpha \times (v[16545] - v[3423]) = 0.5 + 0.1 \times (0 - 0.5) = 0.45 v[3423]=v[3423]+α×(v[16545]v[3423])=0.5+0.1×(00.5)=0.45.

在这里插入图片描述

图3. 第k轮经验总结

如图3所示, 经过很多轮训练后, v向量的值变得多种多样, 但根据前面的式子, 这些值均处于 [ 0 , 1 ] [0, 1] [0,1]区间.

比赛过程

Player 1从状态0出发,有9种选择, 根据自己的v向量,找到价值最大的那个选择(一般是4号位置,也就是最中间那个)。
Player 2从该状态出发,剩下8种选择,同理找到最佳选择。

  1. 比赛时就不去探索(胡下)了. 机器之间比赛的话, 由于每次都用它自己认为的最佳下一步,所以棋局一般是重复的.
  2. 机器之间的对抗,只有平局才是合理的。否则是训练过程出了问题,或者训练轮数还不够。
  3. 可以进行人机对抗,如果训练轮次足够,机器是不会输的。

代码

https://github.com/FanSmale/MFReinforcement
入口是 gui/ReinforcementGUI.java。运行效果如图4所示。需要先点击 “Train” 按钮进行训练,然后点击问号对应的位置。由于我是胡下的,所以输给了机器。读者要想赢下机器,不妨将Training rounds设置成50。
图里插入图片描述
图4. 运行界面

结尾彩蛋

  1. 训练的开始阶段,经常有价值相同的下一个状态,这时就随机选一个最佳的咯。
  2. 棋谱不需要保存,只需要黑白双方各自保存的价值向量即可。
  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
【资源说明】 课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip 课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip 课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip 课程设计基于max-min算法以及深度强化学习井字棋游戏python源码.zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!
以下是使用Alpha-Beta剪枝算法实现井字棋的示例代码: ```python # 定义玩家和电脑的标记 player = "X" computer = "O" # 初始化棋盘 board = [" " for i in range(9)] # 打印棋盘 def print_board(): row1 = "|".join(board[0:3]) row2 = "|".join(board[3:6]) row3 = "|".join(board[6:9]) print(row1) print("-" * 5) print(row2) print("-" * 5) print(row3) # 判断是否有空位 def has_space(): return " " in board # 判断是否有获胜者 def has_winner(): # 判断行 for i in range(0, 9, 3): if board[i] == board[i+1] == board[i+2] != " ": return True # 判断列 for i in range(3): if board[i] == board[i+3] == board[i+6] != " ": return True # 判断对角线 if board[0] == board[4] == board[8] != " " or board[2] == board[4] == board[6] != " ": return True return False # 评估函数 def evaluate(): if has_winner(): if player == "X": return 0 else: return 2 else: return 1 # 极小化极大算法 def minimax(alpha, beta, depth, is_maximizing): if has_winner(): return evaluate() elif not has_space(): return evaluate() elif depth == 0: return 1 if is_maximizing: best_score = -float("inf") for i in range(9): if board[i] == " ": board[i] = computer score = minimax(alpha, beta, depth-1, False) board[i] = " " best_score = max(score, best_score) alpha = max(alpha, score) if beta <= alpha: break return best_score else: best_score = float("inf") for i in range(9): if board[i] == " ": board[i] = player score = minimax(alpha, beta, depth-1, True) board[i] = " " best_score = min(score, best_score) beta = min(beta, score) if beta <= alpha: break return best_score # 电脑下棋 def computer_move(): best_score = -float("inf") best_move = None for i in range(9): if board[i] == " ": board[i] = computer score = minimax(-float("inf"), float("inf"), 5, False) board[i] = " " if score > best_score: best_score = score best_move = i board[best_move] = computer # 玩家下棋 def player_move(): valid_move = False while not valid_move: move = input("请输入你的下棋位置(1-9): ") try: move = int(move) - 1 if move >= 0 and move <= 8 and board[move] == " ": board[move] = player valid_move = True else: print("无效的位置,请重新输入!") except: print("无效的输入,请重新输入!") # 游戏循环 while True: print_board() player_move() if has_winner(): print_board() print("你赢了!") break elif not has_space(): print_board() print("平局!") break computer_move() if has_winner(): print_board() print("电脑赢了!") break elif not has_space(): print_board() print("平局!") break ``` 在这个示例中,我们使用了极小化极大算法和Alpha-Beta剪枝算法来实现井字棋游戏。玩家使用X标记,电脑使用O标记。玩家和电脑轮流下棋,直到有一方获胜或者平局。在每次电脑下棋时,我们使用Alpha-Beta剪枝算法来遍历博弈树,以提高搜索效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值