franciscoBSalgueiro / en-croissant

The Ultimate Chess Toolkit
https://encroissant.org
GNU General Public License v3.0
639 stars 78 forks source link

En Croissant ignores "bestmove" in favor of "info ... pv ..." #325

Open JimViebke opened 4 months ago

JimViebke commented 4 months ago

Describe the bug

When my engine sends the bestmove ... command, En Croissant seems to ignore the contents of the command and instead plays the move from the most recently sent info ... pv ... command.

Is this correct behavior?

The UCI protocol says of bestmove: "the engine has stopped searching and found the move best in this position." It also contains the following example:

// the engine has finished searching and is sending the bestmove command
// which is needed for every "go" command sent to tell the GUI
// that the engine is ready again
        bestmove g1f3 ponder d8f6

My understanding is that the engine is committing the move g1f3 in the above example, regardless of what info commands have been sent.

Excerpt from my engine's logs, reflecting what is sent to En Croissant:

05:39:12.0734835 Sending UCI command: info depth 7 score cp -520 nps 2082707 nodes 1030940 hashfull 714 tbhits 179249 time 495 pv e1f1 d6h2 c1d2 h4h6 d2c3 h6d6 d1d3 d6d5
05:39:14.3146750 Sending UCI command: info depth 8 score cp -32767 nps 2802365 nodes 2566967 hashfull 722 tbhits 430503 time 916 pv e1g1 h4h2
05:39:17.0714235 Sending UCI command: bestmove e1f1

Expected behavior: En Croissant applies e1f1 for my engine (from line 3). Actual behavior: En Croissant applies e1g1 (from line 2), possibly because it was the start of the most recently sent PV, via info.

Reproduction

  1. Have an engine emit any move via UCI command: info <some info> pv <move_X> <move_Y> <...>.
  2. Have an engine emit a best move via UCI command: bestmove <move_A>.
  3. En Croissant applies move_X instead of move_A.

Platform and versions

En Croissant

Version: 0.10.0
Tauri version: 1.6.1
OS: Windows_NT x86_64 10.0.19045

JimViebke commented 4 months ago

After further debugging, it looks like En Croissant is not playing the most recent PV move either. As an attempted workaround, I added an additional info command with the intended move, but it still seems that En Croissant is playing a different move. From my engine's logs:

21:19:54.4559055 Sending UCI command: info depth 8 score cp -520 nps 2262618 nodes 5753839 hashfull 608 tbhits 841406 time 2543 pv e1g1 c6e5 f3e5 e7d6 e5d3 c8f5 d3e1 d8e7 c3d5
21:19:57.6100710 Sending UCI command: info pv b5c6
21:19:57.6108471 Sending UCI command: bestmove b5c6

In this case, En Croissant still applies e1g1 instead of b5c6.

mollthecoder commented 4 months ago

I had an engine that would only send the bestmove command, which En Croissant wouldn't recognize. I wrote a script that acts as a proxy between the UI and the engine, and when it sees a "bestmove" command it automatically adds "info pv {move}" beforehand. This actually worked in my case. If you're curious, I'll leave the script below, but it's written in TypeScript which is suboptimal.

import {createInterface} from "node:readline";
import {spawn} from "node:child_process";

// ADD PATH HERE
// Example is for Windows, but works on any OS
const PATH = "C:\\absolute\\path\\to\\engine.exe";

const proc = spawn(PATH, {
    stdio: "pipe"
});
const ownRl = createInterface(process.stdin, process.stdout);
const childRl = createInterface(proc.stdout, proc.stdin);

ownRl.on("line", line=>{
    proc.stdin.write(line + "\n");
});

childRl.on("line", line=>{
    if(line.startsWith("bestmove"))
        console.log(`info pv ${line.split(" ")[1]}`);
    console.log(line);
});

However En Croissant expects an executable, and Node.js scripts are only interpreted. While it's possible to use batch or powershell or shortcuts or such to work around this, I simply used Bun to convert it into an executable, using the command below.

bun build index.ts --minify --compile

After this, I just added the emitted index.exe as an engine to En Croissant.