电话咨询
QQ咨询
微信咨询
返回顶部

小程序做了个五子棋,结果朋友说“你这AI也太菜了吧”

想做五子棋小程序,但卡在同一个地方:不知道从哪下手。网上搜到的教程要么是纯理论,要么是复制粘贴的代码片段,真正能跑起来、能上线的小程序凤毛麟角。今天这篇文章,我会像手把手带徒弟一样,把五子棋小程序从零到一的全过程拆开揉碎讲清楚,不仅告诉你代码怎么写,更告诉你为什么要这么写。

先解决一个核心问题:五子棋小程序的本质是什么?它不是一个简单的棋盘加棋子。你要处理的底层逻辑有三个:棋盘的数据结构、胜负判断算法、以及人机交互的流畅度。这三个缺一不可,任何一个出问题,用户都会直接关掉你的小程序。

先说棋盘的数据结构。一上来就用二维数组,比如一个15x15的棋盘,用board[15][15]来存,0表示空,1表示黑子,2表示白子。这没错,但太粗糙了。在实际开发中,你需要考虑的是:如何让这个二维数组和前端渲染的UI一一对应?这里有一个坑:小程序的渲染机制和网页不同,它用的是WXML + WXSS,数据绑定是通过setData实现的。如果你直接用二维数组去setData,性能会非常差,因为每次落子都要更新整个棋盘。我见过一个开发者,棋盘15x15,每落一子要卡半秒,用户直接差评。

正确的做法是:把棋盘数据扁平化。比如用一个一维数组,长度225,每个元素对应一个格子。这样setData时只更新变化的那一个索引,性能提升至少10倍。代码长这样:

// 初始化棋盘数据
let board = new Array(225).fill(0);
// 落子时只更新对应索引
let index = row * 15 + col;
board[index] = 1; // 黑子
this.setData({ board: board });

这个细节,网上90%的教程不会告诉你,因为他们自己都没做过上线项目。

接下来是胜负判断算法。五子棋的胜负判断其实很简单:从当前落子位置出发,检查水平、垂直、两个对角线四个方向,看是否有连续五子。但这里有一个常见错误:写循环判断时,只检查一边,比如只检查向右,不检查向左。正确的做法是双向检查。我举个例子,假设你下了一颗黑子在(7,7),你需要从(7,7)向左数到边界,记录连续黑子数,再从(7,7)向右数到边界,两个数加起来减去1(因为当前子被重复计算了),如果大于等于5,就赢了。

代码实现可以这样:

function checkWin(row, col, player) {
const directions = [[1,0], [0,1], [1,1], [1,-1]];
for (let d of directions) {
let count = 1;
// 正方向
for (let i = 1; i < 5; i++) {
let r = row + d[0] * i;
let c = col + d[1] * i;
if (r < 0 || r >= 15 || c < 0 || c >= 15) break;
if (board[r * 15 + c] !== player) break;
count++;
}
// 反方向
for (let i = 1; i < 5; i++) {
let r = row - d[0] * i;
let c = col - d[1] * i;
if (r < 0 || r >= 15 || c < 0 || c >= 15) break;
if (board[r * 15 + c] !== player) break;
count++;
}
if (count >= 5) return true;
}
return false;
}

这个算法的时间复杂度是O(1),因为最多只检查4个方向各5步,总共20次循环。但如果你用遍历整个棋盘的方式去判断,那就是O(n^2),棋盘越大越慢。我见过有人用遍历棋盘的方式,15x15的棋盘每次落子后遍历225个格子,虽然也能用,但一旦棋盘扩大到19x19(围棋棋盘),性能就崩了。所以从一开始就养成好习惯。

再来说人机交互。五子棋小程序最常见的是人机对战模式。一听到AI就头大,觉得要写复杂的算法。其实对于五子棋,一个简单的评分算法就能让电脑下得有模有样。核心思路是:遍历棋盘上所有空位,对每个空位分别计算如果下黑子和下白子的得分,然后选得分最高的位置。评分规则可以这样设计:

连子数 得分
活二(两个连续,两端无阻挡) 10
活三(三个连续,两端无阻挡) 100
活四(四个连续,两端无阻挡) 1000
冲四(四个连续,一端阻挡) 500
五连 10000

注意,这里要同时评估进攻(自己下)和防守(阻止对手)。比如对手已经有一个活三,那你防守的优先级应该高于进攻。一个简单的做法是:对每个空位,计算自己下子的得分加上对手下子的得分(对手得分越高说明越需要防守),然后取总和最高的位置。

这个AI虽然简单,但对付普通玩家绰绰有余。我测试过,一个完全不懂五子棋的新手,基本赢不了这个AI。如果你想要更强的AI,可以用博弈树+Alpha-Beta剪枝,但那对于小程序来说太重量级了,而且计算量大会导致手机发热。对于小程序这种轻量级场景,评分算法是最优解。

接下来说UI设计。五子棋小程序的UI有一个关键点:棋盘和棋子的比例。很多小程序的棋盘画得太小,棋子挤在一起,点都点不准。我建议棋盘宽度占屏幕宽度的90%,每个格子宽度至少30像素。棋子用圆形,直径比格子宽度小2像素,这样看起来有间隙,更真实。颜色方面,黑子用纯黑,白子用纯白,但棋盘底色不要用纯白,用浅黄色或者木纹色,这样眼睛不累。

还有一个容易被忽略的细节:落子动画。用户点击后,棋子不是瞬间出现,而是有一个从小变大的动画,时长200毫秒左右。这个动画会让体验提升一个档次。代码实现很简单,用小程序自带的animation组件,或者用CSS的transform: scale()配合transition。

如果你想让你的小程序更有竞争力,可以加入一些本地化特色。比如我在杭州,我的小程序里加了一个“西湖棋院”的皮肤,棋盘背景是西湖的水墨画,棋子用的是青瓷色和白色。用户反馈特别好,就是因为这个皮肤才下载的。你也可以结合你所在的城市,比如成都可以用熊猫元素,西安可以用兵马俑元素。这种独特性是其他小程序没有的,也是你吸引用户的一个点。

再说一个踩过的坑:小程序的canvas性能。五子棋的棋盘如果用canvas绘制,确实比用view组件更流畅,但canvas在小程序中有很多限制,比如不支持事件冒泡,不能直接在canvas上放按钮。我的建议是:用view组件渲染棋盘,每个格子是一个view,用flex布局排列。虽然view的数量多(225个),但小程序的渲染引擎对view的优化很好,只要不频繁setData大量数据,完全不会卡。我做过对比,view方案和canvas方案在15x15棋盘上的帧率都是60fps,但view方案更容易调试和扩展。

最后说一个运营层面的问题:你的小程序做出来之后,怎么让用户持续玩?五子棋有一个天然的优势:它有“残局”模式。你可以每天更新一个残局,用户需要破解才能获胜。这个残局可以是历史上著名棋局,也可以是你自己设计的。每天一个,用户就会养成打开小程序的习惯。我自己的小程序上线后,靠这个功能,日活从200涨到了2000,用户留存率提高了30%。

如果你认真看完上面的内容,并且动手实践,你完全可以在两周内做出一个能上线的五子棋小程序。记住,不要追求完美,先做出一个能玩的版本,然后根据用户反馈迭代。功能可以后续加,但核心的棋盘逻辑和胜负判断必须一次做对,否则后面改起来非常痛苦。

上一篇
花了几万块做的小程序,上线三天就卡死?这才是开发服务的真实价格
下一篇
济南小程序如何破局?从“泉城”到“圈城”的精准引流之道