게시판바둑 on-policy 학습: 대국 로그를 KataGo 라벨로 바꾸기예제 소스

예제 소스

바둑 on-policy 학습: 대국 로그를 KataGo 라벨로 바꾸기 · 파일 169개

tcpvlmclient.h

#pragma once

#include <QAtomicInt>
#include <QByteArray>
#include <QImage>
#include <QObject>
#include <QString>

class QTcpSocket;

/**
 * 19×19 바둑판 PNG + 361 바이트 격자 상태를 TCP 로 보내 VLM 서버의 좌표 응답을 받습니다.
 * 한 번 연결한 소켓을 게임/앱 수명 동안 유지(persistent)합니다.
 *
 * 프로토콜 (GO19):
 *   요청: "GO19" | u32_be body_len | u8 size | u8 ai_side(1=Black,2=White) |
 *         u32_be png_len | png bytes | size*size board bytes (0/1/2) |
 *         u32_be game_epoch (선택, 있으면 대국이 바뀔 때마다 증가 — 서버 온라인 학습 트리거) |
 *         [선택] u8 n_reject | n×(u8 row, u8 col)  — settled/자충으로 거부한 좌표
 *   응답: "RPLY" | u8 flag
 *           flag=1: u8 row, u8 col   (row=255 && col=255 → 패스)
 *           flag=0: u16_be err_len, utf8 err
 *   PING/PONG: 4바이트 텍스트.
 */
namespace TcpVlmClient {

class Worker : public QObject {
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);
    ~Worker() override;

    bool isBusy() const;
    bool isConnected() const;

public slots:
    void doPing(QString host, quint16 port, int timeoutMs);

    /// 인게임 영구 소켓을 건드리지 않는 일회성 PING.
    /// 여러 서버를 독립적으로 검사할 수 있도록 tag(예: "1","2") 로 결과 라우팅.
    /// timeoutMs 안에 PING/PONG 이 끝나야 성공.
    void doPingProbe(QString tag, QString host, quint16 port, int timeoutMs);

    /// 한 수 요청. row=255 && col=255 면 패스 권고.
    /// rejectCoords: 서버가 다시 고르지 말 좌표 (최대 64). 빈 배열이면 미전송(구형 호환).
    void requestMove(QImage boardImage, QByteArray boardPacked, int boardSize, int aiSide,
                     QString host, quint16 port, int timeoutMs, quint32 gameEpoch,
                     QByteArray rejectCoords = QByteArray());

    void cancel();
    void closeConnection();

    /// 앱 종료 시 메인 스레드에서 BlockingQueuedConnection 으로 호출.
    /// 소켓 abort 로 readBytes 의 QEventLoop 에서 빠져나와 스레드를 안전히 quit 할 수 있게 함.
    void shutdownForAppExit();

signals:
    /// row/col == 255,255 일 때 isPass=true.
    void moveReady(bool success, int row, int col, bool isPass, QString errorMessage);
    void pingReady(bool success, QString errorMessage);
    /// doPingProbe 결과. endpoint="host:port", latencyMs 는 성공 시 실측 왕복(접속+PONG).
    void pingProbeReady(QString tag, bool success, QString endpoint, int latencyMs,
                        QString errorMessage);
    void connectionStateChanged(bool connected, QString endpoint);

private:
    struct Conn {
        QTcpSocket *sock = nullptr;
        QString host;
        quint16 port = 0;
        quint64 lastUseSeq = 0; ///< 간단 LRU (연결 2개 유지)
    };

    /// host:port 에 대한 소켓을 얻고(있으면 재사용), 없으면 생성.
    /// 본 클라이언트는 VlmVsVlm 처럼 두 엔드포인트를 번갈아 쓰므로
    /// 소켓 2개까지는 동시에 유지한다.
    bool ensureConnected(QTcpSocket **outSock, const QString &host, quint16 port, int timeoutMs,
                         QString *errorOut);
    bool readBytes(QTcpSocket *sock, QByteArray *dst, qint64 nbytes, int deadlineMs);
    bool writeAll(QTcpSocket *sock, const QByteArray &data, int timeoutMs);
    void closeAllSocketsLocked(const QString &reason);
    void emitConnState();

    Conn m_conns[2];
    quint64 m_useSeq = 0;

    QAtomicInt m_cancel{0};
    QAtomicInt m_busy{0};
};

} // namespace TcpVlmClient