Skip to main content

7 posts tagged with "development note"

View All Tags

· 9 min read
Wei Ji

前情提要

如果對我的 Side Project 「底火的芬芳」不清楚的讀者可能需要先閱讀底火的芬芳 - 專案起源才能理解這場旅程是怎麼開始的。而最近 (2022-12-19) 在逛 Github 的時候晃到一個 repo,它是少女前線這款遊戲的數值模擬器,往程式語言構成一看是用 Typescript 寫的立刻勾起了我的興趣。

neko-gg/gfl-combat-simulator

2022-12-19 在 Github 上閒逛的時候,看到一個 repo

喔啦啦,這不是 Typescript 嗎? 喔啦啦,這不是 React 嗎? 是我的主場呢 = =+

於是就 clone 下來研究研究。

後來(約 2022-12-20)照著 README.md 的指示:

yarn update-resources
yarn update-assets

找到了 ResourcesUpdaterAssetsUpdater 這兩個程式,發現它把遊戲素材放在另外一個 repo。以及會從遊戲官方的 cdn 下載名為 ResData.ab 的檔案,因為不知道是什麼檔案類型,就用 Hex 瀏覽器打開來看:

啊啦啦,原來是 Unity ResData Bundle。

另外,從 ResourcesUpdater 看到它呼叫了來自 ChibiUpdater.ts 的方法,稍微研究一下內容發現它使用 Puppeteer 打開一個網站並做了某些事,不過因為程式碼已經有點過時了,網站已經被移到其他地方,DOM 的操作也和原本腳本內的方式有點落差。研究了它使用 Puppeteer 的方式,看得出來目的是取得一張 Chibi 站立的圖片。

  • 原始的 Chibi (程式內對人偶的稱呼)檔案是將人偶拆件,並透過組態檔組裝的。
  • naganeko 的網站能夠上傳 Spine 檔案,並播放動畫。
  • naganeko 的網站也能下載組裝後的圖檔。

試著逆向工程 naganeko 的網站,發現它應該是使用了 Spine 的 Runtime。

2D 骨架動畫

在 3D 模型/動畫中,會使用骨架 (Armature) 綁定模型節點,並透過權重、關鍵影格等方式讓模型執行動畫。

透過把模型壓扁,將這種技術應用在 2D 場景就是 2D 骨架動畫,可以解決傳統 Sprite 的一些限制。另外還有三渲二之類在遊戲開發上常見的手段,不過不在本次題目的討論範圍內。

Spine

Spine 是目前極為普及的 2D 骨架動畫 tool chain。用工業建模比喻的話就像 SolidWork,或是美術產業中的 PhotoShop 以及 Illustrator。原因在於其應用端成功的布局,包含 Unity3D, 又或是網頁遊戲開發 Three.js, Phaser, PixiJS...之類的執行環境,有著非常廣泛的 Runtime 支援。換句話使用它的產品,佈署目標的選擇十分多元。

然而這個格式終究是商業產品,編輯器本身需要付費,使用者(如:遊戲開發者)也需要購買授權。即便有 Runtime 的原始碼,其授權範圍仍不算開源的範疇,對我而言尋找替代方案就很重要了,因為我只是想寫寫 side project。

替代方案

與 Spine 類似的產品還有 Creature AnimationRive 之類的,不過它們跟 Spine 一樣都有一股商業味,自然就排除在我的選項之外了。

往開源的生態系望去,則有 Blender 的生態系內有個名為 COA Tools 的工具能夠製作 2D 動畫。或是 Godot 作為開源的遊戲引擎也有內建的 2D 骨架動畫系統

只是我懶得自己重頭綁骨架跟刷權重,能夠直接用撿到的少女前線檔案轉檔是在好不過了。

DragonBones

最後 (2022-12-22) 決定使用 DragonBones 的格式,雖然編輯軟體本身沒開源而且只能在 Windows 執行,但是至少是免費的而且能夠用 Wine 跑起來。這個工具背後的中國公司的 Tech Stack 算是從 Flash 長出來的,後來往 Javascript 轉型。因此它的 runtime 支援不少諸如 Three.js Pixi, Phaser 等知名的 Javascript 的套件。

最重要的是,它能夠匯入 Spine 格式的檔案,並輸出成 DragonBones 的格式。~~這樣我就可以直接撿 Chibi 的檔案來用囉~~~還有其他考量就是,如果編輯器也是基於 Javascript 的話,日後逆向工程起來比較容易。

DragonBonesJS 重構

然而 DragonBonesJS 的 Javascript Runtime 自 2020 就沒有維護了,而且並不支援 ES6 的模組載入方式。從程式碼看得出來它使用的設計是有一個處理事務邏輯的核心,以及針對各種不同套件實作的 Runtime 。因此即便我重構了核心,也無法直接測試,必須選用一個 Runtime 作為測試用途。

原本支援的 Runtime 中,Three.js 是我的目標,但是沒有充足的 Demo code 能夠測試重構,當中的 Phaser 是我有接觸過得遊戲引擎,但是它的實作較為複雜,而 PixiJS 的版本我雖然沒使用過,但是實作比較簡單,只有 4 個檔案,於是重構的任務就拆成了 3 個部份:

  • Core
    • 真正處理動畫邏輯的部份
  • PixiJS Runetime
    • 主要是為了完整的 Demo 範例,好測試 Core 的重構運作正不正常
  • Three.js Runtime
    • 目標,因為我想把 Chibi 丟進 3D 的空間中

Core 跟 PixiJS Runtime 的重構過程沒什麼好紀錄的,就是要轉換成 ES6 的寫法跟解決 tsconfig 設定不同的問題。Three.js Runtime 的重構過程倒是問題不少。

Three.js Runtime 重構

DragonBones 提供的 Demo 中,只有線性變換的部份(Translate, Rotation, Scale),並沒有包含網格變形,當我匯入 Chibi 的檔案時,得到了破圖的結果:

慶幸的是在 issues 撿到野生的 patch

接著要處理的要件是升級 Three.js 的版本,DragonBones 遺留的程式碼僅支援到 0.89.0,而目前版本是 0.148.0。升級的過程遇到一個問題是 Three.js 在 0.125.0 的時候棄用了一個名為 Geometry 的物件,而在 DragonBones 的實作中是仰賴這個物件的,即使可以透過 Legacy Code 拿來使用,卻會有性能問題。

重構成果

Three.js Chibi Game Sample

就只是想做個概念驗證:

註解

Sprite 限制

傳統 Sprite 是透過切換數張點陣圖達到視覺暫留的動畫效果,點陣圖除了繪製費工之外、檔案較大還有幀數受限制等問題。

與之相對的 3D 模型是用點數據構成的,也就是所謂的向量圖檔。透過綁定骨架就重複使用同一張貼圖製作不同的動畫。

Spine 授權

Spine 的授權規則很哆嗦:


創用 CC 授權條款
Wei Ji以創用CC 姓名標示-相同方式分享 4.0 國際 授權條款釋出。

· 4 min read
Wei Ji

大約 2019 年四月的時候,身邊有玩少女前線的朋友,並且當時又接觸一個名為 Dental Defender: Saga of the Candy Horde小遊戲,它使用 Phaser 引擎撰寫,於是我稍微試著了解一下 Phaser 引擎的運作方式與 Tiled 劃地圖的方法並分別在 FB 留下了兩篇貼文

2020 年六月份的時候在 Github 晃到一個 repo,它是一個有點像 diep.io 的簡單小遊戲,只是主軸是人類玩家對抗一堆 BOT,並且這些 BOT 透過簡單的機器學習理(論上應該)會越來越強,然後當玩家輸掉時遊戲還會嘲諷「你真是人類之恥」。因為我很好奇這個機器學習的效果如何,於是就簡單的改寫程式變成 BOT 4v4 的形式。

接著我又想「何不把機器學習包裝成類似少女前線這樣的角色蒐集遊戲?角色的強度依靠機器學習的性能而不是由開發商設定參數決定」,並且把這個概念寫成簡單的 UI。

2020 年七月底,透過重構 Dental Defender 來熟悉 Typescript 與 Phaser 引擎,就是為了實作這個概念而做的準備。也大約是這個時間,這個專案的名稱敲定為「底火的芬芳」。然而後來 Sprite 畫得有點煩加上工作忙碌再加上又跑去追其他蝴蝶,因為跟 AI 相關,這個專案就併入 The Key Of Huanche 作為子專案並暫時被冷凍了起來。


後來斷斷續續有畫一些素材也是為了這個專案:

名詞解釋

Dental Defender: Saga of the Candy Horde

遊戲開發圈有一種活動叫做 Game Jam,就是聚集一群愛好者在數小時到數天之內完成一款遊戲。而這個小遊戲來自一場名為 candyjam 的 Game Jam,原因是一間名為 King.com 的公司(就是 Candy Crush 的那間公司)在 2014 年將 "candy" 一詞申請商標,並指控一些獨立開發公司侵犯他們的商標。這一舉動激怒了獨立開發圈,於是他們舉辦一場 Game Jam 專門製作 "Cahndy" 相關的遊戲來對 King.com 表示抗議。1

蝴蝶

本人有嚴重的閃亮事物症候群,習慣將自己跑去玩其他 Side Project 比喻成追蝴蝶。


創用 CC 授權條款
Wei Ji以創用CC 姓名標示-相同方式分享 4.0 國際 授權條款釋出。

Footnotes

  1. Indie Developers Troll King Games Hard With 'Candy Jam' | WIRED. Retrieved 2023-01-17, from https://www.wired.com/2014/02/candy-crush/

· 9 min read
Wei Ji

說起來這已經是我快接近兩年前(2020年9月)做的 side project 了,最近想說把資料整理一下發個文。

Hordes.io

先簡單介紹一下這個遊戲,它就是一個經典的 MMORPG,等級、技能、道具、職業、組隊、團戰...等基本的要素都有,但是沒有像一般 MMORPG 那般廣大的地圖、豐富的 NPC 跟細緻的模型,並且可以直接用網頁瀏覽器遊玩。

有一陣子我還蠻享受這個遊戲的,甚至有考慮剋金,不過營運方過於側重 PvP,不定期舉辦的活動也給我一種 Minecraft 伺服器那種娛樂 OP 吃力不討好的感覺,不過那又是另外一段故事了,總之最後就棄坑了。

動機

個人在 MMORPG 最享受兩種角色:補師跟生產者(偏偏正是主流遊戲較為不重視的兩種類型_(:3」∠)_),而在這遊戲確實滿足了我對補師職業的喜好,不過並沒有生產系統,所有道具與裝備都是透過打怪掉落,並且玩家與玩家之間的交易只能透過和重生點的商人對話開啟拍賣界面:

而且遊戲系統還會抽上架手續費,換句話說遊戲的經濟系統不夠強大,讓我「想對它做點什麼」,而這幾個前提成立下:

  • 遊戲是以 Javascript 跑在網頁上的
  • 有集中交易的拍賣功能

讓我「能對它做點什麼」,讓我手癢想把市場資料倒出來畫折線圖分析。

這世界上有兩樣東西能讓我開心:

  1. Data
  2. More data

by 我那特別喜歡 Data 的一部分靈魂

正文

一張圖解釋我做了什麼,流程大致上是:

  1. 逆向工程遊戲的 Javascript
  2. 在當中嵌入程式碼來獲取拍賣資訊
  3. 執行遊戲時以修改過得 client.js 取代官方原本的版本
  4. 修改過得腳本會把攔截到的拍賣資訊回傳到資料庫
  5. 分析資料

逆向工程主程式

Javascript 是直譯語言,即便一般開發者會經過 minify, uglify 等步驟降低腳本的可讀性,但是獲得程式後仍然是明碼可讀的,而遊戲的主程式是放在這個路徑中:

https://hordes.io/client.js?v=4305950

把程式下載後經過 beautify 加上縮排,再透過 Javascript 的基本語法跟一些沒有被混淆的變數名稱加上一些技巧來試圖了解程式運作,具體怎麼做的我就不贅述了,任何會寫 Javascript 的人應該都知道這些方法(?)

透過逆向工程了解程式運作之後就能添加程式碼來達成我的目的,比如:把拍賣資料送到我的資料庫。

fetch("/api/item/get", {
method: "POST",
body: JSON.stringify({
auction: 1,
ids: t
})
}).then(async t => {
const e = await t.json();
let items = [];
//edition
//e: object from json string
e.fail ? console.error(e) : (
s(11, P.length = 0, P), r.forEach((t, s) => {
const i = e.find(e => e.id === t.dbid);
i && (i.store = C[s] || (C[s] = Wt()), i.store.temp = t, t.hydrate(i), P.push(i.store));
let item = {
item_id: i.id,
price: i.auctionprice,
amount: (i.stacks ===null)?1:i.stacks,
tier: i.tier,
type: i.type,
upgrade: i.upgrade,
attributes:Object.fromEntries(i.store.temp.stats),
posted_by: i.name,
posted_at: i.auction
};
items.push(item);
//console.log(JSON.stringify(item));
}),
//console.log(JSON.stringify(items)),
fetch("http://0.0.0.0:8989/stack", {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
headers: {
'Content-Type': 'text/plain'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(items) // body data type must match "Content-Type" header
}),
s(6, g = !0)
)
})

比較麻煩的是每次他們改版我就要重新逆向工程一次,流程其實應該可以用透過程序化來自動完成,不過那需要更多心力,加上先前提過得,我對於這遊戲的熱情正在冷卻,加上逆向工程並不是什麼輕鬆愉快的過程,這算是我最後棄坑的原因之一。

瀏覽器插件

我使用 Tampermonkey 來載入腳本:

var pattern = "https://hordes.io/client.js*";

function redirect(requestDetails) {
console.log("Redirecting: " + requestDetails.url);
let version = [...requestDetails.url.matchAll(/\?v=([\d]+)/g)];
return {redirectUrl: "http://0.0.0.0:8123/"+ version[0][1] +"/client.js"};
switch(requestDetails.url){
case "https://hordes.io/client.js?v=4305950":
return {redirectUrl: "http://0.0.0.0:8123/4305950/client.js"};
case "https://hordes.io/client.js?v=4298202":
return {redirectUrl: "http://0.0.0.0:8123/4298202/client.js"};
case "https://hordes.io/client.js?v=4308820":
return {redirectUrl: "http://0.0.0.0:8123/4308820/client.js"};
default:
console.warn("Version unmatch! using origin.");
return {redirectUrl: requestDetails.url};
}
}

browser.webRequest.onBeforeRequest.addListener(
redirect,
{urls:[pattern], types:["script"]},
["blocking"]
);

作用就是當遊戲網頁試圖載入主程式的時候,導向到我的 local server 取得修改過得版本。

資料庫

當時用 PHP + Slim 寫了個簡單的後端來接收資料。程式碼可以在這裡找到。

分析

從遊戲中撈到的資料包含了賣單的創立時間,而遊戲機制讓賣單只會在拍賣系統中掛上 12 小時,而付費會員則是 24 小時,由此可推測出賣單的失效時間。

當掛單失效時就物品會從拍賣中消失回到賣家的倉庫,當然玩家也可以手動取消掛單,但是因為物品會凍結在倉庫中直到原本掛單失效的時間,相較之下發生的機率很低,因此推論皆以「掛單消失是因為逾時自動失效」為前提。

每次進行抽樣時,腳本會遍歷拍賣系統的所有頁面,並把資料送到資料庫,換句話說一次抽樣就代表一次掛單的快照。透過複數個抽樣就能推估掛單是逾時消失的,還是因為交易而消失的。

依據抽樣推測掛單逾時消失:

掛單在逾時之前消失,很高的可能性是因為完成交易:

透過這種方式把成交的掛單價格拉出來統計,就能知道物品的當前行情。

那個時候遊戲改版的蠻頻繁的,我又還沒完成腳本的程序化,每次改玩程式只能採個幾小時的資料,根據 2020-08-27T15:54:49Z2020-08-29T09:46:35Z 這段時間的資料,我計算出 T0 符文石的交易情況為:

統計量價格
最小值40
最大值808
平均值360.7
標準差92.3

如果完成腳本程序化,抽樣的時間跨度大一點,應該就能做成折線圖了(或是更棒的盒鬚圖)。不過這個如果應該是永遠不會發生的如果了。 (=w=)

後記

回去翻當時進行逆向工程寫的東西之後,發現我確實已經開始進行一些重構以利逆向工程能夠程序化,只是還沒完成,比如:fetch hook, auto clicker, data transform...

· 2 min read
Wei Ji

未採用的幾何方案

原本我打算從波函數中尋找用來描述仿星環的函數,比如電子軌域或是電磁波:

https://wifflegif.com/gifs/496745-quantum-mechanics-atomic-orbitals-gif

https://www.researchgate.net/project/Open-Source-Tools-for-FEM-and-FDTD-Simulations

但是這些模型的產物都是張量,要另外設定條件把線(面)畫出來是很困難的。

摸索的過程中也有朋友建議一個接近的函數:腎形線,可惜它中間不是貫穿的。

幾何參數

最後採用尺規作圖直接刻的方式,簡單暴力。

接著計算 Voxel 平面的長度,也就是經度線H(紅)

H=0.5(2π0.5R)+2πR0.25+2π(2R)0.25+0.5(2π0.5R)=2.5πR\begin{align} H &= 0.5(2\pi \cdot 0.5R) + 2\pi R\cdot 0.25 + 2 \pi (2R) \cdot 0.25 + 0.5(2\pi \cdot 0.5R) \\ &= 2.5 \pi R \end{align}

與 Voxel 平面的寬度;緯度線W(藍),取小圓周與大園周的平均值:

W=2π(R)+2π(2R)2=3πR\begin{align} W &= \frac{2 \pi (R) + 2 \pi (2R)}{2} \\ &=3 \pi R \end{align}

因此平面面積為

A=HW=7.5π2R2A = H \cdot W = 7.5 \pi^2 R^2

等效仿星環

一個球體行星的表面積為:

S=4πRs2S = 4\pi {R_s}^{2}

若要將仿星環近似已知行星的表面積;如地球,則可透過下述關係達成:

S=A4πRs2=7.5π2R211.875πRs=R\begin{align} S &= A \\ 4\pi {R_s}^{2} &= 7.5 \pi^2 R^2 \\ \sqrt{\frac{1}{1.875 \pi}} R_s &= R \end{align}

地球的半徑為 6,371 km,因此等效仿星環的 R 為 2,625 km

· 3 min read
Wei Ji

A Technique to Quilting Gradient Noise

目的

我想在一個 torus 的空間中使用演算法生成地形 (Procedural generation),因此演算法必須使地圖邊界無接縫 (Seamless)。

被排除的方案

The image quilting

該算法的原理是在兩張貼圖的交界處做重疊 (overlay),並使用 error 值尋找最短路徑而達到縫合的目的。1 2

https://people.eecs.berkeley.edu/~efros/research/quilting/efros-siggraph01.ppt

但是該算法的目的是從原始貼圖中抽出拼貼塊 (patches),並用拼貼塊重新製作圖片,並不適合用於兩個固定邊界的縫合。

當我試著用這個算法處理 noise map 的交界會變成這樣:

Periodic Perlin Noise

Perlin Noise 的產生是透過在網格中指定隨機的向量來創造梯度,再透過內插來生成最終的 noise map。

https://upload.wikimedia.org/wikipedia/commons/2/24/PerlinNoiseDotProducts.png

因此只要兩個邊界使用同一組向量,就能生成無接縫的 noise map。3

不過我個人不太想重新實做 noise 生成層級的演算法;加上雖然我的目標是生成無接縫的 noise map,但是很有可能實際上會需要縫合兩張算法完全不同的 noise map,再加上交界處很有可能不具有完整的網格(貼圖的大小不是 lattice 的整數倍),所以最終不考慮這個方案。

演算法

其實就是做線性內插:

f(x)={g(x),x<xav(x),xaxxbh(x),xb<xv(x)=g(x)(1r)+h(x)rr(x)=xxaxbxaf(x) = \begin{cases} g(x), & x < x_a \\ v(x), & x_a \leq x \leq x_b \\ h(x), & x_b < x \end{cases} \\ v(x)=g(x) \cdot (1-r) + h(x) \cdot r \\ r(x)=\frac{x-x_a}{x_b-x_a}

只是線性內插本質上就是加權平均數,這會使 noise map 變得模糊,因此加上一個修飾:

vs(x)=smoothstep(v)rs+v(x)(1rs)rs(x)={r,r0.5(1r),r>0.5v_s(x) = \text{smoothstep}(v) \cdot r_s + v(x) \cdot (1-r_s ) \\ r_s(x) = \begin{cases} r, & r \leq 0.5 \\ (1-r), & r > 0.5 \end{cases}

smoothstep 函數能夠增加 noise 的對比來修復部份因為線性插值造成銳利度降低(模糊)的區域。

沒有 smoothstep:

加上 smoothstep:

Footnotes

  1. Image Quilting for Texture Synthesis and Transfer. (Alexei A. Efros). Retrieved 2022-06-04, from https://people.eecs.berkeley.edu/~efros/research/quilting/quilting.pdf

  2. Image Quilting for Texture Synthesis & Transfer. (Alexei Efros). Retrieved 2022-06-04, from https://people.eecs.berkeley.edu/~efros/research/quilting/efros-siggraph01.ppt

  3. tiles - How do you generate tileable Perlin noise? (Boojum). Retrieved 2022-06-04, from https://gamedev.stackexchange.com/questions/23625

· 11 min read
Wei Ji

Evaluate resources

We can evaluate scarcity of resources intuitively; the more rare it is, the more value it is. But scarcity of economics are measured by supply and demand, it can't be a unit which had clearly definition. We can get probability of resources generated on map, but the probability wasn't enough to measured agent, it needed been processed additionally.

Shannon Entropy and Shannon Information

The nature of Shannon entropy is mathematics quantity which well known "uncertainty of phenomenon".1

H(X)=E[I(X)]=E[ln(P(X))]\mathrm H (X) = \mathrm{E}[\mathrm{I}(X)] = \mathrm{E}[-\ln(\mathrm{P}(X))]

and II is Shannon Information, it be defined as:

I(X)=log2P(X)I(X) = - \log_2 \mathrm{P}(X)

We can noted the meaning of log2pi-\log_2 p_i is "the lower the probability of an event, the greater the information carried when the event occurs", so Shannon entropy is expected value of Shannon information, can be express by:

H(X)=ipilog2pi\mathrm H (X) = - \sum_i p_i \log_2 p_i

Formula of Crafting

To describe pattern of items changes easily, which caused by crafting or another mechanism in the game, I borrow the form of chemical equation:

aX+bYPcZaX + bY \xrightarrow[P]{} cZ

a,b,ca,b,c:amount of materials or products X,YX,Y:materials, items would vanished after crafting process ZZ:products PP:the path of crafting, item(s) won't vanished after crafting process

Information of Items

The nature of Minecraft World are a bunch of digital data, so were reasonably considered it as a string of information, and quantify it as Shannon Entropy or Information. For a block which status was uncertain, the value is:

H(X)=E(I)=ipilog2pi\mathrm H (X) = \mathrm{E} ( \mathrm{I}) = - \sum_i p_i \log_2 p_i

and pip_i is probability of (some type of) block generated in map, therefore we can get information of block of certain type by:

Ix=log2pxI_x = -\log_2 p_x

Information of Crafting

It could calculation entropy of nature resources by probability of generated in the map, but most of items are obtained by crafting in Minecraft, when I mention "crafting" are meaning "the mechanism can be expressed by formula of crafting in the game". Need to gain entropy of non-nature items through calculation.

Let do a crafting example:

X+YWZX + Y \xrightarrow[W]{} Z

X,Y,WX,Y,W represents three different types of blocks, can be treated as three randomly independently event, and their probability of exists in map are pX,pY,pWp_X, p_Y, p_W, so we can get probability of event ZZ happened was multiplied by probabilities, also was summing by information of probabilities:

IZ=log2(pXpYpW)=log2pXlog2pYlog2pW=IX+IY+IW\begin{align} I_Z &= -\log_2(p_X p_Y p_W) \\ &= -\log_2 p_X -\log_2 p_Y -\log_2 p_W \\ &= I_X + I_Y + I_W \end{align}

The Ratio Between Material(s) and Product(s)

When the ratio isn't 1, total number of items would changed.

mXPnYmX \xrightarrow[P]{} nY

and:

HY=mnHXH_Y = \frac{m}{n}H_X

Crafting Path

The item(s) won't vanished as crafting path in the process, so:

XWYΔI=IW\begin{array}{l} X \xrightarrow[W]{} Y & \Delta I = I_W \end{array}

Multiple Crafting Path

When there are multiple path to gain item(s), it can been treats as a parallel system, therefore, the information of products can be expressed by:

I=log2[1i(1pi)]I = -\log_2 [1 - \prod_i(1-p_i)]

The Items with Zero Entropy

The probability of items dropped by monster, are affected by probability of monster generation, and its not related space, but time, so:

HΔt=0=limp0+plogp=0H |_{\Delta t = 0}= -\lim_{p \rightarrow0^+} p\log p = 0

or consider it could been generated in infinity time, the probability of generation is 1, therefor:

Ht=log(1)=0H |_{t \rightarrow \infty} = -\log(1) = 0

Information of Environment

We must define "system", when quantified information of system, so need to defined a range of space and how many blocks or items contained in the space.There are 3 kind of definition of system which common been used under below:

  1. Complete System
    • Blocks, items in inventory block, item entity dropped and items in player inventory, which contained in a limited space.
  2. Block System
    • Blocks and items in inventory block, which contained in a limited space.
  3. Player System
    • Items in the player inventory.

We can quantify entropy of system through rules has been created, and intervening variables can been gain by making statistics on map. We can expect the entropy of environment would changed through behavior of agent, therefore those changes could be one of index of evaluated the agent.

Table of Items Information

The following information evaluation value is for reference only, there are not included every items in the game, and process are not very rigorous. The goal is demonstrated the theory of this article, anyone are interested can finished the table by self.

Natual Resource

Using Cuberite 1.7.X-linux642 to generate map and making statistics by plugin3. There were sampling 10 times, and remove map file to generate new seed every time. To simplify data, I merged yellow flower and red flower, removed flowing water, flowing lava, cobblestone, torch, flame, monster spawner, chest and dead bush, replace them by air.

BlockCountProbabilityInformationEntropy
Air1514640790.75060490.41387440.3106562
Bedrock7882240.00390628.00002870.0312495
Brown Mushroom6480.000003218.24842480.0000586
Cactus110.000000124.12884320.0000013
Clay87900.000043614.48662740.0006310
Coal Ore3459620.00171459.18802080.0157526
Cobweb19980.000009916.62393400.0001646
Diamond Ore196290.000097313.32757580.0012964
Dirt23478570.01163526.42536170.0747603
Double Plant142420.000070613.79041070.0009733
Fence85430.000042314.52774780.0006151
Flower40140.000019915.61744990.0003107
Gold Ore436970.000216512.17302820.0026360
Grass4716460.00233738.74092990.0204303
Gravel5345010.00264888.56044170.0226750
Iron Ore3953360.00195928.99555500.0176237
Lapis Lazuli Ore176900.000087713.47762840.0011815
Leaves3827790.00189699.04212270.0171522
Lily Pad1220.000000620.65753750.0000125
Obsidian23900.000011816.36547990.0001938
Rail25280.000012516.28449410.0002040
Red Mushroom9740.000004817.66049690.0000852
Redstone Ore1601170.000793510.29950790.0081725
Sand2722200.00134909.53386130.0128615
Sandstone1098880.000544610.84260050.0059045
Snow30330.000015016.02174510.0002408
Still Lava1580880.000783410.31790650.0080834
Still Water32819080.01626405.94217150.0966437
Stone408120300.20225072.30578370.4663463
Sugar Canes410.000000222.23072280.0000045
Tallgrass668180.000331111.56032570.0038279
Vines11790.000005817.38492680.0001016
Wood Plank127620.000063213.94870800.0008822
Wood556140.000275611.82511440.0032591

Products

Wood Plank

Wood4 Wood Plank\text{Wood} \xrightarrow[]{} 4 \text{ Wood Plank} IWP=14IW=0.2511.8=2.95\begin{align} I_{WP} &= \frac1{4} I_{W} = 0.25 \cdot 11.8\\ &= 2.95 \end{align}

Crafting Table

4 Wood PlankCrafting Table4 \text{ Wood Plank} \xrightarrow[]{} \text{Crafting Table} ICT=4IWP=42.95=11.8\begin{align} I_{CT} &= 4 I_{WP} = 4 \cdot 2.95 \\ &= 11.8 \end{align}

Stick

2 Wood Plank4 Stick2 \text{ Wood Plank} \xrightarrow[]{} 4 \text{ Stick} IS=24IWP=0.52.95=1.475\begin{align} I_{S} &= \frac{2}{4} I_{WP} = 0.5 \cdot 2.95 \\ &= 1.475 \end{align}

Wooden Pickaxe

3 Wood Plank+2 StickCrafting Table Wooden Pickaxe3 \text{ Wood Plank} + 2 \text{ Stick} \xrightarrow[\text{Crafting Table}]{} \text{ Wooden Pickaxe} IWPickaxe=3IWPlank+2IS+ICT=32.95+21.475+11.8=23.6\begin{align} I_{\text{WPickaxe}} &= 3 I_{\text{WPlank}} + 2 I_{S} + I_{CT}\\ &= 3 \cdot 2.95 + 2 \cdot 1.475 + 11.8\\ &= 23.6 \end{align}

Cobblestone

To simplify calculation, there's not consider crafting path with another type of pickaxe except wooden one.

 Stone Wooden Pickaxe Cobblestone\text{ Stone } \xrightarrow[\text{Wooden Pickaxe}]{} \text{ Cobblestone} IC=IS+IWP=2.31+23.6=25.91\begin{align} I_{C} &= I_{S} + I_{WP} \\ &= 2.31 + 23.6 \\ &= 25.91 \end{align}

Furnace

8 Cobblestone Crafting Table Furnace8\text{ Cobblestone } \xrightarrow[\text{Crafting Table}]{} \text{ Furnace} IF=8IC+ICT=825.91+11.8=219.08\begin{align} I_{F} &= 8I_{C} + I_{CT} \\ &= 8 \cdot 25.91 + 11.8 \\ &= 219.08 \end{align}

Stone Pickaxe

3 Cobblestone+2 StickCrafting Table Stone Pickaxe3 \text{ Cobblestone} + 2 \text{ Stick} \xrightarrow[\text{Crafting Table}]{} \text{ Stone Pickaxe} ISP=3IC+2IS+ICT=325.91+21.475+11.8=92.48\begin{align} I_{\text{SP}} &= 3 I_{\text{C}} + 2 I_{S} + I_{CT}\\ &= 3 \cdot 25.91 + 2 \cdot 1.475 + 11.8\\ &= 92.48 \end{align}

Iron Ore Item

To simplify calculation, there's not consider crafting path with another type of pickaxe except stone one.

 Iron Ore Stone Pickaxe Iron Ore Item\text{ Iron Ore } \xrightarrow[\text{Stone Pickaxe}]{} \text{ Iron Ore Item} IIOI=IIO+ISP=9.00+92.48=101.48\begin{align} I_{IOI} &= I_{IO} + I_{SP} \\ &= 9.00 + 92.48 \\ &= 101.48 \end{align}

Coal

To demonstrate calculation of parallel system, there's consider crafting path with wooden and stone pickaxe.

 Coal Ore Wooden(Stone) Pickaxe Coal\text{ Coal Ore } \xrightarrow[\text{Wooden(Stone) Pickaxe}]{} \text{ Coal} IC1=ICO+IWP=9.18+23.6=32.78\begin{align} I_{C1} &= I_{CO} + I_{WP} \\ &= 9.18 + 23.6 \\ &= 32.78 \end{align} IC2=ICO+ISP=9.18+92.48=101.66\begin{align} I_{C2} &= I_{CO} + I_{SP} \\ &= 9.18 + 92.48 \\ &= 101.66 \end{align} p1=2I1=232.78=1.355928353111010p2=2I2=2101.66=2.496264731271031p=1(1p1)(1p2)=1.3559287026511010p_1 = 2^{-I_1} = 2^{-32.78} = 1.35592835311 \cdot 10^{-10} \\ p_2 = 2^{-I_2} = 2^{-101.66} = 2.49626473127 \cdot 10^{-31} \\ p = 1 - (1-p_1)(1-p_2) = 1.355928702651 \cdot 10^{-10} IC=log2p=32.78I_C = -\log_2 p = 32.78

Iron Ingot

 Iron Ore Item+18 CoalFurnace Iron Ingot\text{ Iron Ore Item} + \frac1{8} \text{ Coal} \xrightarrow[\text{Furnace}]{} \text{ Iron Ingot} III=IIOI+18IC+IF=101.48+0.12532.78+219.08=324.6575\begin{align} I_{II} &= I_{IOI} + \frac1{8} I_{C} + I_{F} \\ &= 101.48 + 0.125 \cdot 32.78 + 219.08\\ &= 324.6575 \end{align}

The Table

It is summary those value are calculated or mention in this article, and just as I said, this article didn't calculated all information of items in the game, therefore the table are unfinished:

Block/ItemInformation (bit)Block/ItemInformation (bit)
Iron Ingot324.66Lapis Lazuli Ore13.48
Furnace219.08Diamond Ore13.33
Iron Ore Item101.48Gold Ore12.17
Stone Pickaxe92.48Wood11.83
Coal32.78Crafting Table11.80
Cobblestone25.91Tallgrass11.56
Cactus24.13Sandstone10.84
Wooden Pickaxe23.60Still Lava10.32
Sugar Canes22.23Redstone Ore10.30
Lily Pad20.66Sand9.53
Brown Mushroom18.25Coal Ore9.19
Red Mushroom17.66Leaves9.04
Vines17.38Iron Ore9.00
Cobweb16.62Grass8.74
Obsidian16.37Gravel8.56
Rail16.28Bedrock8.00
Snow16.02Dirt6.43
Flower15.62Still Water5.94
Fence14.53Wood Plank2.95
Clay14.49Stone2.31
Wood Plank13.95Stick1.48
Double Plant13.79Air0.41

Conclusion

Just like James Lovelock said4:

I’d look for an entropy reduction, since this must be a general characteristic of life.

We can quantify extropy which agent created by reform the environment, to measue the policy:

ΔHπ,t=HoHπ,tπ=argminπΔHπ,t\Delta H_{\pi,t} = H_o - H_{\pi,t} \\ \pi^* = \arg \min_{\pi} \Delta H_{\pi,t}
tags: The Key Of Huanche

Footnotes

  1. 信息熵是什么? - 知乎. (返朴). Retrieved 2020-05-31, from https://www.zhihu.com/question/22178202/answer/667876061

  2. Releases · cuberite/cuberite. (cuberite). Retrieved 2020-05-31, from https://github.com/cuberite/cuberite/releases/download/1.7EOL/Cuberite-linux64.tar.gz

  3. FlySkyPie/cuberite-block-counter: To make statistics of blocks. Retrieved 2020-05-31, from https://github.com/FlySkyPie/cuberite-block-counter

  4. Lovelock, James (1979). GAIA – A New Look at Life on Earth. Oxford University Press. ISBN 978-0-19-286218-1.

· 14 min read
Wei Ji

評價資源

我們能夠直觀的以資源的稀有程度去抽象的衡量資源的價值;越稀有的資源越有價值,反之亦然。但是經濟學上的價值是透過供需市場產生的,並不能作為有明確定義的綱量。我們能獲得的是資源生成在地圖上的機率,但是僅有資源機率並不能夠適當直接作為 agent 的評價手段,需要一些額外的處理。

夏農熵與夏農資訊 (Shannon Entropy and Information)

夏農熵本質上是對我們司空見慣的「不確定現象」的數學化度量。1

H(X)=E[I(X)]=E[ln(P(X))]\mathrm H (X) = \mathrm{E}[\mathrm{I}(X)] = \mathrm{E}[-\ln(\mathrm{P}(X))]

其中 II 為夏農資訊,被定義為:

I(X)=log2P(X)I(X) = - \log_2 \mathrm{P}(X)

我們可以注意到當中的 log2pi-\log_2 p_i 所描述的意義為:「事件發生的機率越低,則當該事件發生時所夾帶的資訊越大」,因此夏農熵也是對不確定的夏農資訊的期望值,亦可寫作:

H(X)=ipilog2pi\mathrm H (X) = - \sum_i p_i \log_2 p_i

合成式 (Formular of Crafting)

為了方便描述合成或其他遊戲機制使資源 (items) 發生變化的過程,我借用了化學反應式的形式來表達:

aX+bYPcZaX + bY \xrightarrow[P]{} cZ

a,b,ca,b,c:原料或產物的數量 X,YX,Y:原料,合成過程會被消耗的物品 ZZ:產物 PP:合成途徑,參與合成但是不會被消耗的物品。

資源的資訊量 (Information of Items)

Minecraft 世界在本質上就是一團數位資料,因此將其視作一串由方塊構成的資訊,並用夏農熵或夏農資訊去衡量其資訊量是十分合理的作法。對於一個不確定狀態的方塊來說,其值為:

H(X)=E(I)=ipilog2pi\mathrm H (X) = \mathrm{E} ( \mathrm{I}) = - \sum_i p_i \log_2 p_i

其 中pip_i 為某種類型的方塊出現在地圖中的機率,換句話說我們可以得知特定種類方塊其資訊量為:

Ix=log2pxI_x = -\log_2 p_x

合成的資訊量 (Information of Crafting)

天然資源能夠以其在地圖生成的機率來計算熵,但是 Minecraft 中的大部分物品需透過合成途徑獲得,當我提到合成是指:「能夠被合成式所表示的所有遊戲機制」,這些非天然物品的熵需透過計算取得。

以一個合成為例:

X+YWZX + Y \xrightarrow[W]{} Z

X,Y,WX,Y,W 分別代表三種不同類型的方塊,我們可以視作三個隨機事件,而這三者出現在地圖的機率分別為 pX,pY,pWp_X, p_Y, p_W 並且互為獨立事件,因此我們可以得知事件 ZZ 發生的機率為三者的機率相乘,也就是三者資訊量的加總:

IZ=log2(pXpYpW)=log2pXlog2pYlog2pW=IX+IY+IW\begin{align} I_Z &= -\log_2(p_X p_Y p_W) \\ &= -\log_2 p_X -\log_2 p_Y -\log_2 p_W \\ &= I_X + I_Y + I_W \end{align}

數量的變化 (The ratio between resource and product)

當原料與產物的比值不為 1 時,物品的總數量發生了變化。

mXPnYmX \xrightarrow[P]{} nY

則:

HY=mnHXH_Y = \frac{m}{n}H_X

合成途徑 (Crafting Path)

作為合成途徑的物品不會被消耗,因此:

XWYΔI=IW\begin{array}{l} X \xrightarrow[W]{} Y & \Delta I = I_W \end{array}

多重途徑 (Multiple Crafting Path)

當一個物品有多種取得途徑時可以視作一個並聯系統 (parallel systems),因此該物品的資訊量可以寫作:

I=log2[1i(1pi)]I = -\log_2 [1 - \prod_i(1-p_i)]

零資訊的資源 (The Items with Zero Entropy)

怪物掉落物仰賴怪物的生成,但是怪物生成的機率並不是與空間關聯,而是與時間關聯的,以空間機率考慮:

HΔt=0=limp0+plogp=0H |_{\Delta t = 0}= -\lim_{p \rightarrow0^+} p\log p = 0

若或是考慮其在無窮的時間中能夠被無限生成,事件發生的機率為 1,因此:

Ht=log(1)=0H |_{t \rightarrow \infty} = -\log(1) = 0

環境的資訊量 (Information of Environment)

當我們要量化系統的資訊量時,必須先界定所謂的「系統」。要定義一個系統,我們要給定一個範圍(空間大小)以及具體包含了多少方塊或物品。以下有三種定義應該是我們比較常用的系統類型:

  1. 完整系統
    • 在一劃定的範圍內,所包含的方塊、收納方塊內容物、掉落物與玩家物品欄(裝備欄)內容物。
  2. 方塊系統
    • 在一劃定的範圍內,所包含的方塊與收納方塊內容物。
  3. 玩家系統
    • 玩家物品欄(裝備欄)內容物。

透過到目前為止建立的規則,我們可以量化一個系統的熵,而該值能透過統計地圖中的方塊與實體取得。隨著 agent 在環境中活動,我們可以預期環境的熵會因此發生改變,這個變化量便可做為評量 agent 的指標之一。

資訊量表 (Table of Items Information)

以下的資訊量評估值僅供參考,過程並不嚴謹並且沒有對所有的物品進行計算。目的只是透過實做示範如何實踐本文建構的理論,有興趣的人可以自行用更嚴謹的方式建構完整的遊戲物件資訊量表。

天然資源 (Natual Resource)

使用 Cuberite 1.7.X-linux642 生成地圖並搭配插件3統計方塊數量。總共抽樣 10 次,每次都會將地圖檔案刪除以生成新的種子。為了簡化數據,將紅花與黃花視為同樣的東西,並將流動水、流動岩漿、鵝卵石、火把、火焰、生怪磚、箱子與枯萎灌木移除並用空氣取代。統計資料如下表:

方塊數量生成機率資訊量
Air1514640790.75060490.41387440.3106562
Bedrock7882240.00390628.00002870.0312495
Brown Mushroom6480.000003218.24842480.0000586
Cactus110.000000124.12884320.0000013
Clay87900.000043614.48662740.0006310
Coal Ore3459620.00171459.18802080.0157526
Cobweb19980.000009916.62393400.0001646
Diamond Ore196290.000097313.32757580.0012964
Dirt23478570.01163526.42536170.0747603
Double Plant142420.000070613.79041070.0009733
Fence85430.000042314.52774780.0006151
Flower40140.000019915.61744990.0003107
Gold Ore436970.000216512.17302820.0026360
Grass4716460.00233738.74092990.0204303
Gravel5345010.00264888.56044170.0226750
Iron Ore3953360.00195928.99555500.0176237
Lapis Lazuli Ore176900.000087713.47762840.0011815
Leaves3827790.00189699.04212270.0171522
Lily Pad1220.000000620.65753750.0000125
Obsidian23900.000011816.36547990.0001938
Rail25280.000012516.28449410.0002040
Red Mushroom9740.000004817.66049690.0000852
Redstone Ore1601170.000793510.29950790.0081725
Sand2722200.00134909.53386130.0128615
Sandstone1098880.000544610.84260050.0059045
Snow30330.000015016.02174510.0002408
Still Lava1580880.000783410.31790650.0080834
Still Water32819080.01626405.94217150.0966437
Stone408120300.20225072.30578370.4663463
Sugar Canes410.000000222.23072280.0000045
Tallgrass668180.000331111.56032570.0038279
Vines11790.000005817.38492680.0001016
Wood Plank127620.000063213.94870800.0008822
Wood556140.000275611.82511440.0032591

加工品 (Products)

木板

Wood4 Wood Plank\text{Wood} \xrightarrow[]{} 4 \text{ Wood Plank} IWP=14IW=0.2511.8=2.95\begin{align} I_{WP} &= \frac1{4} I_{W} = 0.25 \cdot 11.8\\ &= 2.95 \end{align}

工作台

4 Wood PlankCrafting Table4 \text{ Wood Plank} \xrightarrow[]{} \text{Crafting Table} ICT=4IWP=42.95=11.8\begin{align} I_{CT} &= 4 I_{WP} = 4 \cdot 2.95 \\ &= 11.8 \end{align}

木棒

2 Wood Plank4 Stick2 \text{ Wood Plank} \xrightarrow[]{} 4 \text{ Stick} IS=24IWP=0.52.95=1.475\begin{align} I_{S} &= \frac{2}{4} I_{WP} = 0.5 \cdot 2.95 \\ &= 1.475 \end{align}

木鎬

3 Wood Plank+2 StickCrafting Table Wooden Pickaxe3 \text{ Wood Plank} + 2 \text{ Stick} \xrightarrow[\text{Crafting Table}]{} \text{ Wooden Pickaxe} IWPickaxe=3IWPlank+2IS+ICT=32.95+21.475+11.8=23.6\begin{align} I_{\text{WPickaxe}} &= 3 I_{\text{WPlank}} + 2 I_{S} + I_{CT}\\ &= 3 \cdot 2.95 + 2 \cdot 1.475 + 11.8\\ &= 23.6 \end{align}

鵝卵石

為了簡化計算,下列過程不考慮其他材質的鎬挖掘的情況。

 Stone Wooden Pickaxe Cobblestone\text{ Stone } \xrightarrow[\text{Wooden Pickaxe}]{} \text{ Cobblestone} IC=IS+IWP=2.31+23.6=25.91\begin{align} I_{C} &= I_{S} + I_{WP} \\ &= 2.31 + 23.6 \\ &= 25.91 \end{align}

熔爐

8 Cobblestone Crafting Table Furnace8\text{ Cobblestone } \xrightarrow[\text{Crafting Table}]{} \text{ Furnace} IF=8IC+ICT=825.91+11.8=219.08\begin{align} I_{F} &= 8I_{C} + I_{CT} \\ &= 8 \cdot 25.91 + 11.8 \\ &= 219.08 \end{align}

石鎬

3 Cobblestone+2 StickCrafting Table Stone Pickaxe3 \text{ Cobblestone} + 2 \text{ Stick} \xrightarrow[\text{Crafting Table}]{} \text{ Stone Pickaxe} ISP=3IC+2IS+ICT=325.91+21.475+11.8=92.48\begin{align} I_{\text{SP}} &= 3 I_{\text{C}} + 2 I_{S} + I_{CT}\\ &= 3 \cdot 25.91 + 2 \cdot 1.475 + 11.8\\ &= 92.48 \end{align}

鐵礦

為了簡化計算,下列過程不考慮其他材質的鎬挖掘的情況。

 Iron Ore Stone Pickaxe Iron Ore Item\text{ Iron Ore } \xrightarrow[\text{Stone Pickaxe}]{} \text{ Iron Ore Item} IIOI=IIO+ISP=9.00+92.48=101.48\begin{align} I_{IOI} &= I_{IO} + I_{SP} \\ &= 9.00 + 92.48 \\ &= 101.48 \end{align}

為了示範並聯系統的計算,以下僅考慮木鎬與石鎬的挖掘途徑。

 Coal Ore Wooden(Stone) Pickaxe Coal\text{ Coal Ore } \xrightarrow[\text{Wooden(Stone) Pickaxe}]{} \text{ Coal} IC1=ICO+IWP=9.18+23.6=32.78\begin{align} I_{C1} &= I_{CO} + I_{WP} \\ &= 9.18 + 23.6 \\ &= 32.78 \end{align} IC2=ICO+ISP=9.18+92.48=101.66\begin{align} I_{C2} &= I_{CO} + I_{SP} \\ &= 9.18 + 92.48 \\ &= 101.66 \end{align} p1=2I1=232.78=1.355928353111010p2=2I2=2101.66=2.496264731271031p=1(1p1)(1p2)=1.3559287026511010\begin{align} p_1 = 2^{-I_1} = 2^{-32.78} = 1.35592835311 \cdot 10^{-10} \\ p_2 = 2^{-I_2} = 2^{-101.66} = 2.49626473127 \cdot 10^{-31} \\ p = 1 - (1-p_1)(1-p_2) = 1.355928702651 \cdot 10^{-10} \end{align} IC=log2p=32.78I_C = -\log_2 p = 32.78

鐵錠

 Iron Ore Item+18 CoalFurnace Iron Ingot\text{ Iron Ore Item} + \frac1{8} \text{ Coal} \xrightarrow[\text{Furnace}]{} \text{ Iron Ingot} III=IIOI+18IC+IF=101.48+0.12532.78+219.08=324.6575\begin{align} I_{II} &= I_{IOI} + \frac1{8} I_{C} + I_{F} \\ &= 101.48 + 0.125 \cdot 32.78 + 219.08\\ &= 324.6575 \end{align}

資訊量總表 (The Table)

正如我提過得,本文並沒有計算所有物品的資訊量,僅是將先前統計或計算得到的數值做個整理,因此這個表並不完整。

方塊/物品資訊量方塊/物品資訊量
Iron Ingot324.66Lapis Lazuli Ore13.48
Furnace219.08Diamond Ore13.33
Iron Ore Item101.48Gold Ore12.17
Stone Pickaxe92.48Wood11.83
Coal32.78Crafting Table11.80
Cobblestone25.91Tallgrass11.56
Cactus24.13Sandstone10.84
Wooden Pickaxe23.60Still Lava10.32
Sugar Canes22.23Redstone Ore10.30
Lily Pad20.66Sand9.53
Brown Mushroom18.25Coal Ore9.19
Red Mushroom17.66Leaves9.04
Vines17.38Iron Ore9.00
Cobweb16.62Grass8.74
Obsidian16.37Gravel8.56
Rail16.28Bedrock8.00
Snow16.02Dirt6.43
Flower15.62Still Water5.94
Fence14.53Wood Plank2.95
Clay14.49Stone2.31
Wood Plank13.95Stick1.48
Double Plant13.79Air0.41

結語

就如像詹姆斯·拉夫洛克(James Lovelock)所說的4

I’d look for an entropy reduction, since this must be a general characteristic of life.

我們可以透過 agent 對環境進行改造從而造成的熵減量,作為衡量策略的方法:

ΔSπ,t=SoSπ,tπ=argminπΔSπ,t\Delta S_{\pi,t} = S_o - S_{\pi,t} \\ \pi^* = \arg \min_{\pi} \Delta S_{\pi,t}

Footnotes

  1. 信息熵是什么? - 知乎. (返朴). Retrieved 2020-05-31, from https://www.zhihu.com/question/22178202/answer/667876061

  2. Releases · cuberite/cuberite. (cuberite). Retrieved 2020-05-31, from https://github.com/cuberite/cuberite/releases/download/1.7EOL/Cuberite-linux64.tar.gz

  3. FlySkyPie/cuberite-block-counter: To make statistics of blocks. Retrieved 2020-05-31, from https://github.com/FlySkyPie/cuberite-block-counter

  4. Lovelock, James (1979). GAIA – A New Look at Life on Earth. Oxford University Press. ISBN 978-0-19-286218-1.