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

来源: The latest on open source – The GitHub Blog
作者: Lee Reilly
原文链接:https://github.blog/ai-and-ml/github-copilot/dungeons-desktops-building-a-procedurally-generated-roguelike-with-github-copilot-cli/


我被技术挑战吸引,参与了 GitHub Copilot CLI Challenge,并做了一个可疑的决定:我把我的代码库变成了一个 roguelike 地牢游戏。

一切始于一个简单的提示词:Build a GitHub CLI extension in Go that takes the current repository and turns it into a playable roguelike dungeon, with dungeons generated with BSP [snip]。然后我加上了 /yolo

结果就是 GitHub Dungeons,一个终端游戏,能从你的代码库生成一个地牢。房间、走廊和敌人,全部由你的仓库构建,并直接渲染在终端中。你可以用方向键移动、与 bug 战斗、寻找出口。每个仓库都会生成不同的地图。每次提交都会重塑布局。如果你的 HP 归零,就得重新开始。

💡 趣闻:/yolo(“你只活一次”)是 Copilot CLI 的一个命令(/allow-all 的别名)。这很贴切,因为 roguelike 游戏的核心就是永久死亡。你真的只有一条命。

Roguelike 游戏可以追溯到 20 世纪 80 年代的《Rogue》等游戏——基于终端的冒险游戏,每次运行都会生成一个新的地牢,死亡意味着重新开始。

程序化生成、永久死亡和基于文本的界面(后来在“柏林诠释”等规则中被正式化)的结合,让这一类型游戏出奇地现代,并且非常适合命令行环境。

GitHub Dungeons 继承了这一传统。它用 Go 语言编写,虽然我平时不常用 Go,但借助 Copilot,我可以专注于行为逻辑而非语法细节。

什么是程序化生成?

程序化生成(或者用行话来说叫“procgen”)是一种通过算法而非手工设计来创建内容的方式。在游戏中,这通常意味着关卡、地图、敌人或物品会在运行时根据一组规则加上一些随机性生成。

因此,你不是设计一个地牢,而是设计一个能生成无数地牢的系统。

这正是 roguelike 游戏高重复可玩性的原因:

  • 每次运行都不同
  • 布局每次都会变化
  • 某些内容
  • 某些内容

在 GitHub Dungeons 中,这个系统与你的仓库绑定。布局由你最新的提交作为种子生成,因此相同的代码会产生相同的地牢,而每次代码变更都会重塑地牢。

那么,一个仓库究竟是如何变成地牢的呢?

从高层来看,GitHub Dungeon 的布局使用二叉空间分割(Binary Space Partitioning,BSP)算法生成,并以你仓库最新提交的 SHA 作为种子(稍后会详细介绍 BSP)。这意味着相同的代码库会产生一致的布局,同时随着代码变化而不断演变。

实际效果

  • 相同的提交总是生成相同的地图
  • 不同的仓库会产生结构上截然不同的布局
  • 随着代码变化,地牢也随之演变

这就是程序化生成——但直接与你的代码库挂钩。这就是核心理念。

有趣的部分实际上是构建过程。

使用 Copilot CLI 构建

使用 GitHub Copilot CLI 意味着描述行为,而不是从头编写所有代码。其中一个非常有用的命令是 /delegate。它不会仅仅内联生成代码,而是将任务交给运行在云端的 GitHub Copilot 编码代理。

我可以用简单的英语描述我想要的功能,启动它,然后去做其他事情,让它独立工作。完成后,它会打开一个包含结果的拉取请求。

例如,/delegate 让每一关逐渐变难,比如在第二关增加更多敌人,但也要有更多生命药水

Copilot 异步生成了一个不错的初版,我随后审查并调整了拉取请求,直到平衡性感觉合适。我对其他功能也采用了同样的方法,比如添加让玩家无敌的作弊码(为什么不呢?)。

我甚至让 Copilot 生成了一个“地牢书记员”代理,这是一个小助手,负责添加文档和 ASCII 艺术图,以解释地牢是如何生成的。对于一个终端 roguelike 游戏来说,这非常符合其风格。

我甚至让 Copilot 生成了一个地牢书记员代理,用于创建文档用 ASCII 艺术图解释地牢生成过程,这非常符合终端 roguelike 游戏的调性。

使用 Copilot(尤其是 /delegate)就像拥有一支 NPC 大军,可以随时为我做任何我想做的事。

Lee Reilly,地牢主宰

以这种方式工作(描述功能,委托给 Copilot,然后审查生成的拉取请求)意味着我可以花更少的时间处理边界情况和样板代码,而将更多时间投入到玩家体验上,包括添加供玩家发现的彩蛋。与 Copilot 迭代协作让我能够保持游戏设计的思维模式,而不是不断切换到实现细节。因为 Copilot 处理了大部分构建和脚手架工作,我可以专注于设计机制、测试想法,并找出真正让游戏变得有趣的东西。

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

每个地牢设计的核心是一种称为二叉空间分割Binary Space Partitioning, BSP 的技术——如果你想给朋友和同事留下深刻印象,不妨在聊到中出压缩时随口提一句。听起来很唬人,但思路却出奇简单:不断将空间分割成更小的区块,直到得到一堆可以连接的房间。

为什么 BSP 在 Roguelike 游戏中如此有效

Roguelike 游戏需要的地图应当具备以下特点:

  • 有结构(不是完全随机的胡扯)
  • 可重玩(每次游玩都不同)
  • 可通行(没有死胡同或不可能的布局)

BSP 恰好找到了平衡点。它能提供:

  • 整洁的矩形房间
  • 保证连通性
  • 恰到好处的随机感,显得自然

下面是它的工作原理……

1. 从一个巨大的空白空间开始

一切始于一个大的矩形:你的整个地牢。

2. 分割(递归)

我们将空间分割成两个区域。

然后再次拆分它们。

再来一次。

每次分割可以是水平(horizontal)或垂直(vertical)。

3. 当区域过小时停止

我们持续分割,直到区域小到无法容纳一个房间。

这样就产生了一堆“叶子(leaf)”区域,即最终的构建块。

4. 将每个 region(区域)变成一个 room(房间)

每个 leaf(叶子)变成一个房间,但并非完全对齐。我们稍微随机化大小和位置。

这种轻微的随机性正是避免一切显得过于网格化的关键。

5. 用走廊连接房间

现在,我们通过回溯树并连接兄弟节点来连接房间。

每个连接都是L形:

6. 最终结果:有序的混乱

将所有部分组合在一起,你会得到类似这样的结果:

  • 房间感觉有意图
  • 走廊让一切可达
  • 每次运行都不同(但可通过种子复现)

为何如此受欢迎

我喜欢 BSP(二叉空间分割)的地方在于,它看起来像是精心设计的,尽管实际上并非如此。

它避免了程序化生成的两大问题:纯随机性(混乱)和刚性网格(可预测、无聊)。相反,你得到的是介于两者之间的东西……有时还很漂亮。

如何安装并运行

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

gh extension install leereilly/gh-dungeons 

之后,运行 gh dungeons 将你的仓库转化为一个自定义地牢,等待征服。使用 WASD、方向键或 Vim 键控制你的英雄。

你的目标是在五个关卡中找到隐藏的门,并逃离(以及攻击!)敌人。我加入了一些有趣的功能,比如限制视野的战争迷雾(fog of war)、自动攻击、追踪击杀数和已征服关卡等统计数据的能力,以及更多你需要自行探索的内容。

危险区域!

如果你觉得自己无所畏惧,可以在疯狂模式下游玩:你可以设置一个预提交钩子(pre-commit hook),除非你通关整个游戏,否则它会删除你保存的更改。

⚠️ 警告:除非你完全理解这将对你的仓库造成什么影响,否则不要这样做——你会丢失已保存的工作,可能还会失去一些理智。

# Create the pre-commit hook 
cat > .git/hooks/pre-commit << 'EOF' 
#!/bin/bash 
gh dungeons 
if [ $? -ne 0 ]; then 
    echo "You died! Your changes have been stashed into oblivion..." 
    git stash && git stash drop stash@{0} 
    exit 1 
fi 
EOF 
 
# Make it executable 
chmod +x .git/hooks/pre-commit 
 
# To be clear, you’ll lose all your uncommited changes if you enable this 
# and fail to beat the dungeon, adventurer. 

(编者注:请千万、千万、千万不要这样做。我们对任何工作丢失概不负责。但 Lee 绝对要负责。)

带着这个离开

这最初只是一个随手做的实验,但它改变了我对 GitHub Copilot CLI 的看法。

我能够快速将其做成 MVP(最小可行产品),迭代那些重要的部分,并让 Copilot 处理繁重的工作,比如 BSP(二叉空间分割)生成,甚至包括在一个有点邪门的 Yendor YAML 文件中定义的怪物和移动逻辑。

高级项目经理,GitHub 开发者关系(Developer Relations)。开源(Open source)宣传者,AI 低语者,黑客松(hackathon)和游戏开发大赛(game jam)组织者。我编写 && 管理项目,支持开发者社区,偶尔发布一些东西。