我被技术好奇心带偏了,一头扎进了 GitHub Copilot CLI 挑战赛,并做了一个可疑的决定:我把自己的代码库变成了一款类 Roguelike 地牢游戏。
这一切始于一个简单的提示词:用 Go 语言构建一个 GitHub CLI 扩展,将当前仓库变成一个可玩的类 Roguelike 地牢,地牢使用 BSP 算法生成。然后执行 `/yolo`。
最终成果是 GitHub Dungeons,一款终端游戏,能从你的代码库生成一个地牢。房间、走廊和敌人,全部由你的仓库构建,并直接渲染在你的终端里。你可以用方向键导航,与 Bug 战斗,并寻找出口。每个仓库都会生成不同的地图。每次提交都会重塑地牢布局。如果你的生命值归零,就得重新开始。
💡 有趣的事实:`/yolo`(“你只活一次”)是 Copilot CLI 的一个命令(`/allow-all` 的别名)。这很贴切,因为类 Roguelike 游戏的核心就是永久死亡。你真的只有一条命。
类 Roguelike 游戏可以追溯到 20 世纪 80 年代的《Rogue》等游戏——这些是基于终端的冒险游戏,每次运行都会生成一个新的地牢,而死亡意味着重新开始。
程序化生成、永久死亡和基于文本的界面(后来在“柏林诠释”等规则中被正式化)的结合,让这个游戏类型出奇地具有现代感,并且与命令行完美契合。
GitHub Dungeons 继承了这一传统。它用 Go 语言编写,这并非我常用的语言,但与 Copilot 合作意味着我可以专注于行为逻辑,而不是语法细节。
什么是程序化生成?
程序化生成(或者像圈内人说的“procgen”)是一种通过算法而非手工设计来创建内容的方法。在游戏中,这通常意味着关卡、地图、敌人或物品是在运行时根据一组规则加上一些随机性生成的。
所以,你不是在设计一个地牢,而是在设计一个能生成无数地牢的系统。
这正是类 Roguelike 游戏具有高重复可玩性的原因:
- 每次游戏都不同
- 布局每次都会变化
- 某些内容
- 某些内容
在 GitHub Dungeons 中,这个系统与你的仓库绑定。地牢布局由你最新的提交作为种子,因此相同的代码会产生相同的地牢,而每一次更改都会重塑它。
那么,一个仓库究竟是如何变成地牢的呢?
从高层来看,GitHub Dungeon 的地图布局是使用二叉空间分割(BSP)算法生成的,其种子值取自你仓库最新提交的 SHA(稍后会详细介绍 BSP)。这意味着,相同的代码库会产生一致的布局,同时布局也会随着代码的变化而演变。
实际效果如下:
- 相同的提交始终生成相同的地图
- 不同的仓库会产生结构上截然不同的布局
- 随着代码的变化,地牢也随之演变
这是程序化生成——但直接与你的代码库绑定。这就是核心理念。
有趣的部分实际上是把它构建出来。
使用 Copilot CLI 构建
与 GitHub Copilot CLI 协作意味着描述行为,而不是从头编写所有代码。其中一个带来巨大差异的命令是 `/delegate`。`/delegate` 不是仅仅内联生成代码,而是将任务交给运行在云端的 GitHub Copilot 编码智能体。
我可以用简单的英语描述我想要什么,启动它,然后在它独立工作时去做别的事情。当它完成后,它会打开一个包含结果的拉取请求。
例如,`/delegate 让每一关逐渐变难,比如在第二关增加更多敌人,但也要有更多生命药水`
Copilot 异步生成了一个扎实的初版,我随后审查并调整了那个拉取请求,直到游戏平衡性感觉合适为止。我对其他功能也采用了同样的方法,比如添加让玩家无敌的作弊码(为什么不呢)。
我甚至让 Copilot 生成了一个“地牢书记员”智能体,这是一个小助手,用于添加文档和 ASCII 艺术图来解释地牢是如何生成的,这感觉非常符合终端 Roguelike 游戏的风格。
我甚至让 Copilot 生成了一个地牢书记员智能体来创建文档,并用 ASCII 艺术图解释地牢是如何生成的,这感觉非常符合终端 Roguelike 游戏的风格。
使用 Copilot(尤其是配合 `/delegate`)就像拥有一支非玩家角色大军,随时可以去做任何我想让他们做的事情。
Lee Reilly, Dungeon Master
以这种方式工作(描述功能、将其委托给 Copilot,并审查由此产生的拉取请求),意味着我可以花更少的时间处理边界情况和样板代码,而将更多时间投入到玩家体验上,包括添加供玩家发现的彩蛋。借助 Copilot 进行迭代,让我能够保持游戏设计的思维模式,而不是不断切换到实现细节上。由于 Copilot 处理了大部分构建和框架搭建工作,我可以专注于设计机制、测试想法,以及弄清楚究竟是什么让游戏变得有趣。
程序化生成的地牢关卡(使用 BSP 算法)
每个地牢设计的核心是一种称为二叉空间分割(BSP)的技术。如果你想给朋友和同事留下深刻印象,这玩意儿和“中出压缩”一样,都是可以随口提一嘴的好东西。它听起来很吓人,但思路却出奇地简单:不断将空间分割成更小的区块,直到你得到一堆可以连接起来的房间。
为什么 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 键控制你的英雄。
你的目标是在五层地牢中找到隐藏的门,逃离并攻击敌人!我加入了一些有趣的功能,比如限制视野的战争迷雾、自动攻击、追踪击杀数和已征服层数等统计数据,以及更多等你自行发现的特性。
危险区域!
如果你觉得自己够莽,可以试试疯狂模式:设置一个预提交钩子,除非你通关整个游戏,否则它会删除你所有已保存的更改。
⚠️ 警告:除非你完全清楚这会对你的仓库造成什么影响,否则不要这样做——你会丢失已保存的工作,可能还会失去一些理智。
# 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 文件中定义的怪物和移动逻辑。
I got nerd-sniped into the GitHub Copilot CLI Challenge and made a questionable decision: I turned my codebase into a roguelike dungeon.
It started with a simple prompt: 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]. And then /yolo .
The result is GitHub Dungeons, a terminal game that generates a dungeon from your codebase. Rooms, corridors, and enemies, all built from your repo and rendered right in your terminal. You navigate with arrow keys, fight bugs, and hunt for the exit. Every repository produces a different map. Every commit reshapes the layout. And if your HP hits zero, you start over.
💡 Fun fact: /yolo (“you only live once”) is a Copilot CLI command (an alias for /allow-all). Fitting, since roguelikes are built around permadeath. You really do only get one life.
Roguelikes trace back to games like Rogue in the 1980s – terminal-based adventures where each run generated a new dungeon, and death meant starting over.
That combination of procedural generation, permadeath, and text-based interfaces (later formalized in things like the “Berlin Interpretation”) makes the genre feel surprisingly modern, and a perfect fit for the command line.
GitHub Dungeons leans into that tradition. It’s written in Go, which I don’t normally use, but working with Copilot meant I could focus on behavior instead of syntax.
What is procedural generation?
Procedural generation (or “procgen” as the cool kids call it) is a way of creating content algorithmically instead of designing it by hand. In games, that usually means levels, maps, enemies, or items are generated at runtime using a set of rules plus a bit of randomness.
So instead of designing one dungeon, you design a system that generates many.
That’s what gives roguelikes their replayability:
- Every run is different
- Layouts change every time
- Something
- Something
In GitHub Dungeons, that system is tied to your repo. The layout is seeded by your latest commit, so the same code produces the same dungeon, and every change reshapes it.
So how does a repository actually become a dungeon?
At a high level, GitHub Dungeon layouts are generated using Binary Space Partitioning (BSP), seeded by your repository’s latest commit SHA (more on BSP in a bit). That means the same codebase produces a consistent layout, while still evolving as the code changes.
In practice:
- The same commit always generates the same map
- Different repositories produce layouts that feel structurally distinct
- As the code changes, the dungeon evolves with it
It’s procedural generation—but tied directly to your codebase. That’s the idea.
The interesting part was actually building it.
Building it with Copilot CLI
Working with GitHub Copilot CLI meant describing behavior instead of writing everything from scratch. One command that made a big difference was /delegate. Instead of just generating code inline, /delegate hands the task off to GitHub’s Copilot coding agent running in the cloud.
I could describe what I wanted in plain English, kick it off, and then go do something else while it worked independently. When it finished, it opened a pull request with the results.
For example,/delegate Make each level progressively harder e.g. on level 2 there are extra baddies, but more health potions
Copilot generated a solid first pass asynchronously, and I reviewed and tweaked the PR from there until the balance felt right. I took the same approach to other features like adding cheat codes that make the player invincible (because why not).
I even had Copilot generate a “dungeon scribe” agent, a small helper that added documentation and ASCII art diagrams to explain how dungeons were generated, which felt very on-brand for a terminal roguelike.
I even had Copilot generate a dungeon scribe agent to create documentation and explain how dungeons were generated with ASCII art diagrams, which felt very on-brand for a terminal roguelike.
Using Copilot (especially with
Lee Reilly, Dungeon Master/delegate) is like having an army of NPCs available to do whatever I want them to do.
Working this way (describing features, delegating them to Copilot, and reviewing the resulting pull requests) meant I could spend less time on edge cases and boilerplate, and more time on the player experience, including adding easter eggs for players to discover. Iterating with Copilot let me stay in a game design mindset instead of constantly switching into implementation details. Because Copilot was handling most of the build and scaffolding, I could stay in the flow of designing mechanics, testing ideas, and figuring out what actually made the game fun.
Procedurally generated levels (with BSP)
At the heart of each dungeon design is a technique called Binary Space Partitioning (BSP), which is a great thing to casually mention alongside middle-out compression if you want to impress your friends and colleagues. It sounds intimidating, but the idea is surprisingly simple: keep splitting a space into smaller chunks until you have a bunch of rooms you can connect.
Why BSP works so well for roguelikes
Roguelikes need maps that feel:
- Structured (not completely random nonsense)
- Replayable (different every run)
- Navigable (no dead ends or impossible layouts)
BSP hits a sweet spot. It gives you:
- Clean, rectangular rooms
- Guaranteed connectivity
- Just enough randomness to feel organic
Here’s how it works…
1. Start with a big empty space
Everything begins as one big rectangle: your entire dungeon.
2. Split it (recursively)
We split the space into two regions.
Then split those again.
And again.
Each split can be horizontal or vertical.
3. Stop when it gets too small
We keep splitting until regions are too small to fit a room.
That creates a bunch of “leaf” regions, the final building blocks.
4. Turn each region into a room
Each leaf becomes a room, but not perfectly aligned. We randomize size and position slightly.
That slight randomness is what stops everything feeling too grid-like.
5. Connect rooms with corridors
Now we connect rooms by walking back up the tree and linking siblings.
Each connection is an L-shape:
6. End result: structured chaos
Put it all together and you get something like this:
- Rooms feel intentional
- Corridors make everything reachable
- Every run is different (but reproducible with a seed)
Why this lands so well
What I like about BSP is that it feels designed, even though it isn’t.
It avoids the two big problems of procedural generation: pure randomness (messy), and rigid grids (predictable, boring). Instead, you get something in between… and sometimes beautiful.
How to install and play it
If you want to see what your own codebase looks like as a dungeon, and you already have GitHub Copilot CLI installed, you can run:
gh extension install leereilly/gh-dungeons After that, run gh dungeons to transform your repository into a custom dungeon, ready to be conquered. Control your hero with WASD, arrow keys, or Vim keys.
Your goal is to find the hidden door and escape from (and attack!) enemies over five levels. I’ve added fun features like fog of war that limit visibility, auto-attack, ability to track stats like kills and conquered levels, and more that you’ll have to discover yourself.
Danger zone!
If you’re feeling reckless, play it on crazy mode: you can set up a pre-commit hook that deletes your saved changes unless you beat the entire game.
⚠️ WARNING: Do not do this unless you fully understand what this will do to your repository, you’ll lose saved work and probably some sanity as well.
# 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. (Editor’s note: Please, please, please do not do this. We are not responsible for any lost work. But Lee definitely is.)
Take this with you
This started as a throwaway experiment, but it changed how I think about GitHub Copilot CLI.
I was able to MVP it quickly, iterate on the parts that mattered, and let Copilot handle the heavy lifting, things like BSP generation and even monsters and movement defined in a slightly cursed YAML file of Yendor.