nmrugg / stockfish.js

The Stockfish chess engine in Javascript
GNU General Public License v3.0
902 stars 129 forks source link

issue with stockfish + webpack + electron #38

Open kevinludwig opened 4 years ago

kevinludwig commented 4 years ago

Im trying to get a basic electron+webpack+stockfish setup working. I'm getting the following error back in the onmessage callback (as well as different, subsequent errors):

failed to compile wasm module: CompileError: WebAssembly.Module(): expected magic word 00 61 73 6d, found 3c 21 44 4f @+0

My setup is basically like this:

import StockfishWorker from 'worker-loader!stockfish';
let stockfish = null; 
let isReady = false;
let handler = null;
const cmd = (cmd: string) => stockfish.postMessage(cmd);

export const registerHandler = (h) => {
    handler = h;
};

const init = () => {
    if (!stockfish) {
        stockfish = new StockfishWorker();
        stockfish.onmessage = (ev) => {
            const line = ev && typeof ev === 'object' ? ev.data : ev;
            console.log('line:', line);
            if (line.startsWith('uciok') || line.startsWith('option name')) return;
            if (line === 'readyok') isReady = true;
            else if (handler) {
                handler(line);
            }
        }

        cmd('uci');
        cmd('isready');
    }
};

export const shutdown = () => {
    if (stockfish) {
        cmd('quit');
    }
};

export const newGame = () => {
    init();
    cmd('ucinewgame');
};

export const newPosition = (fen) => {
    if (!stockfish) {
        init();
        newGame();
    }
    if (isReady) {
        cmd('position ' + fen);
        cmd('go depth 20');
    }
};

export default {
    shutdown,
    newGame,
    newPosition,
    registerHandler
}

Where, I think the relevant info is that I'm trying to lazy load stockfish based on when the first newGame or newPosition gets called. And I'm using worker-loader webpack loader to run stockfish in a web worker.

Also, sort of an aside question, is the way Im doing analysis considered OK? I.e. I'm doing UCI commands 'position ' and never giving moves that led up to the position? Because that's much easier given that I'll be having a PGN to play through with RAVs and a UX where the user can jump around? I notice in the examples that position is typically specified as position startpos <moves>, which is fine for human vs computer play through with chess.js but a bit more annoying in my case?

EDIT: I tried command line stockfish just giving UCI commands, to see how this works. What I am noticing is that it probably only wants the first segment of FEN string (before first space), not the full FEN with castling rights and en passant string etc. Also, if I just give arbitrary FEN string and then do "go depth 20" its always recommending a move for white even in a black to move position (probably related to above). Which seems to mean, that the expectation is that FEN string is only for specifying alternate start positions (960 or something) and you're always expected to provide the move list that led up to that position? This is totally getting into criticism of UCI at this point, but how do I provide an endgame study type position where it's black to move? I don't have a movelist that led up to it, and based on what Im figuring out right now I would need to flip colors or something?

EDIT2: After researching more, I've realized that I mis-understood the protocol around the position command. I should be specifying "position fen [fen-string]". The spec isn't actually very clear on this, but glad this works!

nmrugg commented 4 years ago

expected magic word 00 61 73 6d, found 3c 21 44 4f @+0

3c 21 44 4f is hex for <!DO. It looks like it is looking for the engine in the wrong place and finding an HTML page. By default, it will look for stockfish.wasm in the same directory as stockfish.js. You may want to enable devTools in electron to see what path it is looking up.

Also, sort of an aside question, is the way Im doing analysis considered OK? I.e. I'm doing UCI commands 'position ' and never giving moves that led up to the position?

This is not ideal. You should always give the moves, not the current FEN, if possible because without the moves, it cannot calculate three-fold repetition, which will lead to erroneous evaluation occasionally.

kevinludwig commented 4 years ago

I'm not sure it matters but I've configured the worker-loader differently and now I;m getting this error:

issuing command to Stockfish: uci game.ts?db45:9 STOCKFISH: sync fetching of the wasm failed: you can preload it to Module['wasmBinary'] manually, or emcc.py will do that for you when generating HTML (but not JS) game.ts?db45:9 STOCKFISH: sync fetching of the wasm failed: you can preload it to Module['wasmBinary'] manually, or emcc.py will do that for you when generating HTML (but not JS) game.ts?db45:9 STOCKFISH: failed to compile wasm module: abort("sync fetching of the wasm failed: you can preload it to Module['wasmBinary'] manually, or emcc.py will do that for you when generating HTML (but not JS)"). Build with -s ASSERTIONS=1 for more info. game.ts?db45:9 STOCKFISH: Assertion failed: Cannot call unknown function init, make sure it is exported game.ts?db45:9 STOCKFISH: Assertion failed: Cannot call unknown function init, make sure it is exported

I haven't worked with Web assembly before so just trying to wrap my head around how this is supposed to be working. Apparently there is some boot up sequence that is needed to start another file stockfish.wasm. Is stockfish.js trying to XHR load the file stockfish.wasm, assuming that both stockfish.js and stockfish.wasm are hosted on a server in the same folder? Because that's just not going to be the case here, and I'm not sure if that's going to work at all?

hunght commented 3 years ago

same here im trying to use expo web with stockfish

honkskillet commented 3 years ago

@kevinludwig Did you get using worker-loader to work? What was did your webpack config file look like?

honkskillet commented 3 years ago

I use vuejs. I didn't want to have to hard copy the stockfish file into the public folder of my vue app. I wanted to be able to import them from my node_module folder. To do this I ended up using worker-plugin instead of worker-loader. I'm sure worker-loader would work, I just tried worker-plugin first.

My vue.config.js looks like

const WorkerPlugin = require('worker-plugin');  //don't forget to npm install this package
module.exports = {
  configureWebpack: { 
    < .... >
    plugins: [
      new WorkerPlugin()
    ]
  }
}

If you were using a webpack.config.js it would be

const WorkerPlugin = require('worker-plugin');
module.exports = {
    plugins: [
      new WorkerPlugin()
    ]
}

Then in my app I have...

let engine = new Worker('stockfish/src/stockfish.asm', { type: 'module' });
...
engine.onmessage = myReceiveMessageFromEngineFunction;
...
engine.postMessage(uciString);