手记

iOS-五子棋大战延伸之 AI 与联机

(更新:app 在应用商店里上架啦!!!iTunes 地址:https://itunes.apple.com/cn/app/id1264708584?mt=8

在大学里学习完 Artificial Intelligence 这门课程后,了解了极小化极大这种广泛应用在零和游戏 AI 中的博弈树搜索算法,我突发奇想,能不能趁着还没完全忘记这些知识,自己做点东西出来呢?由于自己 iOS 相对来说写的多一点,于是打算写一个 iOS版本的五子棋游戏。

在前期,我浏览了很多有关五子棋的博客和项目,我发现,这些五子棋项目质量参差不齐,界面设计好的 AI 能力不够,AI 比较强的功能又很少。就比如慕课网上面的 iOS-五子棋大战 这个系列课程,虽然老师讲的很清楚,但仍然有着很多不足,比如说,使用 UIView 画线,效率不高并且在运行时容易出现线的粗细不均匀,而且 AI 的设计还有着很大可以改进的地方。于是在这些项目的基础之上,我决定写一个 AI 能力强并且功能比较多的五子棋游戏。

在我的五子棋游戏里,主要分为了两个大块,AI 和联机,对于 AI,我分别实现了贪心算法和极小化极大算法,并进行了很多优化,最终 AI 的强度还算可以,程序能够战胜很多我曾经看过的游戏。对于联机这一块,只做了局域网对战,用的是 iOS 自带的 Bonjour 来进行广播和搜索,使用了 GCDAsyncSocket 这个第三方库来进行了消息的传输。再加上了一些音乐和音效,最终游戏的效果还是比较令人满意的。

由于笔者时间不够,不能对代码进行详细的讲解,只能等以后有时间再写一个系列博客了。大家如果有兴趣可以直接参考我的源码:https://github.com/Kesoyuh/Gomoku

如果发现程序写的不对,或者有什么其它问题的话,可以通过邮箱与我联系:zhangtian1104@gmail.com

下面是取自 Github 里的 README 文件。

一、游戏截图

二、功能介绍

1. 人机对战

在人机模式下,AI 共有三种难度可供选择。简单难度的 AI 由贪心算法实现,中等和困难难度的 AI 基于极小化极大算法实现。算法通过多种方式优化,在困难难度下,博弈树深度可达8层。

1.1 贪心算法

贪心算法的主要思路是,评价当前棋局中所有可以下的位置,依据一个评分表对该位置进行打分,然后随机选取一个分数最高的位置落子。对于五子棋来说,我们把五个连续的位置称为五元组,对于一个可以下的位置来说,我们对于所有包含它的五元组,根据五元组中黑棋和白棋的数量进行评分,这个位置的最终得分就是所有这些五元组的评分的和。下面是游戏中所用的针对黑棋的评分表:

// tuple is empty  
GGTupleTypeBlank = 7,  
// tuple contains a black chess  
GGTupleTypeB = 35,  
// tuple contains two black chesses  
GGTupleTypeBB = 800,  
// tuple contains three black chesses  
GGTupleTypeBBB = 15000,  
// tuple contains four black chesses  
GGTupleTypeBBBB = 800000,  
// tuple contains a white chess  
GGTupleTypeW = 15,  
// tuple contains two white chesses  
GGTupleTypeWW = 400,  
// tuple contains three white chesses  
GGTupleTypeWWW = 1800,  
// tuple contains four white chesses  
GGTupleTypeWWWW = 100000,  
// tuple contains at least one black and at least one white  
GGTupleTypePolluted = 0

1.2 极小化极大算法

对于五子棋这种零和游戏,极小化极大算法是最常用的算法,维基百科上已有详细介绍:极小化极大算法,在此不再详细解释。

游戏在博弈树搜索的基础之上,进行了许多优化,用于提升搜索树的深度:

Alpha-Beta 剪枝,维基百科上的详细介绍:Alpha-Beta 剪枝

启发式搜索函数:在博弈树中,对于每一层的节点来说,如果粗略的按照好坏对其进行一个排序,Alpha-Beta 算法就可以减去更多的节点,游戏中,我们用贪心算法对于每一个节点上一步所下的位置进行评分,根据这个评分对节点进行排序,从而进一步提升博弈树搜索的效率。

缩减子节点:游戏中所使用的贪心算法效果很好,基本可以保证最优解在评分前十的点中,所以我们可以缩减博弈树中的每个节点的子节点数量,只取评分前十的点,这大大的减少了节点的数量,并且将博弈树的搜索深度提升到了8层。

迭代加深:在游戏中有时候会发生如下情况:明明 AI 已经快要胜利,但是会选择一些其他的点并且多下几部才取胜,看起来像是在调戏玩家,实际上这是由于在博弈树中已经找到一个解之后便没有考虑其他层数较小的解。归根结底是因为极小化极大算法是深度优先搜索,不保证可以找到最优解。对此我们使用迭代加深博弈树搜索深度的方法,确保 AI 可以返回最优解。

2. 双人同屏

此模式支持两位玩家在同一手机上一同下棋。

3. 联机游戏

在局域网联机模式下,两台处于同一子网的手机可通过网络进行连接并一同下棋。游戏使用Bonjour维基页面)作为局域网内广播服务(棋局)和寻找棋局的解决方案。当找到棋局后,游戏使用GCDAsyncSocket来建立网络连接,进而进行网络通信。

游戏定义了网络间包的类型与内容,以确保通信的简介与准确。包的类型分为三中:下棋,悔棋和重赛。当游戏的任何一方希望进行悔棋或重赛时,需得到对方同意。因此网络包有以下定义:

// GGPacket.h

......

// 包类型
typedef NS_ENUM(NSInteger, GGPacketType) {
    // 未知类型
    GGPacketTypeUnknown,
    // 下棋
    GGPacketTypeMove,
    // 重赛
    GGPacketTypeReset,
    // 悔棋
    GGPacketTypeUndo
};

// 包具体动作
typedef NS_ENUM(NSInteger, GGPacketAction) {
    GGPacketActionUnknown,
    // 重赛请求/同意/拒绝
    GGPacketActionResetRequest,
    GGPacketActionResetAgree,
    GGPacketActionResetReject,
    // 悔棋请求/同意/拒绝
    GGPacketActionUndoRequest,
    GGPacketActionUndoAgree,
    GGPacketActionUndoReject
};

@interface GGPacket : NSObject

// data用来在下棋类型的包中存放棋的坐标
@property (strong, nonatomic) id data;
@property (assign, nonatomic) GGPacketType type;
@property (assign, nonatomic) GGPacketAction action;

......

@end

4. 其他功能

4.1 界面设计

由于没有美工,游戏没有华丽的特效,但整体界面依然不失简洁优雅大方,五子棋的棋盘使用 Core Graphics 画出,并使用 NSTimer 对游戏双方进行计时,落子指示图标也可以方便的提醒玩家最新落子。

4.2 游戏设置

在设置界面可以设置游戏的难度,游戏的音乐以及音效,玩家的偏好设置通过 NSUsersDefaults 进行存储。游戏的音乐使用 AVAudioPlayer 进行播放与控制。

三、总结

该五子棋游戏具有人机、人人、联机等多种功能,并且棋力不俗,在实际测试中可以轻松战胜大多数网络上的五子棋程序。

下面是我们的五子棋与 Google 搜索 Gomoku 排名第一的五子棋游戏进行对战的截图:

先手情况下,轻松取胜。

后手情况下,经过一番鏖战,取得胜利。

下面是与 Github 上面有名的五子棋程序 Gobang 进行对战的截图:

由于 GoBang 不能设置先后手,这是在我方后手的情况下,最终取得了胜利。

10人推荐
随时随地看视频
慕课网APP

热门评论

好文章!最近正好想学习一下啊AI方面的东西,Github里的代码很有帮助。如果上架了我一定下载!

好文章!最近正好想学习一下啊AI方面的东西,Github里的代码很有帮助。如果上架了我一定下载!

查看全部评论