0
\$\begingroup\$
#include "Game.h"

#include <Windows.h>


void Game::GameLoop()
{
    StartStockfish();



    while(!quit)
    {

    }
}

void Game::StartStockfish()
{
    saAttr.nLength=sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle=TRUE;
    saAttr.lpSecurityDescriptor=NULL;

    hStdinWrite=GetStdHandle(STD_INPUT_HANDLE);
    hStdoutRead=GetStdHandle(STD_OUTPUT_HANDLE);


    // Start the Stockfish process

    ZeroMemory(&si,sizeof(si));
    si.cb=sizeof(si);
    ZeroMemory(&pi,sizeof(pi));

    si.hStdError=GetStdHandle(STD_ERROR_HANDLE);
    si.hStdOutput=GetStdHandle(STD_OUTPUT_HANDLE);
    si.hStdInput=GetStdHandle(STD_INPUT_HANDLE);
    si.dwFlags|=STARTF_USESTDHANDLES;

    // Create pipe for the child process's standard input
    if(!CreatePipe(&hStdinRead,&hStdinWrite,&saAttr,0))
    {
        printf("CreatePipe failed (%d).\n",GetLastError());
        return;
    }

    // Create pipe for the child process's standard output
    if(!CreatePipe(&hStdoutRead,&hStdoutWrite,&saAttr,0))
    {
        printf("CreatePipe failed (%d).\n",GetLastError());
        return;
    }

    // Create pipe for the child process's standard error
    if(!CreatePipe(&hStderrRead,&hStderrWrite,&saAttr,0))
    {
        printf("CreatePipe failed (%d).\n",GetLastError());
        return;
    }

    

    if(!CreateProcess(
        (LPCTSTR)L"D:/_Dev/stockfish_15.1_win_x64_avx2/stockfish-windows-2022-x86-64-avx2.exe",   // No module name (use command line)
        (LPTSTR)L"",                // Command line
        NULL,                   // Process handle not inheritable
        NULL,                   // Thread handle not inheritable
        TRUE,                  // Set handle inheritance to TRUE
        0,                      // No creation flags
        NULL,                   // Use parent's environment block
        NULL,                   // Use parent's starting directory
        &si,                    // Pointer to STARTUPINFO structure
        &pi)                    // Pointer to PROCESS_INFORMATION structure
        )
    {
        printf("CreateProcess failed (%d).\n",GetLastError());
    }

    // Close the Stockfish process
    CloseHandle(hStdinRead);
    CloseHandle(hStdoutWrite);

    // Send a command to Stockfish
    SendStockfishCommand("uci\n");
    Sleep(2000);
    SendStockfishCommand("isready\n");
    

    Sleep(2000);
 
    std::vector<std::string> testOutputList=ReadStockfishOutput();
    printf("testOutputList size=%d\n",(int)testOutputList.size());

    for(int i=0; i < testOutputList.size(); i++)
    {
        printf("testOutputList %d=%s\n",i,testOutputList[i].c_str());
    }
    

    SendStockfishCommand("d");

    Sleep(2000);

    std::vector<std::string> testOutputList2=ReadStockfishOutput();
    printf("testOutputList2 size=%d\n",(int)testOutputList2.size());
    for(int i=0; i < testOutputList2.size(); i++)
    {
        printf("testOutputList2 %d=%s\n",i,testOutputList2[i].c_str());
    }
}



void Game::CloseStockfish()
{
    
}

void Game::SendStockfishCommand(std::string str)
{
    DWORD bytesWritten=0;
    str+='\n';
    WriteFile(hStdinWrite,str.c_str(),str.length(),&bytesWritten,NULL);
    printf("Sent command:%s\n",str.c_str());
}

std::vector<std::string> Game::ReadStockfishOutput()
{
    printf("ReadStockfishOutput() called \n");
    std::vector<std::string> lines;
    DWORD bytesRead=0;
    char buffer[1024];
    while(ReadFile(hStdinRead,buffer,sizeof(buffer),&bytesRead,NULL))
    {
        if(bytesRead == 0)
        {
            printf("bytesRead was zero\n");
            break;
        }
            
        printf("reading the stdin buffer\n");
        std::string line(buffer,bytesRead);
        std::cout << line << std::endl;
        lines.push_back(line);
    }

    return lines;
}

I am trying to learn more and more about c++ and this time I try to write a chess game. I already made a chess game, but the way I created it was not easy to work with (mainly I want to change the whole game, because I want to use the outputted "fen string" from the engine to find where to draw the pieces. In my previous version, the pieces were moved with my own logic and then I converted those positions into a fen string each time.

But this time, I learned by reading here and other sites, I should use CreateProcess() (I am only targeting Windows). Previously I used _popen (but as I say I ran into problems).

I admit I am in quite far over my head here. Lots of new concepts I've learned in the last couple of days only. But essentially from what I can tell, my ReadStockfishCommand() does not work correctly (or, the way I am using it maybe wrong)

The only 2 things I can think is either:

a) I need to wait some time before calling the read function (I doubt this is the answer, as I tried adding Sleep(5000) etc between and this made no difference.

b) I am reading from the wrong "pipe". I think this is most likely, as I have very little understanding of what the pipe actually is. I do realise I created this, so that I can push the data from Std::cin etc to the pipe and then free up the actual Std::cin channel with CloseHandle(hStdinRead); etc.

But I guess I am very mixed up now, having tried several similar versions of this code now and always ending up with the "lines" vector being empty. (eg. I send command "isready", then try to read the response. But the response lines always equals zero).

Finally, when running this code. This is what I see in the Console Window:

Sent command:uci


Stockfish 15.1 by the Stockfish developers (see AUTHORS file)
Sent command:isready


ReadStockfishOutput() called
testOutputList size=0
Sent command:d

I am hoping it is something as silly as changing one parameter in a function call. But really, I am stuck and have no idea what to try next.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$
#include "Game.h"

#include <Windows.h>
#include <sstream>


void Game::GameLoop()
{
    StartStockfish();



    while(!quit)
    {

    }
}

void Game::StartStockfish()
{
    saAttr.nLength=sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle=TRUE;
    saAttr.lpSecurityDescriptor=NULL;

    // Start the Stockfish process

    ZeroMemory(&si,sizeof(si));
    si.cb=sizeof(si);
    ZeroMemory(&pi,sizeof(pi));


    // Create pipe for the child process's standard input
    if(!CreatePipe(&hStdinRead,&hStdinWrite,&saAttr,0))
    {
        printf("CreatePipe failed (%d).\n",GetLastError());
        return;
    }

    // Create pipe for the child process's standard output
    if(!CreatePipe(&hStdoutRead,&hStdoutWrite,&saAttr,0))
    {
        printf("CreatePipe failed (%d).\n",GetLastError());
        return;
    }

    // Create pipe for the child process's standard error
    if(!CreatePipe(&hStderrRead,&hStderrWrite,&saAttr,0))
    {
        printf("CreatePipe failed (%d).\n",GetLastError());
        return;
    }

    si.hStdError=GetStdHandle(STD_ERROR_HANDLE);
    si.hStdOutput=hStdoutWrite;
    si.hStdInput=hStdinRead;
    si.dwFlags|=STARTF_USESTDHANDLES;

    if(!CreateProcess(
        (LPCTSTR)L"D:/_Dev/stockfish_15.1_win_x64_avx2/stockfish-windows-2022-x86-64-avx2.exe",   // No module name (use command line)
        (LPTSTR)L"",                // Command line
        NULL,                   // Process handle not inheritable
        NULL,                   // Thread handle not inheritable
        TRUE,                  // Set handle inheritance to TRUE
        0,                      // No creation flags
        NULL,                   // Use parent's environment block
        NULL,                   // Use parent's starting directory
        &si,                    // Pointer to STARTUPINFO structure
        &pi)                    // Pointer to PROCESS_INFORMATION structure
        )
    {
        printf("CreateProcess failed (%d).\n",GetLastError());
    }

   
    // Send a command to Stockfish
    SendStockfishCommand("uci\n");
    std::vector<std::string> uci_response=ReadStockfishOutput();
    for(int i=0; i < uci_response.size(); i++)
    {
        printf("uci_response %d=%s\n",i,uci_response[i].c_str());
    }

    SendStockfishCommand("isready\n");
    std::vector<std::string> isready_response=ReadStockfishOutput();
    for(int i=0; i < isready_response.size(); i++)
    {
        printf("isready_response %d=%s\n",i,isready_response[i].c_str());
    }

    SendStockfishCommand("d\n");
    std::vector<std::string> d_response=ReadStockfishOutput();
    for(int i=0; i < d_response.size(); i++)
    {
        printf("d_response %d=%s\n",i,d_response[i].c_str());
    }
    
}

void Game::CloseStockfish()
{
    
}

void Game::SendStockfishCommand(std::string str)
{
    DWORD bytesWritten=0;
    //str+='\n';
    if(WriteFile(hStdinWrite,str.c_str(),str.length(),&bytesWritten,NULL))
    {
        printf("Sent command:%s  . BytesWritten=%ld\n",str.c_str(), bytesWritten);
    }
        
}

std::vector<std::string> Game::ReadStockfishOutput()
{
    Sleep(500);
    printf("*************************Read() called\n");
    std::vector<std::string> lines;
    DWORD bytesRead=0;
    char buffer[1024*4];
    while(true)
    {
        // Check if the read operation was successful
        if(!ReadFile(hStdoutRead,buffer,sizeof(buffer),&bytesRead,NULL))
        {
            printf("FAIL bytes READ=%ld\n",bytesRead);
            DWORD errorCode=GetLastError();
            if(errorCode == ERROR_BROKEN_PIPE)
            {
                printf("Pipe was closed\n");
            }
            else
            {
                printf("Error occurred while reading from the pipe: %d\n",errorCode);
                // Additional error handling code
            }
            break;
        }

        std::stringstream bufferStream(std::string(buffer,bytesRead));
        std::string line;
        while(std::getline(bufferStream,line))
        {
            printf("line=%s\n",line.c_str());
            lines.push_back(line);
        }

        fflush(stdout);
        break;

    }

    printf("lines size=%d\n",(int)lines.size());
    return lines;
    
}

Well, I seem to have fixed the code. But sadly, this was more by trial-and-error rather than fully understanding the code.

The problem revolved around the argument I sent to ReadFile (ie. which pipe I was trying to read) along with the Start function where I had to change the values of si.hStdInput etc.

Hopefully, someone can provide a better answer, but I figured I would post this here, since it does at least print the stockfish responses (and store them in my "lines" vector, as I needed).

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .