這是一個半技術向的博客,主題來源于我讀過的某本書的片段,這是一個稍稍有些前置知識的故事,主題的大概內容就是假定世界存在某個規則序列,通過一代代的探索,可以獲取到此序列的內容。本文將模擬此情形,寫一個隨機數的小測試應用,來不嚴謹的證明,或者準確來說是用代碼講述這個故事
天有五賊,見之者昌。 傳說,在一個由某逗寫的代碼世界里面,世界里藏著一段取值范圍為 0 到 100 且長度為 10 的序列。某逗將此序列稱為世界序列。在此世界里面,還生活著一群名為 Element 的小人兒。這些 Element 小人兒,都在探索和追求著世界序列。因為呀,每當此世界經過一代的時候,嗯,約等于某個大循環執行一遍的時候,將會根據此世界序列的內容,再加上某逗規定的算法,決定哪些 Element 小人兒需要被淘汰掉。已知的是,如果 Element 小人兒越了解準確世界序列,那 Element 小人兒就越能生存下來
此世界每經過一代,將會淘汰一批 Element 小人兒。而存活下來的 Element 小人兒將會迎來新的一輪生娃過程。可以由存活下來的 Element 小人兒創建出更多的下一代的 Element 小人兒
在經過此世界的很多代之后,咱來看看存活下來的 Element 小人兒是否都掌握了正確的世界序列
接下來就是寫代碼的內容了,命題確定了,其實通過簡單的大學數學的知識,是能夠計算出結果的,只不過咱是計算機系的,肯定要寫點代碼啦
按照本文的題注,天有五賊,見之者昌,先用代碼寫天有五賊。我的文學修養還不夠,不能達意的解釋天有五賊,見之者昌這句話。只能用代碼的方式,以一個片面的方式來解釋。在某逗寫的代碼世界里面,此世界的世界序列就是對應著天有五賊的意思。這片天地有五賊,五賊大概就是金木水火土五行的意思,也等于說世界的規則或者說世界的本源。本文的世界序列,就大概屬于某逗寫的代碼世界里面的本源。而見之者昌可以認為是,如果有人可以知曉五賊,約等于說如果有人可以知道世界的規則或者說世界的本源,那他將會昌盛。對應某逗寫的代碼世界里面的 Element 小人兒,如果 Element 小人兒能夠知曉世界序列,那他更能在此代碼世界的一代代循環里面存活,而且 Element 小人兒也就能生產更多的下一代的 Element 小人兒,不斷昌盛
某逗寫的代碼世界里的世界序列,序列里面的每個項,都可以用一個 Key 的只讀結構體來表示。之所以不直接使用 int 等來表示,那是為了給一個單位,這屬于程序員的修養。如以下代碼定義了 Key 這個只讀結構體,本文代碼基于 dotnet 7 版本編寫,本文代碼都在 GitHub 上開源
readonly record struct Key(int N)
{public const int MaxKeyValue = 100;
}
世界序列也就是一個?List<Key>
?類型。當然,有了代碼世界,為了代碼世界能夠好好工作,還需要一個世界管理者,也就是以下代碼定義的 Manager 類型,在 Manager 里面就存放了世界序列,代碼如下
class Manager
{public List<Key> KeyList { get; }... // 忽略其他代碼
}
生活在代碼世界里面的是一群 Element 小人兒,使用的是 Element 類型,每個 Element 小人兒都有自己的一段序列。這就是 Element 小人兒最重要的,也是不斷探索的內容
class Element
{public List<Key> KeyList { get; } = new List<Key>();... // 忽略其他代碼
}
在代碼世界剛開始的時候,也就是 Manager 被創建出來的時候,將會立刻初始化世界序列
public Manager(){KeyList = new List<Key>();for (int i = 0; i < 10; i++){var key = Random.Shared.Next(Key.MaxKeyValue);KeyList.Add(new Key(key));}}
世界開始的時候,將會進入 Manager 的 Start 方法,在初始化世界時,將會創建出首批的 Element 小人兒,如以下代碼,由于怕電腦被玩壞,這里約束了 Element 小人兒的數量大概只有 10000 個
class Manager
{public void Start(){for (int i = 0; i < 10000; i++){var element = CreateElement();ElementList.Add(element);}}public List<Element> ElementList { get; } = new List<Element>();... // 忽略其他代碼
}
以上的 CreateElement 方法是一個用來輔助創建 Element 小人兒對象的方法,在實現他之前,還請先看看 Element 類型的構造函數吧
在 Element 類型里面,不僅包含了 KeyList 屬性,還包括了兩個輔助代碼運行的屬性,分別是 Random 隨機數屬性和 FinishBuildKey 屬性,還請讓我賣個關子,在本文后續部分再來揭曉 FinishBuildKey 屬性的作用。在 Element 類型的構造函數里面,將要求傳入 Random 屬性,以讓 Element 在每一代里面,擁有猜測世界序列的能力,且多個 Element 之間有奇妙的關系
class Element
{public Element(Random random){Random = random;}public Random Random { get; }private bool FinishBuildKey { get; set; }public List<Key> KeyList { get; } = new List<Key>();... // 忽略其他代碼
}
在了解到 Element 的構造函數之后,相信大家也能猜到 CreateElement 方法的實現了
private Element CreateElement(){Element element = new Element(new Random(Random.Shared.Next()));return element;}
在完成了代碼世界的首批 Element 小人兒創建之后,就要進入激烈的世界迭代了。先創建一個大循環,大概是 10000 次。表示這個世界將會開始迭代 10000 次
for (int i = 0; i < 10000; i++){... // 忽略其他代碼}
在每一代的開始時,都會讓每個 Element 小人兒進行一輪思考,讓 Element 決定是否在自己的 KeyList 里面,加上新的 Key 值。當然,每個 Element 小人兒的思考方式,現在就是通過 Random 隨機數生成的哈
for (int i = 0; i < 10000; i++){foreach (var element in ElementList){element.BuildKey();}... // 忽略其他代碼}
以上的 BuildKey 函數就是放在 Element 里的函數,用于讓 Element 小人兒思考新的 Key 值是什么,大概的實現如下
class Element
{public bool BuildKey(){var key = Random.Next(Key.MaxKeyValue);KeyList.Add(new Key(key));return true;}... // 忽略其他代碼
}
當然了,有些 Element 小人兒決定放棄思考,也就是 Element 可以不再生成新的 Key 值。這也就是上文賣關子的 FinishBuildKey 屬性的作用,此屬性用來判斷是否此 Element 已不再生成新的 Key 值了,約等于此 Element 認為自己已猜出了世界序列
class Element
{public bool BuildKey(){if (KeyList.Count > 0 && (FinishBuildKey || Random.Shared.Next(10) == 1)){FinishBuildKey = true;return false;}var key = Random.Next(Key.MaxKeyValue);KeyList.Add(new Key(key));return true;}... // 忽略其他代碼
}
等待各個 Element 思考完成之后,此代碼世界的考驗就要開始了。在此世界的天道規則是從當前的世界序列里面通過當前世界的代數,也就是世界大循環的 i 變量的值,決定出世界序列中的一個 Key 值。對于每個 Element 來說,也要根據當前世界的代數返回一個 Key 值。一旦 Element 的返回的 Key 和此世界的 Key 值不相同,那么此 Element 將會被淘汰
說起來簡單,先來看看代碼如何實現
先實現從當前的世界序列里面通過當前世界的代數,也就是世界大循環的 i 變量的值,決定出世界序列中的一個 Key 值
class Manager
{public void Start(){... // 忽略其他代碼for (int i = 0; i < 10000; i++){... // 忽略其他代碼var key = GetKey(i);... // 忽略其他代碼}}public Key GetKey(int n){var index = n % KeyList.Count;return KeyList[index];}... // 忽略其他代碼
}
每個 Element 也實現 GetKey 方法,也是通過傳入的序號返回一個 Key 值
class Element
{public Key GetKey(int n){var index = n % KeyList.Count;return KeyList[index];}... // 忽略其他代碼
}
判斷 Element 返回的 Key 是否和世界的相同,如果不相同,將會淘汰。代碼只需要一句話就可以完成實現,那就是返回 Key 不相同的,從 Manager 的 ElementList 列表里面移除
class Manager
{public void Start(){... // 忽略其他代碼for (int i = 0; i < 10000; i++){... // 忽略其他代碼var key = GetKey(i);ElementList.RemoveAll(element => element.GetKey(i).N != key.N);... // 忽略其他代碼}}... // 忽略其他代碼
}
對于從 ElementList 被移除的 Element 來說,等待這些 Element 就是凌駕于此世界之上的 dotnet 的 GC 回收機制將其當初垃圾清理掉
經過了世界的一次考驗,最后剩下的 Element 不多。此世界顯得十分空曠,就給了這些存活的 Element 創建子 Element 的機會,在 Element 里實現 Create 方法,用來通過當前的 Element 創建新的 Element 對象
class Element
{public Element Create(){Element element = new Element(Random);foreach (var key in KeyList){element.KeyList.Add(key);}return element;}... // 忽略其他代碼
}
通過以上代碼可以看到,新創建的 Element 不僅繼承了用來思考的 Random 隨機數,還繼承了前輩對世界序列的思考,也就是 KeyList 元素。接下來新的 Element 可以繼續在前輩的基礎上,對世界序列進行思考
在 Manager 完成考驗,將調用 Element 的 Create 方法,讓此世界繼續被填充
class Manager
{public void Start(){... // 忽略其他代碼for (int i = 0; i < 10000; i++){... // 忽略其他代碼var currentCount = ElementList.Count;while (ElementList.Count < 10000){for (int index = 0; index < currentCount; index++){var element = ElementList[index];ElementList.Add(element.Create());}}... // 忽略其他代碼}}... // 忽略其他代碼
}
如果當前的世界考驗,干掉了絕大部分的 Element 對象,那么剩下的 Element 就有更多的機會創建出新的 Element 對象
為了防止滅世,也就是某次考驗干掉了此世界上所有 Element 小人兒,在每次迭代的時候,如果世界太過空曠,將會額外加上從空白里創建的新 Element 對象
和世界的首批 Element 一樣從石頭蹦出來的,每次迭代,都會嘗試加上一些從石頭里面蹦出來的新 Element 對象,更改后的代碼如下
class Manager
{public void Start(){... // 忽略其他代碼for (int i = 0; i < 10000; i++){... // 忽略其他代碼bool addElement = false;var currentCount = ElementList.Count;while (ElementList.Count < 10000){for (int index = 0; index < currentCount; index++){var element = ElementList[index];ElementList.Add(element.Create());}if (addElement){continue;}addElement = true;var addCount = 10000 - ElementList.Count;addCount = Math.Min(addCount, 100);for (int index = 0; index < addCount; index++){var element = CreateElement();ElementList.Add(element);}}... // 忽略其他代碼}}... // 忽略其他代碼
}
如此的進行迭代,在完成了世界的大循環,也就是 10000 次之后,將會開始最后的審判。判斷剩下的 Element 是否了解了世界序列,以及了解了多少次
class Manager
{public void Start(){... // 忽略其他代碼for (int i = 0; i < 10000; i++){... // 忽略其他代碼}for (var i = 0; i < KeyList.Count; i++){var key = GetKey(i);ElementList.RemoveAll(element => element.GetKey(i).N != key.N);}}... // 忽略其他代碼
}
最后剩下的,都是明了世界序列,也就是明了此世界本質的 Element 小人兒
完成代碼編寫之后,放入 Main 函數用來啟動世界
internal class Program
{static void Main(string[] args){Manager manager = new Manager();manager.Start();}
}
此代碼版本,放在 GitHub 上,可以從?https://github.com/lindexi/lindexi_gd/commit/49878e97df5c75c22d40294b6970aaf46b11c218?獲取全部代碼
運行代碼的結果是,最后剩下的 Element 小人兒,都明了世界序列。其實這也是因為繼承了前輩們的知識,從前輩那里拿到了 KeyList 才讓越后創建的 Element 有越高的生存率
以上是一個簡單的版本,世界序列是非常裸的參與計算,或者說沒有參與計算,就根據序列進行返回。接下來對這個世界的游戲規則加上更多的難度,應用上加法規則。加法規則就是取隨機的數值,例如 3 個數值,作為序號,再根據序號一一取出 Key 值,接著將 Key 值取和,返回一個數值。此規則同時也對每個 Element 執行,一旦發現 Element 計算出來的最終數值和世界計算出來的不匹配,那就將此 Element 淘汰
新的這個游戲規則其實對 Element 來說,更有挑戰性,也同時帶來了新的數學上的計算方法,那就是如果 Element 猜測的世界序列和此世界的世界序列不匹配,也有可能在取出的數值里面,通過加法返回相同的值。換句話說,每個 Element 在一輪迭代里面,如果沒有被淘汰,那也是無法知道當前猜測的世界序列是否正確。對比之前的規則,之前的規則,一旦猜測錯誤,自然就會被淘汰
改造一下代碼,讓 Element 和 Manager 都繼承 IKeyManager 接口,方便同時應用上相同的加法規則。這個 IKeyManager 接口定義如下
interface IKeyManager
{Key GetKey(int n);
}
加法計算規則的代碼實現如下,根據傳入參數的序列,獲取的 Key 值,取 Key 值的總和
class Manager
{private int BuildByKey(IKeyManager keyManager, IList<int> indexList){var n = 0;foreach (var index in indexList){var key = keyManager.GetKey(index);n += key.N;}return n;}... // 忽略其他代碼
}
看到這里,也許機智的大家也就猜到了。世界序列對應著世界的本源數據,而加法規則對應的通過世界本源數據進行的一套規則。有世界本源數據加上世界的本源規則,即可展現出有趣的世界。有世界的本源規則的存在,也就是此代碼世界的加法規則的存在,將會讓 Element 小人兒更加難以知道自己所了解的世界規則是對是錯
按照以上的加法規則,只要幾個 Key 相加的和相等即可,而從數學上,這是無法反算唯一解的。舉個例子,假定 indexList 里面有三個序號,分別是 1 2 3 三個序號。而 keyManager 里面根據 1 2 3 返回的 Key 分別是 10 20 30 三個 Key 值,計算出的結果是 60 的值。那反過來,已知返回值是 60 且傳入序號是 1 2 3 三個序號。請問 keyManager 存放的 KeyList 里面的第 1 和 第 2 和 第 3 的 Key 應該是多少? 抽象出來的數學題就是,已知三個數加起來的總和是 60 求這三個數。讀過小學數學的大家,自然就知道,這三個數沒有唯一解
如此加法規則,一開始就要求取序列里面的幾個 Key 值,這就要求 Element 小人兒需要一開始就初始化一段 Key 列表。否則就不好玩了。經過實際的測試結果,我發現如果不告訴 Element 小人兒 世界序列的長度 的話,那 Element 小人兒 幾乎不能在世界大循環結束之前,明了世界序列。降低游戲的難度,我將告訴 Element 小人兒 世界序列 的長度是 10 個 Key 值。同時也更改了 Element 的 BuildKey 方法,讓此方法可以在一開始就生成了序列
在之前的代碼基礎上,修改 Element 的 BuildKey 方法,本來是 BuildKey 方法每次世界迭代都會調用,現在修改為只有 Element 被創建時才調用,修改過之后的代碼如下,以下的代碼還不是最終的版本,在下文將會告訴大家最終的版本代碼
class Element
{public void BuildKey(){const int count = 10;while (KeyList.Count < count){var key = Random.Next(Key.MaxKeyValue);KeyList.Add(new Key(key));}}... // 忽略其他代碼
}
以上的代碼可以讓 Element 創建出來一段序列,序列長度和世界序列長度相同
接著從世界循環里面,刪掉每次迭代都調用 BuildKey 方法的代碼,也就是這段代碼
foreach (var element in ElementList){element.BuildKey();}
將調用 BuildKey 方法的代碼放入到 CreateElement 方法里,修改之后的方法代碼如下
class Manager
{private Element CreateElement(){Element element = new Element(new Random(Random.Shared.Next()));element.BuildKey();return element;}... // 忽略其他代碼
}
每次世界迭代都會取出隨機三個數值,將這三個隨機數值傳入到 BuildByKey 方法里,通過加法規則算出總和。將此方式分別應用在世界序列和每個 Element 上,將計算結果和世界序列計算出來的不相同的 Element 淘汰掉
實現的代碼如下,每次迭代隨機三個數值,可以先放入到一個數組里面
class Manager
{public void Start(){... // 忽略其他代碼var indexList = new int[3];for (int i = 0; i < 10000; i++){... // 忽略其他代碼}}... // 忽略其他代碼
}
每次調用 UpdateRandomIndexList 方法,將 indexList 加上三個隨機數值
class Manager
{public void Start(){... // 忽略其他代碼var indexList = new int[3];for (int i = 0; i < 10000; i++){UpdateRandomIndexList(indexList);... // 忽略其他代碼}}private void UpdateRandomIndexList(int[] indexList){for (var i = 0; i < indexList.Length; i++){indexList[i] = Random.Shared.Next();}}... // 忽略其他代碼
}
先計算世界序列經過 BuildByKey 的值
class Manager
{public void Start(){... // 忽略其他代碼var indexList = new int[3];for (int i = 0; i < 10000; i++){UpdateRandomIndexList(indexList);var key = BuildByKey(this, indexList);... // 忽略其他代碼}}... // 忽略其他代碼
}
接著依然是一句代碼,將 ElementList 里面,計算結果不等于 key 值的 Element 淘汰
class Manager
{public void Start(){... // 忽略其他代碼var indexList = new int[3];for (int i = 0; i < 10000; i++){UpdateRandomIndexList(indexList);var key = BuildByKey(this, indexList);ElementList.RemoveAll(element => BuildByKey(element, indexList) != key);... // 忽略其他代碼}}... // 忽略其他代碼
}
淘汰之后,依然就到了根據剩下存活的 Element 構建出來新的 Element 的步驟了。由于這次的 BuildKey 只有在元素創建的時候才被調用,這就意味著需要改造 Create 方法,讓 Element 的在創建時調用 BuildKey 方法,修改之后的代碼如下
class Element
{public Element Create(){Element element = new Element(Random);foreach (var key in KeyList){element.KeyList.Add(key);}element.BuildKey();return element;}... // 忽略其他代碼
}
而根據以上的 BuildKey 方法的代碼,只是將 KeyList 序列生成到 10 個,顯然不符合當前的需求。畢竟現在在創建的時候,就設置了 KeyList 內容了,這會讓 BuildKey 方法啥都沒有做。 繼續優化 BuildKey 方法,讓此方法可以將 KeyList 進行更改
class Element
{public void BuildKey(){const int count = 10;while (KeyList.Count < count){var key = Random.Next(Key.MaxKeyValue);KeyList.Add(new Key(key));}KeyList[Random.Next(count)] = new Key(Random.Next(Key.MaxKeyValue));}... // 忽略其他代碼
}
以上代碼就是優化完成之后的 BuildKey 方法。大概映射的含義就是,子 Element 可以從前輩繼承到 KeyList 知識,只是子 Element 要做出變化,變化就是修改 KeyList 中的數據。當前的變化只是修改一項而已
更改完成之后,即可開始跑代碼。當前的代碼的含義總的來說就是,在此世界里面,有一段世界序列,世界規則將根據世界序列計算出一個值,同樣的世界規則也使用相同的算法應用在每個 Element 小人兒上,一旦發現兩者計算結果不相同,那么 Element 小人兒將被淘汰。存活下來的 Element 小人兒將可以創建出下一代 Element 小人兒出來,創建的時候,可以將自己的知識傳授給下一代。而下一代 Element 小人兒也不是全盤接受的,而是會經過自己的思考,優化從前輩繼承到 KeyList 知識
只不過呢,在此世界里面,世界規則只是一個加法規則。而下一代 Element 小人兒的思考也只是一段隨機數
嘗試運行程序,可以看到這一次滅世的情況發生的次數比一開始的代碼的多。滅世的情況就是在世界的某一代中,存活下來的 Element 小人兒的數量是 0 個。可以看到當前代碼的規則讓 Element 小人兒更加難以應對。但也同時發現了這樣的規律,一旦開始有某個 Element 小人兒掌握了一定數量的正確的世界序列的值時,此 Element 小人兒將可以創建出更多更好存活的下一代 Element 小人兒
舉個例子來說明運行的情況
假定世界序列就是從 0 到 9 的 10 個 Key 數值。某個 Element 掌握的序列就是 1020120120 序列。剛好一開始的 indexList 就是取前三個,這就讓 Element 存活起來了。于是接下來 Element 將創建出下一代的 Element 出來。下一代的 Element 剛好修改的第 3 個(從0開始)序號為 4 的值,修改之后的是 1024120120 序列。下一輪世界取的 indexList 是 1 2 3 三個序號,世界序列計算結果是 1+2+3=6 的值。而原先的 Element 計算出來的是 0+2+0=2 被淘汰,而下一代的 Element 計算出來的是 0+2+4=6 存活。可以看到,盡管下一代的 Element 沒有思考出正確的世界序列,然而在世界規則的處理下,依然還是可以存活的,只不過在偏離世界序列的情況下,可以存活的代數自然是有限的
這也能說明能存活下來的 Element 前輩掌握的世界序列是有一定的正確性的,但是下一代的 Element 是不知道前輩有哪些序列是正確的,甚至前輩的 KeyList 都是偏離世界序列的,只是剛好滿足當前的世界規則而存活
在經過了世界大循環之后,可以再來看看存活下來的 Element 小人兒掌握世界序列的情況。這次的結果是絕大部分的 Element 小人兒掌握了正確的世界序列,之后少部分的 Element 小人兒掌握了錯誤的世界序列。這些少部分的掌握錯誤的世界序列的 Element 小人兒都是在最后幾代世界循環里面創建的。也就是說活的足夠久的 Element 小人兒都是掌握了正確的世界序列的
這也能映射到現實世界的一部分,能夠存活下來的生物,或者人,都是掌握了一定的世界序列,也就是世界本源或者說世界的知識,或者說是符合世界規則。掌握的越多,或者說行為越靠近世界規則,就越能存活
此代碼版本,放在 GitHub 上,可以從?https://github.com/lindexi/lindexi_gd/commit/5f7d17b206b904db424d17fed6cef48eb0965496?獲取全部代碼
這個代碼游戲到這里還沒有結束,可以玩的還有很多。例如修改下一代的 Element 的思考邏輯,讓下一代的 Element 更加有想法,想要更改更多的知識。在修改之后的代碼世界,下一代的 Element 更加質疑前輩的知識,會更多的思考世界序列,而不是和之前的代碼一樣,只是修改世界序列里面的一項
修改之后的代碼如下
class Element
{public void BuildKey(){const int count = 10;while (KeyList.Count<count){var key = Random.Next(Key.MaxKeyValue);KeyList.Add(new Key(key));}var updateCount = Random.Next(1, count);for (int i = 0; i < updateCount; i++){KeyList[Random.Next(count)] = new Key(Random.Next(Key.MaxKeyValue));}}... // 忽略其他代碼
}
運行一下代碼,可以看到此時運行的情況是,在很多代的世界大循環里面,即世界代數里,大量存在滅世的情況。因為下一代的 Element 小人兒思考越多,代表著繼承到的前輩的知識越少。繼承的知識越少,越難以存活。這也能說明前輩們的知識是有用的
此代碼版本,放在 GitHub 上,可以從?https://github.com/lindexi/lindexi_gd/commit/9bb128484abfa9fe6f5ba3ff66a91505fb3105b5?獲取全部代碼
好了,今天的小測試就到這里。通過這個代碼故事可以告訴咱,在這個代碼世界里面,是符合天有五賊,見之者昌的道理,越接近世界序列的 Element 小人兒將越能存活。這個和當前的現實世界也差不多
在這個代碼世界里面,前輩的知識是有用的,學習前輩的知識可以更好幫助存活,符合書籍是人類進步的階梯這句話的道理。通過讓前輩的知識傳授給下一代,可以逐漸讓整個世界在一代一代的循環里面存活的 Element 小人兒的數量越多
在此代碼世界里面,前輩傳授給下一代的知識也不一定是完全正確的,下一代需要進行質疑,因為前輩說不定活不過世界的下次迭代,前輩的知識只是剛好在當前的世界規則下是正確的,不代表著在下一次迭代的世界規則還是正確的。映射到現實世界,那就是需要質疑前輩的知識,前輩的知識放在前輩的時代是正確的,不代表在當前也是正確的。但是將前輩們的知識全拋掉,那自然也是不對的
現實世界里面也許存在世界序列和世界規則,然而現實世界里面可不會告訴大家,有多少個世界序列列表,世界序列的每個項的取值范圍是多少,世界序列有多長等。試試在代碼世界里面,讓代碼世界的世界序列的取值范圍更大,或者是世界序列更長,試試看修改之后的滅世次數。同樣現實世界不會告訴大家世界規則是什么,有多少個規則。現實世界的世界序列和世界規則更加復雜,這就讓在現實世界進行探索世界的本質更加難。但是有一點是正確的,那就是學習前輩們的知識同時進行自己的思考,在一代代的努力下,可以不斷靠近世界的本質
這個代碼世界還有很多可以玩的,例如可以修改更有趣的世界規則,也就是 BuildByKey 方法。修改 Element 的思考方式等