我是如何在第一款登月游戏中发现一个 55 年前的漏洞的

编译 | 郑丽媛

出品 | 程序人生(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!

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

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

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

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

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

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

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

接触地面后,方案失效

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

本文文字及图片出自 CSDN

你也许感兴趣的:

共有 115 条讨论

  1. 如果有人好奇的话,2009 年,我发现吉姆-斯托尔(Jim Storer)是第一款 “月球着陆器 “游戏的作者,并就此对他进行了采访(此外还记录了该游戏的历史)。他后来还提供了源代码,真是太棒了。

    https://technologizer.com/2009/07/19/lunar-lander/index.html

    我最喜欢的部分是

    “斯托尔说:”离开高中后,我再也没有想过这款游戏。”直到几个月前,有人给我发了这封电子邮件,我才意识到,除了我在高中时写的那款游戏之外,我完全不知道还有其他的登月者游戏”。

    1. 我很荣幸能被收录到这篇文章中–我最初是为 Windows 2.x 编写 Lander(1990 年)的。

      当我应聘 Lotus Notes 工作时(1989 年),我给面试官(Tim Halvorsen)看了我的 Lander 游戏。他说:”这很酷,让我们试试在 Windows 3 上运行它。

      起初,我觉得很酷!我可以看到 Windows 3(当时还没有发布)。但他接着说:”Windows 3 在保护模式下运行所有程序,所以如果你有任何越界指针访问,它就会立即崩溃。让我们看看你是怎么做的。

      整个过程我如坐针毡。

      但幸运的是,Lander 没有崩溃,蒂姆也很高兴。我最终得到了这份工作,并从此改变了我的职业生涯。

      1. 我曾是 Anacreon 的忠实粉丝,感谢你们给我带来了数小时的娱乐!

        1. 非常感谢!我在工作中得到了很多乐趣。谢谢!

    2. 太棒了

      我只想补充一点,还有比月球着陆器更早的机械着陆器游戏

      我找不到图片。我记得机器是这样的

      https://content.invisioncic.com/r322239/monthly_10_2015/post

      只不过它有地形和坑洞。坑洞会亮起来,你需要降落在坑洞里(飞船降落时会按下坑洞中央的按钮)。如果你没有瞄准好,你的飞船就会撞到坑的边缘,然后倾斜,你就失败了。如果你真的按下了按钮,那么灯就会熄灭,另一个坑就会亮起来。

      — 更新

      现在想想,也许控制方式更像 UFO 捕手,你对准顶部,然后按下 “着陆 “键。

      总之,它曾经在迪斯尼乐园的主街商场里出现过。

      1. 我在拉斯维加斯的弹球博物馆玩过这张照片中的游戏。一个操纵杆操作后面的风扇,另一个操纵杆操作下面的风扇。这款模拟街机游戏非常有趣。

        几年前我回到博物馆时,它已经不能用了。我希望有一天他们能修好它。

    3. 哇,这篇文章太棒了。令人难以置信的研究和深度。感谢您的贡献。

    4. 嗯,是的,有两点:

      a) 太棒了 b) 我大胆推断你就是那个复古游戏记者 Benj Edwards,他曾是臭名昭著的 Retronauts East 团队的一员。我只想说,我一直很喜欢你的存在 🙂

  2. 很酷,违规行似乎是 08.10 [1]。

    我觉得有点奇怪的是,他从头到尾多次提到 “1969 年的高中生令人印象深刻”–老实说,我可以想象在太空时代长大的人一定会对有技术头脑的人产生巨大影响,这让我想起了前不久那部名为《十月的天空》的电影。

    在《全职高手》中对游戏作者的采访中,他提到自己精通微积分–在我看来,如果你对太空/火箭等感兴趣,并且有这方面的天赋,那么你尝试编程一款登月游戏也是情理之中的。

    [1]: https://www.cs.brandeis.edu/~storer/LunarLander/LunarLander/

    1. 1969 年,美国只有数百名高中生可以使用电脑,而具备电脑知识的高中生则更少。在太空时代成长的经历或许很有启发性,但这并不能改变一个事实,那就是当时对于普通大众来说,计算机基本上是不存在的。与现在不同,软件开发并不是一个广为人知的职业。直到 1962 年,美国才有了计算机科学专业。我认为,1969 年他还是一名高中生,这一点非常值得注意。

      1. 编辑:我个人认为,为了澄清问题,我确实认为这段代码令人印象深刻,但上面的来来回回并没有真正解释原因。

        这种解释仍然不能证明编写着陆器游戏是 “对 1967 年的高中生来说令人印象深刻 “的部分。

        如果有的话,这种解释只能说明他们能使用电脑才是 “令人印象深刻 “的部分。

        1. 对于一个高中生来说,这需要有电脑、创造力和令人印象深刻的能力。

          这比乒乓球早了 5 年–你是在从零开始发明游戏概念,而不是站在巨人的肩膀上。

          1. > 作为一名高中生,你的能力令人印象深刻

            之所以令人印象深刻,是因为只有极少数(没有?)高中按照他所要求的水平教授微积分。高中生完全有能力处理这种微积分,只是高中不教而已。

            1. 对我来说,这与微积分知识水平无关–这是用 20 世纪 60 年代的技术,用 50 行基本代码和极少的现有技术,从零开始构建的。

              做第二个人更容易。

              1. 为了准确起见,它是用 FOCAL 而不是 BASIC 写的。

            2. 我比较肯定的是,虽然学校可能有能力教授这个级别的微积分,但无论如何,对微积分感兴趣的学生人数与已经掌握这个级别微积分的学生人数大致相当。

            3. 英国甚至不再将微积分列为物理课程的一部分!

              我因为做了一个积分而被开除了。

            4. 是的,但 “先行 “也是其中的一部分,不是吗?20多岁时,我读了卡尔-波普尔的《猜想与反驳》(Conjectures and Refutations)一书,意识到自己在十几岁时就走上了认识论的证伪之路。但究竟是我是个天才,还是我只是幸运地生在了这个时代,在这个时代里,我们所沐浴的概念之汤使得这一点显而易见?我认为是后者。哥德巴赫猜想也是如此,我在十几岁时就提出了一个等价猜想。

              此后的证据表明,我还算聪明,但不是波普尔或哥德巴赫,甚至不是我们这个时代的沃尔特-布莱特。

              人类这个有机体在不断进化,因此更多的事情对更多的普通成员来说变得显而易见。也许将来十岁的孩子就能看到一元结构的直观使用。

        2. 我认为两者都是。在 1967 年,可能只有非常厉害的高中生才能接触到这么多电脑。

          这让我想起多年前读过的一篇关于 “Jai Alai “的文章:这项运动早已过了巅峰期,其中一位受访者说,现在的球员是有史以来最优秀的,因为只有那些非常非常优秀、非常非常热爱这项运动的人才会继续尝试职业比赛。

      2. 我不是不同意这里的总体观点,但当时肯定有一些高中开设了计算机和编程课程–我妈妈从 1967 年开始在芝加哥的一所高中教授编程(FORTRAN)。我认为她是全国最早的十几位高中编程教师之一,但我不认为她是第一位。

      3. 是的,比尔-盖茨在西雅图的湖畔高中是全国拥有计算机的三所高中之一。

      4. 不过,这几百人中至少有一个会写这样的游戏,这并不奇怪。特别是当那些已经有电脑的人在某种程度上是自我选择的时候。

        1. 有人做任何事情都不足为奇。事实上,最令人印象深刻的事情必然是由某个人完成的,否则它就不会成为一件被完成的事情。但是,成为那个人仍然会令人印象深刻。

        2. 有人是世界上最擅长跑步的人,这不足为奇,但当他们做到这一点时,仍然会让人印象深刻。

    2. 1969 年,我上高中,懂一些微积分,对编程很感兴趣。在一个规模不小的城市里,有一所重点工程大学和一所大型高中,主要的障碍是如何使用计算机。我们学校的电传打字机与远程主机相连。我和朋友们在大学里找到了几台可以在晚上使用的电脑,但大多数都只有读卡器和行式打印机,没有任何图形终端。我认为,在当时,拥有这样的技能、兴趣和访问能力的特殊组合是非常罕见的。

      1. 我是 OP。因此,在 Lunar Lander 中,M*G/Z*K 就是数学家和其他语言所写的 M*G/(Z*K)。当我第一次看到这句话时,我看了两眼。)正如维基百科文章所说:”在将 FOCAL 源代码转换到其他系统时,这可能会导致微妙的错误”。

        另外,IF 语法有点局限性,读起来很费劲,不过我想程序员应该会习惯的。

        1. 从今天的角度来看,这很令人吃惊,但当我想象一下当时的思维过程时,这似乎是合理的,尤其是在数学和科学领域,乘除运算符通常会被归类为除法条上方的一系列乘法和除法条下方的一系列乘法。

          1. 正确。+ 的优先级也高于 -,因此 a – b + c 表示 a – (b + c)。

            1. Ah, the old

                  My

              Dear

              Aunt

              Sally

              vs.

              ;    My Dear

              Aunt Sally

              debate. 🙂

        2. 您的代码被弄乱了,请使用反斜杠转义 *(否则它将启动斜体文本的跨距),或使用四个前导空格来转义代码块(在代码块中通常不需要转义):

          M*G/Z*K

          我想您的意思是,上述和

          M*G/(Z*K)

          的意思。

        3. 另一个奇怪的特征是,标签似乎是浮点的,如 “G 5.9 “似乎将控制权转移到了以 “05.90 “为起点的行。

          1. 它们实际上不是浮点数:每个标签都是一对数字,第一个数字表示组,第二个数字表示该组中的行。组没有语义上的重要性,只是为了便于组织。

            1. 事实上,我弄错了。行号并不是偶数,而是数字对,因为一位数标签的第一位是数字。

              而 “DO “命令使用一个组号,运行组内的每一行,然后在组结束后返回。因此,每个组可以有效地充当一个子程序。(在这个游戏中,第 6 组和第 9 组就是这样)。

    3. 我觉得有点奇怪的是,他从头到尾多次提到 “1969 年的高中生令人印象深刻”–老实说,我可以想象,在太空时代长大的人一定会对有技术头脑的人产生巨大影响,这让我想起了前不久那部名为《十月的天空》的电影。

      这里是 OP。我明白你的意思。但想想制作这款游戏需要什么?

      – 从高中物理开始,你就知道要从自由体图入手。有两种力,重力和推力。到目前为止,物理成绩为 A 的普通高中学生应该能够做到这一点。

      – 重力取决于到中心的距离,当然这个距离是变化的,这就是着陆器的意义所在。我是说,你从120英里高的地方开始你必须意识到它变化不大 所以可以近似为一个常数但你在物理课上已经学过了 所以你可能会认为它是个常数

      – 推力到底是如何与燃烧速度相关联的?燃烧越多,排气速度就越高吗?换句话说,考虑到 100 磅/秒与 200 磅/秒的对比,当你将进入发动机的燃料流量增加一倍,然后进行燃烧时,在相同的体积内会变成两倍的燃料。难道不会以两倍的速度强制排出吗?或者至少是更高的速度?也许你会想到通用气体定律:PV=nRT。体积不变(发动机的体积),n 增加一倍,R 是一个通用常数。因此,这意味着 P 或 T 发生变化,或者两者都发生变化。为什么 T(分子速度的函数)不变,而 P 却增加了一倍?为什么两者都不变化?

      – 于是你和你的爸爸商量,他碰巧是一位物理学家。大多数高中生,即使是物理成绩得了 A 的学生,也没有一个物理学家父亲能查到火箭发动机的特性,并找到齐奥尔科夫斯基火箭方程。所以,一个高中生能找到火箭方程,对我来说已经很了不起了。

      – 从速度到位置,你需要积分。我不确定普通的高一物理生是否会想到用泰勒级数代替 FLOG() 调用,然后逐项积分。

      – 您需要多少项泰勒级数?它是否会收敛?如果他想到了这些微妙之处,那就太了不起了。但年轻的吉姆也有可能没有意识到这些问题,只是使用了 5 个项,因为这似乎是一个很大的项。

      – 现在你可以在月球附近进行模拟了。太酷了但如何检测它何时着陆呢?你可以试着求解高度等于零,看看是否有零或更多的解。但即使有解,也可能是过去或未来的解。因此,你决定寻找速度为零的地方,因为你知道这在转弯过程中只发生过一次。虽然我不知道这是否是 18 岁的吉姆的思维过程,但我认为这显示了他的聪明才智。

      – 因此,你可以试着反推火箭方程式:给定一个所需的 delta-V 值,你需要燃烧多少燃料才能达到这个值?如果你用纸笔和高中数学(包括微积分)来尝试这个问题,你就会一直被卡住。你没有工具来证明这实际上是不可能的,需要你引入一个新的函数,即兰伯特 W。

      – 也许你放弃了,也许你的物理学家爸爸又帮了你。利用泰勒级数,你现在必须求解五次多项式。于是你决定舍弃第 3、第 4 和第 5 项,得到一个二次项。为什么现在可以舍弃这些项,而在计算常规动力学时却不行呢?我印象深刻的是,他意识到可以在不同情况下使用不同程度的近似值,而不会产生一些不一致或其他问题。

      – 你想出了如何使用二次方程的另一种形式,这说明你并不是简单地查找和输入。可能令人印象深刻。

    4. 虽然作为第一个月球着陆器游戏而闻名,但令人印象深刻的是它所使用的特定数字技术。

      1. 这里是 OP。是的,我就是这个意思。使用火箭方程、截断泰勒级数近似、进一步截断使其可解,然后迭代改进解法,这些都是我意想不到的。

        1. 这对我来说并不快,而且我已经学过大学微积分。

  3. 70 年代中期,我为 Adage 图形终端编写了一款二维矢量图形登月游戏。如果速度太快或燃料耗尽,就会出现一个弹坑,否则你就会看到一面或多面插着的美国国旗,这取决于着陆的质量。

    多年前,我扔掉了我唯一的一份源代码拷贝,认为它没有任何价值,永远不会被重新使用。后来,当我意识到它在历史上是一款多么早期的图形游戏,以及通过简单的仿真就能轻松复活它时,我后悔不已。

    1. 哎呀,我的意思是 “垂直着陆”。

  4. > 到 1973 年,它已成为 “迄今为止最受欢迎的电脑游戏”。

    关于 “月球登陆者 “和错误:我的第一本编程书中有一个用 Basic 编写的游戏版本,但我从未正确运行过。25 年后,我又重温了一遍,发现其中的错误之多、逻辑之复杂(”440 IF <condition> GOTO 450″)令人发指。

    我最终以成人的身份重写了它[1],但年轻的我毫无机会。直到今天,我还在想,那篇被遗忘的西班牙文社论里到底发生了什么,让(几乎)可以正常工作的代码变成了最终版本。

    [1] https://7c0h.com/blog/new/moon_landing_in_basic.html

    1. 哎呀呀,说 “没戏 “太轻巧了。

      很多 BASIC 代码都源于 20 世纪 60-70 年代。那时,编辑们统治着印刷杂志,这些代码经常出现在杂志中,尤其是在代码集书籍中,他们更是铁腕统治。人们很少认为源代码必须逐字逐句地输入,绝对不能有任何改动,因此编辑们会 “明智地 “修改源代码。他们认为自己是在 “帮助””显而易见 “的排版和编辑决定。

      这个教训是慢慢地、痛苦地吸取的,直到 20 世纪 80 年代开始,整个行业的材料改进开始占据上风,源代码不应该在印刷中被触碰的意识开始真正渗透到印刷行业。有时我也在想,如果印刷媒体的掌权者能更开放地接受 “外来者 “对 “他们 “的部分内容拥有绝对控制权,那么另一条时间线可能会是什么样子。

      几十年后,我才从一位从印刷媒体界崛起的年长朋友口中了解到以上种种。在我还是个孩子的时候,没有大人的指导,只有从学校和社区图书馆借来的几本关于编程的书,我从印刷媒体上手写输入的无数程序同样错误百出,能坚持编码简直是个奇迹,所以你的回忆唤起了我强烈的记忆。

      1. > 他们认为自己是在 “帮助””明显 “的排版和编辑决定

        听起来完全莫名其妙。这个思维过程是如何运作的?他们认为代码是什么意思?他们到底做了什么改动?他们以前学过不碰数学公式吗?

        1. > 这个思维过程是如何进行的?

          有人向我解释说,这是领地标记/办公室政治。当时的编辑在决定内容时权力很大。如果有人解释/告诉他们他们无法控制内容的任何部分,他们是不会善罢甘休的。

          > 他们认为代码是什么意思?

          这就有意思了。他们被告知这是为计算机准备的,但当他们看到隐约像英语单词的高级语言人工制品时,他们拿起了红色记号笔,跃跃欲试,绝对确信自己是正确的,就像他们过去一直做的那样。这是最开始的情况。后来,情况变得更加微妙。

          > 他们到底做了哪些改动?

          早期的事件有这样的故事:使用非比例字体的样稿,仅仅因为美观而被迫改成比例字体。红笔改变了 PRINT 语句的内容(因为上面写着 “打印”,而这是他们的职责,对吧?)即使后来吸取了更多的经验教训,细微差别也开始慢慢出现,但那是在程序员字体出现之前的事了,手工转录的典型错误比比皆是。在很长一段时间里,当时还不存在将原始源代码转入非数字排版系统的管道,因此所有这些都是由非技术人员费力地从打印输出上转录下来的。

          > 他们以前学过不碰数学公式吗?

          有数学经验的技术写作编辑和那些负责印刷普及 BASIC 编程产品的编辑之间很少有交流。当时的《BYTE》杂志就是因为意识到了这一点而取得了革命性的进步,但即使是他们,也时不时会出现一些失误。

    2. 读来令人愉快。我小时候有时也会这么想,我的 C64 会 “神奇地 “加载一些与游戏相关的图片,只需几行代码,而这些代码……就在它的某个地方?这种想法超级离奇,但作为孩子,你还是很容易产生很多神奇的想法。

      > 和 “440 IF <条件> GOTO 450 “的复杂逻辑

      再扩展一下,虽然你书中代码的某些情况确实需要一些清理(第 440 行尤其严重),但书中的代码很可能是为当时家用电脑的普通 BASIC 编写的,这种 BASIC 只对行号进行操作,而且分支语句非常有限?

      您使用的 BASIC 似乎是 “结构化 “的,这在当时的家用电脑中极为罕见。我最近才看到,1984 年的一本 C64 杂志至少用了三期的篇幅,向读者介绍了结构化编程的奥妙!

      IF 语句的严格限制使得汇编语言风格的条件分支(使用 GOTO)变得极为常见和必要。

      您绝对不能嵌套 IF(就像您的代码中那样),因此如果您想将 IF 组合在一起,就必须跳过未被使用的 IF。Commodore/C64 的 BASIC(实际上是 Microsoft BASIC)甚至没有 ELSE,所以通常 ELSE 必须通过否定条件来实现,并跳过 “ELSE “分支。

      不过,C64 BASIC 有一个有趣的怪癖,即同一行中的任何其他语句都属于 “THEN”,例如

      10 如果 a=1 则打印 “foo”:打印 “bar”

      如果 A=1 则打印 FOO BAR,否则什么也不打印。当然,这只能在(有限的)单行 BASIC 中容纳语句。其他 BASIC 方言会认为 PRINT “BAR “不再属于 ELSE,这在语法上更简洁,但可能不那么方便,这取决于方言还提供了什么。

      我们今天认为理所当然的便利性和严谨性在当时并不存在。在我看来,C64 BASIC 尤其 “肮脏”,它有很多怪异之处,而这些怪异之处更多是其实现的结果。(另一个随机例子:每个函数都必须有一个参数,无论是否需要,所以你必须写类似”?FRE(123) “的东西来打印可用内存的数量,而 123 根本不重要)。

    3. 可能是复制/粘贴错误(编辑),加上最后期限,再加上没有质量保证。

  5. >只要在第 14 转结束时降低高度和速度,然后在第 15 转时使用低推力,就能在 150 秒后着陆。我们只是不知道理论上的全推力着陆自杀式燃烧需要 148 秒左右的时间。

    我预计燃料最优的软着陆策略(由于不符合 “自杀式燃烧 “的确切形式而被忽略)是在 t=70 秒时以 164.31426784 磅/秒的速度推进,然后用 199.99999999 磅/秒的速度替换随后的 200 磅/秒的推进。

    199.99999999 越早 “播放 “越好,因此只需使用穷举搜索,选择仍能实现软着陆的最早播放。

    1. 这个错误的本质是,它很难发现着陆器何时接触了地面。您需要在 0.05 秒左右的时间内高度小于零,游戏才会注意到您已经着陆。如果您在这段时间内的推力是 200 或 199,那么要在这么长的时间内高度小于 0,您需要在高度为零时速度大于 1 英里/小时。

      即使修复了这个错误,代码也仍然只能接近最低点。另外,即使它检测到你已经着陆,现在也需要计算着陆的时间,即高度为零时,而不是速度。为此,它也使用了近似值。

      因此时间会有些偏差。如果你在最后一个时间步燃烧了 200 或 199 个燃料,那么你的加速度会很高,因此即使是少量的时间也会转化为大量的速度。相反,如果你的燃烧速度为 10 磅/秒,那么 0.08 秒的误差不会对你的速度产生太大影响。

      1. 我明白你的意思,但我认为这与我概述的最佳策略并不矛盾。

        与其在燃烧结束时大幅偏离自杀式燃烧,不如在燃烧开始时小幅偏离,以更低的成本(相对于燃料消耗)”搜索 “越野车代码,寻找可能的软着陆方案。

        总之,写得很有趣!感谢您的发布。

        1. 是的,我想我们意见一致。)

          事实证明,在发现这个错误之前,我其实已经写了代码,来寻找当你的选择被限制为整数时的最优序列。我和你的想法一样,”也许如果你在第一个非零项烧掉 165 或 170 或其他东西,那么你就可以在第 14 个回合少烧一些,而且还能着陆”。

          这就是我如何知道这是不可能的,至少在整数燃烧率的情况下是不可能的。)我检查了所有 201^9 种组合,并进行了一些优化以缩小搜索空间。

          这与你所说的最后一次燃烧使用浮点数不同。但精神是相通的。

          1. >我和你的想法一样,”也许如果在第一个非零项中燃烧 165 或 170 或更多,那么在第 14 个回合中就可以少燃烧一些,而且还能着陆”。

            我不是这么想的。

            这显然行不通,因为只要在 t=70 秒的步骤中改变最小有效数字(即增加 1e-8 磅/秒),就会 “冲过 “软着陆窗口,这部分是由于错误造成的。

            显然,在 t=70 秒时进行的操作过于 “精确”,无法有效瞄准(小)软着陆窗口。通过将 “减去 1e-8 磅/秒 “的动作(即执行 199.99999999 磅/秒的动作)在燃烧过程中越拉越后,可以有效地使动作越来越精细(以达到最小的燃料损耗),直到实现软着陆。

            再次感谢。我不确定谁是对的,但这确实是一个非常有启发性的问题!

          2. 我认为他们的意思是,在所有 200 磅/秒的燃烧中,你都需要切换到 199.999…磅/秒,而不仅仅是最后一次。

            我只是想澄清一下,你们似乎是在各说各话,不过这似乎只会导致非最佳策略(即着陆时间比理论上可能的时间更长,无论差值有多微小)。

            1. 说到彼此

              > 然后用 199.99999999 磅/秒替换随后的 200 磅/秒输入之一

              > 替换一个

              确实不是最后一个,但不是 “所有”

  6. 这很有趣。最简单的写法是不使用任何特殊公式,只根据新质量重新计算每帧的质量和加速度。并计算每帧边界与地面的交点。

    我猜这种方法的原理是帧率越低,精确度就越低,也可能是使用实际公式更有趣。

    我很好奇在原始帧速率下,两者之间的差异有多大。

      1. 以 10 秒为增量显示结果并不意味着模拟帧速率有任何问题。你仍然可以在每毫秒打印结果之间以 1 毫秒的粒度模拟 10k 帧。对于 20 世纪 60 年代的机器来说,挑战在于计算,因此封闭式解决方案是明智的。

      2. 哦,谢谢。有一款街机游戏叫 “月球登陆者”,我还以为是一模一样的东西,看来我没太注意图片。

    1. 这里是 OP(这篇博文的 OP,而不是原始游戏的 OP)。天真的方法也是我所期望的,在博文中被描述为欧拉方法。

      就物理精确度而言,尤其是在接近地表时,如果燃料燃烧速度较快,质量确实会发生很大变化。但就其挑战性/趣味性以及作为玩家所使用的策略而言,我认为这不会有太大区别。事实上,在《BASIC 电脑游戏》一书中还有其他的月球着陆器模拟,我认为其中一个确实使用了天真的方法。

      如果 10 秒钟太长,你仍然可以在用户界面中将其作为一个回合,但在内部将每个回合分成若干个较小的时间步长,例如 10 个时间步长,每个步长 1 秒钟。事实上,现有游戏在某些地方就是这么做的,这也是为什么他的物理模拟采用了任意的时间 S,而不是总是 10 秒。

  7. >火箭方程是自杀式燃烧达到最佳效果的原因

    吹毛求疵,但严格来说这并不正确。

    即使不考虑飞行器在燃烧燃料时变轻的情况(火箭方程在这里只起到这个作用),自杀式燃烧仍然是最优的。

    真正的原因是自杀式燃烧能最大限度地减少重力损失。

    https://en.wikipedia.org/wiki/Gravity_loss

    1. 我是 OP。没错,我过于简化了。我的意思是,动力学有两个部分,火箭方程和重力。它们是线性相加的,所以重力带来的任何额外速度都需要通过增加火箭方程中的 delta V 来消除。重力 delta V 只是重力加速度乘以时间,所以你要尽量减少时间。令人惊讶的是(对我来说),在火箭方程中,时间的长短、燃烧的顺序、是否一直以恒定的速度燃烧与短时间的强力爆发等都无关紧要。因此,要想用最少的燃料以零速度着陆,你只需要在最短的时间内着陆。

  8. 我还有一卷打孔带,应该是 PDP11 用的,上面写着 “月球着陆器”,我不知道该把它送给谁。

    1. 互联网档案馆还是计算机历史博物馆?请 @textfiles 推荐可能感兴趣的地方。

  9. 哇,太神奇了。我记得在 20 世纪 70 年代中期的某个时候,有人把这个游戏移植到了 Wang 2200 BASIC 上,之后我就玩过这个游戏。我自己并没有弄明白如何着陆,但我记得有人向我演示了在施加最大推力之前的前几圈滑行的技巧。不过我不记得有 “自杀式燃烧 “这个词。(或许是后来 “Kerbal 太空计划 “流行起来后才有了这个词)。

    我还记得在 20 世纪 70 年代中期,加利福尼亚州伯克利劳伦斯科学大厅的几台终端机上也运行着这款月球着陆器游戏。不过我不知道它是在哪台电脑上运行的。

    我从来没有看过这个程序的源代码。我不知道数学有多复杂。反正我也看不懂,因为 20 世纪 70 年代中期我还太年轻。不过,我也不确定我现在能不能理解它….。

    1. > 我还记得,同样是在 20 世纪 70 年代中期,在加利福尼亚州伯克利劳伦斯科学馆的几台终端机上运行着这个月球着陆器游戏。

      我记得在 1973 年初,劳伦斯的 ADM-3 终端机上就有月球着陆器游戏,周围通常都是一群男性青少年。游戏亭模式的一个 “特点 “是,用户只要按住 Ctrl-C 键,就能摆脱游戏亭模式,从而可以玩其他游戏。是偶然现象,还是原生黑客的 “猫薄荷”?

    1. 我用 hp97/hp67 播放过。

      记忆模糊,但我想我使用了计算器内置的微积分功能,在第 12 转时以 45 的燃烧量(大约一半的燃料)实现了完美着陆。

      我事先并不知道最佳燃烧量是多少,当然也没听说过 “自杀式燃烧”。

  10. Myst》的创作者之一(Rand Miller)认为,最初就是因为这款游戏,他才走上了游戏开发之路[1]。

    Myst 也启发了许多人进入计算机科学和游戏开发领域,因此,这款 “月球着陆器 “游戏所产生的滚雪球效应有多大,我们不妨拭目以待

    [1] https://www.youtube.com/watch?v=EWX5B6cD4_4&t=1099s

    1. 当我提议恢复 CHM 的 pdp-1 时,理由是 “太空战!”

  11. 我还记得 80 年代在 TI 可编程计算器上玩过这个游戏的一个版本。当时还有一个图形版本,适用于各种 8 位计算机,你可以在月球表面导航,寻找平坦的着陆区。

  12. 哦,美好的回忆。

    1981 年(我想应该没错),我 11 岁时在一个计算机夏令营里用 Apple II 学习了 BASIC 编程。

    我做了一个简化版的月球着陆器……制作和游戏都非常有趣。

    我的一个同班同学是 “高级 “Pascal班的,至今仍是我的朋友。

    1. 我是 OP。真酷!我出生于 1969 年,距离尼尔-阿姆斯特朗登上月球不到一个月的时间,在这款游戏诞生时,我只有几个月大。在我拥有自己的电脑之前,我也学习了 BASIC,然后花了几个小时研读《BASIC 计算机游戏》一书。正如你所说,那是一段美好的回忆。

  13. 我当年玩过 ABC-80 版本的 “月球登陆者”。今天一看,似乎用的是更简单的欧拉积分,G 值也很奇怪(?)

  14. 也许有人会在 2500 年修复当代游戏的错误,并写一篇类似的文章。

  15. 这就是为什么老年人能创造出如此伟大的东西。写该死的游戏

    1. ……什么?这个游戏是一个十几岁的孩子做的。他们现在变老的唯一原因是 55 年过去了。

  16. 我想知道这是否适用于汽车制动,以减少刹车片磨损。

    1. 对于再生制动,一个类似的问题是如何以最佳方式断裂,以尽可能多地回收能量。

    2. 当汽车使用发动机制动而不是刹车时,其制动(磨损)效率更高,因此这可能是更好的自动制动方法。

      1. 很久以前,在一本我不记得书名的书中,一个人物说:”但发动机零件比制动器零件更贵”。这句话一直让我记忆犹新,即使我在滑行到停车时几乎不需要制动。

        1. 我是一名狂热的发动机制动器爱好者,住在丘陵地区,经常在山麓/山区驾驶,我认为在乘用车相对较小的负载和速度下,我只是在利用发动机已经持续的磨损状态,同时保留我的制动器,以备可能需要其最佳制动性能的时候使用。

        2. 非常正确–但我开车一直开到死–从来没有因为发动机制动而出现过汽车问题–通常是汽车的另一个部件在我对发动机造成损害之前就出现了问题。所以,发动机制动是不存在的!

        3. 没错,但这种说法有点假设发动机制动与摩擦制动是相同的烧蚀系统,具有相同的磨损特性。

          我必须承认,我不知道集成电路发动机在破损运行时的确切磨损特性,但我认为它与正常运行时的磨损没有任何区别。

          1. 它们当然完全相同,但要实现发动机断油,通常需要切换到更低的档位(或更低的几个档位),这意味着离合器需要重新连接,同时发动机的滚动转速要高于变速箱在该档位下所能承受的转速。每次降挡都会通过离合器进行另一次 “重新连接”。

            这意味着,在发动机内部阻力 “接管 “之前,会有一些摩擦制动。现在,虽然刹车片和离合器使用的材料 “相似”,但并不相同,成本也不一样(现代汽车的离合器通常更贵)。

            1. 如果驾驶员(或现在的汽车电脑)正确匹配转速,情况就不会这样。在我看来,这是所有手动挡驾驶员都应该掌握的技能。

        4. 拖拉机使用防抖制动器,其使用寿命接近一百万英里。最好的格言是 “适当的维护是最便宜的部分”。

      2. 这不就是因为发动机通过离合器提供的阻力比制动器小吗?同样的制动力(轻踩刹车),离合器的花费真的会比刹车片少吗?

        顺便说一句,现代飞轮离合器套件比刹车片贵得多,所以成本并不是那么一目了然。

        1. 不过,您并不是真的利用离合器表面来制动,您主要是依靠摩擦力和能量来压缩进气并将其从排气管中排出,同时尽量减少或不使用燃烧/燃料输入,如果是在高速行驶时,则依靠空气阻力。其他所有因素加起来也有帮助,例如变速箱、差速器、凸轮和曲柄轴承的摩擦,希望这些都是最小的,还有泵送冷却液和机油以及运行交流发电机等。在自动变速器中,液力变矩器中的液体加热也会损失一些能量。

          1. 同意,我主要指的是我在另一条评论中描述的用于发动机制动的换挡(降挡):这当然比刹车片的摩擦要小,但也比通常的换挡略多(假设转速匹配更顺畅)。但我们说的是两者的微小磨损,但价格差异很大,所以我想知道,换算成数千公里/英里,两者相比如何?

    3. 我经常对我的自行车产生同样的疑问。

  17. 那首主题曲至今仍在我脑海中回响。

  18. 如果 55 年来一直没有影响游戏的完整性,这真的是一个错误吗?

    1. 同意。这是一个只影响准确性的数字错误,但不是像《吃豆人》杀戮屏幕错误那样的严重故障,不能被利用(作弊/获取资源/生命,比如在《捍卫者》中达到 99 万分),也不是复活节彩蛋。

      请参阅 Glitchipedia: https://errors.fandom.com/wiki/Glitchipedia:About。

    1. 不只是你,还有一个选项可以了解更多信息,然后全盘否定。

    2. 我同意你的抽象观点,但 uMatrix 为我解决了这个问题。

      另外,有趣的是,人们常说 “因为 GDPR 才会有愚蠢的 cookie 横幅”,但实际上 GDPR 要求拒绝按钮和接受按钮一样大,一样容易点击,而大多数公司并不关心这部分。

      (另外,如果你的网站不放置 cookie,GDPR 也不需要 cookie 横幅。说真的,对于那些不打算登录的用户,你为什么需要它们呢?)

      1. 人们在指责 GDPR 的时候,应该指责那些进行侵入式跟踪的公司。这就好比把在食物中添加毒药定为非法,然后因为餐馆必须征得你的同意才能为你提供毒药而恼羞成怒。

        > 为什么不登录的用户也需要呢?

        因为无论如何,它们都会在你的浏览器中存储 Cookie,用于跟踪和广告目的

      2. > 当您的网站不放置 cookie 时,GDPR 并不需要 cookie 广告条

        您可以放置 cookie,但不需要 cookie 横幅。

        只是在尚未征得同意的情况下,您不能将同意作为法律依据,而这正是所有这些网站正在做的唯一一件事:他们想做的侵入性跟踪没有合法理由,因此他们有法律义务以明确的方式询问您是否同意。

        如果数据处理属于以下理由:

        – 为了履行合同(例如,您的访问者要求网站进行登录)

        – 或合法权益(不明确,但像设置 returnVisitor=true 这样的汇总统计数据应属于此范畴)

        – 或 GDPR 中定义的其他原因之一(我认为除了同意外,还有三个原因,如公共利益、主体的重大利益,还有一个我忘了)

        那么你就不需要征得同意来处理这些数据,或者在这种情况下,特别是放置 Cookie。

  19. 当然,这款游戏和其他游戏一样,肯定有成百上千个 BUG。

    1. 为什么?这是一个没有 “其他游戏 “的时代。见鬼,当时根本就没有电脑游戏:只有一些应用程序可以被称为 “电脑上的一种游戏”,但仅限于要求你执行一项任务,告诉应用程序你认为任务参数应该是多少,然后应用程序告诉你你是否正确。

      当时的应用程序仍以字节为单位(一般程序都非常庞大,以至于一切都需要以千字节为单位来描述,这种想法仍处于萌芽阶段)。因此,你根本没有足够的空间来隐藏 100 个错误,而这些错误会徘徊 55 年而不被发现:100 个错误就是你的全部源代码。事实上,文章中的游戏源代码如下:https://www.cs.brandeis.edu/~storer/LunarLander/LunarLander/…- 它有 2027 个字节。如果它有 100 个错误,甚至 5 个错误,要么就无法运行,要么就是明显错误。

      源代码也说明了这是一款什么样的 “游戏”:它只是一个文本提示,让你填入设置飞行的数值,然后根据你的参数运行模拟,并给出结果。在当时,这足以构成一款让人痴迷的游戏。

      后来情况发生了很大变化 =D

    2. 游戏很小。它甚至没有任何图形。如果能把 10 个小虫子(bug)都装进去,还能让它看起来像个正常运行的游戏,那将是一项了不起的壮举。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注