「MC篩種雜談3」如何得到54黑曜石的鐵匠鋪 Part 1
一、回顧
????前兩期中我們已經(jīng)講解了:
如何從世界種子和區(qū)域得到該區(qū)域內(nèi)結(jié)構(gòu)的具體位置
如何從世界種子和結(jié)構(gòu)位置得到結(jié)構(gòu)內(nèi)箱子的具體位置
如何從世界種子和箱子位置得到該區(qū)塊的裝飾種子
如何從裝飾種子得到箱子的lootTableSeed
如何從lootTable和lootTableSeed得到最終戰(zhàn)利品
????如果想要獲取接近戰(zhàn)利品表極限的戰(zhàn)利品生成,用上述方法需要地毯式搜索大量種子和大量區(qū)塊,這是十分消耗算力的。
????然而,以LCG為原理的Java隨機(jī)數(shù)生成器(rand),擁有著可逆的特性,這允許我們寫出相應(yīng)的算法,以rand的部分結(jié)果出發(fā),推斷rand的種子。
????因此我們能夠?qū)⑸鲜鲞^程完全反過來,僅用數(shù)秒就能得到結(jié)果。
二、相關(guān)知識介紹
????你需要知道這些事實:
每個rand被初始化時都需要種子。
如果你不提供,Java會調(diào)取系統(tǒng)時間并進(jìn)行一些運(yùn)算來生成一個種子。
初始化時,有是否混淆(scramble)的選項。如果混淆,會先把種子與一個常數(shù)先異或一次,也就是說 seed ^=?25214903917L。
現(xiàn)在rand擁有了種子,記為seed1。
當(dāng)我調(diào)用nextLong時,rand會對seed1進(jìn)行一些運(yùn)算,得到一個種子,記為seed2,和一個長整數(shù)long2,它通常與seed2不相等。
然后rand會將seed2設(shè)為種子,并為我的調(diào)用返回這個long2。
類似地,當(dāng)我調(diào)用nextInt(bound)時,rand會對seed2進(jìn)行一些運(yùn)算,得到一個種子,記為seed3,和一個在值在bound之內(nèi)的整數(shù),它通常與seed3不相等。
????可見,調(diào)用次數(shù)和順序都對結(jié)果有重要影響。當(dāng)我們想找符合條件的種子時,我們必須知道這個種子被調(diào)用隨機(jī)數(shù)的次數(shù)和順序。
三、實戰(zhàn):廢棄傳送門的附魔金蘋果
????我想獲取對應(yīng)著8個附魔金蘋果的廢棄傳送門箱子的lootTableSeed。首先要分析它生成戰(zhàn)利品時,調(diào)用隨機(jī)數(shù)的情況。貼出戰(zhàn)利品表:
????首先,調(diào)用nextInt(5),且必須得到4,才能擁有8次抽獎次數(shù)。然后開始抽獎,第一次抽獎,調(diào)用nextInt(398),得到396,表明附魔金蘋果。因為這一獎項并沒有任何函數(shù),第一次抽獎結(jié)束。很明顯,后面七次也如此。用代碼表示如下:
????先創(chuàng)建一個dynamicProgram(這僅僅是個名字),然后加條件,然后用循環(huán)加條件。最后用dynamicProgram.reverse()就得到了一個長整數(shù)流(LongStream),熟悉Jvav的同學(xué)可以對這個流進(jìn)行一些更花哨的操作,但我這里還是把它直接收集成一個List<Long>,以便解釋。
????運(yùn)行!結(jié)果發(fā)現(xiàn)list是空的,這表明不存在這樣的lootTableSeed,與我們概率預(yù)測相符。不過,我們?nèi)匀豢梢詼p弱條件,得到一些較接近極限的lootTableSeed。
????例如:我們還是讓抽獎次數(shù)是8,然后我們只讓前5次抽獎是附魔金蘋果,無視后面3次。我相信讀者知道怎么修改程序?,F(xiàn)在它就能夠跑出一些結(jié)果。需注意,這些種子在setSeed而不scramble的情況下滿足條件。因為MC在對使用lootTableSeed生成戰(zhàn)利品時,setSeed使用了scramble,可見,我們需要將上述程序輸出的“經(jīng)過異或的”xoredLootTableSeed,再進(jìn)行一次異或,來得到原始的lootTableSeed。(注意,異或兩次相當(dāng)于什么都沒做,相當(dāng)于還原,這是編程常識)
????我在dynamicProgram后面加了個映射方法,將每個種子異或一下LCG.JAVA.multiplier,也就是前面說過的25214903917L?,F(xiàn)在它們直接就是可用的lootTableSeed了。為了驗證,我創(chuàng)建一個lootContext,使用list中第一個種子作為lootTableSeed,用廢棄傳送門的戰(zhàn)利品表來生成戰(zhàn)利品??梢娸敵隽宋鍌€附魔金蘋果和其它三件獎項。
????下一步,既然lootTableSeed是由:
將rand設(shè)置種子為DecoratorSeed并scramble,然后nextLong
得到的,那么我們使用getSeeds方法得到一些種子,它們滿足:被setSeed而不scramble的時候,其nextLong是我們輸入的值(lootTableSeed)。我們稱其為“經(jīng)過異或的裝飾種子”xoredDecoratorSeeds。
????現(xiàn)在遍歷我們上面得到的lootTableSeed的列表,對每個lootTableSeed,我們用getSeeds方法得到它對應(yīng)的經(jīng)過異或的裝飾種子列表。
????再遍歷這個列表,將每個種子異或一下,得到真正的裝飾種子。因為裝飾種子是由族群種子(populationSeed)加上salt得到的,我們將裝飾種子減去salt就得到了族群種子。注意廢棄傳送門的salt是40005,我們使用了一個reverseDecoratorSeed的方法來進(jìn)行這一過程。
????族群種子是由世界種子和區(qū)塊位置完全決定的。由此我們可以遍歷一些區(qū)塊,并進(jìn)行后繼的操作。因為廢棄傳送門Region參數(shù)的關(guān)系,我們選取區(qū)塊X坐標(biāo)和區(qū)塊Z坐標(biāo)在0到25之間。這些位置可能生成廢棄傳送門,且離原點(diǎn)較近。
????然后對族群種子,我們使用reversePopulationSeed方法來得到結(jié)構(gòu)種子(structSeed)的列表。別以為到這里就結(jié)束了,事實上這些結(jié)構(gòu)種子對應(yīng)的該Region內(nèi)的廢棄傳送門的坐標(biāo)并不一定就是之前遍歷的位置。
????我們再遍歷一下結(jié)構(gòu)種子的列表,每次用getInRegion得到廢棄傳送門的位置,然后與之前遍歷的位置作比較?,F(xiàn)在它輸出的種子就真正地能用了。
????程序輸出:
????我們不妨再正向驗證一下。例如對第一個74348597233418,我們用它先獲取該Region內(nèi)的廢棄傳送門的區(qū)塊坐標(biāo),然后對這個區(qū)塊設(shè)置populationSeed,再用populationSeed加上salt得到decoratorSeed,并設(shè)置為rand的種子,現(xiàn)在nextLong得到lootTableSeed,用lootContext得到戰(zhàn)利品并輸出。
????可見符合五個附魔金蘋果的要求。實在不放心你還可以進(jìn)游戲查看。
????實際上,因為每個結(jié)構(gòu)種子對應(yīng)65536個姐妹世界種子,它們都在相同的區(qū)塊位置有著廢棄傳送門。然而,不同的世界種子具有不同的群系,不同的群系會影響廢棄傳送門的生成,得到不同群系對應(yīng)的變種。這些變種可能使得廢棄傳送門的箱子的位置跨越區(qū)塊,得到“錯誤的”戰(zhàn)利品。萬幸地,我們有廢棄傳送門的Generator,可以檢查每個世界種子實際情況如何。
????我們還是沿用74348597233418的結(jié)構(gòu)種子,查看它的65536個姐妹世界種子。我們讓高16位(up)遍歷0到65536,然后把up往左移48位,再與結(jié)構(gòu)種子做或運(yùn)算,就得到了世界種子(worldSeed)。然后我們new一個gen,并調(diào)用generate方法。我們需要傳入主世界地形生成器(overworldTerrainGenerator),而它又需要傳入一個主世界群系,而這又需要世界種子和版本。我們一一傳入,再給gen提供廢棄傳送門的坐標(biāo)pos,和一個rand。
????現(xiàn)在gen就生成完畢了,我們可以對gen調(diào)用方法來獲取信息。我們調(diào)用getChestsChunkPos得到一個列表,它的每個元素都是一個pair,這個pair左邊是ILootType,可以理解為戰(zhàn)利品表,而右邊是CPos,即箱子對應(yīng)的區(qū)塊坐標(biāo)。因為廢棄傳送門只有一個箱子,我們get(0)得到這個箱子對應(yīng)的pair,又getSecond()得到區(qū)塊坐標(biāo)。
????現(xiàn)在我們知道,如果箱子在pos內(nèi),它就能生成5附魔金蘋果。如果不在pos內(nèi),戰(zhàn)利品會改變,不滿足需求。所以我們再檢查chestChunk與pos是否相同,如果相同就能輸出世界種子了。
? ? 這個程序?qū)敵龃蠹s六萬個世界種子。每個世界種子在區(qū)塊Pos{x=12, y=0, z=2}的地方都有一個五附魔金的箱子。
????注意到我們有三個不同的結(jié)構(gòu)種子,可見滿足我們的條件的世界種子大約有18萬個?;仡櫼幌聴l件:在Region(0,0)內(nèi)有一個廢棄傳送門,使得它的箱子沒有跨過結(jié)構(gòu)所在的區(qū)塊,而且這個區(qū)塊對應(yīng)的廢棄傳送門箱子,生成戰(zhàn)利品時,抽獎會抽8次,其中前5次都是附魔金蘋果,而不管后3次如何。
????很明顯,這并不是全部擁有五附魔金廢門的世界種子。還有許多種子我們沒有考慮到,比如允許讓廢門不在Region(0,0),比如允許箱子跨過區(qū)塊,比如允許抽獎不是8次,比如允許抽獎前5次不全是附魔金蘋果,而是所有抽獎次數(shù)加起來有5個附魔金蘋果。
????但我想說的是,我們關(guān)心的是存在性,只要我們找到了5附魔金蘋果的種子就行了。放寬這些條件都僅僅能讓數(shù)量提高幾倍或者幾十倍,屬于量變,但無法引起質(zhì)變:找到6附魔金蘋果的種子。事實上,我們通過一些程序驗證,幾乎可以斷言廢門6附魔金是不存在的。
四、收尾
????本來用廢棄傳送門舉例只是為了方便理解這種逆向思考的體系的,但寫到這里已經(jīng)三千五百字了,所以我們將54黑曜石的實戰(zhàn)放到下一篇講吧。