c++开发五子棋

二次信任 提交于 2019-11-30 01:49:09

  本来是打算编写这个游戏一边写博客的,结果太懒了,五子棋都写好了很多天了才来写这篇文章。那就写一写总结吧。

  师兄14号发了作业的题目,当时没怎么在意,后来17号出去玩的时候才突然意识到——哦,要我写一个五子棋,还得是人机对战。当时就感觉要凉凉了,还有十多天,但是连c++基本的类的知识都还没掌握。只好硬着头皮先去看算法。算法还好,看看网上的讲解和代码,再问问师兄,大致思路差不多就理下来了。那就先来说一说算法吧:

  博弈树 + 极大极小值搜索 + alpha-beta剪枝

  过程中看了很多的博客,这里总结一下讲的较为清楚的几篇:

       https://github.com/colingogogo/gobang_AI          这一篇的代码比较清楚,等读完文章慢慢搞懂算法后可以看一看这一篇的代码

      https://blog.csdn.net/lihongxun945/article/details/50625267    //讲极大极小值搜索

       https://blog.csdn.net/lihongxun945/article/details/50668253   //讲alpha-beta剪枝      这两篇讲的很详细

      https://blog.csdn.net/qq_42058018/article/details/81142881    //这一篇相对更好懂一些吧。

    说一说我的理解吧,因为发现第一次看的话确实要花一番功夫去想,那就把当时的困惑点记录下来。

   我们人类下棋的时候怎么判断下在哪里呢?就看看哪里对我们最有利,最有希望赢。计算机怎么看呢?就需要一个评估函数,来评估当前局势对我的得分,得分越高就越有利。那么选出得分最高的相应位置就行啦(其实评估函数有两种,一种是对整个棋局的评估,启发式搜索的时候还会有一个启发式评估函数是对某一个位置的评估。这两个评估是不同的。

  但是呢,所谓“知己知彼,百战不殆”。下棋如果只考虑简单的一步棋的话,那也太吃亏了,高手都要多考虑几步的。AI也是这样。

下棋的时候,不是看你这一步下的有多好,而是看你能撑多久,谁撑到对方输,谁就赢了,所谓谁笑到最后,谁笑得最好。所以,向前看步数越多的人,越能做出对未来越有利的决定,这就是博弈树,极大极小值搜索的中心思想。

   博弈树是用dfs来实现的,其实就是一种穷举。先穷举我能下的所有地方,对于我下的一个地方,穷举对方能下的所有地方,以此类推。。。。由此生成一个博弈树。

 

 

  实际上,对方也不傻,我们希望得分最高,对方肯定希望我们得分越低越好。所以每次轮到对方下棋,他都会选择得分最低的地方下棋(这样对他最有利)。可以看一看下面的博弈树手动模拟一下,就是dfs自上而下,又自下而上的过程。(先忽略那个红叉号,那个是剪枝用的)

 

 

  我们规定该树的深度,假如是三,即,向前考虑三步,看我三步之后的评估函数的得分。哪种情况的得分高,就下在哪里。

  剪枝是肯定要的。因为博弈树是成指数级上升的,不剪枝简直就玩不下去——复杂度太高了。(更何况师兄给的棋盘是32*32的棋盘。。)

   怎么剪枝呢?简单来说就是,如果对方发现,他下在1位置,我会得到一个分数a,但是他下在2位置,我可能会得到一个更高的分数b,那他还会再看他下在位置2我会不会得到比b更高的分数c吗?肯定不会了,因为我至少能得到分数b,而b是比a大的,他肯定不希望我得到b而不是a,他当然希望我得分越少越好。大致思路就是这样,具体可以看博客和代码。

    那么我们怎么知道下一步哪些位置可以下?难道要遍历整个棋盘吗?那样也太慢了吧。。我重写了一个可遍历的双端队列,每次下一个棋都把该位置周围的位置加进去,那么哪些位置就是最有可能下棋的位置了。今天看到其实海可以再优化,启发式搜索给节点排个序,这样就能较早剪枝,提高效率。

  主要算法就是这么多,具体编程时还遇到了一些小问题,又添加了一些存储的结构。这里就不详细说了。

   当时的状况是,算法是搞懂了,但是怎么代码实现呢?从POP一下子转变到OOP,对于像我这样的菜鸡真的有点难。。。于是跑去学习QT,但是QT的内容好多,那么多函数一时间也用不顺手。琢磨了五六天,搞不清楚怎么写那些类,怎么互相调用balabala。。。问来问去总算有了一点思路。

  最后一共写了四个类,界面类,游戏类,算法类,队列类。

  界面类完成交互和显示;  游戏类有存储棋盘,判断输赢,执行下棋等抽象操作;  算法类主要是AI的下棋算法;   队列类重写了队列,实现了双端队列+可遍历的特性。

  之所以没有写玩家类把人和AI 统一起来,是因为我的算法里这两个的共同点比较少,所以分开写了。

  代码量嘛。。比较小,只有九百多行(捂脸)

  由于缺乏较大规模代码调试的经验(而且还是第一次写面向对象),导致写的过程基本没怎么调试,写好后就出了一堆的bug,接下来的几天就是de呀de呀debug,本来以为要凉凉了,这下只能展示带bug的代码了,但是后来居然改好了,请一些朋友测试了一下,改了改参数,现在虽然不能每盘都赢,但还是有了一点水平。哈哈开心!

贴一张成果图吧

 

 

 

 

 

PS:后来又了解了一下机器学习的内容,看了看蒙特卡洛树搜索,发现这种五子棋算法还算是最简单的,所以还是要继续努力呀~

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!