You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中与StockFish交互并捕获其输出?

解决C++与StockFish双向交互并捕获输出的问题

原代码的关键错误

  • 路径中的> nul将StockFish的标准输出重定向到空设备,导致完全无法获取输出。
  • 使用popen的"w"模式仅支持写入子进程的标准输入,无法读取输出;而"r+"模式在Windows平台的_popen中不支持双向管道,因此启动进程失败。

简洁解决方案(Windows平台)

使用Windows API的CreateProcess配合管道实现双向通信,代码简洁且易理解:

#include <windows.h>
#include <iostream>
#include <string>
#include <unordered_map>
#include <sstream>
#include <algorithm>
#include <cctype>

std::unordered_map<std::string, int> stockFishPerft(std::string fenString, int depth) {
    std::unordered_map<std::string, int> moveCounts;

    // 配置管道安全属性,允许句柄继承
    SECURITY_ATTRIBUTES sa{};
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = TRUE;

    HANDLE hStdinRead, hStdinWrite;
    HANDLE hStdoutRead, hStdoutWrite;

    // 创建输入、输出管道
    if (!CreatePipe(&hStdinRead, &hStdinWrite, &sa, 0) ||
        !CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0)) {
        std::cerr << "Error: Failed to create communication pipes." << std::endl;
        return moveCounts;
    }

    // 设置进程启动信息,重定向标准输入输出
    STARTUPINFO si{};
    si.cb = sizeof(STARTUPINFO);
    si.hStdInput = hStdinRead;
    si.hStdOutput = hStdoutWrite;
    si.hStdError = hStdoutWrite; // 将错误输出合并到标准输出管道
    si.dwFlags |= STARTF_USESTDHANDLES;

    PROCESS_INFORMATION pi{};
    const std::string stockfishPath = R"(C:\Users\1flor\CLionProjects\Chess\src\utils\stockfish\stockfish-windows-x86-64-avx2.exe)";

    // 启动StockFish进程
    if (!CreateProcess(nullptr, const_cast<char*>(stockfishPath.c_str()), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &pi)) {
        std::cerr << "Error: Failed to start Stockfish. Error code: " << GetLastError() << std::endl;
        // 清理管道句柄
        CloseHandle(hStdinRead);
        CloseHandle(hStdinWrite);
        CloseHandle(hStdoutRead);
        CloseHandle(hStdoutWrite);
        return moveCounts;
    }

    // 关闭父进程不需要的管道句柄(子进程已继承)
    CloseHandle(hStdinRead);
    CloseHandle(hStdoutWrite);

    // 封装命令发送函数
    auto sendCmd = [&](const std::string& cmd) {
        DWORD bytesWritten;
        const std::string cmdWithNewLine = cmd + "\n";
        WriteFile(hStdinWrite, cmdWithNewLine.c_str(), cmdWithNewLine.size(), &bytesWritten, nullptr);
        FlushFileBuffers(hStdinWrite);
    };

    // 发送StockFish命令
    sendCmd("position fen " + fenString);
    sendCmd("go perft " + std::to_string(depth));
    sendCmd("quit"); // 发送退出命令,避免进程挂起

    // 读取并输出StockFish的响应
    char buffer[4096];
    DWORD bytesRead;
    std::string fullOutput;

    while (ReadFile(hStdoutRead, buffer, sizeof(buffer)-1, &bytesRead, nullptr) && bytesRead > 0) {
        buffer[bytesRead] = '\0';
        fullOutput += buffer;
        std::cout << buffer;
    }

    // 解析perft结果到map中
    std::istringstream outputStream(fullOutput);
    std::string line;
    while (std::getline(outputStream, line)) {
        const size_t colonPos = line.find(':');
        if (colonPos == std::string::npos) continue;

        // 提取走法并去除前后空格
        std::string move = line.substr(0, colonPos);
        move.erase(move.begin(), std::find_if(move.begin(), move.end(), [](unsigned char c) { return !std::isspace(c); }));
        move.erase(std::find_if(move.rbegin(), move.rend(), [](unsigned char c) { return !std::isspace(c); }).base(), move.end());

        // 提取数量并转换为整数
        try {
            const int count = std::stoi(line.substr(colonPos + 1));
            moveCounts[move] = count;
        } catch (...) {
            continue; // 解析失败跳过该行
        }
    }

    // 等待进程结束并清理资源
    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    CloseHandle(hStdinWrite);
    CloseHandle(hStdoutRead);

    return moveCounts;
}

代码关键点说明

  • 管道双向通信:创建两个独立管道分别处理StockFish的输入和输出,实现命令发送与结果读取的双向交互。
  • 移除输出重定向:删除原路径中的> nul,确保StockFish的输出能被捕获。
  • 主动退出命令:发送quit命令让StockFish执行完perft后主动退出,避免读取输出时无限等待。
  • 结果解析:针对StockFish perft的输出格式(走法: 数量),提取走法和对应计数存入unordered_map。

注意事项

  • 该代码仅适用于Windows平台,依赖Windows API。
  • 在CLion等IDE中编译时,需确保链接kernel32.lib(通常IDE会自动处理,若报错可在CMakeLists.txt中添加target_link_libraries(你的项目名 kernel32))。

内容的提问来源于stack exchange,提问作者Popo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 06:34:58