网页资讯视频图片知道文库贴吧地图采购
进入贴吧全吧搜索

 
 
 
日一二三四五六
       
       
       
       
       
       

签到排名:今日本吧第个签到,

本吧因你更精彩,明天继续来努力!

本吧签到人数:0

一键签到
成为超级会员,使用一键签到
一键签到
本月漏签0次!
0
成为超级会员,赠送8张补签卡
如何使用?
点击日历上漏签日期,即可进行补签。
连续签到:天  累计签到:天
0
超级会员单次开通12个月以上,赠送连续签到卡3张
使用连续签到卡
01月30日漏签0天
c4droid吧 关注:42,765贴子:262,837
  • 看贴

  • 图片

  • 吧主推荐

  • 游戏

  • 1 2 3 4 下一页 尾页
  • 64回复贴,共4页
  • ,跳到 页  
<<返回c4droid吧
>0< 加载中...

记录贴:从头开始写五子棋AI

  • 只看楼主
  • 收藏

  • 回复
  • 牲口才开电车
  • 编程狂魔
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼

之前写过一个五子棋AI,后来优化的时候遇到了困难,维护不下去了,就弃坑了。。。
今天我又打算从头开始重新写一个五子棋AI,每天完成一部分,同时以这个贴子纪录每天的成果。
为了使主题连贯,建议大家尽量在楼中楼回复。


  • DXKite
  • 大哲
    13
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
插。


2026-01-30 12:42:10
广告
不感兴趣
开通SVIP免广告
  • 万恶的验证码38
  • 高手寂寞
    11
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
顶


  • 大搜罗今年初
  • 大名鼎鼎
    10
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
插


  • 牲口才开电车
  • 编程狂魔
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
先介绍一下这个AI的大体框架:
1.局势评价函数
局势评价函数的任务就是对于给定的局面,计算出这个局面的分值。局面越有利,分值越高,局面越不利,分值越低。
显然,五子棋可能的局面数非常多,是不可能穷举的,要构造出一个好的局面评价函数并不容易。

我的方案如上图所示:先统计出棋盘上黑白方各棋型的数量,再根据统计结果给出评分。
2.搜索策略
有了局面评价函数,怎么得到最佳的落子点呢?
最简单的方法可以这样:对于当前棋盘上所有的空点,分别计算在各点处落子后的局面的评分值,找出落之后评分最高的那个点,就是AI下一步要落子的点。
上述方法是一种贪婪算法,它只考虑了一步以后的局势,十分短视。
(理论上,只要局面评价函数足够理想,贪婪法也可以是无懈可击的。而实际上,要构造一个理想的评价函数几乎不可能,贪婪法的效果并不好)
为了解决贪婪法短视的问题,应该往后考虑多步,使用minmax搜索算法。可是这又带来一个大问题:搜索的局面数关于搜索的步数指数增长。在minmax搜索的基础上使用alphabeta剪枝可以显著减少不必要的搜索数量。(具体有多显著?以我之前写的那个AI为例:不使用alphabeta剪枝时大概只能搜索6步,使用alphabeta剪枝后可以搜索12步)
搜索12步就够了吗?并不够。进一步压榨AI性能的方法有:迭代加深,hash表,等等
这里就不一一介绍了


  • 牲口才开电车
  • 编程狂魔
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
棋盘局面以什么样的结构来存储?
这里的棋盘大小为15*15,那么使用一个15*15的数组不就搞定了?

棋盘上每个点有三种可能:空,黑,白 , 分别用0,1,2表示, 棋盘上的点和数组元素一一对应。
是否有更好的表示方法?
很显然表示空,黑,白 3种状态中的一种,只需要2个bit就够了:
空:00
黑:01
白:10
棋盘上每一行有15个点,所以只需要 15 * 2 = 30 个bit就能表示了。
大部分机器上,int的长度为32bit,一个int就可以表示棋盘上的一行,15个int可以表示一个完整的棋盘:

这样表示不仅节省空间,更重要的是能够加速后面的一些函数,比如对棋型的统计,对棋盘局面求hash等。


  • 4O4Not
  • 未知领域
    14
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
顶


  • 雷神lyc
  • 高手寂寞
    11
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
我就要插


2026-01-30 12:36:10
广告
不感兴趣
开通SVIP免广告
  • 牲口才开电车
  • 编程狂魔
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
到现在为止,这个棋型统计器使用了 “查表大法” 和 “位运算大法” 加速,性能已经做到极致了
吗?
还是图样,速度还能再快10倍:
下五子棋的时候,每一步只落一个子,落子前和落子后的棋盘仅仅是落子点所在的行,列,斜列发生了变化,而其它行列完全没变。
只要记下棋盘每一行每一列的棋型,落子后只需要重新扫描落子点所在的行,列,斜列,而不用扫描整个棋盘,工作量一下子就降到了之前的1/15
于是棋盘的存储结构就变成了这样:

Ntype是棋型的种类数,我一共定义了16种不同的棋型,所以它等于16
现在,这个棋盘结构看起来有点臃肿了。
我试图把它压缩,写成这样:

能进一步压缩吗?
计算一下,每一行或列或斜列上最多可能有多少个棋型?由于一行的长度是15,扫描框的长度是6,所以扫描框在一行上可以扫10个不同的位置,最多产生10个棋型,表示0到10至少需要4个bit,一共16种棋型,需要64个bit。 所以,一个64位的int就能表示某一行或列或斜列上各棋型的数量了。


  • 1421126961
  • 大名鼎鼎
    10
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
厉害(ง •̀_•́)ง!!!!


  • 云Jenk
  • 大神你好
    5
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
如果你有那个Yixin源代码就好了
据说是非常吊的五子棋软件


  • 云Jenk
  • 大神你好
    5
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
我一般恒星 或者峡月开局


  • 牲口才开电车
  • 编程狂魔
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
今天发现 __int64 的移位操作在有的编译器下会出错,妥协于兼容性,还是采用折中的 unsigned char 数组来表示某一条线上的棋型。

在棋盘A上的(x, y)坐标落子的函数,n取值范围为0到2


  • 牲口才开电车
  • 编程狂魔
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
几张图理清结构体中 row[15], column[15], right[19], left[19], 与棋盘位图之间的关系:



给出棋子的坐标,通过变换即可求出该棋子在 row[15], column[15], right[19], left[19],
中的位置:
假设棋子坐标为 ( x, y )
则其在 row[15] 中的位置为 ( row[x] >> 2*y ) & 3
在 column[15] 中的位置为 ( column[y] >> 2*x ) & 3
令 int p = x + y - 5;
若 p>=0 && p<19
则其在 right[15] 中的位置为 ( right[p] << ( p<10 ? 2*y : 28-2*x ) ) & 3
令 int p = x - y + 9;
若 p>=0 && p<19
则其在 left[19] 中的位置为 ( left[p] << ( p<10 ? 2*x : 2*y ) ) & 3
之前的落子函数先将要落子的位置清空,再对其赋值,而大多数情况下要落子的点是空点。
为了提高运行效率,将之前的落子函数拆分成撤子和落子两个函数:



2026-01-30 12:30:10
广告
不感兴趣
开通SVIP免广告
  • 牲口才开电车
  • 编程狂魔
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
完整版的棋型 扫描与统计 函数。
对于棋盘A,扫描 坐标( x, y ) 所在行,列,斜列,并更新结构体A中的棋型数据

我已经尽量让它简短了,可是它的长度还是超过了屏幕的高度。有没有更简洁的写法?


登录百度账号

扫二维码下载贴吧客户端

下载贴吧APP
看高清直播、视频!
  • 贴吧页面意见反馈
  • 违规贴吧举报反馈通道
  • 贴吧违规信息处理公示
  • 1 2 3 4 下一页 尾页
  • 64回复贴,共4页
  • ,跳到 页  
<<返回c4droid吧
分享到:
©2026 Baidu贴吧协议|隐私政策|吧主制度|意见反馈|网络谣言警示