本文由公眾號 “把科學帶回家” 提供
給孩子最好的科學教育


作者 七君


玩過吃豆人吧。通關過嗎?


通關是不可能的,這輩子都不可能的。因為吃豆人根本不可能通關。


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


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


基本上,你玩到這一關,就會看到這樣的東西——




你沒有注意到這個問題,可能是你平時太愛學習了。有些有編程經驗的小朋友可能會馬上反應過來,是不是256這個數字有問題啊?


的確,這個問題和256有關,不過不是你想的這種關系。


是這樣的,在吃豆人游戲剛出來的時候,也就是80年代的時候,大多數微芯片的處理器是8位的。




計算機是2進制的,所以1就是12就是103就是114就是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個桃子。




56是蘋果,78是蜜瓜,910星際戰艦1112是鈴鐺,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 再和 對比一下。


如果 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永遠也看不到獲勝的希望,可能是這個宇宙的程序員沒有料到你這么能打,還能來到這個關卡吧。恭喜你啦!


在吃豆人的這一關里,隱藏著來自程序員的深深惡意

圖文簡介

原來吃豆人游戲里還有這么多暗藏的玄機。