跳到正文
zenn · 游戏开发社区· 来源· ランウィズ·· 2 天前AI 评分62

在发布前让计算机解完100个下落式益智关卡的故事

落ち物パズルの100面を、出す前に全部コンピューターに解かせた話

正文 · AI 翻译

初次见面。我是爱知县一家小公司ランウィズ的佐藤。
前几天,我们在 Android 和浏览器上发布了一款木块掉落消除游戏。本文将介绍那 100 个关卡和 50 道题“不靠人工制作,而是在发布前全部让计算机求解验证”的做法。

另外,本文的代码由 AI 编程工具(Claude Code)编写,我负责决定要做什么以及是否可以发布。文章草稿也由 AI 撰写,我阅读后进行了修改。

游戏规则(简述)

  • 棋盘横 18×纵 30。一开始就几乎填满,有一条细缝一直通到底部。
  • 放置落下的棋子,当凑齐的列消除后,上方的方块会落到最底部。
  • 如果落下后又有列凑齐,就会继续消除(连锁)。

这个“会落到多深”因棋盘而异,所以好棋盘和坏棋盘之间的差距会变得非常大。

关卡根据“种子”的数量来生成

不是为每一关手动画棋盘,而是根据关卡编号 n 和尝试编号 k 生成种子数,再用该种子生成棋盘。

function candidateSeed(n, k) {
  return (Math.imul(n * 7919 + k * 104729 + 1, 2654435761) ^ 0x5eed1e55) >>> 0 || 1;
}

只要种子相同,无论什么时候玩,都会从同一个棋盘开始。

电脑玩家

电脑玩家采用贪心式设计。它会尝试当前棋子的所有可放置位置,并按“连锁更多 → 消除的列更多 → 位置更低”的顺序,把棋子放在最优位置。

function bestPlacement(b, piece) {
  let best = null;
  for (const p of placements(b, piece)) {
    const { res } = tryPlacement(b, p);
    const v = res.chain * 1000 + res.rows * 10 + p.y;
    if (!best || v > best.v) best = Object.assign({ v, chain: res.chain, rows: res.rows, points: res.points }, p);
  }
  return best;
}

不做预先推演。我们的想法是:如果是“连比人类更弱的玩家也能通关的盘面”,那么人类无法通关的情况基本不会发生。

作为关卡保留的条件

对每个关卡 n,依次尝试 k = 0, 1, 2 … 作为种子,并把满足以下两个条件的第一个种子写入表中。

  1. 用初始棋子(直棒)能引发 4 连锁以上
  2. 在没有计时器的情况下,玩家能用固定数量的棋子通关(关卡越往后,可使用的棋子数量越多)。

第一条是为了“开始瞬间就能爽快地崩塌”,第二条是为了“不会卡死”。找到的种子表会写入游戏本体的文件,并在测试中重新解一遍所有关卡。

50 道题统计的是「全部摆放方式」

连锁拼图(棋子只有 1〜3 个)摆放方式很少,所以可以全部试一遍。在全部摆放方式中,统计能达到目标(几连锁,或几列)的比例,第 1 题能达到目标的摆放方式很多,第 50 题只有极少数才能达到,据此决定目标,并按从易到难的顺序排列。把其中一个答案留在表里,并在测试中实际输入该答案,确认能够解开。

做下来发现的事

  • 每次改变生成棋盘的规则时,都能立刻重新生成 100 个关卡并全部验证一遍,这是最大的收获
  • 在加入「第一手就达成 4 连锁以上」这个条件之前,会混入一些一开始什么都不发生的关卡
  • 因为玩家比较贪心,所以「人来做能解得更好」的关卡偏多

制作的游戏:Timber Drop(免费)

感谢您读到最后。

来源:zenn · 游戏开发社区 · zenn.dev