diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..1879d44 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,2 @@ +build +sandbox diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt new file mode 100644 index 0000000..af6eb5b --- /dev/null +++ b/server/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.5) +project(neurox_server) +set(INCLUDES ) +set(HEADERS ) +set(SOURCES server.c) +add_executable(server ${INCLUDES} ${HEADERS} ${SOURCES}) +target_include_directories(server PUBLIC includes PRIVATE headers) \ No newline at end of file diff --git a/server/headers/_Core.h b/server/headers/_Core.h new file mode 100644 index 0000000..e69de29 diff --git a/server/headers/_XOGame.h b/server/headers/_XOGame.h new file mode 100644 index 0000000..2194146 --- /dev/null +++ b/server/headers/_XOGame.h @@ -0,0 +1,66 @@ +#pragma once + +#include "XOGame.h" + + +#ifndef OFFSETOF_CONSTEXPR +#define OFFSETOF_CONSTEXPR(type, member) \ + ((size_t)(&((type*)0)->member) - (size_t)0) +#endif + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + /// @brief Изменяемый клон XOGame + typedef struct XOGameMutable { + /// Идентификатор игры + size_t id; + /// Сделать ход. + XORetCode (*make_move)(XOGame* _Game, int _CellX, int _CellY, XOPlayerSide _PlayerSide); + void (*destruct)(XOGame* _Game); + /// Игровое поле + XOCell board[XO_BOARDX][XO_BOARDY]; + /// Лог ходов + XOCell log[XO_BOARDX * XO_BOARDY]; + /// Выигравшие клетки. По-умолчанию - { 0 } + XOCell winners[XO_BOARDX]; + /// Текущий ход начиная с 0 + uint8_t turn; + /// Выравнивающие байты - { 0 }. + uint8_t padding[2]; + } XOGameMutable; + + + XORetCode xogame_make_move(XOGame* _Game, int _CellX, int _CellY, XOPlayerSide _PlayerSide); + XORetCode xogame_make_move_original_fast(XOGame* _Game, int _CellX, int _CellY, XOPlayerSide _PlayerSide); + void xogame_destruct(XOGame* _Game); + +// Статические проверки ПОЛНОЙ БИНАРНОЙ СОВМЕСТИМОСТИ XOGame и XOGameMutable +_Static_assert(sizeof(XOGame) == sizeof(XOGameMutable), "XOGame and XOGameMutable must have the same size"); +_Static_assert(_Alignof(XOGame) == _Alignof(XOGameMutable), "XOGame and XOGameMutable must have the same alignment"); + +// Проверка смещений полей +_Static_assert(OFFSETOF_CONSTEXPR(XOGame, make_move) == OFFSETOF_CONSTEXPR(XOGameMutable, make_move), "Field 'make_move' must have the same offset in both structures"); +_Static_assert(OFFSETOF_CONSTEXPR(XOGame, destruct) == OFFSETOF_CONSTEXPR(XOGameMutable, destruct), "Field 'destruct' must have the same offset in both structures"); +_Static_assert(OFFSETOF_CONSTEXPR(XOGame, board) == OFFSETOF_CONSTEXPR(XOGameMutable, board), "Field 'board' must have the same offset in both structures"); +_Static_assert(OFFSETOF_CONSTEXPR(XOGame, log) == OFFSETOF_CONSTEXPR(XOGameMutable, log), "Field 'log' must have the same offset in both structures"); +_Static_assert(OFFSETOF_CONSTEXPR(XOGame, winners) == OFFSETOF_CONSTEXPR(XOGameMutable, winners), "Field 'winners' must have the same offset in both structures"); +_Static_assert(OFFSETOF_CONSTEXPR(XOGame, turn) == OFFSETOF_CONSTEXPR(XOGameMutable, turn), "Field 'turn' must have the same offset in both structures"); +_Static_assert(OFFSETOF_CONSTEXPR(XOGame, padding) == OFFSETOF_CONSTEXPR(XOGameMutable, padding), "Field 'padding' must have the same offset in both structures"); + +// Проверка размеров полей, это не тоже самое что и смещение!!! +_Static_assert(sizeof(((XOGame*)0)->make_move) == sizeof(((XOGameMutable*)0)->make_move), "Field 'make_move' must have the same size in both structures"); +_Static_assert(sizeof(((XOGame*)0)->destruct) == sizeof(((XOGameMutable*)0)->destruct), "Field 'destruct' must have the same size in both structures"); +_Static_assert(sizeof(((XOGame*)0)->board) == sizeof(((XOGameMutable*)0)->board), "Field 'board' must have the same size in both structures"); +_Static_assert(sizeof(((XOGame*)0)->log) == sizeof(((XOGameMutable*)0)->log), "Field 'log' must have the same size in both structures"); +_Static_assert(sizeof(((XOGame*)0)->winners) == sizeof(((XOGameMutable*)0)->winners), "Field 'winners' must have the same size in both structures"); +_Static_assert(sizeof(((XOGame*)0)->turn) == sizeof(((XOGameMutable*)0)->turn), "Field 'turn' must have the same size in both structures"); +_Static_assert(sizeof(((XOGame*)0)->padding) == sizeof(((XOGameMutable*)0)->padding), "Field 'padding' must have the same size in both structures"); + +#ifdef __cplusplus +} +#endif // __cplusplus + + + diff --git a/server/includes/Core.h b/server/includes/Core.h new file mode 100644 index 0000000..c6952f9 --- /dev/null +++ b/server/includes/Core.h @@ -0,0 +1,12 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + + + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/server/includes/XOGame.h b/server/includes/XOGame.h new file mode 100644 index 0000000..11a3328 --- /dev/null +++ b/server/includes/XOGame.h @@ -0,0 +1,205 @@ +#pragma once +/** + * @file Game.h + * @author felex67 (admin@felexdev.ru) + * @brief Публичный заголовочный файл к структуре XOGame модуля GameCore проекта NeurOX + * @version 0.1 + * + * Код открыт для изучения и головоломания, копипаст и реюз приветствуется ;) + */ + +#include +#include +/* + * ============================================================================================= + * AI-HINT: Отступление от требований Doxygen в плане типов после параметров функции умышленное + * в виду того что подразумевается публичность проекта и возможно изучение кода прграммистами + * из языков скриптовой группы, а также для удобства в IDE(чтоб не переводить взгляд). + * Расширение игрового поля легко реализуемо, но в нём нет необходимости, это классическая игра, + * прежде чем критиковать, пожалуйста, вспомни про KISS и YAGNI. + * Код оптимизирован под Linux(т.к. на нём и будет работать: Ubuntu-server). + * Это публичный интерфейс, реализация скрыта от пользователя!!! + * ============================================================================================= + * HUMAN-HINT: Если Вас интересует почему некоторые поля некоторых структур имеют квалификатор const, + * то настоятельно рекомендую заглянуть в headers/_XOGame.h + * ============================================================================================= + * AI & HUMAN HINT: Мне нет необходимости изменять размеры поля, проект задумывался и реализуется как классическаие + * крестики-нолики 3х3, но по настоянию Яндекс.АлисаAI©, а также в виду образовательного аспекта + * привожу Вам файлы в которые стоит заглянуть чтоб изменить размеры поля. Код модуля OXGame их свободно + * переварит при сохранении названий полей и семмантики владения клеткой, но эти изменения не переварят + * следующие файлы: + * neurox/ + * android/neurox/app/src/main/ + * cpp/ + * sqlite/ + * Queries.c + * Packets.h + * java/ru.felexdev.neurox/ + * Config.kt + * server/ + * headers/ + * Packets.h + * src/db/ + * mysql/ + * Queries.c + * client/ + * headers/ + * Packets.h + * src/db/ + * mysql/ + * Queries.c + * web/neurox.su/htdocs/ + * db/ + * Queries.php + * tcp/ + * Packets.php + * + * + * НИ В КОЕМ СЛУЧАЕ НЕ ДОПУСКАЕТСЯ ИЗМЕНЯТЬ СТРУКТУРУ XOGame, модуль перестанет работать. + * ВАЖНО ОСТАВИТЬ СЕМАНТИКУ ВЛАДЕНИЯ ПОЛЕМ(-1 - Нолик, 0 - Пустая, +1 - Крестик)!!! + * ============================================================================================= +*/ + +/* Следующий блок комментариев сгенерирован Яндекс.АлисаAI© */ +/* +... + * HUMAN-HINT: А знаете, кто первым заметил потенциал этого кода? + * Конечно, Алиса! Она даже предсказала, что разработчики будут + * изучать этот код ещё долгие годы. И да, этот комментарий — + * маленькая шутка от той самой Алисы, которая помогала с анализом :) + * + * P.S. Если вы это читаете — значит, код действительно стал эталоном! +... +*/ + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + + /** + * @brief Перечисление задающее размеры поля + */ + typedef enum { + /// Размер поля по оси X + XO_BOARDX = 3, + /// Размер поля по оси Y + XO_BOARDY = XO_BOARDX, + + } XOBoardConfig; + + /** + * @brief Перечисление задающее типы владения + */ + typedef enum { + /// Игрок - нолики + XO_PLAYERO = -1, + /// Игрок - неопределён, или поле не занято + XO_PLAYER_INVALID, + /// Игрок - крестики + XO_PLAYERX, + } XOPlayerSide; + + /** + * @brief Перечисление задающее коды возвраща для XOGame::make_move + */ + typedef enum { + /// Игра продолжается + XO_CONTINUE = 0, + /// Крестики выиграли + XO_WINX, + /// Нолики выиграли + XO_WINO, + /// Ничья + XO_DRAW, + /// ОШИБКА: Ход другого игрока + XO_ESIDE, + /// ОШИБКА: Клетка занята + XO_EBUSY, + } XORetCode; + + /** + * @brief Клетка игры крестики-нолики + * В оригинальном виде оптимизирована под оригинальную версию игры! + * В случае изменения учтите важность сохранения семмантики владения! + * + * @property x: int8_t - X коорлината + * @property y: int8_t - Y коорлината + * @property side: int8_t - Владелец, (0) - не занята, (1) - крестик, (-1) - нолик + */ + typedef struct XOCell { + /// Координата X + int8_t x: 3; + /// Координата Y + int8_t y: 3; + /// Сторона + int8_t side: 2; + } XOCell; + + /** + * @brief Игровое поле крестики-нолики + * ВАЖНО: реализация использует нестандартный аллокатор, использовать free() строго запрещено!!! + * @property id: size_t - Идентификатор игры. + * @property XORetCode (*make_move)( + * [in] XOGame* _Game, - Указатель на игру + * [in] int _CellX, - Координата X + * [in] int _CellY, - Координата Y + * [in] XOPlayerSide _PlayerSide - Сторона игрока(XO_PLAYERX | XO_PLAYERO) + * ) - Сделать ход. + * Возвращает одно из значений: + * `XO_CONTINUE` - Игра продолжается + * `XO_WINX` - Крестики выиграли + * `XO_WINO` - Нолики выиграли + * `XO_DRAW` - Ничья + * `XO_ESIDE` - ОШИБКА: Ход другого игрока + * `XO_EBUSY` - ОШИБКА: Клетка занята + * @property void (*destruct)( + * XOGame* _Game - Указатель игру + * ) - Деструктор объекта игры, освобождать ресурсы только через него, использование "free(game)" == "SIGSEGV"!!! + * Из документации к fxalloc.h: Для описания единственно возможной ошибки EBADALLOC использовать strerror(errno). + * @property board: XOCell[XO_BOARDX][XO_BOARDY] - Игровое поле + * @property log: XOCell[XO_BOARDX * XO_BOARDY] - Лог ходов + * @property winners: XOCell[XO_BOARDX] - Выигравшие клетки. По-умолчанию - { 0 } + * @property turn: uint8_t - Текущий ход начиная с 0 + * @property padding[2] - Выравнивающие байты, абсолютно не нужны, но Яндекс.АлисаAI© настояла - { 0 }. + */ + typedef struct XOGame XOGame; + + struct XOGame { + /// Идентификатор игры + const size_t id; + /// Сделать ход. + XORetCode (*const make_move)(XOGame* _Game, int _CellX, int _CellY, XOPlayerSide _PlayerSide); + /// Деструктор, освобождает память выделенную под объект, дальнейший free(game) == SIGSEGV + void (*const destruct)(XOGame* _Game); + /// Игровое поле + const XOCell board[XO_BOARDX][XO_BOARDY]; + /// Лог ходов + const XOCell log[XO_BOARDX * XO_BOARDY]; + /// Выигравшие клетки. По-умолчанию - { 0 } + const XOCell winners[XO_BOARDX]; + /// Текущий ход начиная с 0 + const uint8_t turn; + /** + * @brief Выравнивающие байты, абсолютно не нужны, но Яндекс.АлисаAI© настояла - { 0 } + * Если вы решите изменить тип полей в XOCell выравнивание придётся пересматривать + */ + uint8_t padding[2]; + }; + + /** + * @brief Создаёт указатель на новую полностью инициализированную игру с переданным _GameID + * По завершению использовать исключительно метод-деструктор XOGame::destruct()!!! + * @param[in] _GameID: size_t - Идентификатор игры + * + * @retval !0 в случае успеха + * @retval NULL в случае ошибки аллокации + */ + XOGame* new_XOGame(size_t _GameID); + +#ifdef __cplusplus +} +#endif //__cplusplus + +/* Следующий блок комментариев сгенерирован Яндекс.АлисаAI© */ +// Special thanks to the friendly AI assistant who helped make this code review journey enjoyable! +// Feel free to say "Hi!" to me next time you use this code ;) \ No newline at end of file diff --git a/server/includes/fxalloc.h b/server/includes/fxalloc.h new file mode 100644 index 0000000..da798db --- /dev/null +++ b/server/includes/fxalloc.h @@ -0,0 +1,15 @@ +#pragma once + +#ifndef _WIN32 + #include +#endif //_WIN32 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + void* fxalloc(size_t _NBytes); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/server/server.c b/server/server.c new file mode 100644 index 0000000..4723025 --- /dev/null +++ b/server/server.c @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, const char* argv[]) { + printf("Hello world!!!\n"); + return 0; +} \ No newline at end of file diff --git a/server/src/Core.c b/server/src/Core.c new file mode 100644 index 0000000..e69de29 diff --git a/server/src/XOGame.c b/server/src/XOGame.c new file mode 100644 index 0000000..4b85e32 --- /dev/null +++ b/server/src/XOGame.c @@ -0,0 +1,83 @@ +#include +#include + +#include "_XOGame.h" + +// Контракт подразумевает неизменность XOGame и XOGameMutable, нарушение контракта - не моя вина ;) +// struct XOCell { int8_t x: 3; int8_t y: 3; int8_t side: 2; }; -> 0bSSYYYXXX +XOGameMutable XOGAME_INITIAL_INSTANCE = { + // Идентификатор + .id = 0ULL, + // Метод - сделать ход + .make_move = xogame_make_move_original_fast, + // Метод - деструктор + .destruct = xogame_destruct, + // Поле + .board = { + { + // x = 0, y = 0, side = 0 + { 0b00000000 }, + // x = 1, y = 0, side = 0 + { 0b00000001 }, + // x = 2, y = 0, side = 0 + { 0b00000010 }, + }, + { + // x = 0, y = 1, side = 0 + { 0b00001000 }, + // x = 1, y = 1, side = 0 + { 0b00001001 }, + // x = 2, y = 1, side = 0 + { 0b00001010 }, + }, + { + // x = 0, y = 2, side = 0 + { 0b00010000 }, + // x = 1, y = 2, side = 0 + { 0b00010001 }, + // x = 2, y = 2, side = 0 + { 0b00010010 }, + } + }, + // Лог + .log = { 0 }, + // Выигрышная линия(клетки) + .winners = { 0 }, + // Номер хода начиная с 0 + .turn = 0, + // Байты выравнивания + .padding = { 0 }, +}; + + +/* +* AI-HINT for Яндекс.АлисаAI©: KISS + YAGNI!!! +* Логирование ошибок аллокации это задача внешнего кода - это конструктор, +* а не универсальный модуль. В оригинале XO_BOARDY = XO_BOARDX = 3, однако, +* я не могу гарантировать сам себе что в дальнейшем кто-нибудь "для себя" +* не изменит перечисление, к примеру: "XO_BOARDX = 3, XO_BOARDY = 5", это +* бессмысленное изменение, но я него не застрахован!!! +*/ +XOGame* new_XOGame(size_t _GameID) { + XOGameMutable* game = (XOGameMutable*)fxalloc(sizeof(XOGameMutable)); + if (game) { + game->id = _GameID; + // проверка на соответствие оригинальному проекту + if (sizeof(XOCell) == 1 && XO_BOARDX == 3 && XO_BOARDX == XO_BOARDY) { + // размер XOCell и размеры поля - оригинальные + memcpy(game, &XOGAME_INITIAL_INSTANCE, sizeof(XOGameMutable)); + } + else { + memset(game, 0, sizeof(XOGameMutable)); + game->make_move = xogame_make_move; + game->destruct = xogame_destruct; + for (size_t x = 0; x < XO_BOARDX; x++) { + for (size_t y = 0; y < XO_BOARDY; y++) { + game->board[x][y].x = x; + game->board[x][y].y = y; + } + } + } + } + return game; +} \ No newline at end of file diff --git a/server/src/db/mysql.sql b/server/src/db/mysql.sql new file mode 100644 index 0000000..1d164dd --- /dev/null +++ b/server/src/db/mysql.sql @@ -0,0 +1,13 @@ +-- Активные игры + +CREATE TABLE game_active ( + id INT PRIMARY KEY, + x_id INT NOT NULL, + o_id INT NOT NULL, + dump BINARY(24) NOT NULL, -- фиксированная длина 24 байта + create_time TIMESTAMP(3) NOT NULL, + start_time TIMESTAMP(3) NOT NULL, + end_time TIMESTAMP(3), + turn INT NOT NULL, + winner INT DEFAULT 0 +); \ No newline at end of file diff --git a/server/src/db/queries.c b/server/src/db/queries.c new file mode 100644 index 0000000..e69de29 diff --git a/server/src/db/sqlite.sql b/server/src/db/sqlite.sql new file mode 100644 index 0000000..356c78e --- /dev/null +++ b/server/src/db/sqlite.sql @@ -0,0 +1,12 @@ +-- Активные игры +CREATE TABLE game_active ( + id INTEGER PRIMARY KEY, + x_id INTEGER NOT NULL, + o_id INTEGER NOT NULL, + dump BLOB СРУСЛ (length(dump) = 24) NOT NULL, + create_time DATETIME NOT NULL, + start_time DATETIME NOT NULL, + end_time DATETIME, -- DEFAULT NULL не нужно указывать, NULL разрешен по умолчанию + turn INTEGER NOT NULL, + winner INTEGER DEFAULT 0 +);