分享讨论IT相关的内容
回复
头像
ejsoon
一枝独秀
帖子: 6293
注册时间: 2022年 11月 18日 17:36
联系:

準備研究Wasm

帖子 ejsoon »

代码: 全选

你所提到的 **"wrbassemble"** 應該是 **WebAssembly (Wasm)** 的拼寫誤植。

**皮卡魚 (Pikafish)** 是一款基於 Stockfish 架構開發的強力開源中國象棋引擎。因為它是由 C++ 編寫的,若想在瀏覽器(網頁端)運行,我們需要使用 **Emscripten** 工具鏈將其 C++ 源碼編譯為 WebAssembly(`.wasm` 和 `.js` 膠水代碼),然後透過 **Web Worker** 在前端非同步調用。

以下是完整的實作指南,從「編譯環境搭建」到「網頁前端調用」。

---

## 一、 將皮卡魚編譯為 WebAssembly (Wasm)

編譯 C++ 項目至 WebAssembly 最主流的工具是 **Emscripten (emcc)**。

### 1. 準備環境

首先,你需要安裝 Emscripten SDK (emsdk):

```bash
# 下載 emsdk
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk

# 安裝並激活最新版(建議配合皮卡魚源碼要求,通常最新穩定版即可)
./emsdk install latest
./emsdk activate latest

# 激活環境變量(每次新開終端都需要執行)
source ./emsdk_env.sh

```

### 2. 獲取皮卡魚源碼

```bash
git clone https://github.com/official-pikafish/Pikafish.git
cd Pikafish/src

```

### 3. 使用 Emscripten 進行編譯

因為皮卡魚繼承自 Stockfish,其 `Makefile` 通常已經支持交叉編譯,或者你可以直接使用 `emcc` 命令手動編譯核心代碼。

最簡單、最直接的手動編譯命令如下(在 `src` 目錄下執行):

```bash
emcc -O3 -std=c++17 \
  -s WASM=1 \
  -s ALLOW_MEMORY_GROWTH=1 \
  -s EXPORTED_FUNCTIONS="['_main']" \
  -s EXIT_RUNTIME=0 \
  -s MODULARIZE=1 \
  -s EXPORT_NAME="Pikafish" \
  *.cpp -o pikafish.js

```

> **💡 說明:**
> * `-O3`:開啟最優化,提升 Wasm 運行效率。
> * `-s WASM=1`:輸出為 WebAssembly 格式。
> * `-s MODULARIZE=1` 與 `-s EXPORT_NAME="Pikafish"`:將輸出的 JS 包裝為一個模組,方便前端載入。
> * 執行後會生成兩個文件:`pikafish.js`(膠水代碼)與 `pikafish.wasm`(核心二進制檔)。
> 
> 

---

## 二、 網頁程式調用 (HTML + JavaScript)

象棋引擎計算非常消耗 CPU。如果直接在瀏覽器主線程運行,網頁會直接卡死。因此,**必須使用 Web Worker 在後台線程運行皮卡魚**。

### 1. 建立 Web Worker 腳本 (`pikafish-worker.js`)

這個文件負責在後台線程載入 Wasm,並透過 **UCI 協議**(象棋引擎通用通訊協議)與主線程通訊。

```javascript
// 引入編譯好的膠水 JS
importScripts('pikafish.js');

let engine;

// 初始化 Wasm 引擎
Pikafish().then(instance => {
    engine = instance;
    postMessage({ type: 'ready', data: 'Pikafish Wasm Loaded!' });
});

// 監聽來自網頁主線程的指令 (UCI 協議)
onmessage = function (e) {
    const cmd = e.data;
    console.log("收到主線程指令: ", cmd);
    
    // 將指令傳送給 C++ 的 standard input
    if (engine && typeof engine.ccall === 'function') {
        // 假設編譯時將輸入接口導出,或通過內置 stdin 處理
        // 註:Stockfish 家族通常會重定向 stdin/stdout
    }
    
    // 這裡模擬引擎計算並回傳結果給主線程
    // 實際完整對接中,Emscripten 會自動將引擎的 stdout 透過 print 函數導出
};

// 重定向 Emscripten 的輸出到主線程
var Module = {
    print: function(text) {
        postMessage({ type: 'stdout', data: text });
    },
    printErr: function(text) {
        postMessage({ type: 'stderr', data: text });
    }
};

```

### 2. 建立前端網頁 (`index.html`)

這是用戶交互的介面,用來向 Worker 發送 UCI 指令(例如 `uci`、`isready`、開始計算 `go depth 10` 等),並接收皮卡魚返回的評估分數與最佳著法(bestmove)。

```html
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
    <meta charset="UTF-8">
    <title>皮卡魚 WebAssembly 網頁測試</title>
    <style>
        body { font-family: sans-serif; padding: 20px; }
        #log { width: 100%; height: 300px; background: #222; color: #0f0; padding: 10px; overflow-y: scroll; font-family: monospace; }
        button { padding: 10px 20px; margin-right: 10px; cursor: pointer; }
    </style>
</head>
<body>

    <h2>皮卡魚 (Pikafish) Wasm 引擎主控台</h2>
    
    <button id="btn-init">1. 初始化引擎</button>
    <button id="btn-isready" disabled>2. 檢查準備狀態 (isready)</button>
    <button id="btn-go" disabled>3. 開始計算最佳著法 (go depth 5)</button>
    
    <h3>引擎輸出日誌:</h3>
    <div id="log"></div>

    <script>
        const logDiv = document.getElementById('log');
        const btnInit = document.getElementById('btn-init');
        const btnIsReady = document.getElementById('btn-isready');
        const btnGo = document.getElementById('btn-go');
        
        let pikafishWorker = null;

        function log(msg) {
            logDiv.innerHTML += msg + '<br>';
            logDiv.scrollTop = logDiv.scrollHeight;
        }

        // 1. 初始化 Worker
        btnInit.onclick = () => {
            log("正在加載 Worker 及 WebAssembly...");
            pikafishWorker = new Worker('pikafish-worker.js');

            // 監聽 Worker 返回的消息
            pikafishWorker.onmessage = function (event) {
                const { type, data } = event.data;
                if (type === 'ready') {
                    log(`系統提示: ${data}`);
                    btnIsReady.disabled = false;
                } else {
                    log(`[Pikafish] ${data}`);
                }
            };
            btnInit.disabled = true;
        };

        // 2. 發送 isready 握手指令
        btnIsReady.onclick = () => {
            if (pikafishWorker) {
                log("發送指令: uci 和 isready");
                pikafishWorker.postMessage('uci');
                pikafishWorker.postMessage('isready');
                btnGo.disabled = false;
            }
        };

        // 3. 讓引擎計算當前局面
        btnGo.onclick = () => {
            if (pikafishWorker) {
                log("設定初始局面並計算 5 步深度的最佳走法...");
                // position startpos 為象棋起手式,go depth 5 限制計算深度為 5 層
                pikafishWorker.postMessage('position startpos');
                pikafishWorker.postMessage('go depth 5');
            }
        };
    </script>
</body>
</html>

```

---

## 💡 關鍵開發提示:

1. **NNUE 權重文件 (Neural Network):** 皮卡魚是一個 **NNUE** 強化的象棋引擎。Wasm 運行時需要加載 `.nnue` 權重文件才能發揮最大棋力。你可以利用 Emscripten 的 `--preload-file` 參數,將 `.nnue` 權重打包到虛擬文件系統中,或者在 JavaScript 端手動下載後傳給 WebAssembly。
2. **同源策略 (CORS) 限制:** 由於瀏覽器的安全限制,Web Worker **無法透過本地雙擊 `index.html` 運行**。你必須使用本地伺服器(例如 VS Code 的 Live Server 插件,或是執行 `python3 -m http.server 8000`)來託管運行網頁。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
帖子: 6293
注册时间: 2022年 11月 18日 17:36
联系:

Re: 準備研究Wasm

帖子 ejsoon »

代码: 全选

如果你在編譯時使用了 --preload-file 參數,NNUE 權重文件並不會直接整合進 pikafish.js 或 pikafish.wasm 中。

事實上,執行後會生成 三個文件(而不是原本的兩個):

    pikafish.js(膠水代碼,包含虛擬文件系統的加載邏輯)

    pikafish.wasm(C++ 編譯出來的二進制二進位核心)

    pikafish.data(新生成的文件! 裡面打包了你的 .nnue 權重與其他預載入的文件)

1. 為什麼會多一個 .data 文件?(Preloading 機制)

當你使用 --preload-file nnue_folder 時,Emscripten 採用的是預載入(Preloading)機制:

    它是將你指定的資料夾或文件,單獨打包成一個名為 pikafish.data 的二進位封包。

    網頁載入時,pikafish.js 會發起一個非同步請求(XHR/Fetch)去下載 pikafish.data,然後在瀏覽器的記憶體中構建虛擬文件系統(MEMFS),並將其掛載。

    這樣做的好處是:網頁可以一邊下載 Wasm 代碼,一邊下載權重數據,且不會因為權重文件太大(通常 NNUE 有數十 MB)而導致 Wasm 文件本身體積過大、影響網頁編譯與啟動速度。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复
  • 相似主题
    回复总数
    阅读次数
    最新帖子

在线用户

正浏览此版面之用户: 没有注册用户 和 12 访客