@Caven 是年会主持人, 有天找我聊天:

我们能否做一个程序来支持年会游戏?游戏规则是这样的: 每人从 1-100 之间选一个数字提交(必须是整数)。对所有人提交的数字做一个算术平均,谁的数字和这个平均值的一半最接近,谁就获胜。如有相同答案,则先提交者获胜。

这是个经典的博弈游戏,对此感兴趣的同学可以参考: 猜数字游戏的最佳策略是什么?。年会游戏的结果与前文的结论很吻合,结论似乎具有社会统计学含义 :)

在此,我们不继续讨论游戏本身。接下来将分享我们实现的游戏程序。

心路历程

有过 Web 编程经验的人,首先会想到这样的方案: 使用前后端架构(CS 架构)。前端网页用于用户交互, 从游戏规则说明到数据提交,这类的工作都应该放在前端。后端则用于收集用户提交的数据,并执行业务逻辑,在此的业务逻辑是

对所有人提交的数字做一个算术平均,谁的数字和这个平均值的一半最接近,谁就获胜。如有人提交了相同的答案,则先提交的人获胜。

我们可以把这些业务逻辑都扔到一个函数里,函数只需返回获胜者的名字给我们就行。

我正是“有过 Web 编程经验”的人,曾号称全栈工程师 : ) 所以我最早想到的方案也是这个。

和大多工程师一样,我迫不及待想在这个新项目中使用最近感兴趣的技术,如果能全部用上我就更开心了!

对于完成这个年会游戏项目,尽管我对过去掌握的技术有十足把握(诸如 Bootstrap、Vue、Flask、Django 等),而且知道用时会更短,挑战和 Bug 都会更少,但不知怎么搞的,我脑子告诉我说, 用新的玩意儿!最好用 Hacker News 上被热烈讨论的框架,退而求其次,也得用近期 Github 上热门的。看起来越酷的越好,如果它的主页上说当前领域的东西都很傻,所以作者在他周末时间做了这个新项目,来拯救可怜的程序员,天啊,我肯定就选它了!

这正是我喜欢把《小飞侠彼得·潘》推荐给跟我一样有黑客精神的朋友们的原因, 因为书里说:

最容易的方法是,等海盗离开后再去救她,可是他这样的一个人,做事从来不用容易的办法。


过了几天, @Caven 给我发信息说:

种瓜 有空记得做一下那个平均数一半的那个游戏的网页哦 感谢

当时,我还沉浸在挑选哪个新框架的纠结里。

我很想使用 Phoenix LiveView 来构建实时后端,因为最酷的那一群黑客已经这样做了!我也想用 FastAPI, 因为 @hidaris 曾给我推荐过, 他已将其用于 thingtalk,Python 社区对 FastAPI 的反馈也相当好。

至于前端,我想用 htmx,它让我重新回到超文本的简单性里,而不是在一个项目里来回摆弄好多门语言。但我也想用 Svelte,因为它告诉我模版代码是不必要的邪恶,而不像托马斯·潘恩眼里的政府那样。

政府即使在最好的情况下,也只是一种必要的恶,而在最坏的情况下就是一种不可容忍的邪恶 – 托马斯·潘恩《常识》

虽然大卫·格雷伯可能并不这么看 :)

突然之间,我有了一个绝妙的好注意! 为何不在前端使用 htmx,而后端使用 Smalltalk 呢,这简直可能开启 Web 编程的新纪元!很可惜,我 Google 了一圈, Smalltalk 社区似乎还没人这样想, 这不正是我的机会吗? 只不过我得从零开始而已。于是我在犹豫是不是要为 Smalltalk 社区构建一个新的 Web 框架。想到这些,我差点就跟 @Caven 说,今年的年会程序我来不及写了,我们后年再玩这个游戏如何? 因为我没法在年前完成 Smalltalk 和 htmx 搭档的新 web 框架。

苍天在上,这是我当时切切实实的所思所想,并不是胡编出来取笑大家。工程师有时候交付产品困难,其可能原因之一便是我上头的心路历程,但表现出来的症状是这个:

啊这个功能短期内我们无法完成上线。

当然,在工具箱里不停纠结也是识别一个优秀工程师的小技巧,因为 Ta 热爱这些工具!如 Alan Kay 说的。

陷入到一种与工具的浪漫之中

这浪漫如果过火,就可能引起争端,vi 和 Emacs 用户长达数十年的圣战便是如此。我曾是一位忠实的 vi 用户,使用了它超过 5 年,为了使用一个文本编辑器,严肃且认真地读过 vi 相关的书(《Vim 实用技巧》这本书写得真不赖),以便于让手的速度快过脑子,但事实证明这快出的速度用处不大 :) 直到理解 Alan Kay 和个人社区对计算机的看法,我才彻底与 vi 决裂。我现在有点难以置信我曾经在 vi 注入的热情,正如许多浪漫关系一样,事后总会让人有点难以置信。


吃自家狗粮

在 IT 业界这句俚语可能最早是于 1988 年开始使用的。当时微软公司的高级主管保罗·马瑞兹曾写过一封题为“Eating our own Dogfood”(吃我们自家的狗粮)的邮件,在邮件中他向微软局域网管理工具项目的测试主管布莱恩·瓦伦蒂尼提出“提高内部使用自家产品比重”的挑战 – 维基百科

@Caven 的信息让我如梦初醒。

种瓜 有空记得做一下那个平均数一半的那个游戏的网页哦 感谢

我决定含泪放弃我那些新鲜玩意儿,先做出一个可用的原型交差。@Caven 觉得如果能尽量使用我们自家的产品做就更好了。

是呀,为何不用 Scratch 呢!

这不仅可能是最快的方法,它也使年会游戏可以称为一个真实的教学案例,甚至成为一次产品宣传!

更棒的是,一旦这样做,我只需要完成最小原型,@Caven 和其他参与这个项目的人,就可以迅速加入了,他们可以在 Scratch 里设计出有趣的游戏动画和彩蛋等东西。事实证明,这样做效果好极了,@Caven 甚至在 Scratch 里做了很棒的统计和数据分析!用于游戏开奖环节。

一旦有了激动人心的想法,写代码是相当容易的,毕竟编程只是表达想法的方式,Alan Kay 让我意识到,大多数人(当然包括我)在编程中遇到的困难都是因为不理解自己在做什么。这正是 Debug 不可能被自动化的原因,复杂的的 bug 都来自脑袋。人工智能对此也不会有作为,如果你的工作能够轻松被 AI 替代,只是说明工作中包含的思考极少(只是重复某种模式或搬运结构化的知识)。

我当晚回去就完成了可用的原型,结果证明这个结构相当健壮、可扩展和优雅。

这个 Scratch 程序充当抽奖程序的后端,是的!完全运行在浏览器里的后端!后端并不一定要运行在服务器上(那是相当狭义的概念) :)

我很喜欢这些程序,它模块化、清晰、可理解,而且相当容易调试,你可以实时观察运行期间的每个变量。

我甚至为它构建了客户端模拟器!这样在客户端构建出来之前,就可以开始调试后端了。

模拟器可以用来模拟真实的用户提交数据, 这让算法调试和检验变得极为方便:

这个项目是前后端分离的,如果你看一眼代码会发现,我们采用消息(MQTT)进行前后端通信。使用消息非常容易解耦系统,这是 Alan Kay 一直以来的忠告。

当然,我们尽可以在另一个 Scratch 程序里实现客户端,但我保留了最后的倔强, 使用新东西: replit ! 我有把握用一会儿功夫在 replit 做出 web 客户端并部署上线,因为根本不用部署!

前端要做的事情相当简单,只需准备一个表单,让用户可以填写 Ta的名字和猜的数字,然后用MQTT发给后端即可。

没使用 Scratch 的另一个考虑是,查看和修改 Scratch 十分容易,而且同事们都很熟悉,为了防止恶作剧,需要在后端构建大量的防御性代码,那样会伤害项目可读性(我们将来可能会把这个真实项目用作教学案例)。

我在里头使用了两个有趣的前端库:

源码在此: happyNewYear2022


@Caven 拿到这些原型后,十分满意,他将这些原型改造为真正有趣的年会游戏项目, 我代他将这个项目分享到了 CodeLab 社区.

参考