生命游戏,但是多人联机、超媒体加 Python

查看原文 HN 讨论

文章摘要

这是一个在线的康威生命游戏(Conway’s Game of Life),特别之处在于所有访问者共享同一张棋盘,任何人画下的细胞,其他人都会实时看到它们繁衍、演化、消亡。作者是开发者 Adam Bobowski(HN 用户名 bobowski),这个应用是他自己写的 Python Web 框架 Stario 的演示作品之一,挂在 Stario 官网的 /apps/life 路径下。HN 帖子没有正文说明,下面的介绍综合了页面本身的提示文字、页面源码、Stario 官网文档以及作者在评论区的回答。

怎么玩。 打开页面,顶栏显示当前有多少人在玩、棋盘上有多少活细胞,以及刷新频率 5 Hz。棋盘是一张 256 x 256 的网格,按 16 x 16 的区块分片加载,只渲染你视口里能看到的部分。页面上的问号提示是这样写的:这是一张共享的康威棋盘;点击或拖动会盖下一个「加号」形状的印章,空的细胞会绽放成不断演化的花纹,已经活着的细胞则会被擦除;在触屏上,一根手指画,两根手指平移棋盘;你的操作在下一个节拍生效,每 200 毫秒一帧,只在服务器上计算一次,然后广播给所有观看者;新生的细胞发出灼热的光,随着年龄增长逐渐冷却成灰烬。

为什么一次点击是五格的加号而不是单个细胞?有评论者问这是刻意设计还是 vibe coding 的副产品,作者回答是反复试出来的:单个细胞太无聊,产生不了足够的「混沌」。一个加号放在空地上,按生命游戏规则会先扩张成星形,最后稳定为四个闪烁子(blinker),视觉上很热闹。

为什么叫「超媒体」。 标题里的 Hypermedia 指的是这个应用的架构:没有前端框架,没有客户端构建步骤,浏览器端几乎不跑业务逻辑,服务器直接推送 HTML。具体用的是 Stario 加 Datastar。Datastar 是一个轻量的浏览器端超媒体库,通过 HTML 属性声明交互,并能把服务器经由 SSE(服务器推送事件)发来的 HTML 片段「变形合并」(morph)进页面。从页面源码可以看到,整张棋盘只有一个长连接 GET 请求订阅实时更新,你的每次点击或拖动则是一个短小的 POST 请求,带上被点中的细胞编号和印章类型;视口滚动或窗口尺寸变化时,也会 POST 告诉服务器你现在在看哪些区块,以便只推送这部分画面。

Stario 文档把这种模式称为「实时页面」:一个永不结束的响应负责「用户看到什么」,服务器状态一变就重新渲染并把新 HTML 写进这条连接;短命令负责「用户做了什么」,它只校验请求、回一个空的 204,然后修改状态并发布一条「有东西变了」的消息,所有打开的订阅处理器被唤醒、各自重新渲染。单进程内这个发布订阅叫 Relay,多进程时换成消息代理即可,处理器代码不用改。文档专门解释了为什么命令请求不直接返回新画面:那样点击的那个标签页会有两个真相来源互相竞争,而其他标签页仍然需要推送流,渲染逻辑就得写两遍。连接断了也不需要回放,Datastar 会自动重连并拿到最新状态。生命游戏正好是这个模式的理想展示:状态只在服务器上算一次,每个人看到的都是同一帧。

Stario 是什么。 官网的介绍是「一个小巧的 Python Web 框架」:路由是值,处理器直接写响应,HTML 就是 Python 代码,一个页面可以通过一条打开的连接保持实时,不需要模板语言、客户端构建或单独安装的服务器。它是 asyncio 原生的 HTTP 栈,不是挂在 Uvicorn 上的 ASGI 应用,需要 Python 3.12 以上,只支持 Linux 和 macOS。框架采用 MIT 许可证,GitHub 上一百多颗星。作者在 About 页面写道,他做了十年后端工程师,主要用 Python,后来开始独自构建完整应用,想要一个能从头读到底的小技术栈,于是有了 Stario,以及一个把 git push 变成运行中应用的托管工具 Yard,他发布的所有东西都跑在这两者之上。两件事决定了他做的大部分决定:编程的乐趣,以及设计上的简单。就在本月,Stario 5 刚发布,把 Python 实现的 HTTP 协议层换成了编译实现,增加了 HTTP/2 和直接 TLS 支持,并能在自由线程版 Python 上每个线程跑一个事件循环。Stario 文档里还有一个「实时方块」教程,用大约 180 行代码做一个多人共享的涂色棋盘,可以看作这个生命游戏的简化版原型。

HN 评论精华

本帖 58 分、二十来条评论,讨论不多,基本是作者 bobowski 与读者的一问一答,氛围友好。

关于玩法设计。 TZubiri 说太巧了,他一周前也开始做类似的东西,接着问了上面提到的加号印章问题,作者回答单个细胞太无聊、混沌不够。zahlman 希望能有几种不同的 3x3 图案可选,尤其是能方便地放出一串滑翔机(glider)。作者说会考虑,「明天是周五,也许我不在公司往生产环境部署了,改来加这个」。waltbosz 提出一个更有意思的点子:让玩家可以圈出一小块地,在自己画的时候让那块区域的时间冻结,别人能看到你在画什么,等你解冻时,混沌才开始。zahlman 觉得这可能就是正解,取决于用户有多「热情」。

io84 称赞五格画笔是个出色的设计选择,比 Golly 这类生命游戏软件默认的单格画笔好用得多,并问「超媒体」指什么,顺便报告了移动端 About 按钮点不了的问题。作者解释应用是用 Stario 加 Datastar(实时超媒体)写的,「一个很酷的概念验证,展示了能做到什么,而且极其简单」。weee322 抱怨「左边得清干净!」,作者告诉他点击已经停滞的细胞就能把它们擦掉。

关于运行环境。 andersmurphy 问跑在什么上面。作者先回答跑在他自己写的 Python Web 框架 Stario 上,和基于 Datastar 的实时应用配合得非常好;「如果你问的是机器,那大概是一个土豆」。追问之下,他说是某台 Hetzner 虚拟机,具体配置记不清了,还坦白自己可能有内存泄漏,应用吃内存吃得很凶,并贴出监控数据:运行 6 小时,CPU 约 10%,内存 347 MB。另一边,fisher-brett 说一打开页面浏览器就卡死了,不得不重启电脑。作者表示这绝非本意,询问重启后是否好些,请对方提供线索以便修复。

其他反馈。 hmartin 分享了自己用 OPENRNDR 做的桌面版实时互动生命游戏,作者确认那是桌面应用。jan_m_savage 说这是推介新框架和工具的好方法,展示它能做什么,「而且是我见过最好的生命游戏」。作者趁机推荐大家去看看 Stario,jan_m_savage 回复说已经看过了,确实不错。