Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"<script src="stockfish.js"></script>" vs. "import Stockfish from 'stockfish'" #12

Open
sombor-shuffle opened this issue Jun 10, 2023 · 7 comments

Comments

@sombor-shuffle
Copy link

sombor-shuffle commented Jun 10, 2023

Hello! I'm trying to use Fairy Stockfish inside of a Next.js project.

I installed the npm package fairy-stockfish-nnue.wasm.

Now, placing <script src="stockfish.js"></script> and then referencing Stockfish inside of another script element works just fine:

<script>

    console.log(Stockfish) // can be referenced here.

</script>

However, it remains inaccessible to the rest of my application. This lead me to try to import Stockfish from 'stockfish'. Sadly, using Stockfish now leads to an error:

console.log(Stockfish) gives Module not found: Can't resolve 'fs'.

Looking inside of stockfish.js, I can see that "fs" is indeed used, and from here on out I don't know how to proceed.

Any pointers to what I should do to be able to use Fairy Stockfish? :)

@ianfab
Copy link
Member

ianfab commented Jun 11, 2023

Hi. Did you already have a look at the demo https://github.com/ianfab/fairy-stockfish-nnue-wasm-demo? I hope that should help to get started.

@sombor-shuffle
Copy link
Author

sombor-shuffle commented Jun 11, 2023

Hi @ianfab, thank you for writing back to me!

I spent some time with the demo yesterday and today.

By reusing the code found there, it's possible for me to create a new html file (foo.html) inside of the public folder which...

  1. References Stockfish
  2. Prints all incoming messages from the engine
  3. Posts the initial "uci"
<html>
    <body>
        <script src="./lib/stockfish.js"></script>
        <script>

            (async () => {

                let stockfish = null;

                await Stockfish().then(_stockfish => {
                    stockfish = _stockfish;
                    stockfish.addMessageListener(console.log);
                });

                stockfish.postMessage("uci");

            })()
            
        </script>
    </body>
</html>

Opening localhost:5000/foo.html and looking at the console shows the engine's id and options, followed by "uciok".

This is really, really cool, and I'm trying to get it to work inside of a Next.js project (created with npx create-next-app).

The problem is that I get strange errors if I try to import it using ES6 syntax, i.e import Stockfish from '../../<path>'. I'm able to add a "raw" <script> as part of the JSX returned by a React component, but the Stockfish exposed there is not available elsewhere. I also get some linting issues.

Do you maybe have some advice on how I could proceed? Perhaps it's more appropriate to post this elsewhere? :)

...could I maybe use a worker that talks to Fairy Stockfish?

@sombor-shuffle
Copy link
Author

An update on using a worker:

If foo.html becomes:

<html>
    <body>
        <script>

            const worker = new Worker('./worker.js');
            
        </script>
    </body>
</html>

and worker.js contains:

importScripts("./lib/stockfish.js");

let stockfish = null;

Stockfish().then(_stockfish => {
    stockfish = _stockfish;
    stockfish.addMessageListener(console.log);
})

Then the console will output the following error (formatting changed):

wasm streaming compile failed: TypeError: Failed to execute 'compile' on 'WebAssembly': HTTP status code is not ok (stockfish.js:11)

falling back to ArrayBuffer instantiation (stockfish.js:11)

failed to asynchronously prepare wasm: CompileError: WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 3c 21 44 4f @+0 (stockfish.js:11) 

CompileError: WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 3c 21 44 4f @+0 (stockfish.js:25)

Uncaught (in promise) RuntimeError: abort(CompileError: WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 3c 21 44 4f @+0). Build with -s ASSERTIONS=1 for more info.
    at M (stockfish.js:25:112)
    at stockfish.js:138:293

@sombor-shuffle
Copy link
Author

sombor-shuffle commented Jun 11, 2023

Working solution:

"use client"

import React, { useEffect } from 'react';

export default function Component() {

  useEffect(() => {
    // @ts-ignore
    console.log(Stockfish)
  });

  return (
      <React.Fragment>
        <script src="lib/stockfish.js"></script>
        { /* other things go here... */ }
      </React.Fragment>
  );
}

It is possible to include a <script> alongside the rest of the component and refer to Stockfish inside of an effect hook. It is necessary to also use @ts-ignore or else the linter complains "Cannot find name 'Stockfish'".

Is this a good solution? I'm really confused as to why it has to be done this way.

@zeth
Copy link

zeth commented Jun 26, 2023

The pure Javascript code example two posts up by @sombor-shuffle should be the official documentation IMHO, the basic demo site and the fairyland demo site are both too mixed in with frameworks that the user probably won't be using.

@sombor-shuffle
Copy link
Author

sombor-shuffle commented Jun 27, 2023

Hey @zeth, I realized later that the async... await pattern can be safely omitted. Also, if you happen to be using Next.js, there is the <Script /> component that could be used instead of the vanilla <script /> tag. From what I understand it lets Next.js bundle Stockfish together with all other assets, producing a single minified main script file. I found that you then have to set the "strategy" property to "beforeInteractive" (for this package).

I agree that the basic demo could be more accessible and I wouldn't mind re-writing it in plain js, if there is any enthusiasm for it.

It wouldn't be terrible at all if you could import Fairy Stockfish, although I lack whatever WebAssembly knowledge is required to make that possible.

@mbelaldev
Copy link

mbelaldev commented Oct 30, 2024

Working Solution for stockfish.js, didn't try fairystockfish

Put stockfish.js in public folder of next.js app Very important

then this should work

useEffect(() => {
  const stockfish = new Worker("/stockfish.js");
  const DEPTH = 8; // number of halfmoves the engine looks ahead
  const FEN_POSITION =
    "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";

  stockfish.postMessage("uci");
  stockfish.postMessage(`position fen ${FEN_POSITION}`);
  stockfish.postMessage(`go depth ${DEPTH}`);

  stockfish.onmessage = (e) => {
    console.log(e.data); // in the console output you will see `bestmove e2e4` message
  };
}, []);

for more advanced example https://react-chessboard.vercel.app/?path=/docs/stockfishintegration--docs

if any one has solution to removing stockfish.js out of public this would be good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants