0

I've coded some toy chess engines in the past using different languages, all of them providing their own simple text-based UI. Now I want to write an engine that can be used with chess GUIs like ChessX or Cute Chess. I've read and understood (at least I thought so) the Universal Chess Interface (UCI) protocol.

To get a first working version, I'm using Python (3.9 on macOS) for my engine, here's the part that's looking for input via STDIN a full (non-)working example as requested:

import sys
import os
import time
import re
import select
import fileinput


if __name__ == '__main__':
    while True:
        if select.select([sys.stdin, ], [], [], 0.0)[0]:
            for line in fileinput.input():
                tokens = [ x.strip().lower() for x in re.split("\s+", line.strip()) ]
                if tokens[0] == "uci":
                    sys.stdout.write("id name mychess\n")
                    sys.stdout.write("id author myname\n")
                    sys.stdout.write("uciok\n")
                elif tokens[0] == "isready":
                    sys.stdout.write("readyok\n")
        time.sleep(2)

When I put my engine in one of the GUIs (or a simple python-chess app, see below), my engine gets the initial "uci" command and answers accordingly as I can verify using some test and logging code not shown in the minimal example above.

But then, nothing happens ... until the GUIs are telling me they timed-out. Obviously, they didn't get any answer from my engine. Using the python-chess library, I can verify that the response from my engine isn't coming through to the GUI process.

This is the code in my python-chess app to connect to my engine:

engine = chess.engine.SimpleEngine.popen_uci(r"./engine.py")

If I put there the path to the stockfish engine, I can see the response from that engine and how the python-chess app answers with "ucinewgame" and a lot of other communication.

So, why is the response from my engine not read by the GUI client?

Update: I suspect it has something to do with the select code in my example. It's there to simulate non-blocking access to STDIN. In a simpler approach, I'm just using a blocking call to input and it works (here's the gist of it):

while True:
    line = input()
    if line.strip().lower() == "uci":
        print("id name mychess")
        print("uciok")
z80crew
  • 1,150
  • 1
  • 11
  • 20
  • Please [edit] your question to include your source code as a working [mcve], which can be tested by others. Also add a complete transcript of the commands you are receiving from the GUI and the commands you are sending to the GUI. – Progman Jul 15 '22 at 19:34
  • Hi, can you please share a little more info. What's your full code look like for running the UCI protocol? What commands is the GUI sending? Most GUIs have a debug window you can look at to see if the engine crashes, doesn't respond, or what. Check there too. – Christian Dean Jul 15 '22 at 23:51
  • I've added a working example. The engine debug window in Cutechess doesn't show anything during the initial connection stage, no matter if the connection to the engine is working or not. E.g. when successfully connecting Cutechess to Stockfish, nothing is shown either. – z80crew Jul 18 '22 at 08:45
  • Why are you using `stdin/out` rather than just `print` and `input`? – Axeltherabbit Jul 18 '22 at 08:52
  • @Axeltherabbit The `fileinput.input()` call should (together with `select`) allow for non-blocking access to STDIN and read multiple lines if the GUI sent multiple commands since the last call to `input()`. Using `sys.stdout.write()` instead of `print` was just an artifact of previous experiments and doesn't change the situation. – z80crew Jul 18 '22 at 12:15

1 Answers1

0

The problem seems to be in using select.select() and/or fileinput.input(). If I omit those fancy techniques and simply read from STDIN, everything works and cutechess accepts my engine. This is the basic code for a UCI chess engine to get recognized:

import sys
import os
import re


if __name__ == '__main__':
    while True:
        line = input()
        tokens = [ x.strip().lower() for x in re.split("\s+", line.strip()) ]
        if tokens[0] == "uci":
            sys.stdout.write("id name mychess\n")
            sys.stdout.write("id author myname\n")
            sys.stdout.write("uciok\n")
        elif tokens[0] == "isready":
            print("readyok")

Of course, the blocking call to input() now doesn't allow for concurrent calculations in my engine while accepting input from the GUI immediately, but that's stuff for perhaps another question.

z80crew
  • 1,150
  • 1
  • 11
  • 20