воскресенье, 5 июля 2026 г.

Таймер сброс

 А, теперь понял! Вы имеете в виду стандартный компонент Timer (часики на панели элементов), а не расчет времени через API.

Да, если игра «подвисла» (например, из-за вызова MsgBox или долгого расчета), стандартный таймер VB5 тоже накапливает очередь событий в Windows. Как только программа отвиснет, таймер начнет судорожно выстреливать все пропущенные тики подряд, из-за чего игра резко ускорится.
В VB5 стандартный таймер нельзя «очистить» от накопившейся очереди команд напрямую, потому что эта очередь хранится в самой операционной системе Windows (в очереди сообщений WM_TIMER).
Но этот эффект можно полностью нейтрализовать двумя способами.

Способ 1. Выключение и включение (Самый простой)

Когда вы знаете, что сейчас произойдет задержка (например, перед показом окна или сложным расчетом), выключите таймер, а затем включите обратно. Это полностью стирает всю очередь тиков в Windows:
' 1. Выключаем таймер перед задержкой
Timer1.Enabled = False

' 2. Тут происходит то, что вешает программу (например, сообщение пользователю)
MsgBox "Игра на паузе. Нажмите ОК для продолжения", vbInformation, "Пауза"

' 3. Включаем обратно — таймер начнет отсчет чистого времени с нуля, без рывков
Timer1.Enabled = True

Способ 2. Защита внутри самого таймера (Если зависания случайные)

Если программа зависает непредсказуемо (например, из-за лагов системы), можно аппаратно игнорировать «набежавшие» тики. Для этого внутри события таймера проверяют реальное время. Если с момента последнего тика прошло слишком много времени — мы просто пропускаем лишние расчеты.
Для этого в самый верх кода формы (в раздел General Declarations) добавьте WinAPI функцию для точного времени:
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private LastTickTime As Long ' Переменная для запоминания времени
А в самом событии таймера сделайте проверку:
Private Sub Timer1_Timer()
    Dim CurrentTime As Long
    CurrentTime = GetTickCount()
    
    ' Если интервал таймера равен 33 мс, а прошло, например, больше 100 мс —
    ' значит программа зависала, и этот тик "догоняющий" (из очереди).
    ' Мы его просто игнорируем.
    If (CurrentTime - LastTickTime) > (Timer1.Interval + 50) Then
        ' Сбрасываем время и выходим, не двигая игру
        LastTickTime = CurrentTime
        Exit Sub
    End If
    
    ' Запоминаем время текущего успешного тика
    LastTickTime = CurrentTime
    
    ' --- ДАЛЕЕ ВАШ ОБЫЧНЫЙ КОД ИГРЫ ---
    UpdateGameLogic
    RenderGraphics
End Sub
Если этот вариант вам подходит, подскажите:
  • Какой Interval сейчас выставлен у вашего компонента Timer?
  • Какое именно действие в программе (например, MsgBox, открытие формы) сейчас вызывает эту задержку?

Да, в Visual Basic 5.0 можно делать полноценные консольные приложения, но среда разработки (IDE) изначально для этого не предназначена. По умолчанию VB5 компилирует только GUI-приложения (с окнами).
Чтобы сделать консольное приложение, используют два основных способа.

Способ 1: Использование Windows API (Стандартный путь)

Вы можете создать обычный проект (Standard EXE), удалить из него автоматическую форму, добавить стандартный модуль (.bas) и использовать функции Win32 API для работы с консолью.
  1. Создайте проект Standard EXE.
  2. Из меню выберите Project -> Add Module (Добавить модуль).
  3. Удалите стандартную форму (Form1) из проекта.
  4. В свойствах проекта (Project -> Properties) на вкладке General установите Startup Object в значение Sub Main.
Вставьте следующий код в созданный модуль:
Option Explicit

' Объявление функций Windows API для работы с консолью
Private Declare Function AllocConsole Lib "kernel32" () As Long
Private Declare Function FreeConsole Lib "kernel32" () As Long
Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Long) As Long
Private Declare Function WriteConsole Lib "kernel32" Alias "WriteConsoleA" (ByVal hConsoleOutput As Long, ByVal lpBuffer As String, ByVal nNumberOfCharsToWrite As Long, lpNumberOfCharsWritten As Long, lpReserved As Any) As Long
Private Declare Function ReadConsole Lib "kernel32" Alias "ReadConsoleA" (ByVal hConsoleInput As Long, ByVal lpBuffer As String, ByVal nNumberOfCharsToRead As Long, lpNumberOfCharsRead As Long, lpReserved As Any) As Long

Private Const STD_OUTPUT_HANDLE As Long = -11&
Private Const STD_INPUT_HANDLE As Long = -10&

Public Sub Main()
    Dim hStdout As Long
    Dim hStdin As Long
    Dim msg As String
    Dim userInput As String * 256
    Dim charsRead As Long
    
    ' Создаем окно консоли
    AllocConsole
    
    ' Получаем указатели на ввод и вывод
    hStdout = GetStdHandle(STD_OUTPUT_HANDLE)
    hStdin = GetStdHandle(STD_INPUT_HANDLE)
    
    ' Выводим текст в консоль
    msg = "Hello from VB5 Console!" & vbCrLf
    WriteConsole hStdout, msg, Len(msg), 0&, ByVal 0&
    
    msg = "Enter your text: "
    WriteConsole hStdout, msg, Len(msg), 0&, ByVal 0&
    
    ' Читаем ввод пользователя
    ReadConsole hStdin, userInput, Len(userInput), charsRead, ByVal 0&
    
    ' Эхо-вывод полученного текста
    msg = "You entered: " & Left$(userInput, charsRead) & vbCrLf
    WriteConsole hStdout, msg, Len(msg), 0&, ByVal 0&
    
    ' Небольшая пауза перед закрытием
    msg = "Press Enter to exit..."
    WriteConsole hStdout, msg, Len(msg), 0&, ByVal 0&
    ReadConsole hStdin, userInput, Len(userInput), charsRead, ByVal 0&
    
    ' Освобождаем консоль
    FreeConsole
End Sub

Способ 2: Изменение флага подсистемы в EXE (Настоящая консоль)

При использовании Способа 1 скомпилированный .exe файл Windows все равно будет считать "оконным" приложением. Если запустить его из командной строки cmd.exe, оно откроется в отдельном новом окне, а не внутри запущенной командной строки.
Чтобы приложение работало как родная утилита командной строки:
  1. Напишите код, используя API из Способа 1.
  2. Скомпилируйте проект в .exe файл.
  3. Используйте внешнюю утилиту LINK.EXE (которая идет в комплекте с VB5/Visual Studio) или стороннюю утилиту (например, EditBin.exe), чтобы изменить заголовок исполняемого файла с GUI на Console.
Команда в командной строке выглядит так:
link.exe /edit /subsystem:console MyProject.exe
После этого программа будет выводить данные прямо внутрь той консоли, из которой её запустили.
Если вам нужно принимать аргументы командной строки (например, параметры при запуске program.exe -file.txt), используйте встроенную функцию Command$, которая возвращает всю строку параметров. Хотите посмотреть пример работы с аргументами?


среда, 1 июля 2026 г.

Win resurs graf

 Поскольку ты хочешь картинку на форме, а не в меню, и при этом работаешь на чистом C с cl.exe (без C++/WinForms), есть два рабочих пути: простой (BMP без прозрачности) и с прозрачностью (PNG через GDI+).


Вариант 1: BMP на форме (самый простой, без GDI+)

Подходит, если картинка — обычный BMP без прозрачных краёв.

.rc (resources.rc)

#include <windows.h>

IDB_LOGO BITMAP "logo.bmp"

main.c

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#define IDB_LOGO 201

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nShowCmd)
{
    const char CLASS_NAME[] = "ImgForm";

    WNDCLASS wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, "Картинка на форме",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
        NULL, NULL, hInstance, NULL
    );

    if (!hwnd) return 0;

    ShowWindow(hwnd, nShowCmd);
    UpdateWindow(hwnd);

    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

static HBITMAP hLogo = NULL;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
        case WM_CREATE: {
            HINSTANCE hInst = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
            hLogo = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_LOGO));
            return 0;
        }

        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            if (hLogo) {
                HDC hMemDC = CreateCompatibleDC(hdc);
                HBITMAP hOld = (HBITMAP)SelectObject(hMemDC, hLogo);

                BITMAP bm;
                GetObject(hLogo, sizeof(bm), &bm);

                // рисуем картинку в точке (50,50)
                BitBlt(hdc, 50, 50, bm.bmWidth, bm.bmHeight,
                       hMemDC, 0, 0, SRCCOPY);

                SelectObject(hMemDC, hOld);
                DeleteDC(hMemDC);
            }

            EndPaint(hwnd, &ps);
            return 0;
        }

        case WM_DESTROY:
            if (hLogo) DeleteObject(hLogo);
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

Сборка:

rc resources.rc
cl /TC main.c resources.res /link user32.lib gdi32.lib

Картинка logo.bmp должна лежать рядом с исходниками (или указать полный путь).


Вариант 2: PNG с прозрачными краями на форме (через GDI+)

Если нужны прозрачные края — BMP не годится: нужен PNG и GDI+.

Важное: GDI+ в чистом C

GDI+ API сделан под C++, но его можно использовать из C: вместо классов используем указатели на интерфейсы и вызовы через таблицы функций. Для простоты покажу вариант с использованием C++ синтаксиса только для инициализации GDI+ и работы с Image/Graphics, но логика остаётся максимально близкой к C‑подходу. Если критично «строго C без C++», можно использовать AlphaBlend с 32‑bit DIB вручную, но это заметно сложнее.

gdiplus_img.c (упрощённо)

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <gdiplus.h>

#pragma comment(lib, "gdiplus.lib")

ULONG_PTR gdiToken;
Gdiplus::Image *pImage = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nShowCmd)
{
    GdiplusStartupInput input = {1};
    GdiplusStartup(&gdiToken, &input, NULL);

    // PNG с прозрачностью
    pImage = Gdiplus::Image::FromFile(L"logo.png");

    const char CLASS_NAME[] = "GdiPlusImg";
    WNDCLASS wc = {0};
    wc.lpfnWndProc = [](HWND h, UINT m, WPARAM w, LPARAM l) -> LRESULT {
        if (m == WM_PAINT) {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(h, &ps);
            Gdiplus::Graphics g(hdc);
            if (pImage) g.DrawImage(pImage, 50, 50);
            EndPaint(h, &ps);
            return 0;
        } else if (m == WM_DESTROY) {
            delete pImage;
            GdiplusShutdown(gdiToken);
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(h, m, w, l);
    };

    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    RegisterClass(&wc);

    CreateWindowEx(0, CLASS_NAME, "PNG с прозрачностью",
                   WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                   640, 480, NULL, NULL, hInstance, NULL);

    ShowWindow(GetLastActivePopup(), nShowCmd);
    UpdateWindow(GetLastActivePopup());

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

Сборка:

cl /TP gdiplus_img.c /link gdiplus.lib user32.lib gdi32.lib

/TP — компилировать как C++ (нужно для GDI+ классов). Если принципиально только /TC (чистый C), тогда вместо GDI+ придётся вручную готовить 32‑bit DIB с альфа‑каналом и вызывать AlphaBlend.

PNG logo.png должен лежать рядом с .exe и иметь альфа‑канал.


Что выбрать под твои задачи

  • Если ты делаешь шахматную программу (учитывая прошлые вопросы про битборды, Syzygy и т. д.) и нужна, например, простая иконка/логотип на форме без сложной графики — Вариант 1 (BMP + BitBlt) проще, быстрее и полностью на чистом C.

  • Если нужны красивые картинки с прозрачными краями (например, фигуры или декоративные элементы) — Вариант 2 (GDI+).


Практические советы

  • Формат BMP для Варианта 1: лучше 24‑bit или 32‑bit BMP без сжатия; не используй PNG/JPG напрямую в ресурсе — BITMAP в .rc работает только с BMP.

  • Позиционирование: в BitBlt меняй координаты (50, 50) на любые нужные. Чтобы центрировать картинку, считай размеры окна в WM_SIZE и перерисовывай.

  • Ресурсы vs файлы: в Варианте 1 картинка встроена в .exe; в Варианте 2 (GDI+) проще читать файл с диска — так удобнее менять картинки без пересборки.


Учитывая твой интерес к сборке через cl.exe, ресурсам и «чистому C», я могу дать полностью C‑версию с AlphaBlend (без GDI+), если скажешь, что важнее: «строго C» или «максимальная простота кода». Что ближе?



Да, в Win32 на чистом C можно:

  • обрабатывать события мыши (WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP и т. д.);

  • обрабатывать нажатия клавиш (WM_KEYDOWN, WM_CHAR);

  • рисовать PNG с альфа‑каналом (прозрачностью) — но для этого почти всегда используют GDI+ либо AlphaBlend с 32‑bit DIB.

Учитывая твой стек (чистый C, cl.exe, шахматные задачи), покажу рабочий пример, где:

  • рисуется PNG с прозрачностью через GDI+;

  • отслеживаются клики мыши и нажатия клавиш;

  • всё собрано одной командой через cl.exe.


Полный пример: окно, PNG с прозрачностью, события мыши и клавиатуры

main.c

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <gdiplus.h>

#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")

ULONG_PTR gdiToken;
Gdiplus::Image *pImage = NULL;

// Глобальные переменные для демонстрации реакции на ввод
int lastX = 0, lastY = 0;
char lastKey = 0;
BOOL mouseDown = FALSE;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
        case WM_CREATE:
            // Инициализация GDI+ уже сделана в WinMain
            return 0;

        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            if (pImage) {
                Gdiplus::Graphics g(hdc);
                // Рисуем PNG с прозрачностью
                g.DrawImage(pImage, 50, 50);
            }

            EndPaint(hwnd, &ps);
            return 0;
        }

        // --- События мыши ---
        case WM_LBUTTONDOWN: {
            lastX = LOWORD(lParam);
            lastY = HIWORD(lParam);
            mouseDown = TRUE;
            // Можно что-то сделать при клике
            InvalidateRect(hwnd, NULL, FALSE); // перерисовать, если нужно показать эффект
            return 0;
        }
        case WM_MOUSEMOVE: {
            if (mouseDown) {
                lastX = LOWORD(lParam);
                lastY = HIWORD(lParam);
                InvalidateRect(hwnd, NULL, FALSE);
            }
            return 0;
        }
        case WM_LBUTTONUP: {
            mouseDown = FALSE;
            return 0;
        }

        // --- События клавиатуры ---
        case WM_KEYDOWN: {
            // wParam = виртуальный код клавиши (VK_*)
            // Например, VK_ESCAPE, VK_LEFT, VK_A и т.д.
            // Здесь можно реагировать на конкретные клавиши
            return 0;
        }
        case WM_CHAR: {
            // wParam = ASCII-код символа (для печатаемых символов)
            lastKey = (char)wParam;
            InvalidateRect(hwnd, NULL, FALSE);
            return 0;
        }

        case WM_DESTROY: {
            delete pImage;
            GdiplusShutdown(gdiToken);
            PostQuitMessage(0);
            return 0;
        }
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nShowCmd)
{
    // Инициализация GDI+
    GdiplusStartupInput input = {1};
    GdiplusStartup(&gdiToken, &input, NULL);

    // Загрузка PNG с прозрачностью (альфа‑канал)
    pImage = Gdiplus::Image::FromFile(L"logo.png");

    const char CLASS_NAME[] = "ImgInputDemo";

    WNDCLASS wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, "PNG + мышь + клава",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
        NULL, NULL, hInstance, NULL
    );

    if (!hwnd) return 0;

    ShowWindow(hwnd, nShowCmd);
    UpdateWindow(hwnd);

    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

Как собрать

cl /TP main.c /link gdiplus.lib user32.lib gdi32.lib

/TP обязателен: GDI+ в стандартной поставке — это C++ API (классы Image, Graphics). Если принципиально только чистый C без C++ синтаксиса, тогда вместо GDI+ используют AlphaBlend и 32‑bit DIB вручную — код будет заметно длиннее.

Рядом с .exe должен лежать logo.png с альфа‑каналом.


Какие события реально пригодятся для шахматной программы

Учитывая твои прошлые задачи (битборды, Syzygy, SEE, движок):

  • WM_LBUTTONDOWN — самый нужный: по нему определяют, куда кликнул пользователь, берут координаты, переводят в клетку доски и генерируют ходы.

  • WM_MOUSEMOVE — можно подсвечивать клетку под курсором или показывать возможные ходы «в предпросмотре».

  • WM_KEYDOWN — например, VK_ESCAPE для выхода, VK_F2 для переключения режима, стрелки для навигации по истории ходов и т. п.

  • WM_CHAR — если делаешь ввод текста (комментарии, PGN и т. д.).

Координаты мыши приходят в lParam:

int x = LOWORD(lParam);
int y = HIWORD(lParam);

Чтобы превратить их в клетку доски, обычно делают:

int cellX = (x - boardX) / cellSize;
int cellY = (y - boardY) / cellSize;

Если нужна строго чистая C‑реализация без C++ (без GDI+)

Тогда для PNG/альфы используют AlphaBlend из msimg32.lib и 32‑bit DIB с альфа‑каналом:

  1. Читают PNG (или заранее готовят 32‑bit BMP с альфа‑каналом).

  2. Создают совместимый DC и битмап.

  3. Вызывают AlphaBlend(&BLENDFUNCTION, ...).

Это полностью на C, но требует аккуратной работы с заголовками DIB и выравниванием строк. Если скажешь, что важнее — «строго C» или «минимум кода» — могу дать вариант именно с AlphaBlend.


Практические советы под твой стек

  • PNG с прозрачностью — самый простой путь: GDI+ (DrawImage).

  • Мышь — чаще всего нужен именно клик (WM_LBUTTONDOWN) и координаты.

  • Клава — для горячих клавиш и отладки (например, «показать битборд», «переключить движок»).

  • Ресурсы — картинку можно держать как файл рядом с .exe (удобно менять без пересборки), либо встроить в ресурс как RCDATA и читать из него.

Если скажешь, какой именно сценарий тебе нужен (например: «клик по клетке — подсветить возможные ходы», или «нажал F1 — показать подсказку», или «строго C без C++»), дам адаптированный фрагмент под эту задачу.

вторник, 30 июня 2026 г.

Sdl corners

 #include <SDL2/SDL.h>

#include <iostream>

#include <vector>

#include <cmath>


// Размеры

const int CELL_SIZE = 64;

const int BOARD_SIZE = 8;

const int WINDOW_WIDTH = CELL_SIZE * BOARD_SIZE;

const int WINDOW_HEIGHT = CELL_SIZE * BOARD_SIZE;


// Цвета (SDL_RGBA)

SDL_Color COLOR_WHITE = {255, 255, 255, 255};

SDL_Color COLOR_BLACK = {0, 0, 0, 255};

SDL_Color COLOR_BOARD_LIGHT = {240, 217, 181, 255};

SDL_Color COLOR_BOARD_DARK = {181, 136, 99, 255};

SDL_Color COLOR_SELECTED = {100, 200, 255, 150};


// Типы фигур

enum PieceType { NONE, WHITE, BLACK };


struct Position {

    int row;

    int col;

    bool operator==(const Position& other) const {

        return row == other.row && col == other.col;

    }

};


// Класс игры

class Game {

public:

    std::vector<std::vector<PieceType>> board;

    PieceType currentPlayer;

    Position selectedPiece;

    bool pieceSelected;


    Game() {

        board.resize(BOARD_SIZE, std::vector<PieceType>(BOARD_SIZE, NONE));

        currentPlayer = WHITE;

        pieceSelected = false;

        selectedPiece = {-1, -1};

        

        // Расстановка фигур (как в шахматах, но только пешки и ладьи для примера)

        // Белые внизу (строки 6-7)

        for (int c = 0; c < BOARD_SIZE; c++) {

            board[6][c] = WHITE; // Верхний ряд белых

            board[7][c] = WHITE; // Нижний ряд белых

        }

        // Черные вверху (строки 0-1)

        for (int c = 0; c < BOARD_SIZE; c++) {

            board[0][c] = BLACK; 

            board[1][c] = BLACK;

        }

    }


    bool isInsideBoard(int r, int c) {

        return r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE;

    }


    bool makeMove(Position from, Position to) {

        if (board[from.row][from.col] != currentPlayer) return false;

        if (board[to.row][to.col] != NONE) return false;


        int dr = std::abs(to.row - from.row);

        int dc = std::abs(to.col - from.col);


        // Проверка правил

        if (dr == 1 && dc == 1) {

            // Обычный ход по диагонали

            board[to.row][to.col] = currentPlayer;

            board[from.row][from.col] = NONE;

            return true;

        } 

        else if (dr == 2 && dc == 2) {

            // Прыжок через фигуру

            int midRow = (from.row + to.row) / 2;

            int midCol = (from.col + to.col) / 2;

            if (board[midRow][midCol] != NONE && board[midRow][midCol] != currentPlayer) {

                board[to.row][to.col] = currentPlayer;

                board[from.row][from.col] = NONE;

                board[midRow][midCol] = NONE; // Съедаем фигуру

                return true;

            }

        }

        return false;

    }


    void switchTurn() {

        currentPlayer = (currentPlayer == WHITE) ? BLACK : WHITE;

    }

};


// Функции отрисовки

void drawBoard(SDL_Renderer* renderer) {

    for (int r = 0; r < BOARD_SIZE; r++) {

        for (int c = 0; c < BOARD_SIZE; c++) {

            SDL_Rect rect = {c * CELL_SIZE, r * CELL_SIZE, CELL_SIZE, CELL_SIZE};

            if ((r + c) % 2 == 0) {

                SDL_SetRenderDrawColor(renderer, COLOR_BOARD_LIGHT.r, COLOR_BOARD_LIGHT.g, COLOR_BOARD_LIGHT.b, 255);

            } else {

                SDL_SetRenderDrawColor(renderer, COLOR_BOARD_DARK.r, COLOR_BOARD_DARK.g, COLOR_BOARD_DARK.b, 255);

            }

            SDL_RenderFillRect(renderer, &rect);

        }

    }

}


void drawPieces(SDL_Renderer* renderer, Game& game) {

    for (int r = 0; r < BOARD_SIZE; r++) {

        for (int c = 0; c < BOARD_SIZE; c++) {

            if (game.board[r][c] == NONE) continue;


            SDL_Rect rect = {c * CELL_SIZE + 8, r * CELL_SIZE + 8, CELL_SIZE - 16, CELL_SIZE - 16};

            

            // Выбор цвета фигуры

            if (game.board[r][c] == WHITE) {

                SDL_SetRenderDrawColor(renderer, COLOR_WHITE.r, COLOR_WHITE.g, COLOR_WHITE.b, 255);

            } else {

                SDL_SetRenderDrawColor(renderer, COLOR_BLACK.r, COLOR_BLACK.g, COLOR_BLACK.b, 255);

            }

            

            // Рисуем круг (используем многоугольник для простоты или можно использовать SDL_RenderDrawPoint, но это медленно)

            // Для простоты рисуем квадрат со скругленными углами, но в SDL2 нет встроенной функции круга.

            // Нарисуем эллипс вручную (или просто квадрат для скорости).

            // Давайте нарисуем круг через серию точек.

            int cx = rect.x + rect.w/2;

            int cy = rect.y + rect.h/2;

            int radius = rect.w/2 - 2;

            

            for (int w = 0; w < radius * 2; w++) {

                for (int h = 0; h < radius * 2; h++) {

                    int dx = radius - w;

                    int dy = radius - h;

                    if ((dx*dx + dy*dy) <= (radius * radius)) {

                        SDL_RenderDrawPoint(renderer, cx + dx, cy + dy);

                    }

                }

            }

            

            // Если фигура выбрана, обводим её

            if (game.pieceSelected && game.selectedPiece.row == r && game.selectedPiece.col == c) {

                SDL_SetRenderDrawColor(renderer, COLOR_SELECTED.r, COLOR_SELECTED.g, COLOR_SELECTED.b, 255);

                SDL_Rect outline = {rect.x - 2, rect.y - 2, rect.w + 4, rect.h + 4};

                SDL_RenderDrawRect(renderer, &outline);

            }

        }

    }

}


int main(int argc, char* argv[]) {

    if (SDL_Init(SDL_INIT_VIDEO) != 0) {

        std::cerr << "SDL Init Error: " << SDL_GetError() << std::endl;

        return 1;

    }


    SDL_Window* window = SDL_CreateWindow("Уголки (Corners)", 

                                          SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,

                                          WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);

    if (!window) {

        std::cerr << "Window Error: " << SDL_GetError() << std::endl;

        SDL_Quit();

        return 1;

    }


    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

    if (!renderer) {

        std::cerr << "Renderer Error: " << SDL_GetError() << std::endl;

        SDL_DestroyWindow(window);

        SDL_Quit();

        return 1;

    }


    Game game;

    bool running = true;

    SDL_Event event;


    while (running) {

        while (SDL_PollEvent(&event)) {

            if (event.type == SDL_QUIT) {

                running = false;

            }

            else if (event.type == SDL_MOUSEBUTTONDOWN) {

                int mouseX = event.button.x;

                int mouseY = event.button.y;

                int col = mouseX / CELL_SIZE;

                int row = mouseY / CELL_SIZE;


                if (game.isInsideBoard(row, col)) {

                    // Если фигура уже выбрана

                    if (game.pieceSelected) {

                        // Пытаемся сделать ход

                        if (game.makeMove(game.selectedPiece, {row, col})) {

                            game.switchTurn();

                            game.pieceSelected = false;

                        } else {

                            // Если ход не удался, либо снимаем выделение, либо выбираем новую фигуру

                            if (game.board[row][col] == game.currentPlayer) {

                                game.selectedPiece = {row, col};

                            } else {

                                game.pieceSelected = false;

                            }

                        }

                    } 

                    // Выбираем фигуру

                    else {

                        if (game.board[row][col] == game.currentPlayer) {

                            game.selectedPiece = {row, col};

                            game.pieceSelected = true;

                        }

                    }

                }

            }

        }


        // Отрисовка

        SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);

        SDL_RenderClear(renderer);


        drawBoard(renderer);

        drawPieces(renderer, game);


        SDL_RenderPresent(renderer);

        SDL_Delay(10);

    }


    SDL_DestroyRenderer(renderer);

    SDL_DestroyWindow(window);

    SDL_Quit();

    return 0;

}

/////////

/*

g++ main.cpp -o corners.exe -I"path\to\SDL2\include" -L"path\to\SDL2\lib" -lSDL2main -lSDL2


    Да, SDL2.dll (или SDL2main.dll, если у вас статическая линковка) обязан находиться в той же папке, где лежит ваш .exe файл.

    Как это сделать правильно:

    1. Скачайте библиотеку SDL2 с официального сайта (обычно это SDL2-devel-2.x.x-mingw.zip).



Откройте командную строку (cmd) или PowerShell в папке с вашим corners.exe и введите команду:

cmd
dumpbin /headers corners.exe | find "machine"

Что вы увидите:

  • Если в выводе есть 14C → это x86 (32-bit).

  • Если в выводе есть 8664 → это x64 (64-bit).


  •  */


понедельник, 29 июня 2026 г.

Связь по винбоард

 

Да, вывести текстовые символы Юникода в графическое окно pygame можно. Для этого используется модуль pygame.font.
Основная хитрость заключается в том, что стандартный шрифт Pygame не всегда корректно поддерживает шахматные символы Юникода. Чтобы они гарантированно отображались, лучше использовать системный шрифт, который точно содержит эти символы (например, Arial, Segoe UI Symbol или DejaVu Sans).
Ниже представлен готовый скрипт, который создаёт графическое окно, рисует цветную шахматную сетку и поверх неё отрисовывает фигуры из кодов символов.

Готовый код на Pygame

import sys
import pygame

# Инициализация Pygame
pygame.init()

# Константы графики
CELL_SIZE = 80
BOARD_SIZE = CELL_SIZE * 8
WINDOW_SIZE = BOARD_SIZE + 40  # Окно чуть больше доски для отступов под буквы/цифры
OFFSET = 20                     # Отступ доски от края окна

# Цвета (RGB)
COLOR_LIGHT = (240, 217, 181)   # Светлые клетки
COLOR_DARK = (181, 136, 99)     # Темные клетки
COLOR_BG = (49, 46, 43)         # Фон окна
COLOR_TEXT = (255, 255, 255)    # Цвет текста разметки

# Настройка окна
screen = pygame.display.set_mode((WINDOW_SIZE, WINDOW_SIZE))
pygame.display.set_caption("Шахматная доска на Pygame (Unicode)")

# Подключение шрифта с поддержкой Юникод-фигур
# SysFont автоматически подберет подходящий шрифт из системы (Arial обычно работает везде)
font_pieces = pygame.font.SysFont("arial", int(CELL_SIZE * 0.8))
font_labels = pygame.font.SysFont("arial", 16)

# Словари с Юникод-кодами шахматных фигур
white_pieces = {
    'R': '\u2656', 'N': '\u2658', 'B': '\u2657', 
    'Q': '\u2655', 'K': '\u2654', 'P': '\u2659'
}

black_pieces = {
    'r': '\u265C', 'n': '\u265E', 'b': '\u265D', 
    'q': '\u265B', 'k': '\u265A', 'p': '\u265F'
}

# Начальная расстановка
board = [
    ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
    ['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
    ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']
]

def draw_board():
    # Заливка фона
    screen.fill(COLOR_BG)
    
    # Отрисовка разметки (буквы и цифры)
    letters = "ABCDEFGH"
    for i in range(8):
        # Цифры слева (8-1)
        lbl_num = font_labels.render(str(8 - i), True, COLOR_TEXT)
        screen.blit(lbl_num, (5, OFFSET + i * CELL_SIZE + CELL_SIZE // 2 - 8))
        
        # Буквы снизу (A-H)
        lbl_let = font_labels.render(letters[i], True, COLOR_TEXT)
        screen.blit(lbl_let, (OFFSET + i * CELL_SIZE + CELL_SIZE // 2 - 5, BOARD_SIZE + OFFSET + 2))

    # Отрисовка клеток и фигур
    for row in range(8):
        for col in range(8):
            # Координаты левого верхнего угла клетки
            x = OFFSET + col * CELL_SIZE
            y = OFFSET + row * CELL_SIZE
            
            # 1. Рисуем клетку доски
            is_light = (row + col) % 2 == 0
            cell_color = COLOR_LIGHT if is_light else COLOR_DARK
            pygame.draw.rect(screen, cell_color, (x, y, CELL_SIZE, CELL_SIZE))
            
            # 2. Рисуем фигуру, если она есть
            cell_content = board[row][col]
            if cell_content != '.':
                # Определяем символ фигуры
                if cell_content.isupper():
                    char = white_pieces[cell_content]
                    # Для белых фигур на светлом фоне можно задать черный контур, 
                    # но Pygame рендерит Юникод-фигуры как обычный текст определенного цвета.
                    # Сделаем сами фигуры черными/белыми для контраста:
                    piece_color = (255, 255, 255) if cell_content.islower() else (255, 255, 255)
                else:
                    char = black_pieces[cell_content]
                
                # Рендерим символ в картинку (True включает сглаживание)
                # Чтобы фигуры хорошо читались на любых клетках, черные сделаем черными, белые — белыми
                p_color = (255, 255, 255) if cell_content.isupper() else (0, 0, 0)
                piece_surface = font_pieces.render(char, True, p_color)
                
                # Центрируем фигуру внутри клетки
                p_rect = piece_surface.get_rect()
                p_rect.center = (x + CELL_SIZE // 2, y + CELL_SIZE // 2)
                
                screen.blit(piece_surface, p_rect)

# Главный цикл программы
running = True
while running:
    for event in pygame.get_調整(): # Обработка событий
        if event.type == pygame.QUIT:
            running = False
            
    # Рисуем актуальное состояние игры
    draw_board()
    
    # Обновляем экран
    pygame.display.flip()

pygame.quit()
sys.exit()

Важные нюансы реализации в Pygame:

  • Выбор шрифта (pygame.font.SysFont): Мы берем "arial", так как он есть на Windows, Mac и Linux, и содержит корректные глифы для шахматных символов. Если фигуры отображаются как "квадратики", замените "arial" на "segoeuisymbol" (для Windows) или "dejavusans" (для Linux).
  • Цвет символов: В отличие от консоли, где у символа Юникода цвет фиксирован (белые фигуры закрашены белым внутри кода \u2654), в Pygame функция font.render() красит весь силуэт символа в выбранный вами RGB-цвет. Поэтому для белых фигур мы передаем белый цвет (255, 255, 255), а для черных — черный (0, 0, 0).
  • Выравнивание (get_rect().center): Текстовые символы могут иметь невидимые отступы. Код находит геометрический центр сгенерированной картинки символа и ровно сопоставляет его с центром клетки (CELL_SIZE // 2).
Если код запущен успешно, вы увидите полноценное графическое окно с привычной деревянно-бежевой доской и контрастными фигурами.
Если вы хотите продолжить разработку, напишите:
  • Хотите ли вы добавить выделение клетки при клике мышкой?
  • Нужно ли реализовать перетаскивание фигур (Drag-and-Drop) мышью?
  • Требуется ли фиксировать историю ходов на экране рядом с доской?


Вот готовый и полностью рабочий код на Pygame, в котором реализована полноценная игра для двух игроков на одном экране.
В программу добавлены:
  1. Выделение клетки при нажатии.
  2. Перетаскивание фигур (Drag-and-Drop) мышкой.
  3. Ограничение по очереди ходов (белые и черные ходят строго по очереди, начиная с белых).
  4. Возврат фигуры, если ход был сделан на ту же самую клетку или за пределы доски.

Полный код игры

import sys
import pygame

# Инициализация Pygame
pygame.init()

# Константы графики
CELL_SIZE = 80
BOARD_SIZE = CELL_SIZE * 8
WINDOW_SIZE = BOARD_SIZE + 40  # Окно чуть больше доски для отступов под буквы/цифры
OFFSET = 20                     # Отступ доски от края окна

# Цвета (RGB)
COLOR_LIGHT = (240, 217, 181)       # Светлые клетки
COLOR_DARK = (181, 136, 99)         # Темные клетки
COLOR_BG = (49, 46, 43)             # Фон окна
COLOR_TEXT = (255, 255, 255)        # Цвет текста разметки
COLOR_SELECTED = (130, 151, 105)    # Цвет выделенной клетки (зеленоватый)

# Настройка окна
screen = pygame.display.set_mode((WINDOW_SIZE, WINDOW_SIZE))
pygame.display.set_caption("Шахматы для двух игроков")

# Подключение шрифта с поддержкой Юникода
font_pieces = pygame.font.SysFont("arial", int(CELL_SIZE * 0.8))
font_labels = pygame.font.SysFont("arial", 16)

# Словари с Юникод-кодами шахматных фигур
white_pieces = {'R': '\u2656', 'N': '\u2658', 'B': '\u2657', 'Q': '\u2655', 'K': '\u2654', 'P': '\u2659'}
black_pieces = {'r': '\u265C', 'n': '\u265E', 'b': '\u265D', 'q': '\u265B', 'k': '\u265A', 'p': '\u265F'}

# Начальная расстановка
board = [
    ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
    ['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
    ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']
]

# Переменные состояния игры
selected_cell = None  # Координаты (row, col) выбранной клетки
dragged_piece = None  # Символ перетаскиваемой фигуры (например, 'P')
drag_start_pos = None # Откуда начали тащить (row, col)
white_turn = True     # True - ходят белые, False - черные

def get_cell_from_pos(pos):
    """Определяет строку и столбец на доске по координатам мыши."""
    x, y = pos
    col = (x - OFFSET) // CELL_SIZE
    row = (y - OFFSET) // CELL_SIZE
    if 0 <= col < 8 and 0 <= row < 8:
        return row, col
    return None

def draw_board():
    screen.fill(COLOR_BG)
    
    # Отрисовка разметки (буквы и цифры)
    letters = "ABCDEFGH"
    for i in range(8):
        lbl_num = font_labels.render(str(8 - i), True, COLOR_TEXT)
        screen.blit(lbl_num, (5, OFFSET + i * CELL_SIZE + CELL_SIZE // 2 - 8))
        lbl_let = font_labels.render(letters[i], True, COLOR_TEXT)
        screen.blit(lbl_let, (OFFSET + i * CELL_SIZE + CELL_SIZE // 2 - 5, BOARD_SIZE + OFFSET + 2))

    # Отрисовка клеток и статичных фигур
    for row in range(8):
        for col in range(8):
            x = OFFSET + col * CELL_SIZE
            y = OFFSET + row * CELL_SIZE
            
            # Подсветка выделенной клетки или клетки, откуда тащат фигуру
            if selected_cell == (row, col) or drag_start_pos == (row, col):
                cell_color = COLOR_SELECTED
            else:
                is_light = (row + col) % 2 == 0
                cell_color = COLOR_LIGHT if is_light else COLOR_DARK
                
            pygame.draw.rect(screen, cell_color, (x, y, CELL_SIZE, CELL_SIZE))
            
            # Рисуем фигуру (только если её сейчас не тащат мышкой)
            cell_content = board[row][col]
            if cell_content != '.' and (drag_start_pos != (row, col)):
                draw_piece_at(cell_content, x + CELL_SIZE // 2, y + CELL_SIZE // 2)

    # Отрисовка перетаскиваемой фигуры прямо под курсором мыши
    if dragged_piece:
        mouse_x, mouse_y = pygame.mouse.get_pos()
        draw_piece_at(dragged_piece, mouse_x, mouse_y)

def draw_piece_at(piece, center_x, center_y):
    """Вспомогательная функция отрисовки фигуры по центру координат."""
    if piece.isupper():
        char = white_pieces[piece]
        p_color = (255, 255, 255)  # Белые фигуры
    else:
        char = black_pieces[piece]
        p_color = (0, 0, 0)        # Черные фигуры
        
    piece_surface = font_pieces.render(char, True, p_color)
    p_rect = piece_surface.get_rect()
    p_rect.center = (center_x, center_y)
    screen.blit(piece_surface, p_rect)

# Главный цикл программы
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:  # Левая кнопка мыши
                cell = get_cell_from_pos(event.pos)
                if cell:
                    row, col = cell
                    piece = board[row][col]
                    
                    # Проверяем, принадлежит ли фигура игроку, который сейчас должен ходить
                    is_white_piece = piece.isupper()
                    correct_turn = (white_turn and is_white_piece) or (not white_turn and not is_white_piece and piece != '.')
                    
                    if correct_turn:
                        selected_cell = cell
                        dragged_piece = piece
                        drag_start_pos = cell
                    else:
                        # Клик по пустой клетке или чужой фигуре сбрасывает выделение
                        selected_cell = None

        elif event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1 and dragged_piece:
                cell = get_cell_from_pos(event.pos)
                start_row, start_col = drag_start_pos
                
                if cell and cell != drag_start_pos:
                    end_row, end_col = cell
                    target_piece = board[end_row][end_col]
                    
                    # Запрещаем бить свои же фигуры
                    is_destination_friendly = (white_turn and target_piece.isupper()) or (not white_turn and target_piece.islower() and target_piece != '.')
                    
                    if not is_destination_friendly:
                        # Делаем ход
                        board[end_row][end_col] = dragged_piece
                        board[start_row][start_col] = '.'
                        white_turn = not white_turn  # Передаем ход другому игроку
                
                # Сбрасываем режим перетаскивания фигурки
                dragged_piece = None
                drag_start_pos = None
                selected_cell = None

    # Обновление графики
    draw_board()
    pygame.display.flip()

pygame.quit()
sys.exit()

Как играть (управление):

  • Очередь ходов: Игра начинается с хода Белых. Черные фигуры взять или перетащить в первый ход не получится. После успешного переноса белой фигуры ход автоматически переходит к Черным.
  • Перетаскивание (Drag-and-Drop): Зажмите фигуру левой кнопкой мыши (клетка подсветится зеленым), перетащите её на нужную позицию и отпустите кнопку.
  • Правила взятия: Вы можете перемещать фигуры на пустые клетки или на клетки с фигурами соперника (в этом случае фигура соперника исчезнет). Поставить свою фигуру на свою же — программа не позволит.
Если вы хотите продолжить улучшение шахмат, дайте знать:
  • Нужно ли добавить валидацию ходов по правилам шахмат (например, чтобы пешка ходила только вперед, конь буквой «Г» и т.д.)?
  • Хотите ли вы добавить звук взятия фигур или анимацию?
  • Нужно ли выводить на экран надпись, чей сейчас ход?

Чтобы связать графический интерфейс Pygame на Python с внешним скомпилированным шахматным движком (например, написанным на C в виде .exe файла), протокол WinBoard/XBoard использовать напрямую неудобно. Движки по протоколу WinBoard или UCI ожидают, что ими управляет шахматная оболочка (GUI), а не наоборот.

Самый простой способ управлять C-движком из Python — запустить его как подпроцесс через встроенный модуль subprocess. Python будет отправлять ходы в движок через текстовый поток и считывать текстовый ответ.
Ниже представлен готовый скрипт, в котором вы можете мышкой передвигать фигуры без правил, а программа автоматически передаст ваш ход C-движку, получит от него ответный ход и отобразит его на доске.

Скрипт интеграции Pygame с внешним C-движком

Перед запуском укажите точное имя или путь к вашему скомпилированному движку в переменной ENGINE_PATH.
import sys
import subprocess
import threading
import pygame

# --- НАСТРОЙКИ ВНЕШНЕГО ДВИЖКА ---
# Укажите путь к вашему .exe (или бинарнику) движка на C
ENGINE_PATH = "my_chess_engine.exe" 

# --- НАСТРОЙКИ ГРАФИКИ PYGAME ---
CELL_SIZE = 80
BOARD_SIZE = CELL_SIZE * 8
WINDOW_SIZE = BOARD_SIZE + 40
OFFSET = 20

COLOR_LIGHT = (240, 217, 181)
COLOR_DARK = (181, 136, 99)
COLOR_BG = (49, 46, 43)
COLOR_TEXT = (255, 255, 255)
COLOR_SELECTED = (130, 151, 105)

white_pieces = {'R': '\u2656', 'N': '\u2658', 'B': '\u2657', 'Q': '\u2655', 'K': '\u2654', 'P': '\u2659'}
black_pieces = {'r': '\u265C', 'n': '\u265E', 'b': '\u265D', 'q': '\u265B', 'k': '\u265A', 'p': '\u265F'}

board = [
    ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
    ['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
    ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']
]

# Переменные состояния мыши
selected_cell = None  
dragged_piece = None  
drag_start_pos = None 
engine_thinking = False # Блокирует интерфейс, пока бот думает

# --- ВЗАИМОДЕЙСТВИЕ С ДВИЖКОМ НА C ---
try:
    # Запускаем движок как независимый процесс с перенаправлением потоков ввода/вывода
    engine_process = subprocess.Popen(
        [ENGINE_PATH],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
        bufsize=1 # Построчная буферизация
    )
except FileNotFoundError:
    print(f"Ошибка: Не найден файл движка по пути '{ENGINE_PATH}'. Запустится только визуализация.")
    engine_process = None

def send_to_engine(text):
    """Отправляет команду в стандартный поток ввода C-движка."""
    if engine_process and engine_process.poll() is None:
        engine_process.stdin.write(text + "\n")
        engine_process.stdin.flush()

def get_engine_move_blocking(player_move):
    """Отправляет ход игрока движку и ждет от него текстовый ответ."""
    global board, engine_thinking
    if not engine_process or engine_process.poll() is not None:
        engine_thinking = False
        return

    # 1. Передаем ход игрока в C-программу
    send_to_engine(player_move)
    
    # 2. Читаем строку ответа от C-программы (ожидается формат "e7e5")
    # readline() заблокирует этот поток, пока C-движок не выведет printf("...\n");
    engine_response = engine_process.stdout.readline().strip()
    
    # 3. Применяем ответный ход на нашей доске
    if len(engine_response) >= 4:
        apply_move_to_board(engine_response)
        
    engine_thinking = False

def request_engine_move(player_move):
    """Запускает общение с движком в отдельном потоке, чтобы окно Pygame не зависало."""
    global engine_thinking
    engine_thinking = True
    threading.Thread(target=get_engine_move_blocking, args=(player_move,), daemon=True).start()

# --- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ДОСКИ ---
def to_notation(row, col):
    """Преобразует индексы матрицы в шахматную нотацию (например, 7, 4 -> 'e1')."""
    return f"{chr(ord('a') + col)}{8 - row}"

def from_notation(coord_str):
    """Преобразует строку 'e1' в индексы (row, col)."""
    col = ord(coord_str[0].lower()) - ord('a')
    row = 8 - int(coord_str[1])
    return row, col

def apply_move_to_board(move_str):
    """Простое перемещение фигуры по строке вида 'e2e4'."""
    global board
    try:
        sr, sc = from_notation(move_str[0:2])
        er, ec = from_notation(move_str[2:4])
        board[er][ec] = board[sr][sc]
        board[sr][sc] = '.'
    except Exception:
        pass

def get_cell_from_pos(pos):
    x, y = pos
    col = (x - OFFSET) // CELL_SIZE
    row = (y - OFFSET) // CELL_SIZE
    if 0 <= col < 8 and 0 <= row < 8:
        return row, col
    return None

def draw_board(screen, font_pieces, font_labels):
    screen.fill(COLOR_BG)
    
    # Разметка
    letters = "ABCDEFGH"
    for i in range(8):
        lbl_num = font_labels.render(str(8 - i), True, COLOR_TEXT)
        screen.blit(lbl_num, (5, OFFSET + i * CELL_SIZE + CELL_SIZE // 2 - 8))
        lbl_let = font_labels.render(letters[i], True, COLOR_TEXT)
        screen.blit(lbl_let, (OFFSET + i * CELL_SIZE + CELL_SIZE // 2 - 5, BOARD_SIZE + OFFSET + 2))

    # Клетки
    for row in range(8):
        for col in range(8):
            x = OFFSET + col * CELL_SIZE
            y = OFFSET + row * CELL_SIZE
            
            if (selected_cell == (row, col) or drag_start_pos == (row, col)) and not engine_thinking:
                cell_color = COLOR_SELECTED
            else:
                cell_color = COLOR_LIGHT if (row + col) % 2 == 0 else COLOR_DARK
                
            pygame.draw.rect(screen, cell_color, (x, y, CELL_SIZE, CELL_SIZE))
            
            # Статичные фигуры
            cell_content = board[row][col]
            if cell_content != '.' and (drag_start_pos != (row, col) or engine_thinking):
                draw_piece_at(screen, font_pieces, cell_content, x + CELL_SIZE // 2, y + CELL_SIZE // 2)

    # Перетаскиваемая фигура под мышью
    if dragged_piece and not engine_thinking:
        mouse_x, mouse_y = pygame.mouse.get_pos()
        draw_piece_at(screen, font_pieces, dragged_piece, mouse_x, mouse_y)

def draw_piece_at(screen, font, piece, center_x, center_y):
    char = white_pieces[piece] if piece.isupper() else black_pieces[piece]
    color = (255, 255, 255) if piece.isupper() else (0, 0, 0)
    surface = font.render(char, True, color)
    rect = surface.get_rect(center=(center_x, center_y))
    screen.blit(surface, rect)

# --- ГЛАВНЫЙ ГРАФИЧЕСКИЙ ЦИКЛ ---
def main():
    global selected_cell, dragged_piece, drag_start_pos
    pygame.init()
    screen = pygame.display.set_mode((WINDOW_SIZE, WINDOW_SIZE))
    pygame.display.set_caption("Pygame + C Chess Engine")
    
    font_pieces = pygame.font.SysFont("arial", int(CELL_SIZE * 0.8))
    font_labels = pygame.font.SysFont("arial", 16)
    
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                
            elif event.type == pygame.MOUSEBUTTONDOWN and not engine_thinking:
                if event.button == 1:
                    cell = get_cell_from_pos(event.pos)
                    if cell and board[cell[0]][cell[1]] != '.':
                        selected_cell = cell
                        dragged_piece = board[cell[0]][cell[1]]
                        drag_start_pos = cell

            elif event.type == pygame.MOUSEBUTTONUP and not engine_thinking:
                if event.button == 1 and dragged_piece:
                    cell = get_cell_from_pos(event.pos)
                    if cell and cell != drag_start_pos:
                        start_str = to_notation(*drag_start_pos)
                        end_str = to_notation(*cell)
                        move_notation = start_str + end_str # Строка вида "e2e4"
                        
                        # 1. Двигаем фигуру игрока на экране
                        apply_move_to_board(move_notation)
                        
                        # 2. Отправляем этот ход в C-движок и запрашиваем ответ
                        request_engine_move(move_notation)
                        
                    dragged_piece = None
                    drag_start_pos = None
                    selected_cell = None

        draw_board(screen, font_pieces, font_labels)
        pygame.display.flip()

    # Корректно закрываем процесс движка при выходе
    if engine_process and engine_process.poll() is None:
        engine_process.kill()
    pygame.quit()

if __name__ == "__main__":
    main()

Как должен принимать и отдавать ходы ваш C-движок:

Чтобы эта связка работала, код вашей программы на C должен быть устроен по следующему базовому принципу (структура бесконечного консольного цикла):
#include <stdio.h>
#include <string.h>

int main() {
    char player_move[10];
    
    // Важно отключить буферизацию printf, чтобы Python сразу получал строки
    setbuf(stdout, NULL); 

    while (1) {
        // 1. Движок замирает и ждет ход от Python через stdin
        if (scanf("%s", player_move) == EOF) break; 
        
        // Здесь ваш Си-движок применяет ход player_move (например, "e2e4")
        // и рассчитывает свой ответный лучший ход...
        
        // 2. Движок ОБЯЗАТЕЛЬНО должен вывести только строку хода и перенос строки \n
        char engine_move[] = "e7e5"; // Пример рассчитанного хода
        printf("%s\n", engine_move); 
    }
    return 0;
}

Главные особенности решения:

  1. Асинхронность (threading): Ожидание ответа от C-движка вынесено в отдельный фоновый поток. Кнопки и окно Pygame не зависают, пока движок думает над позицией.
  2. Отсутствие правил в Python: Вы можете перетащить любую фигуру на любую клетку. Строка перемещения (например, b1c3) тут же улетит в C-движок.
  3. Обмен данными через стандартные потоки (Pipes): Python использует stdin.write для отправки и stdout.readline для чтения, что избавляет вас от необходимости сложной линковки C-библиотек или DLL.
Если ваш C-движок использует специализированный протокол инициализации перед началом игры, сообщите:
  • Нужно ли слать движку какие-то стартовые команды (например, uci или xboard) при запуске?
  • Передаются ли ходы по одному накопительной строкой (историей партии) или только по одному последнему ходу
Да, конечно. Шахматные движки часто используют символ # для вывода отладочной информации, логов или комментариев (например, # thinking... или # score: +1.2).
Чтобы Pygame не воспринимал эти строки как ходы и не пытался их отрисовать, нужно добавить простой цикл while в функцию чтения ответа. Программа будет считывать и игнорировать все строки, начинающиеся с #, пока не встретит чистую строку хода.
Вот обновленный фоновый метод get_engine_move_blocking. Замените им аналогичную функцию в вашем Python-скрипте:
def get_engine_move_blocking(player_move):
    """Отправляет ход игрока движку и ждет от него текстовый ответ, игнорируя строки с '#'."""
    global board, engine_thinking
    if not engine_process or engine_process.poll() is not None:
        engine_thinking = False
        return

    # 1. Передаем ход игрока в Си-программу
    send_to_engine(player_move)
    
    # 2. Читаем строки ответа от Си-программы в цикле
    while True:
        engine_response = engine_process.stdout.readline()
        if not engine_response:  # Если поток закрылся (движок упал)
            break
            
        engine_response = engine_response.strip()
        
        # Если строка пустая или начинается с #, просто пропускаем её и читаем дальше
        if not engine_response or engine_response.startswith('#'):
            continue
            
        # Как только нашли строку, которая не начинается с #, считаем её ходом движка
        if len(engine_response) >= 4:
            apply_move_to_board(engine_response)
        
        break # Выходим из цикла чтения, так как ход получен
        
    engine_thinking = False

Как теперь это работает:

  • Если ваш C-движок напишет в консоль:
    # Анализ позиции...
    # Найдена лучшая ветка: e7e5 d2d4
    # Оценка: +0.15
    e7e5
    
  • Python молча пропустит первые три строки и применит к доске только финальный ход e7e5.
Если ваш движок присылает информацию в каком-то другом формате (например, пишет логи в конце хода или разделяет их пробелами), напишите — я скорректирую парсер под ваш вывод.


Да, так сделать можно, и это самый надежный способ избавить Python-скрипт от знания каких-либо правил. Вместо обмена короткими ходами (e2e4), Python будет отправлять движку всю текущую матрицу доски одной текстовой строкой (или 8 строками) плюс сделанный игроком ход.
Движок на Си проверит ход по своим правилам и вернет обратно:
  • Либо новую позицию (в таком же текстовом формате), если ход игрока был правильным. При этом движок сразу сделает и свой ответный ход.
  • Либо ключевое слово ERROR, если игрок попытался сходить не по правилам. В этом случае Python просто вернет фигуру на место.
Ниже представлен полностью переработанный скрипт.

Как устроен обмен данными (Протокол)

  1. Игрок делает ход: Python берет текущую доску (64 символа) и добавляет ход.
    • Пример отправки в Си: rnbqkbnrpppppppp................................PPPPPPPPRNBQKBNR e2e4
  2. Движок отвечает:
    • Если ход правильный: Возвращает строку из 64 символов (уже с учетом хода игрока и ответного хода компьютера).
    • Если ход ошибочный: Возвращает строку ERROR (или любую строку, начинающуюся с # для отладки).

Обновленный Python-скрипт

import sys
import subprocess
import threading
import pygame

# --- НАСТРОЙКИ ВНЕШНЕГО ДВИЖКА ---
ENGINE_PATH = "my_chess_engine.exe" 

# --- НАСТРОЙКИ ГРАФИКИ PYGAME ---
CELL_SIZE = 80
BOARD_SIZE = CELL_SIZE * 8
WINDOW_SIZE = BOARD_SIZE + 40
OFFSET = 20

COLOR_LIGHT = (240, 217, 181)
COLOR_DARK = (181, 136, 99)
COLOR_BG = (49, 46, 43)
COLOR_TEXT = (255, 255, 255)
COLOR_SELECTED = (130, 151, 105)

white_pieces = {'R': '\u2656', 'N': '\u2658', 'B': '\u2657', 'Q': '\u2655', 'K': '\u2654', 'P': '\u2659'}
black_pieces = {'r': '\u265C', 'n': '\u265E', 'b': '\u265D', 'q': '\u265B', 'k': '\u265A', 'p': '\u265F'}

# Начальное состояние (двумерный массив для удобства отрисовки)
board = [
    ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
    ['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['.', '.', '.', '.', '.', '.', '.', '.'],
    ['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
    ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']
]

selected_cell = None  
dragged_piece = None  
drag_start_pos = None 
engine_thinking = False 

# --- ФУНКЦИИ КОНВЕРТАЦИИ ПОЗИЦИИ ---
def board_to_string():
    """Превращает матрицу доски 8х8 в одномерную строку из 64 символов."""
    return "".join("".join(row) for row in board)

def string_to_board(board_str):
    """Обновляет матрицу доски 8х8 из одномерной строки (64 символа)."""
    global board
    for r in range(8):
        for c in range(8):
            board[r][c] = board_str[r * 8 + c]

def to_notation(row, col):
    return f"{chr(ord('a') + col)}{8 - row}"

def get_cell_from_pos(pos):
    x, y = pos
    col = (x - OFFSET) // CELL_SIZE
    row = (y - OFFSET) // CELL_SIZE
    if 0 <= col < 8 and 0 <= row < 8:
        return row, col
    return None

# --- ВЗАИМОДЕЙСТВИЕ С ДВИЖКОМ НА C ---
try:
    engine_process = subprocess.Popen(
        [ENGINE_PATH],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
        bufsize=1
    )
except FileNotFoundError:
    print(f"Ошибка: Не найден движок '{ENGINE_PATH}'. Работает только интерфейс.")
    engine_process = None

def get_engine_response_blocking(current_board_str, move_str):
    """Отправляет строку доски + ход, ждет новую доску или ошибку."""
    global engine_thinking
    if not engine_process or engine_process.poll() is not None:
        engine_thinking = False
        return

    # Отправляем данные формата: "64_символа_доски e2e4"
    engine_process.stdin.write(f"{current_board_str} {move_str}\n")
    engine_process.stdin.flush()
    
    while True:
        response = engine_process.stdout.readline()
        if not response:
            break
            
        response = response.strip()
        
        # Игнорируем логи и комментарии движка
        if not response or response.startswith('#'):
            continue
            
        # Если ход нелегальный, Си-движок прислал ERROR -> ничего не меняем (фигура вернется назад)
        if "ERROR" in response.upper():
            print("Движок отклонил ход: не по правилам!")
            break
            
        # Если пришла строка из 64 символов — это валидная новая позиция
        if len(response) == 64:
            string_to_board(response)
            break
            
        break 
        
    engine_thinking = False

# --- ОТРИСОВКА ---
def draw_board(screen, font_pieces, font_labels):
    screen.fill(COLOR_BG)
    letters = "ABCDEFGH"
    for i in range(8):
        lbl_num = font_labels.render(str(8 - i), True, COLOR_TEXT)
        screen.blit(lbl_num, (5, OFFSET + i * CELL_SIZE + CELL_SIZE // 2 - 8))
        lbl_let = font_labels.render(letters[i], True, COLOR_TEXT)
        screen.blit(lbl_let, (OFFSET + i * CELL_SIZE + CELL_SIZE // 2 - 5, BOARD_SIZE + OFFSET + 2))

    for row in range(8):
        for col in range(8):
            x = OFFSET + col * CELL_SIZE
            y = OFFSET + row * CELL_SIZE
            
            if (selected_cell == (row, col) or drag_start_pos == (row, col)) and not engine_thinking:
                cell_color = COLOR_SELECTED
            else:
                cell_color = COLOR_LIGHT if (row + col) % 2 == 0 else COLOR_DARK
                
            pygame.draw.rect(screen, cell_color, (x, y, CELL_SIZE, CELL_SIZE))
            
            cell_content = board[row][col]
            if cell_content != '.' and (drag_start_pos != (row, col) or engine_thinking):
                draw_piece_at(screen, font_pieces, cell_content, x + CELL_SIZE // 2, y + CELL_SIZE // 2)

    if dragged_piece and not engine_thinking:
        mouse_x, mouse_y = pygame.mouse.get_pos()
        draw_piece_at(screen, font_pieces, dragged_piece, mouse_x, mouse_y)

def draw_piece_at(screen, font, piece, center_x, center_y):
    char = white_pieces[piece] if piece.isupper() else black_pieces[piece]
    color = (255, 255, 255) if piece.isupper() else (0, 0, 0)
    surface = font.render(char, True, color)
    rect = surface.get_rect(center=(center_x, center_y))
    screen.blit(surface, rect)

# --- ГЛАВНЫЙ ЦИКЛ ---
def main():
    global selected_cell, dragged_piece, drag_start_pos, engine_thinking
    pygame.init()
    screen = pygame.display.set_mode((WINDOW_SIZE, WINDOW_SIZE))
    pygame.display.set_caption("Chess GUI (No Rules in Python)")
    
    font_pieces = pygame.font.SysFont("arial", int(CELL_SIZE * 0.8))
    font_labels = pygame.font.SysFont("arial", 16)
    
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                
            elif event.type == pygame.MOUSEBUTTONDOWN and not engine_thinking:
                if event.button == 1:
                    cell = get_cell_from_pos(event.pos)
                    if cell and board[cell[0]][cell[1]] != '.':
                        selected_cell = cell
                        dragged_piece = board[cell[0]][cell[1]]
                        drag_start_pos = cell

            elif event.type == pygame.MOUSEBUTTONUP and not engine_thinking:
                if event.button == 1 and dragged_piece:
                    cell = get_cell_from_pos(event.pos)
                    if cell and cell != drag_start_pos:
                        # Запоминаем старую доску на случай отправки
                        old_board_str = board_to_string()
                        
                        start_str = to_notation(*drag_start_pos)
                        end_str = to_notation(*cell)
                        move_str = start_str + end_str
                        
                        # Временно двигаем фигуру на экране, чтобы визуализировать намерение игрока
                        board[cell[0]][cell[1]] = dragged_piece
                        board[drag_start_pos[0]][drag_start_pos[1]] = '.'
                        
                        # Отправляем состояние ДО ХОДА и сам ход на валидацию в Си
                        engine_thinking = True
                        threading.Thread(
                            target=get_engine_response_blocking, 
                            args=(old_board_str, move_str), 
                            daemon=True
                        ).start()
                        
                    dragged_piece = None
                    drag_start_pos = None
                    selected_cell = None

        draw_board(screen, font_pieces, font_labels)
        pygame.display.flip()

    if engine_process and engine_process.poll() is None:
        engine_process.kill()
    pygame.quit()

if __name__ == "__main__":
    main()

Как должен выглядеть прием данных на стороне C (Си):

В вашей Си-программе функция main должна обрабатывать входящую строку следующим образом:
#include <stdio.h>
#include <string.h>

int main() {
    char board_string[65]; // 64 символа + '\0'
    char move_string[6];   // e.g. "e2e4" + '\0'
    
    setbuf(stdout, NULL); // Отключаем буферизацию

    while (1) {
        // Ожидаем строку формата: "rnbq...BNR e2e4"
        if (scanf("%s %s", board_string, move_string) == EOF) break;
        
        // 1. Загружаем board_string в массив вашего движка
        // 2. Проверяем, легален ли ход move_string
        
        int is_valid = check_move_rules(board_string, move_string); 
        
        if (!is_valid) {
            // Если ход не по правилам — пишем ошибку
            printf("ERROR\n");
        } else {
            // Если ход правильный:
            // 1. Применяем ход игрока на виртуальной доске движка
            // 2. Запускаем поиск лучшего ответного хода компьютера
            // 3. Применяем ответный ход компьютера
            
            char new_board_string[65];
            generate_new_board_string(new_board_string); // Генерируем 64 символа новой позиции
            
            // Отправляем новую доску обратно в Python
            printf("%s\n", new_board_string); 
        }
    }
    return 0;
}

Почему это идеальное разделение:

  1. Python — просто глупый экран: Он понятия не имеет, как ходит конь или пешка. Если игрок передвинет фигуру неверно, Си ответит ERROR, и Python мгновенно перерисует доску обратно в состояние old_board_str.
  2. Синхронизация рокировок и взятий: Если игрок делает рокировку (король ходит на 2 клетки), Си-движок видит этот ход, понимает его, делает рокировку ладьи у себя в памяти, делает свой ответный ход и присылает готовую итоговую строку из 64 символов. Python просто отобразит её «как есть».
Если ваш Си-движок принимает позицию в другом популярном формате (например, FEN-строка, где пустые клетки заменяются цифрами вроде rnbqkbnr/pppppppp/8/...), скажите мне, и я добавлю в Python-скрипт функцию генерации FEN-строк.

Вы абсолютно правы! Использовать fgets в Си для этой задачи гораздо надежнее и безопаснее, чем scanf, так как fgets считывает всю строку целиком до переноса \n и гарантированно защищает от переполнения буфера.
Поскольку Python отправляет доску и ход на одной строке через пробел (64_символа e2e4\n), мы можем считать её с помощью fgets, а затем аккуратно разбить на две части с помощью функции sscanf или strtok.
Вот полностью рабочий, безопасный и готовый к компиляции код на Си, использующий fgets:
#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE 256

int main() {
    char input_line[BUFFER_SIZE];
    char board_string[65]; // 64 символа + '\0'
    char move_string[10];  // Хватит для "e2e4", "e7e8q" + '\0'
    
    // ОТКЛЮЧАЕМ БУФЕРИЗАЦИЮ: без этого Python зависнет в ожидании ответа
    setvbuf(stdout, NULL, _IONBF, 0); 

    // Бесконечный цикл чтения строк из Python через stdin
    while (fgets(input_line, sizeof(input_line), stdin) != NULL) {
        
        // Убираем символ переноса строки \n в конце, если он есть
        input_line[strcspn(input_line, "\r\n")] = '\0';

        // Пропускаем пустые строки
        if (strlen(input_line) == 0) {
            continue;
        }

        // Парсим строку: читаем сначала 64 символа доски, затем строку хода
        // %64s считает ровно 64 символа, %9s считает оставшийся ход
        if (sscanf(input_line, "%64s %9s", board_string, move_string) == 2) {
            
            // --- ОТЛАДКА ---
            // Строки с префиксом '#' Python просто напечатает в консоль и проигнорирует
            printf("# Движок Си получил строку через fgets!\n");
            printf("# Доска: %s\n", board_string);
            printf("# Ход: %s\n", move_string);

            // --- ЗДЕСЬ ВАША ШАХМАТНАЯ ЛОГИКА ---
            // Для примера: делаем проверку (замените на свои правила шахмат)
            int move_is_legal = 1; 

            if (!move_is_legal) {
                // Если ход не по правилам — возвращаем ошибку
                printf("ERROR\n");
            } else {
                // Если ход правильный:
                // 1. Применяете move_string к board_string
                // 2. Считаете ответный ход вашего Си-бота
                // 3. Модифицируете board_string (формируете новую позицию)
                
                // Для теста просто отправляем доску обратно без изменений
                printf("%s\n", board_string);
            }
        } else {
            // Если пришло что-то некорректное
            printf("ERROR\n");
        }
    }

    return 0;
}

Почему этот вариант с fgets + sscanf идеален:

  1. Безопасность памяти: fgets(..., sizeof(input_line), stdin) никогда не прочитает больше 256 байт, что полностью исключает падение Си-программы из-за кривых данных.
  2. Четкие ограничения: Конструкция sscanf(..., "%64s %9s", ...) жестко контролирует, чтобы в массив доски записалось ровно 64 символа, а в ход — не более 9, защищая от переполнения внутренних массивов.
  3. Устойчивость к мусору: Если в поток случайно попадет пустая строка или пробелы, fgets корректно их переварит, а sscanf вернет результат отличный от 2, и программа не упадет, а просто отправит в Python ERROR.
Если вам потребуется помощь с тем, как в Си удобнее преобразовать эту 64-символьную строку в двумерный массив board[8][8] для вашего движка, дайте знать! На каком компиляторе (GCC, Clang, MSVC) вы собираете Си-код?

Формат ответа движка — это ровно 64 символа в одну строку (плюс \n в самом конце). Каждый символ в этой строке обозначает конкретную клетку шахматной доски, начиная с левого верхнего угла (клетка A8) и заканчивая правым нижним углом (клетка H1).

Карта соответствия строки и доски (8х8)

Строка длиной 64 символа мысленно нарезается движком и скриптом на 8 частей по 8 символов:
Индексы в строке:
 0..7   ->  [A8, B8, C8, D8, E8, F8, G8, H8]  (Черные фигуры)
 8..15  ->  [A7, B7, C7, D7, E7, F7, G7, H7]  (Черные пешки)
16..23  ->  [A6, B6, C6, D6, E6, F6, G6, H6]  (Пустое поле)
...
56..63  ->  [A1, B1, C1, D1, E1, F1, G1, H1]  (Белые фигуры)

Пример начальной позиции

Вот какую строку ваш Си-движок должен отправить в Python в самом начале игры (или вернуть, если он просто соглашается с ходом игрока):
rnbqkbnrpppppppp................................PPPPPPPPRNBQKBNR

Как Python-скрипт автоматически её отрисовывает

В рабочем Python-коде за это отвечает функция string_to_board(response). Вам не нужно ничего дописывать, скрипт делает это сам по следующему алгоритму:
  1. Функция берёт строку из 64 символов.
  2. С помощью формулы row * 8 + col она раскладывает её обратно в двумерную матрицу доски 8х8.
  3. В цикле отрисовки Pygame пробегается по этой матрице. Если видит точку ., то просто рисует пустую клетку (светлую или темную). Если видит букву — подставляет соответствующий код Юникода:
  • Заглавные буквы (P, R, N, B, Q, K) — отрисовываются белым цветом.
  • Строчные буквы (p, r, n, b, q, k) — отрисовываются черным цветом.

Пример: Игрок сходил e2e4, Си-движок ответил e7e5

  1. Игрок передвинул пешку. Python отправляет в Си начальную доску и ход:
    rnbqkbnrpppppppp................................PPPPPPPPRNBQKBNR e2e4
  2. Ваш Си-движок у себя в памяти передвигает белую пешку с E2 на E4, затем делает свой ответный ход (черная пешка с E7 на E5).
  3. Си-движок формирует новую строку из 64 символов, где пешки поменяли позиции, и отправляет её в printf:
    rnbqkbnrppp.pppp............p...................ReferencesPPPP.PPPRNBQKBNR\n
  4. Python мгновенно считывает эту строку, обновляет экран, и игроки видят на красивой графической доске сразу два сделанных хода.
Если вы хотите, чтобы при превращении пешки (дойдя до края доски) на экране рисовался Ферзь или другая фигура, Си-движку достаточно просто заменить символ P на Q (или p на q) на нужной позиции в этой 64-символьной строке. Python увидит новую букву и сам нарисует правильную фигуру.
Уточните, нужно ли показать пример кода на Си, который принимает индексы клетки (строку и столбец) и находит их правильное место в этой 64-символьной строке?