1 Alpha-Beta剪枝树的简单介绍

Alpha-Beta剪枝的本质就是基于极小化极大算法的一种改进算法。因此先简单地介绍下极小化极大算法,这样有利于我们更好的理解Alpha-Beta剪枝算法。

1.1 极小化极大算法

在人机博弈中,双方回合制地进行走棋,己方考虑自己在所有可行的走法中作出某一特定选择后,对方可能会采取的走法,从而选择最有利于自己的走法。这种对弈过程就构成了一颗博弈树,双方在博弈树中不断搜索,选择对自己最为有利的子节点走棋。在搜索的过程中,将取极大值的一方称为Max,取极小值的一方称为Min。Max总是会选择价值最大的子节点走棋,而Min则相反。这就是极小化极大算法的核心思想。举一个简单的例子来理解一下。

图 1  极小化极大算法

如图1所示,Max层的节点需要在下一层中选择与之相连的所有节点中的最大值作为该节点的值,而Min层的节点则正好相反,其选择的是最小值。因此B、C节点的值分别为5、3,从而能够推断出A节点的值为5。

1.2 Alpha-Beta剪枝算法

极小化极大算法虽然能够给出一个对弈的最佳决策,但其缺点也十分明显——数据冗余,而这种冗余有两种情况:①极大值冗余;②极小值冗余,并且这种数据冗余会随着博弈树规模的增大而增大。Alpha-Beta剪枝算法则优化并减小了原本博弈树的规模,在一定程度上解决了数据冗余的问题。接下来讲一下Alpha-Beta剪枝算法的思路。

在Alpha-Bate剪枝算法中,每一个节点都会有Alpha和Beta两个值来确定一个范围[Alpha,Beta],其中 Alpha 值代表下界,Beta 值代表上界。每搜索一个子节点后,都会根据规则来修改范围。Max层的节点可以修改Alpha值,Min层的节点可以修改Beta值。当出现 Beta <= Alpha 的情况时,则会进行剪枝操作,不再搜索更多的子树。我们再以上面所举的例子进行分析。

图 2  Alpha-Beta剪枝算法

如图2所示,博弈树遍历的顺序为从上到下,从左到右。初始时,A节点的 Alpha 和 Beta 值为负无穷和正无穷,遍历至B节点时,A节点将Alpha和Beta的值传递给B节点,B节点遍历D节点后,获得值 5,此时修改B节点的Beta值为5,B节点的范围为[-∞,5],满足继续遍历的条件,接着再遍历E节点,获取值12,由于B节点处于Min层,12 > 5,因此Beta值不变。B节点遍历完成后返回Beta值 5 给 A 节点,A节点处于Max层,则修改Alpha值为5。之后A节点遍历C节点,并将Alpha值5和Beta值 ∞ 传递给C节点。C节点遍历 F 节点后获取返回值 3,并修改Beta值,此时C节点的范围为[5,3],出现了Beta <= Alpha 的情况,因此C节点将不再进行后续的搜索,即G节点分支被剪枝。最后C节点返回Beta值3给A节点,A节点处于Max层,3 < 5,Alpha值不改变,最终A节点的值就为 Alpha 值 5。

我们不难发现使用Alpha-Beta剪枝算法获得的结果和用极小化极大算法所得出来的结果是一样的,并且还减少了对节点G的遍历。其中算法主要的部分有以下几点:①Alpha值代表Max层节点的返回值,Beta值代表Min层节点的返回值;②Alpha、Beta值传递时,向下传递两个值,向上则根据所处层的属性返回其中的某一个值;③当某一节点出现 Beta <= Alpha 情况时进行裁剪操作。

2 井字棋人机博弈实现

2.1 实现原理

将玩家获胜的结果设置为0,平局为1,电脑获胜为2,再根据输入的棋盘情况,采用Alpha-Beta剪枝算法来遍历博弈树。举个例子:

图 3  井字棋博弈

图3为根据棋盘A信息生成的博弈树,再采用Alpha-Beta剪枝算法进行遍历,最终可以得知A的结果值为1,路线为A—D—J—N,因此下一步棋的位置为D所示位置。

当电脑先手时,会随机选择棋盘四个角中的某一个位置。具体原因参考下面的链接文章。

井字棋的最优策略竟是先占角!| 果壳 科技有意思https://www.guokr.com/article/4754/

2.2 具体实现代码

代码主要有界面类、棋盘类、算法类和游戏类,运行时只要在main函数中调用游戏类的start方法即可。具体代码可从下方GitHub链接下载。GitHub - Hunter957/TicTacToe: 基于Alpha-Beta剪枝算法的井字棋人机博弈实现https://github.com/Hunter957/TicTacToe.git

2.2.1 界面类

public class View {public View(){System.out.println("欢迎游玩井字棋游戏");System.out.println("您的棋子符号为“X”,电脑棋子符号为“O”");}//显示棋盘信息,玩家棋子为‘X’,电脑棋子为‘O’public void showChessBoard(int[] chessBoard){char[] chess=new char[9];for (int i=0;i<9;i++){if (chessBoard[i]==-1){chess[i]='X';}else if (chessBoard[i]==0){chess[i]='-';}else {chess[i]='O';}}System.out.println("----------------------------");System.out.println("|"+chess[0]+"|"+chess[1]+"|"+chess[2]+"|");System.out.println("|"+chess[3]+"|"+chess[4]+"|"+chess[5]+"|");System.out.println("|"+chess[6]+"|"+chess[7]+"|"+chess[8]+"|");System.out.println("----------------------------");}//显示用户输入提示信息public void showUserToolTips(){System.out.println("----------------------------");System.out.println("1 2 3");System.out.println("4 5 6");System.out.println("7 8 9");System.out.print("到您的回合了,请输入棋子位置:");}//显示是否先手提示信息public void showFirstToolTips(){System.out.println("是否先手? 先手请输入数字1,否则请输入数字2!");}
}

2.2.2 棋盘类

public class ChessBoard {//默认值为0,玩家棋子为-1,电脑棋子为1public int[] chess;public ChessBoard(){clearChessBoard();}//重置棋盘public void clearChessBoard(){chess=new int[9];}
}

2.2.3 算法类

public class Algorithm {//α-β剪枝算法节点,返回的数组包含值与位置信息public static int[] abPruneNode(int[] chess,int a,int b,boolean isMAX){//统计空余的棋盘位置ArrayList<Integer> pos=new ArrayList<>();for (int i=0;i< chess.length;i++){if (chess[i]==0){pos.add(i);}}//遍历每个空位的情况,当b<=a时,结束尝试int[] result=new int[2];for (Integer po : pos) {//尝试下一步棋if (isMAX) {chess[po] = 1;} else {chess[po] = -1;}//获取尝试下一步棋之后的结果int value = result(chess);//对局未结束,递归遍历博弈树if (value == 2) {value = abPruneNode(chess, a, b, !isMAX)[1];}//根据获取的返回值修改a或b的值if (isMAX) {if (a < value) {a = value;result[0] = po;}} else {if (b > value) {b = value;result[0] = po;}}//回退一步尝试的下一步棋chess[po] = 0;//Beta<=Alpha,结束剩余博弈树的遍历(剪枝)if (b <= a) {break;}}//设置当前节点的返回值if (isMAX){result[1]=a;}else {result[1]=b;}return result;}//判断是否分出胜负//玩家胜出返回-1,平局返回0,电脑胜出返回1,游戏还未结束返回2public static int result(int[] chess){//玩家或者电脑胜出if (chess[0]==chess[1] && chess[1]==chess[2] && chess[0]!=0){return chess[0];}else if (chess[3]==chess[4] && chess[4]==chess[5] && chess[3]!=0){return chess[3];}else if (chess[6]==chess[7] && chess[7]==chess[8] && chess[6]!=0){return chess[6];}else if (chess[0]==chess[3] && chess[3]==chess[6] && chess[0]!=0){return chess[0];}else if (chess[1]==chess[4] && chess[4]==chess[7] && chess[1]!=0){return chess[1];}else if (chess[2]==chess[5] && chess[5]==chess[8] && chess[2]!=0){return chess[2];}else if (chess[0]==chess[4] && chess[4]==chess[8] && chess[0]!=0){return chess[0];}else if (chess[2]==chess[4] && chess[4]==chess[6] && chess[2]!=0){return chess[2];}//对局没有结束for(int i:chess){if (i==0){return 2;}}//游戏平局return 0;}
}

2.2.4 游戏类

public class Game {private View view;private ChessBoard chessBoard;private Scanner scanner;public Game(){this.view=new View();this.chessBoard=new ChessBoard();this.scanner=new Scanner(System.in);}//开始游戏public void start(){boolean gameContinue=true;while (gameContinue){view.showFirstToolTips();int whoFirst=scanner.nextInt();if (whoFirst==1||whoFirst==2){startGame(whoFirst);}else {System.out.println("输入数据错误,请重新输入!");continue;}System.out.println("是否继续游戏? 继续请输入1,退出游戏请输入2!");int next=scanner.nextInt();while (!(next==1 || next==2)){System.out.println("输入数据错误,请重新输入!");next=scanner.nextInt();}if (next==1){chessBoard.clearChessBoard();}else{gameContinue=false;System.out.println("感谢您的游玩,再见!");}}}//开始一局游戏public void startGame(int first){if (first==2){int[] pos={0,2,6,8};Random random=new Random();chessBoard.chess[pos[random.nextInt(3)+1]]=1;System.out.println("电脑回合:");view.showChessBoard(chessBoard.chess);}boolean over=false;while (!over){if (userRound()){break;}over=robotRound();}}//玩家回合public boolean userRound(){view.showUserToolTips();int pos=scanner.nextInt();//判断玩家输入是否合法while (chessBoard.chess[pos-1]!=0){System.out.println("此位置已有棋子,请重新选择!");pos=scanner.nextInt();}chessBoard.chess[pos-1]=-1;view.showChessBoard(chessBoard.chess);return result();}//电脑回合public boolean robotRound(){chessBoard.chess[Algorithm.abPruneNode(chessBoard.chess,-1000,1000,true)[0]]=1;System.out.println("电脑回合:");view.showChessBoard(chessBoard.chess);return result();}//判断是否结束public boolean result(){int result=Algorithm.result(chessBoard.chess);switch (result){case 1:System.out.println("电脑赢得了本次对局");break;case 0:System.out.println("平局,再来一局吧");break;case -1:System.out.println("恭喜您赢得了本次对局");break;}return result != 2;}
}

基于Alpha-Beta剪枝树的井字棋人机博弈实现相关推荐

  1. C++实现的基于α-β剪枝算法的井字棋游戏

    "井字棋"游戏(又叫"三子棋"),是一款十分经典的益智小游戏,操作简单,娱乐性强.两个玩家,一个打圈(O),一个打叉(X),轮流在3乘3的格上打自己的符号,最先 ...

  2. [文档和源码分享]C++实现的基于α-β剪枝算法的井字棋游戏

    "井字棋"游戏(又叫"三子棋"),是一款十分经典的益智小游戏,操作简单,娱乐性强.两个玩家,一个打圈(O),一个打叉(X),轮流在3乘3的格上打自己的符号,最先 ...

  3. 基于vue框架使用canvas实现井字棋小游戏

    引言:最近实现了一个图片上传压缩的功能,使用了canvas,所以学习一下canvas的语法,实现一个井字棋小游戏,这个小游戏可以人人对弈,如果大家有兴趣,可以对代码进行迭代,比如采用极大值极小值搜索法 ...

  4. java 井字棋 人机_一个井字棋tictactoe游戏的java实现 | Soo Smart!

    这是一个井字棋游戏的java实现.摘录于stackoverflow. 游戏规则很简单,只要一方棋子在水平线,垂直线或者对角线任意一条线上排列成功即为获胜. 作者原先的代码存在着一些问题: 代码如下: ...

  5. 使用Java实现alpha-beta剪枝算法(井字棋小游戏)

    1.初始化游戏界面: /*** 初始化游戏界面:*/public void StartGream() {for (int i = 1; i < 4; i++)for (int j = 1; j ...

  6. java 井字棋 人机_井字游戏 人机对战 java实现

    package com.ecnu.Main; /** * 主函数触发游戏 */ public class MainApplication { public static void main(Strin ...

  7. java 井字棋 人机_井字棋(人机对战版)

    1 #include 2 #include 3 4 const int ROW = 3;5 const int COL = 3;6 intchessboard[ROW][COL];7 intscore ...

  8. 组合游戏系列5: 井字棋、五子棋AlphaGo Zero 算法实战

    来源 | MyEncyclopedia 上一篇我们从原理层面解析了AlphaGo Zero如何改进MCTS算法,通过不断自我对弈,最终实现从零棋力开始训练直至能够打败任何高手.在本篇中,我们在已有的N ...

  9. 采用α-β算法实现井字棋游戏

    题目描述 (1)图形化界面. (2)随机选取先手后手. (3)可以人-计算机或计算机-计算机 界面效果 算法 基本思想 Max-Min算法: 采用Max-Min算法进行对抗搜索,Max和Min双方均要 ...

最新文章

  1. SAP MM 物料主数据利润中心字段之修改
  2. Memcache 安装和常见命令
  3. 持续畅销20年的《C#高级编程》出第11版了!
  4. python实现路由功能_python 实现重启路由器
  5. 苹果 SwiftUI 踢馆谷歌 Flutter!
  6. 高数(数一)知识点自我归纳(思维导图)
  7. 小米便签源码分析——gtask包
  8. 9个动作让网站3天被百度收录!
  9. Node的文件操作、文件系统、数据流
  10. c语言小学期大作业学生管理系统,小学期完成
  11. Android系统编译aosp
  12. 锂电池】关于4.2V锂电池充电IC的一些记录
  13. 学完大数据基础,可以按照我写的顺序学下去
  14. STM32+ESP8266使用MQTTAT固件连接云
  15. 解决Dependency ‘xxxx:xxxx-xxxxx-java:x.x.18‘ not found无法导入依赖问题
  16. proftpd的SSH_DISCONNECT (Read TImed out,Key exchange failed.错误解决
  17. CSS学习笔记之学成在线项目(下) 3.3
  18. 在Jetson Nano上学习ROS的记录(版本Ubuntu18.04,课程来源赵虚左老师的《ROS理论与实践》)第十二章 机器人导航(仿真)
  19. 【MSP430】基于MSP430G2553的超声波测距仪
  20. %p 打印地址?它打印的是变量值!什么是格式化输出,指针与地址的区别

热门文章

  1. 大兴区优秀青年人才培养措施等奖励及申报条件,补贴30-80万
  2. 如何正确选择合适的电商系统软件
  3. [百度官方]惊雷算法3.0即将上线 持续打击刷点击
  4. 学生如何使用正版Altium Designer软件;正版AD安装;AD如何使用正版license(适用于老师、学生、校友等等)
  5. android list字体大小,android自定义ListPreference字体大小
  6. (原)游戏用户注册之随机昵称系统
  7. 联发科MT6779(Helio P90)基带处理器介绍
  8. JUnit4单元测试
  9. 《跟二师兄学Nacos吧》EXT-01篇 看看Nacos是怎么活学活用简单工厂模式的!
  10. 简单 黑苹果dsdt教程_史上最全教程,让你的黑苹果不是梦(DSDT. SSDT 篇)