在吃豆人的这一关里,隐藏着来自程序员的深深恶意

游戏

2019-07-10 14:42

本文来自微信公众号「把科学带回家」(ID:steamforkids),作者七君,爱范儿经授权发布。

玩过吃豆人吧。通关过吗?

通关是不可能的,这辈子都不可能的。因为吃豆人根本不可能通关。

不可能通关的秘密,都藏在最后一关里。

吃豆人的最后一关,第 256 关吧,是不可解的。

基本上,你玩到这一关,就会看到这样的东西——

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

你没有注意到这个问题,可能是你平时太爱学习了。有些有编程经验的小朋友可能会马上反应过来,是不是 256 这个数字有问题啊?

的确,这个问题和 256 有关,不过不是你想的这种关系。

是这样的,在吃豆人游戏刚出来的时候,也就是 80 年代的时候,大多数微芯片的处理器是 8 位的。

计算机是 2 进制的,所以 1 就是 1,2 就是 10,3 就是 11,4 就是 100,以此类推。

8 位的意思是,处理器有 8 个「格子」,每个「格子」只能表示 0 或 1,最多能表示 8 位的二进制数字。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

所以,它能显示的最大数字就是二进制的 1111 1111,也就是十进制的 255。

那么此时我让它再加 1 会发生什么事呢?

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

因为最多只有 8 位,不能进 1 位变成 9 位,所以处理器就会变成 0000 0000,也就是 0。这就是整数溢出了。换言之,在 8 位处理器里,能够表达的最大的数字是 255。

可是,吃豆人的死忠粉把它的源代码调了出来,他们发现问题不在于关数上,而是出在了显示的过程中。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

是这样的,吃豆人的程序员利用了 0 来表示第 1 关,所以第一关的代码实际上是 0,第二关是 1,第三关是 2,…,所以第 256 关的代码实际上是 255,并没有发生整数溢出的问题。

但是,水果有问题。

吃豆人的源代码显示,当初那个程序员可能是最早意识到枸杞保温杯不加班,胜过可乐加冰又加薪的程序员之一。

TA 想要在每一关的底部画水果,用水果的个数和种类代表关数。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

具体来说,第一关解锁的水果是樱桃,所以第一关底部是 1 个樱桃。

第二关解锁的草莓,底部是 1 个樱桃+1 个草莓。

第三关和第四关是桃子,第 3 关的底部是 1 个樱桃+1 个草莓+1 个桃子;第 4 关的底部是 1 个樱桃+1 个草莓+2 个桃子。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

5 和 6 是苹果,7 和 8 是蜜瓜,9 和 10 是星际战舰,11 和 12 是铃铛,13 关及以上是钥匙。

在第 7 关及以下,显示过去所有关卡解锁的所有水果。

到了第 8 关及以上,显示的是最近 7 关获得的手办。

总之这些水果和手办的作用就是显摆你通的关多,不能吃也不能拿来+1up 命。

那么,这位养生系的程序员是怎么挑选水果的呢?

TA 把所有关卡能够解锁的水果还有手办做成了一个这样的包含 20 个项目的表格,储存在内存里。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

然后在每一关一开始,养生程序员是这样设定的:

查看一下本关关数代码 A。我们刚才说过,第 1 关的二进制代码是 0000 0000,第 2 关是 0000 0001,第 256 关是 1111 1111。

好,把 A+1,变成真正的关卡数 L。

判断一下 L 是否小于 8。

如果答案为是,则从水果表格的第 1 个画到第 L 个为止。

如果答案为否,则从表格的第 L-6 个画到第 L 个为止(从第 19 关开始画 7 把钥匙)。

所以呢,第 1 关就从表格的第 1 个画到第 1 个,也就是画 1 个水果就可以了。第 2 关就画 2 个水果。

那么第 256 关呢?

第 256 关储存的代码,也就是 A 是 1111 1111。但是要把 A 加 1 变成 L 的时候,就出问题了,因为此时就会产生整数溢出的问题,L 变成了 0。

既然第 256 关的 L 是 0,所以机器判断,要从水果表格的第 1 个画到… 第 0 个…

这可怎么画?

实际上,画水果的程序具体是这样的执行的:

先从第一个水果开始画,画好一个+1 得到 F,F 再和 L 对比一下。

如果 F = L,就停止不画了。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

所以,如果 F = 0,要画多少个水果呢?

其实很简单,因为 F 也会发生整数溢出问题,所以画到 256,F 也变成 0 了。因此在第 256 关,画 256 个水果就刚好了。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

可是我们还记得,记录水果的表格只有 20 个项目,没有 256 这么多啊。那怎么办?

原来,水果表格被储存在内存的$3B08 这个地方,往后的数据是背景音乐什么的。

表格里的水果和手办画完以后,机器就开始画内存后面储存的东西,所以画出来的东西就是乱七八糟的,这就好比计算机把它脑子里的胡话全部打印到屏幕上一样。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

这就导致,吃豆人在 256 关吃不完所有的豆子,因为一部分豆子没有显示出来。因为没有办法吃掉所有的豆子,所以这关是永远过不了的。

玩家会在这一关兜兜转转,直到有社恐的圆脸小黄人被四色小妖怪逼到墙角摸来摸去最后摸死了。

2010 年 5 月 21 日的谷歌涂鸦就还原了这个梗,玩到第 255 关结束,屏幕上就会跳出「Game Over」。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

也因为第 256 关永远过不了,所以吃豆人也有了官方认证的理论最高分——3333360。

第一个拿下这个分数的,是一位叫做 Billy L Mitchell 的选手,记录创建时间为 1999 年 7 月 3 日。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

比利同志也被吃豆人的制作方——南梦宫创始人中村雅哉颁发了「世纪玩家」的头衔。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

▲ 南梦宫创始人中村雅哉(左男)和 Billy L Mitchell(右男)

比利小哥为什么能拿满分?人家的手速为 1 秒的 160 分之一。拥有这个手速是什么样的体验,大家可以自行感受一下——

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

▲ Billy L Mitchell 的手速为 1/160 秒 @greatbigstories

顺便说一句,其实当时程序员可以在内存里分配更多的空间来储存关卡数(关卡的计数器),你看总分就被分配了 21 位,所以可以计到 3 百万嘛。

那么这位养生程序员为什么没有这么做呢?大概 TA 根本瞧不起碳基生物的手速,认为正常人类不可能打到 256 关吧,毕竟比利发现第 256 关秘密的那年,离吃豆人首发已经过去了整整 19 年。

实际上,比利小哥后来在接受采访时表示,南梦宫是收到了他发过去的截图后,才知道原来自家游戏的第 256 关长这样。原来你们不仅瞧不起游戏宅的手速,还根本就没有 debug 过啊。

其实,不光吃豆人的第 256 关有整数溢出问题,我们还可以用整数溢出问题的设定去吓唬里面那个粉红色的小鬼 Pinky。

在官方设定里,Pinky 会走到吃豆人前方埋伏玩家,所以 Pinky 的官方的名称叫做「埋伏者」。

从源代码来看,Pinky 的设定是走到吃豆人前方 16 个像素,也即是 2 个身位的地方伏击它。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

可是 Pinky 也会遇到整数溢出的问题。如果吃豆人朝上,Pinky 就会走到吃豆人左边 4 个身位+上方 4 个身位的地方。而且,因为 Pinky 的设定是走在吃豆人前面伏击,所以如果吃豆人向着它跑,它就会逃走。

利用这一点,就可以调戏 Pinky 了。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

你大概觉得整数溢出问题只存在于二维世界。但实际上,整数溢出问题也存在于三维世界。

比如… 在瑞士,法律规定火车不能有 256 根车轴。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

为什么呢?

因为瑞士联邦铁路(SBB)使用的计轴器,就是在铁路上计算经过的火车的车轴数量的仪器也是 8 位的,它记到了 256 就会溢出变成 0。

0 轴就是没有火车通过,所以这个傻乎乎的计轴器就会报告明明有 256 根车轴的小火车不存在。emmm,原本开往幼儿园的车可能就会开往天堂。

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

▲ 计轴器@wikipedia

总之,在机器寿命大大超过人均寿命的瑞士,大家并不想换计轴器,那就干脆立法规定不允许火车有 256 根车轴好了!

在吃豆人的这一关里,隐藏着来自程序员的深深恶意

▲ 瑞士联邦铁路规定,火车不能有 256 根车轴。从 2012 年 7 月 1 日开始执行。图片来源:Ausführungsbestimmungen zu den, Fahrdienstvorschriften, AB FDV Infrastruktur, Neuausgabe

看完吃豆人的故事我们明白,如果你觉得生活欺骗了你,生活中充满了 bug 永远也看不到获胜的希望,可能是这个宇宙的程序员没有料到你这么能打,还能来到这个关卡吧。恭喜你啦!

登录,参与讨论前请先登录

评论在审核通过后将对所有人可见

正在加载中