一声棒喝,本不立文字
偏要著録,已是二义

GitHub · AI/ML 项目

地牢与桌面:用 GitHub Copilot CLI 构建程序化生成 Roguelike

Dungeons & Desktops: Building a procedurally generated roguelike with GitHub Copilot CLI

二〇二六年五月十二日 · 英文原文

Lee Reilly 使用 GitHub Copilot CLI 构建了 GitHub Dungeons,一个将代码仓库转化为可玩 roguelike 地牢的终端游戏。该游戏用 Go 编写,通过二叉空间分割(BSP)算法以仓库最新提交的 SHA 为种子程序化生成关卡,包含房间、走廊和敌人。玩家可用方向键移动并与 bug 战斗。Copilot 的 /delegate 命令被用于异步生成功能代码并创建 pull request。游戏支持安装命令 `gh extension install leereilly/gh-dungeons` 和运行 `gh dungeons`。

我被技术挑战吸引,参与了 GitHub Copilot CLI 挑战,并做了一个值得商榷的决定:把我的代码库变成了一个 roguelike 地牢。这一切始于一个简单的 prompt:用 Go 构建一个 GitHub CLI 扩展,将当前仓库变成一个可玩的 roguelike 地牢,地牢使用 BSP 生成 [snip]。然后输入 /yolo。结果就是 GitHub Dungeons,一个终端游戏,能从你的代码库生成地牢。房间、走廊和敌人,全部由你的仓库构建,并直接渲染在终端中。你可以用方向键移动,与 bug 战斗,寻找出口。每个仓库都会生成不同的地图,每次提交都会重塑布局。如果你的 HP 归零,就得重新开始。💡 有趣的事实:/yolo("you only live once")是 Copilot CLI 的一个命令(/allow-all 的别名)。这很贴切,因为 roguelike 游戏的核心就是永久死亡。你真的只有一条命。

Roguelike 游戏可以追溯到 1980 年代的 Rogue 等游戏——基于终端的冒险,每次运行都会生成一个新地牢,死亡意味着重新开始。这种程序化生成、永久死亡和基于文本的界面(后来被"柏林解释"等形式化)的结合,让这一类型显得出奇地现代,并且非常适合命令行。GitHub Dungeons 继承了这一传统。它用 Go 编写,虽然我平时不常用 Go,但使用 Copilot 意味着我可以专注于行为而非语法。

什么是程序化生成?程序化生成(或者像酷孩子们说的"procgen")是一种通过算法创建内容而非手动设计的方式。在游戏中,这通常意味着关卡、地图、敌人或物品在运行时根据一组规则加上一些随机性生成。因此,你不是设计一个地牢,而是设计一个能生成多个地牢的系统。这就是 roguelike 游戏可重玩性的来源:每次运行都不同,布局每次都会变化,等等。在 GitHub Dungeons 中,这个系统与你的仓库绑定。布局由你的最新提交作为种子,因此相同的代码产生相同的地牢,而每次更改都会重塑它。

那么,一个仓库实际上是如何变成地牢的呢?在高层面上,GitHub Dungeon 的布局使用二叉空间分割(BSP)生成,以仓库最新提交的 SHA 作为种子(稍后会详细介绍 BSP)。这意味着相同的代码库会产生一致的布局,同时随着代码变化而演变。实际上:相同的提交总是生成相同的地图;不同的仓库会产生结构上感觉不同的布局;随着代码变化,地牢也随之演变。这就是程序化生成——但直接与你的代码库绑定。这就是核心理念。有趣的部分实际上是构建它。

使用 Copilot CLI 构建

使用 GitHub Copilot CLI 意味着描述行为,而不是从头编写所有内容。一个产生巨大差异的命令是 /delegate。它不是内联生成代码,而是将任务交给运行在云端的 GitHub Copilot 编码 agent。我可以用纯英语描述我想要什么,启动它,然后去做其他事情,让它独立工作。完成后,它会打开一个包含结果的 pull request。例如:/delegate 让每个关卡逐渐变难,例如在第二关增加更多敌人,但也要有更多生命药水。Copilot 异步生成了一个扎实的初版,我从中审查并调整 PR,直到平衡感觉合适。我对其他功能也采取了相同的方法,比如添加让玩家无敌的作弊码(为什么不呢)。我甚至让 Copilot 生成了一个"地牢书记员"agent,一个小助手,用于添加文档和 ASCII 艺术图来解释地牢是如何生成的,这非常符合终端 roguelike 的风格。使用 Copilot(尤其是 /delegate)就像拥有一支 NPC 大军,可以让他们做任何我想做的事。——Lee Reilly,地牢主

以这种方式工作(描述功能,委托给 Copilot,审查生成的 pull request)意味着我可以花更少时间在边界情况和样板代码上,更多时间在玩家体验上,包括添加供玩家发现的彩蛋。通过 Copilot 迭代让我保持在游戏设计的思维模式中,而不是不断切换到实现细节。因为 Copilot 处理了大部分构建和脚手架工作,我可以专注于设计机制、测试想法,以及弄清楚什么真正让游戏变得有趣。

程序化生成的关卡(使用 BSP)

每个地牢设计的核心是一种称为二叉空间分割(BSP)的技术,如果你想给朋友和同事留下深刻印象,这是一个可以随意提及的好东西,就像提到 middle-out 压缩一样。它听起来令人生畏,但想法出奇地简单:不断将空间分割成更小的块,直到你得到一堆可以连接的房间。为什么 BSP 对 roguelike 如此有效?Roguelike 需要感觉:有结构(不是完全随机的废话)、可重玩(每次运行都不同)、可导航(没有死胡同或不可能的布局)。BSP 找到了一个最佳点。它给你:干净、矩形的房间;保证的连通性;恰到好处的随机性,感觉自然。

以下是它的工作原理……

  1. 从一个大的空空间开始:一切都从一个大的矩形开始:你的整个地牢。
  2. 递归地分割它:我们将空间分成两个区域。然后再分割这些区域。再分割。每次分割可以是水平或垂直的。
  3. 当它变得太小时停止:我们持续分割,直到区域太小而无法容纳一个房间。这会产生一堆"叶子"区域,即最终的构建块。
  4. 将每个区域变成一个房间:每个叶子变成一个房间,但并非完美对齐。我们稍微随机化大小和位置。这种轻微的随机性防止了所有东西感觉太像网格。
  5. 用走廊连接房间:现在,我们通过回溯树并连接兄弟节点来连接房间。每个连接都是 L 形。
  6. 最终结果:结构化的混乱:把所有东西放在一起,你会得到类似这样的东西:房间感觉有意图,走廊使一切可达,每次运行都不同(但可以用种子重现)。

为什么这效果这么好

我喜欢 BSP 的一点是,它感觉像是设计过的,尽管实际上并非如此。它避免了程序化生成的两个大问题:纯粹的随机性(混乱)和僵硬的网格(可预测、无聊)。相反,你得到的是介于两者之间的东西……有时还很美。

如何安装和游玩

如果你想看看自己的代码库作为地牢是什么样子,并且已经安装了 GitHub Copilot CLI,可以运行:

gh extension install leereilly/gh-dungeons

之后,运行 gh dungeons 将你的仓库转换成一个自定义地牢,准备征服。用 WASD、方向键或 Vim 键控制你的英雄。你的目标是在五个关卡中找到隐藏的门并逃离(以及攻击!)敌人。我添加了有趣的功能,比如限制视野的战争迷雾、自动攻击、追踪击杀数和征服关卡数等统计信息,以及更多你需要自己发现的内容。

危险区域! 如果你感觉鲁莽,可以在疯狂模式下玩:你可以设置一个 pre-commit hook,除非你通关整个游戏,否则它会删除你保存的更改。⚠️ 警告: 除非你完全理解这会对你的仓库造成什么影响,否则不要这样做。你会丢失保存的工作,可能还会失去一些理智。

# 创建 pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
gh dungeons --crazy-mode
EOF
chmod +x .git/hooks/pre-commit

(编者注:请,请,请不要这样做。我们对任何丢失的工作概不负责。但 Lee 肯定负责。)

带着这个离开

这最初只是一个随意的实验,但它改变了我对 GitHub Copilot CLI 的看法。我能够快速实现 MVP,迭代重要的部分,让 Copilot 处理繁重的工作,比如 BSP 生成,甚至是在一个有点邪门的 Yendor YAML 文件中定义的怪物和移动。

《地牢与桌面:用 GitHub Copilot CLI 构建一个程序化生成的 roguelike》一文最初出现在 GitHub 博客上。

译自 GitHub · AI/ML 项目 · 录于 二〇二六年五月十二日