跳到主要內容

精選文章

2026 和 AI 一起寫程式 - 4. 沙堆模型 Abelian Sandpile Model 曼陀羅 Mandala

前篇:2026 和 AI 一起寫程式 - 3 井字遊戲 - 1 kotlin 2.3



(圖 by Midjourney )

關於 沙堆模型 和 曼陀羅, 可以參考底下兩篇網誌:
( 細節實作可以參考 這個 Github 專案的 README.md )

這次我們來玩一個超經典的複雜系統模型:Abelian Sandpile Model(阿貝爾沙堆模型,也叫 BTW 模型)。規則簡單到不行——每個格子堆沙粒,≥4 就「崩塌」分給四個鄰居——卻能從隨機加沙中自動長出高度對稱、層層放射的圖案,很多人直接叫它「沙堆曼陀羅 」(sandpile mandala)。


我用 Kotlin + WebAssembly(Wasm)把它實作出來,讓瀏覽器直接跑,還能互動加沙看它成長。成品 Demo 放這裡:
👉 https://neojou.github.io/mandala/sandpile




先簡單科普:什麼是 Abelian Sandpile Model?

(詳細版見我另一篇:沙堆模型完整介紹

這個模型 1987 年由 Per Bak、Chao Tang、Kurt Wiesenfeld 提出,原本想解釋自然界的 1/f 噪聲(粉紅噪聲),結果意外開啟了「自組織臨界性」(Self-Organized Criticality, SOC)的概念。


規則超簡單: 

- 格子世界,每格有整數高度(沙粒數) z(i,j) 

- 隨機選一格加 1 粒沙 

- 如果某格 z ≥ 4,就「崩塌」:自己減 4,四個鄰居(上、下、左、右)各 +1 

- 連鎖反應繼續,直到所有格子 < 4 才穩定


關鍵特性: 

- Abelian(阿貝爾性):崩塌順序不影響最終結果(Deepak Dhar 1990 年證明) 

- 系統自動趨向「臨界狀態」,小加沙可能引發大「雪崩」(avalanche),雪崩大小呈冪律分佈 

- 最神奇玩法:只從正中央一直加沙,跑幾千到幾萬次,就會長出近似曼陀羅的對稱圖案——同心圓層次、放射臂、甚至 fractal 細節!


為什麼會長出「曼陀羅」?(藝術與數學的奇妙連結)

詳細曼陀羅介紹見:曼陀羅 - 從宗教藝術到科學模型

曼陀羅(Mandala,梵文「圓圈」)本是佛教/印度教的宇宙象徵圖,以中心放射對稱、層層展開為特徵。科學上,它涉及對稱群(群論)、分形自相似。

沙堆模型為什麼這麼像? 

- 從中心持續加沙 → 強制中心對稱 

- 崩塌規則是 Laplacian-like(離散拉普拉斯算子),自然產生放射/同心結構 

- 穩定態: 尤其是 recurrent configurations)常出現 90° 旋轉對稱 

- 顏色映射(高度、崩塌計數)後,視覺上直接變成「數學曼陀羅」


很多人用它生成藝術、冥想圖,甚至哲學上呼應「簡單規則湧現複雜秩序」——跟佛教「緣起性空」有點異曲同工之妙 ^^。


我的實作過程:Kotlin + Wasm 怎麼做?

系列目標是用現代工具重現經典模型,讓瀏覽器直接跑,不裝任何東西。

而這是 Kotlin Multiplatform , 如一開始這篇 井字遊戲 所述,

將來打算擴展到 Android 和 iOS


為什麼選 Kotlin + Wasm? 

- Kotlin 語法現代、null-safe、多平台(我之前用在 Android、桌面、Web) 

- Kotlin/Wasm 編譯到 WebAssembly,效能接近原生,比純 JS 快很多(尤其大格子崩塌迴圈) 

- 與 JS Canvas 互操作簡單


核心資料結構 

- 用 IntArray 存 2D 格子

- 採用 Android 常見的 MVVM 架構, SandpileBoard 物件負責計算

- 參考人的視覺殘留, 定時 25ms 才做一個 snapshot 給 View Model, 而不是每顆沙粒算完就畫一張, 這樣大幅提升了整體的效能


崩塌演算法 用 Queue 處理待崩塌格子(BFS 風格,避免遞迴 stack overflow):

fun topple() {
    while (unstable.isNotEmpty()) {
        val (x, y) = unstable.removeFirst()
        if (grid[x * width + y] < 4) continue
        
        grid[x * width + y] -= 4
        // 四鄰 +1,邊界可吸沙(或反射/開放)
        if (x > 0) { grid[(x-1)*width + y]++; if (grid[(x-1)*width + y] == 4) unstable.add((x-1) to y) }
        if (x < height-1) { ... }
        if (y > 0) { ... }
        if (y < width-1) { ... }
        
        // 記錄崩塌次數(用來畫熱圖曼陀羅)
        collapseCount[x * width + y]++
    }
}

渲染 

- 用 Canvas 2D context 

- 顏色映射:依照高度 0–3 設計顏色讓曼陀羅更華麗, 

    將來色盤可以自訂, 如灰階,彩虹或火山色階 

- 互動:目前是自動從中心加,將來可以改成隨機,或動態指定位置


遇到的坑:

高度不對問題


1. 先前問題:高度不對; 這問題在之前的井字遊戲也有發生



目前我用的是第一種方式 : WasmMain.kt - Github




第二種方式的寫法:


推薦合適的 index.html - Github


執行:

原本:


現在:


成果展示 & 互動玩玩看

直接點這裡跑:https://neojou.github.io/mandala/sandpile

延伸:這模型還能幹嘛?

  • 物理:模擬地震、森林火、股市崩盤
  • 數學:sandpile group(有限阿貝爾群)、身份元素(加到 identity 會回到原態)
  • 藝術:生成 NFT、分形壁紙
  • AI 交叉:臨界學習、神經網路 avalanches

未來想試:3D 沙堆、聲音映射(崩塌當音階)、更大格子用 WebGL。

結語

用最簡單規則,卻能長出宇宙級對稱美感——這就是複雜系統的魅力。我跟 AI 一起 debug、優化、配色,過程超有趣!

GitHub 原始碼:https://github.com/neojou/sandpile
歡迎 fork、star、提 issue,一起玩沙!

下一集再見~(預告:可能玩 Game of Life 或其他 cellular automata?)


留言

熱門文章