一个尘封55年的Bug!由17岁学生开发、风靡了半个世纪的经典游戏,被退休程序员发现关键Bug...

0e4abef03b48b906a23178c0994289c0.gif

编译 | 郑丽媛

出品 | 程序人生(ID:coder_life)

在 1969 年 7 月 20 日,阿波罗登月舱着陆月球,第一位踏上月球的美国宇航员 Neil Armstrong,在出舱时说了一句经典:“这是一个人的一小步,也是人类的一大步。”

当时,全球约有 6.5 亿观众通过电视直播见证了 Neil Armstrong 在月球上迈出第一步,其中就包括 17 岁的美国高中生 Jim Storer——自那时起, 他便产生了编写一款登月模拟程序的想法。

于是同年 11 月,Jim Storer 学会了 PDP-8(一款迷你电脑)特有的编程语言 FOCAL,用不到 50 行的代码写出了第一个“登月(Lunar Lander)游戏”。由于技术限制,这个“登月游戏”只能以文字呈现。

总体而言,“登月游戏”的内容很简单,就是由玩家扮演的宇航员需要手动操控一个登月器,目标是在月球上实现软着陆:

(1)每隔 10 秒钟,屏幕上会显示一行报告,统计飞船的高度、降落速度,以及剩余的燃料总量;

(2)报告结尾是一个空格,玩家要输入 0-200 之间的一个数字,决定接下来 10 秒内的燃料消耗量;

(3)登月舱需以一个极低的速度接触到月球表面,游戏才会提示“完美着陆”,期间若撞上障碍物、以高速撞击天体表面或耗尽燃料,游戏便会结束。

尽管这款游戏只是一个简单的文字游戏,没有画面、也没有声音,这也并不影响它在 1973 年成为当时“最受欢迎的计算机游戏”,并在后来有了许多衍生版本,风靡了近半个世纪。

近期,一名退休的软件工程师 Martin C. Martin 也玩上了这个游戏,想要探索最优燃料方案以实现软着陆,但他在深入研究游戏中复杂的物理和数值计算后发现:这个 55 年前开发的游戏,有一个至今都没人发现的 Bug!

16d62d100767b67d1f7ae8f7e6da1986.png

要求软着陆,却从硬着陆变成完全没有着陆?

根据 Martin 的个人介绍,他自小学六年级起就开始编程,是卡内基梅隆大学机器人研究所的研究生,又成为了麻省理工学院的博士后。毕业之后,他曾在 Meta 工作,也曾在 Rockstar Games New England 担任 AI 主管。

已经退休的的他,近来在闲暇时间开始研究“登月游戏”的最佳方案:在确保安全着陆的前提下, 如何保留最多的剩余燃料。而他惊讶地发现,从游戏中推算出的最佳策略并不符合实际:缺少了一个“除以二”的操作,导致游戏错误地认为已经着陆的登月器还没有触地。

从游戏规则来看,为了在着陆时使用最少的燃料,玩家需要在最短的时间内完成着陆。理想方案是:最初,玩家可以通过关闭发动机来最大限度地提高速度,然后在最后一秒全速燃烧,正好在接触地面时把速度降为零——Kerbal Space Program 社区把这种方法称为“自杀式燃烧”,因为准确掌握时机非常难,且没有任何误差空间。

话虽如此,通过一些反复试验和手动二分查找,还是能找到一种恰好让登月器着陆的燃烧计划:在前 70 秒不燃烧任何燃料,然后在接下来的 10 秒内以每秒 164.31426784 磅的速率燃烧燃料,之后再以每秒 200 磅的最大速率燃烧:

961b16b090634be5be0d3c2909ebf934.png

“登月游戏”中认为,完美着陆的速度应小于 1 英里/小时,但在这种情况下,我们以超过 3.5 英里/小时的速度着陆,游戏还提示说“可以做得更好”?然而实际情况是,哪怕每秒再多燃烧 0.00000001 磅的燃料,玩家也会完全错过地面,并以 114 英里/小时的速度上升:

05aa5c8a6519f8317ab3808e40d79550.png

基于此,Martin 提出一个疑问:“我们是如何从硬着陆变成完全没有着陆,且中间没有一个软着陆的过程呢?”

0628efbf990aa3a64a77e208d5592d03.png

接触地面后,方案失效

Martin 本以为,会在这个“登月游戏”中看到一种在如今电子游戏中依然很常见的欧拉积分,也就是在每个时间步(timestep)开始时计算力,然后使用公式 F=ma 计算加速度,再假设加速度在 Δt\Delta tΔt 秒的时间步内保持恒定:

76ccecd40d19b965eef997d5b765f2d3.png

由于质量在时间步内不断变化,加速度也会随之变化,所以假设加速度为常数只是一个近似值。

但令人惊讶的是,游戏作者 Jim Storer 使用了精确的解决方案,即齐奥尔科夫斯基火箭方程,并用泰勒展开式对其对数进行计算。此外,Jim Storer 还用了一些代数方法来简化计算,减少了四舍五入的误差。对于在 1969 年还是高中生的他来说,这已经非常了不起了。

当 Martin 问他这个问题时,Jim Storer 回答道:“当时我已经熟练掌握微积分、泰勒级数等概念,而且在我的印象中,身为物理学家的父亲还在我推导这些方程时帮了我。”

知晓 Jim Storer 设计游戏的方法后,Martin 很快就懂了:因为用的是火箭方程,所以“自杀式燃烧”成为了最优选择,而且 Jim Storer 使用泰勒级数的五个项,在参数最大为 0.1212 时,可以达到超过六位小数的精度,所以这“不是我们要找的问题所在”。

理论上来说,在登月舱触地之前火箭方程的效果确实很好——但 Martin 指出,等接触地面后这个方法就不成立了,而这也是登月游戏面临的最大挑战。

“想象一下,登月舱在一个 10 秒的回合中开始时下降,但到回合结束时上升。仅仅验证它在这两个时间点都在地表上方是不够的,因为它可能在中间的某个时刻低于地表。当这种情况发生时,程序必须回溯并检查之前的某个时刻。”

有一个显而易见的方法,就是查看轨迹的最低点,即速度为零的时刻。对于火箭方程而言没有一个合适的表达式来表示这个最低点,因此可以使用物理学家最喜欢的技巧,只取泰勒级数的前几项。如果只使用对数的前两项,问题就简化成了一个二次方程,你可以用高中学过的经典二次方程来求解公式。在 10 秒的回合内,这个近似值应该相当不错,精确度在 0.1% 左右。

但 Martin 发现 Jim Storer 不是这么做的:在他的公式中,平方根在分母而不是分子,另外误差也放大了 30 倍。

feb21fd28e11a91c7c4d36b3cf017d88.png

“他少了平方根内分母中的 2!”

fc4c4eaaa18f6c8b0f9b25d5c4f35def.png

Martin 研究了 Jim Storer 的公式很久,发现怎么算也不会涉及平方根。直到他更仔细地看了看 Jim Storer 的平方根,它的形式是:

a75fc8521a5e08a7cb51e0970314581e.png

这看起来很像是在平方根内除以 4 的二次方程式,但为什么会在分母上呢?在尝试了一些方法后,Martin 重新发现了一个二次方程的替代形式,其中平方根在分母上,也与 Jim Storer 代码中的公式相符。

尽管如此,Martin 依旧不知道为什么 18 岁的 Jim Storer 要用这种替代形式。也许他重新推导了二次方程公式,而不是查阅现有公式,结果推导出了这种形式?也许他担心会出现灾难性的抵消(catastrophic cancellation),所以想用正数相加而不是相减的形式?

但是,如果他的公式是等价的,那为什么近似误差会高出 30 倍呢?

Martin 尝试自己推导公式,得到了以下结果:“这与 Jim Storer 的公式几乎完全相同,只是......他少了平方根内分母中的 2!”

3ccf6b7e2e4e0d22e0c337370b5b06c4.png

Martin 指出,这可能是推导公式或输入计算机时的一个简单错误。毕竟,当时的计算机代数系统 MACSYMA 仅在 Jim Storer 开发前一年才开始使用,并且在 Jim 的高中并不可用,所以他必须用铅笔和纸完成所有工作。

由于这个 Bug,Jim Storer 始终低估了到达最低点所需的时间。他可以通过两种方式来补偿:增加 0.05 秒,然后从新的、更接近的位置重新估算。这也就解释了为什么会错过着陆时间:第一次估算是在登月舱还在地表上方并持续下降时,第二次估算是在登月舱到达最低点并再次上升之后,而这不到 0.05 秒。

接下来,如果把缺失的 2 倍系数加上并去掉 0.05,会发生什么呢?现在,“自杀式燃烧”所能达到的最佳效果是:速度降到了 1.66 英里/小时,离 1 英里/小时的完美着陆还有将近四分之三的距离。

27a1571a5a2ecf1b700ad68c396499cb.png

这并不完美,因为我们仍然只使用了泰勒级数的前两项。另外,一旦确定最低点是在地面下,就需要找到首次撞击地面的时间,这就会涉及到类似的近似计算。平缓着陆也是可行的,只需在第 14 个回合结束时降低高度和速度,然后在第 15 个回合中使用低推力,150 秒后在某处着陆即可。Martin 补充道,他无法理解的只是理论上的全推力着陆自杀式燃烧,大约需要 148 秒。

88946a9c8d71f04d8663bc0f21bf0c03.png

即使有这个 Bug,这仍是一个有趣的游戏

最后 Martin 评价称,对于 1969 年使用 PDP-8 的 17 岁高中生来说,Jim Storer 的这个登月游戏已经是一个非常了不起的作品了。

毕竟,当时的高中还没有计算机科学课程,数值计算方面的知识也并不是谁都知道的——甚至 Martin 自己,也是攻读机器人学博士学位时才学到这些知识。令 Martin 惊讶的是,这个错误已经存在了将近 55 年,而之前却没有人注意到它。

不过,对此 Martin 也表示理解:即使有这个 Bug,这仍然是一个有趣的游戏。“不仅要赢,还想找到最佳策略,这种追求自然会让人们试图理解微小的差异。我想其他人都只是乐于玩这个游戏,并从中获得乐趣。”

参考链接:https://martincmartin.com/2024/06/14/how-i-found-a-55-year-old-bug-in-the-first-lunar-lander-game/

推荐阅读:

▶被裁 4 个月后,一名印度程序员的“复仇”:怒删前司 180 台虚拟服务器,造成 67.8 万美元损失,被判两年八个月监禁!

▶历史性一幕!C++ 首次超越 C,仅次榜首 Python | TIOBE 6 月榜单发布

▶史上首位!17岁中专女生“爆冷”:拿下数学竞赛全球第12名,打败一众名校生和AI参赛者


由 CSDN 和 Boolan 联合主办的「2024 全球软件研发技术大会(SDCon)」将于 7 月 4 -5 日在北京威斯汀酒店举行。

由世界著名软件架构大师、云原生和微服务领域技术先驱 Chris Richardson 和 MIT 计算机与 AI 实验室(CSAIL)副主任,ACM Fellow Daniel Jackson 领衔,BAT、微软、字节跳动、小米等技术专家将齐聚一堂,共同探讨软件开发的最前沿趋势与技术实践。

4821b9880150f2f409d213da22bbe25c.png

  • 56
    点赞
  • 138
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值