本文由公眾號(hào) “把科學(xué)帶回家” 提供給孩子最好的科學(xué)教育
作者 七君
玩過吃豆人吧。通關(guān)過嗎?
通關(guān)是不可能的,這輩子都不可能的。因?yàn)槌远谷烁静豢赡芡P(guān)。
不可能通關(guān)的秘密,都藏在最后一關(guān)里。
吃豆人的最后一關(guān),第256關(guān)吧,是不可解的。
基本上,你玩到這一關(guān),就會(huì)看到這樣的東西——
你沒有注意到這個(gè)問題,可能是你平時(shí)太愛學(xué)習(xí)了。有些有編程經(jīng)驗(yàn)的小朋友可能會(huì)馬上反應(yīng)過來,是不是256這個(gè)數(shù)字有問題啊?
的確,這個(gè)問題和256有關(guān),不過不是你想的這種關(guān)系。
是這樣的,在吃豆人游戲剛出來的時(shí)候,也就是80年代的時(shí)候,大多數(shù)微芯片的處理器是8位的。
計(jì)算機(jī)是2進(jìn)制的,所以1就是1,2就是10,3就是11,4就是100,以此類推。
8位的意思是,處理器有8個(gè)“格子”,每個(gè)“格子”只能表示0或1,最多能表示8位的二進(jìn)制數(shù)字。
所以,它能顯示的最大數(shù)字就是二進(jìn)制的 1111 1111,也就是十進(jìn)制的255。
那么此時(shí)我讓它再加1會(huì)發(fā)生什么事呢?
因?yàn)樽疃嘀挥?位,不能進(jìn)1位變成9位,所以處理器就會(huì)變成0000 0000,也就是0。這就是整數(shù)溢出了。換言之,在8位處理器里,能夠表達(dá)的最大的數(shù)字是255。
可是,吃豆人的死忠粉把它的源代碼調(diào)了出來,他們發(fā)現(xiàn)問題不在于關(guān)數(shù)上,而是出在了顯示的過程中。
是這樣的,吃豆人的程序員利用了0來表示第1關(guān),所以第一關(guān)的代碼實(shí)際上是0,第二關(guān)是1,第三關(guān)是2,…,所以第256關(guān)的代碼實(shí)際上是255,并沒有發(fā)生整數(shù)溢出的問題。
但是,水果有問題。
吃豆人的源代碼顯示,當(dāng)初那個(gè)程序員可能是最早意識(shí)到枸杞保溫杯不加班,勝過可樂加冰又加薪的程序員之一。
TA想要在每一關(guān)的底部畫水果,用水果的個(gè)數(shù)和種類代表關(guān)數(shù)。
具體來說,第一關(guān)解鎖的水果是櫻桃,所以第一關(guān)底部是1個(gè)櫻桃。
第二關(guān)解鎖的草莓,底部是1個(gè)櫻桃+1個(gè)草莓。
第三關(guān)和第四關(guān)是桃子,第3關(guān)的底部是1個(gè)櫻桃+1個(gè)草莓+1個(gè)桃子;第4關(guān)的底部是1個(gè)櫻桃+1個(gè)草莓+2個(gè)桃子。
5和6是蘋果,7和8是蜜瓜,9和10是星際戰(zhàn)艦,11和12是鈴鐺,13關(guān)及以上是鑰匙。
在第7關(guān)及以下,顯示過去所有關(guān)卡解鎖的所有水果。
到了第8關(guān)及以上,顯示的是最近7關(guān)獲得的手辦。
總之這些水果和手辦的作用就是顯擺你通的關(guān)多,不能吃也不能拿來+1up命。
那么,這位養(yǎng)生系的程序員是怎么挑選水果的呢?
TA把所有關(guān)卡能夠解鎖的水果還有手辦做成了一個(gè)這樣的包含20個(gè)項(xiàng)目的表格,儲(chǔ)存在內(nèi)存里。
然后在每一關(guān)一開始,養(yǎng)生程序員是這樣設(shè)定的:
查看一下本關(guān)關(guān)數(shù)代碼 A。我們剛才說過,第1關(guān)的二進(jìn)制代碼是0000 0000,第2關(guān)是0000 0001,第256關(guān)是1111 1111。
好,把 A+1,變成真正的關(guān)卡數(shù) L。
判斷一下 L 是否小于8。
如果答案為是,則從水果表格的第1個(gè)畫到第 L 個(gè)為止。
如果答案為否,則從表格的第L-6個(gè)畫到第 L 個(gè)為止(從第19關(guān)開始畫7把鑰匙)。
所以呢,第1關(guān)就從表格的第1個(gè)畫到第1個(gè),也就是畫1個(gè)水果就可以了。第2關(guān)就畫2個(gè)水果。
那么第256關(guān)呢?
第256關(guān)儲(chǔ)存的代碼,也就是A是1111 1111。但是要把 A 加1變成 L 的時(shí)候,就出問題了,因?yàn)榇藭r(shí)就會(huì)產(chǎn)生整數(shù)溢出的問題,L 變成了0。
既然第256關(guān)的 L 是0,所以機(jī)器判斷,要從水果表格的第1個(gè)畫到...第0個(gè)...
這可怎么畫?
實(shí)際上,畫水果的程序具體是這樣的執(zhí)行的:
先從第一個(gè)水果開始畫,畫好一個(gè)+1得到 F,F(xiàn) 再和L對(duì)比一下。
如果 F =L,就停止不畫了。
所以,如果 F = 0,要畫多少個(gè)水果呢?
其實(shí)很簡單,因?yàn)?F 也會(huì)發(fā)生整數(shù)溢出問題,所以畫到256,F(xiàn) 也變成0了。因此在第256關(guān),畫256個(gè)水果就剛好了。
可是我們還記得,記錄水果的表格只有20個(gè)項(xiàng)目,沒有256這么多啊。那怎么辦?
原來,水果表格被儲(chǔ)存在內(nèi)存的$3B08這個(gè)地方,往后的數(shù)據(jù)是背景音樂什么的。
表格里的水果和手辦畫完以后,機(jī)器就開始畫內(nèi)存后面儲(chǔ)存的東西,所以畫出來的東西就是亂七八糟的,這就好比計(jì)算機(jī)把它腦子里的胡話全部打印到屏幕上一樣。
這就導(dǎo)致,吃豆人在256關(guān)吃不完所有的豆子,因?yàn)橐徊糠侄棺記]有顯示出來。因?yàn)闆]有辦法吃掉所有的豆子,所以這關(guān)是永遠(yuǎn)過不了的。
玩家會(huì)在這一關(guān)兜兜轉(zhuǎn)轉(zhuǎn),直到有社恐的圓臉小黃人被四色小妖怪逼到墻角摸來摸去最后摸死了,喵的咪的。
2010年5月21日的谷歌涂鴉就還原了這個(gè)梗,玩到第255關(guān)結(jié)束,屏幕上就會(huì)跳出“Game Over”。
也因?yàn)榈?56關(guān)永遠(yuǎn)過不了,所以吃豆人也有了官方認(rèn)證的理論最高分——3333360。
第一個(gè)拿下這個(gè)分?jǐn)?shù)的,是一位叫做 Billy L Mitchell 的選手,記錄創(chuàng)建時(shí)間為1999年7月3日。
比利同志也被吃豆人的制作方——南夢(mèng)宮創(chuàng)始人中村雅哉頒發(fā)了“世紀(jì)玩家”的頭銜。
南夢(mèng)宮創(chuàng)始人中村雅哉(左男)和 Billy L Mitchell(右男)
比利小哥為什么能拿滿分?人家的手速為1秒的160分之一。擁有這個(gè)手速是什么樣的體驗(yàn),大家可以自行感受一下——
Billy L Mitchell的手速為1/160秒
@greatbigstories
順便說一句,其實(shí)當(dāng)時(shí)程序員可以在內(nèi)存里分配更多的空間來儲(chǔ)存關(guān)卡數(shù)(關(guān)卡的計(jì)數(shù)器),你看總分就被分配了21位,所以可以計(jì)到3百萬嘛。
那么這位養(yǎng)生程序員為什么沒有這么做呢?大概TA根本瞧不起碳基生物的手速,認(rèn)為正常人類不可能打到256關(guān)吧,畢竟比利發(fā)現(xiàn)第256關(guān)秘密的那年,離吃豆人首發(fā)已經(jīng)過去了整整19年。
實(shí)際上,比利小哥后來在接受采訪時(shí)表示,南夢(mèng)宮是收到了他發(fā)過去的截圖后,才知道原來自家游戲的第256關(guān)長這樣。原來你們不僅瞧不起游戲宅的手速,還根本就沒有debug過啊,墳蛋!
(╯‵□′)╯︵┴─┴.
其實(shí),不光吃豆人的第256關(guān)有整數(shù)溢出問題,我們還可以用整數(shù)溢出問題的設(shè)定去嚇唬里面那個(gè)粉紅色的小鬼Pinky。
在官方設(shè)定里,Pinky 會(huì)走到吃豆人前方埋伏玩家,所以 Pinky 的官方的名稱叫做“埋伏者”。
從源代碼來看,Pinky的設(shè)定是走到吃豆人前方16個(gè)像素,也即是2個(gè)身位的地方伏擊它。
可是 Pinky 也會(huì)遇到整數(shù)溢出的問題。如果吃豆人朝上,Pinky就會(huì)走到吃豆人左邊4個(gè)身位+上方4個(gè)身位的地方。而且,因?yàn)镻inky的設(shè)定是走在吃豆人前面伏擊,所以如果吃豆人向著它跑,它就會(huì)逃走。
利用這一點(diǎn),就可以調(diào)戲 Pinky 了。
你大概覺得整數(shù)溢出問題只存在于二維世界。但實(shí)際上,整數(shù)溢出問題也存在于三維世界。
比如...在瑞士,法律規(guī)定火車不能有256根車軸。
為什么呢?
因?yàn)槿鹗柯?lián)邦鐵路(SBB)使用的計(jì)軸器,就是在鐵路上計(jì)算經(jīng)過的火車的車軸數(shù)量的儀器也是8位的,它記到了256就會(huì)溢出變成0。
0軸就是沒有火車通過,所以這個(gè)傻乎乎的計(jì)軸器就會(huì)報(bào)告明明有256根車軸的小火車不存在。emmm,原本開往幼兒園的車可能就會(huì)開往天堂。
計(jì)軸器
@wikipedia
總之,在機(jī)器壽命大大超過人均壽命的瑞士,大家并不想換計(jì)軸器,那就干脆立法規(guī)定不允許火車有256根車軸好了,棒呆!(??????)??
瑞士聯(lián)邦鐵路規(guī)定,火車不能有256根車軸。從2012年7月1日開始執(zhí)行。
圖片來源:Ausführungsbestimmungen zu den, Fahrdienstvorschriften, AB FDV Infrastruktur, Neuausgabe
看完吃豆人的故事我們明白,如果你覺得生活欺騙了你,生活中充滿了bug永遠(yuǎn)也看不到獲勝的希望,可能是這個(gè)宇宙的程序員沒有料到你這么能打,還能來到這個(gè)關(guān)卡吧。恭喜你啦!