周六晚上快十一点,屏幕上的纸牌图片全部变成了裂开的图标。明明在本地跑得好好的,一部署到 GitHub Pages 就集体 404。这个捷克纸牌游戏的网页版,是他为“Vibe Coding 周末挑战”提交的作品,却卡在了最后一公里——路径问题。如果你在那一刻盯住他的终端,会看见一个开发者快速切换窗口、核对路径前缀,手指在键盘上敲出 Vite 的 BASE_URL 变量。几个小时后,游戏上线。而整个故事,从一开始就没有按“氛围编程”的常理出牌。
所谓的 Vibe Coding(氛围编程),通常是这样:你对着 AI 工具描述想法,点几下接受,等代码生成出来,跑起来就算赢。这次挑战官方推荐的也是 Lovable、Cursor、Bolt 这类“全盘接受”式的工具,但他没用。他选了 Claude Code,并给它套上了一根隐形的绳索——一个精心切分好的阶段编排器。整个构建过程被手动切成大约 10 个明确的阶段,每个阶段都有书面目标和可验证的输出:从脚手架到引擎数据模型,再到特殊卡牌规则、AI 启发出牌、UI 渲染、交互循环,直到 200 场 AI 对 AI 模拟和 README 部署。每完成一个阶段,他才允许进入下一个。所以严格来说,这不叫“氛围编程”,而更像用 AI 充当打字员的规范驱动开发。AI 负责写代码,他来制订合约、写测试、划定边界。一旦 AI 在某个角落犯了微妙错误,测试和窄范围可以立刻捕获,而不是面对一个 2000 行的代码块被迫去“信任”。
游戏本身是一版在浏览器里运行的 Prší——一种源于捷克、属于 Mau-Mau 家族的卡牌游戏。规则简单:1v1 对抗一个故意做“笨”的 AI,从发牌一直打到有人清空手牌,支持鼠标和触屏。技术栈极简:TypeScript 加 Vite,不用任何框架,纯 DOM 渲染。真正值得说的是游戏引擎的设计:规则校验、移动合法性检查、胜利判定,全部写成纯函数,零 DOM 依赖。这带来了一个直接好处——它可以独立进行全量测试。最终,这部分积累了约 300 个单元测试和集成测试,形成了一个对规则正确性的安全网。
如果把这个周末项目的困难程度画一张饼图,代码部分只占一小块,大头是规则本身。Prší 里两张特殊牌就是“搞事王”。一张是 7:打出 7 强迫对手摸 2 张牌,而且 7 可以堆叠——最多四张 7 连续打出来,最后那可怜的玩家就得一口气摸 8 张。更麻烦的是出牌限制:“你只能用另一张 7 去回应 7”,这个规则与“哪些牌能够被考虑为合法出牌”的判断逻辑纠在一起,来回调整了好几轮才理清楚。另一张是 Q(皇后):万能牌,可以压在任何牌上,且改变接下来要求的牌花。最初的引擎版本错误地把 Q 当成了普通牌,只允许按花色或点数匹配出牌。幸好,这个错误被测试速迅抓住并修正。事后回想,如果没有那几百个测试,这两张牌的逻辑陷阱可能要多花成倍的时间才能找出来。
部署时的卡壳也是一种细微但典型的工程教训。项目里纸牌图片的路径最初写成了以“/”开头的绝对地址,像“/cards/...”。在本地用 localhost 调试时一切正常,因为文件系统根目录就是项目根目录。可一到 GitHub Pages,项目被部署在“/prsi/”这样的子路径下,浏览器循着绝对路径去根目录找卡片,自然扑了一个空。修复方案是将路径改为通过 Vite 提供的 import.meta.env.BASE_URL 动态拼接,确保无论项目挂在哪个子路径,资源都能被正确加载。另一个不起眼的岔子出现在版本标记上:他打了 v1.0.2 的 Git 标签,却忘了实际修改 package.json 里的版本号,导致标签对应的文件树上仍然是 1.0.1。这种一眼就能注意到的小疏漏
热门跟贴