diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
deleted file mode 100644
index bef6fab..0000000
--- a/.devcontainer/devcontainer.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "name": "Tetris C Development",
- "image": "ubuntu:24.04",
- "features": {
- "ghcr.io/devcontainers/features/common-utils:2": {
- "installZsh": false
- }
- },
- "customizations": {
- "vscode": {
- "extensions": [
- "ms-vscode.cpptools",
- "ms-vscode.cmake-tools",
- "llvm-vs-code-extensions.vscode-clangd",
- "vadimcn.vscode-lldb",
- "ritwickdey.liveserver"
- ],
- "settings": {
- "C_Cpp.default.compilerPath": "/usr/bin/gcc",
- "terminal.integrated.defaultProfile.linux": "bash"
- }
- }
- },
- "postCreateCommand": "apt-get update && apt-get install -y gcc make libncurses-dev check lcov doxygen gdb valgrind clang-format git xdg-utils",
- "remoteUser": "root",
- "mounts": [
- "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached"
- ]
-}
diff --git a/.gitignore b/.gitignore
index 5716886..c6127b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,8 +42,6 @@
*.idb
*.pdb
-*.gc*
-
# Kernel Module Compile Results
*.mod*
*.cmd
@@ -52,18 +50,3 @@ modules.order
Module.symvers
Mkfile.old
dkms.conf
-src/project.md
-src/tetris_bin
-src/.gpskip
-.gpskip
-ginpee.toml
-src/ginpee.toml
-.vscode/launch.json
-src/tetris.log
-src/high_score.txt
-src/build/high_score.txt
-code-samples/frogger/project.md
-dvi/
-gcov_report/
-build/
-src/tetris.tar.gz
diff --git a/README_RUS.md b/README_RUS.md
index 0a9de77..e4f84dc 100644
--- a/README_RUS.md
+++ b/README_RUS.md
@@ -1,6 +1,81 @@
# BrickGame Тетрис
Резюме: в данном проекте тебе предстоит реализовать игру «Тетрис» на языке программирования С с использованием структурного подхода.
+💡 [Нажми сюда](https://new.oprosso.net/p/4cb31ec3f47a4596bc758ea1861fb624), **чтобы поделиться с нами обратной связью на этот проект**. Это анонимно и поможет нашей команде сделать обучение лучше. Рекомендуем заполнить опрос сразу после выполнения проекта.
+
+## Содержание
+
+- [BrickGame Тетрис](#brickgame-тетрис)
+ - [Содержание](#содержание)
+ - [Введение](#введение)
+ - [Chapter I ](#chapter-i-)
+ - [Общая информация](#общая-информация)
+ - [BrickGame](#brickgame)
+ - [История тетриса](#история-тетриса)
+ - [Конечные автоматы](#конечные-автоматы)
+ - [Фроггер](#фроггер)
+ - [Тетрис](#тетрис)
+ - [Chapter II ](#chapter-ii-)
+ - [Требования к проекту](#требования-к-проекту)
+ - [Часть 1. Основное задание](#часть-1-основное-задание)
+ - [Часть 2. Дополнительно. Подсчет очков и рекорд в игре](#часть-2-дополнительно-подсчет-очков-и-рекорд-в-игре)
+ - [Часть 3. Дополнительно. Механика уровней](#часть-3-дополнительно-механика-уровней)
+
+## Введение
+
+Для реализации игры «Тетрис» проект должен состоять из двух частей: библиотеки, реализующей логику работы игры, которую можно в будущем подключать к различным GUI, и терминального интерфейса. Логика работы библиотеки должна быть реализована с использованием конечных автоматов, одно из возможных описаний которого будет дано ниже.
+
+## Chapter I
+## Общая информация
+### BrickGame
+
+BrickGame — популярная портативная консоль 90-ых годов с несколькими ~~тысячами~~ встроенными играми, разработана она была в Китае. Изначально эта игра была копией, разработанной в СССР и выпущенной Nintendo в рамках платформы GameBoy игры «Тетрис», но включала в себя также и множество других игр, которые добавлялись с течением времени. Консоль имела небольшой экранчик с игровым полем размера 10 х 20, представляющим из себя матрицу «пикселей». Справа от поля находилось табло с цифровой индикацией состояния текущей игры, рекордами и прочей дополнительной информацией. Самыми распространенными играми на BrickGame были: тетрис, танки, гонки, фроггер и змейка.
+
+
+
+### История тетриса
+
+«Тетрис» был написан Алексеем Пажитновым 6 июня 1984 года на компьютере Электроника-60. Игра представляла собой головоломку, построенную на использовании геометрических фигур «тетрамино», состоящих из четырех квадратов. Первая коммерческая версия игры была выпущена в Америке в 1987 году. В последующие годы «Тетрис» был портирован на множество различных устройств, в том числе на мобильные телефоны, калькуляторы и карманные персональные компьютеры.
+
+Наибольшую популярность приобрела реализация «Тетриса» для игровой консоли Game Boy и видеоприставки NES. Но кроме нее существуют различные версии игры. Например, есть версия с трехмерными фигурами или дуэльная версия, в которой два игрока получают одинаковые фигуры и пытаются обойти друг друга по очкам.
+
+### Конечные автоматы
+
+Конечный автомат (КА) в теории алгоритмов — математическая абстракция, модель дискретного устройства, имеющего один вход, один выход и в каждый момент времени находящегося в одном состоянии из множества возможных.
+
+При работе КА на вход последовательно поступают входные воздействия, а на выходе КА формирует выходные сигналы. Переход из одного внутреннего состояния КА в другое может происходить не только от внешнего воздействия, но и самопроизвольно.
+
+КА можно использовать для описания алгоритмов, позволяющих решать те или иные задачи, а также для моделирования практически любого процесса. Несколько примеров:
+
+- Логика искусственного интеллекта для игр;
+- Синтаксический и лексический анализ;
+- Сложные прикладные сетевые протоколы;
+- Потоковая обработка данных.
+
+Ниже представлены примеры использования КА для формализации игровой логики нескольких игр из BrickGame.
+
+### Фроггер
+
+
+
+«Фроггер» — одна из поздних игр, выходящих на консолях Brickgame. Игра представляет собой игровое поле, по которому движутся бревна, и, перепрыгивая по ним, игроку необходимо перевести лягушку с одного берега на другой. Если игрок попадает в воду или лягушка уходит за пределы игрового поля, то лягушка погибает. Игра завершается, когда игрок доводит лягушку до другого берега или погибает последняя лягушка.
+
+Для формализации логики данной игры можно представить следующий вариант конечного автомата:
+
+
+
+Данный КА имеет следующие состояния:
+
+- Старт — состояние, в котором игра ждет, пока игрок нажмет кнопку готовности к игре.
+- Спавн — состояние, в котором создается очередная лягушка.
+- Перемещение — основное игровое состояние с обработкой ввода от пользователя: движение лягушки по полосе влево/право или прыжки вперед/назад.
+- Сдвиг — состояние, которое наступает после истечения таймера, при котором все объекты на полосах сдвигаются вправо вместе с лягушкой.
+- Столкновение — состояние, которое наступает, если после прыжка лягушка попадает в воду, или если после смещения бревен лягушка оказывается за пределами игрового поля.
+- Достигнут другой берег — состояние, которое наступает при достижении лягушкой другого берега.
+- Игра окончена — состояние, которое наступает после достижения другого берега или смерти последней лягушки.
+
+Пример реализации фроггера с использованием КА ты можешь найти в папке `code-samples`.
+
### Тетрис

@@ -85,50 +160,3 @@
### Часть 3. Дополнительно. Механика уровней
Добавь в игру механику уровней. Каждый раз, когда игрок набирает 600 очков, уровень увеличивается на 1. Повышение уровня увеличивает скорость движения фигур. Максимальное количество уровней — 10.
-
-# Tetris Game
-
-Classic Tetris implementation in C11 with ncurses interface.
-
-## Requirements
-
-- GCC or Clang
-- ncurses library
-- Check framework (for tests)
-
-## Building
-
-make # Build the game
-make run # Build and run
-make test # Run unit tests
-make install # Install to ~/.local/bin
-
-text
-
-## Controls
-
-- **Arrow Keys**: Move left/right, rotate (up)
-- **Down Arrow**: Instant drop
-- **Space/R**: Rotate figure
-- **P**: Pause
-- **S**: Restart game
-- **Q**: Quit
-
-## Features
-
-- 7 classic Tetris figures
-- Score tracking with persistent high score
-- 10 speed levels
-- Smooth time-based movement using POSIX `clock_gettime()`
-- Structured programming principles
-
-## Architecture
-
-- **FSM-based game logic** with states: Init, Spawn, Move, Moving, Attaching, GameOver
-- **Separated frontend/backend**: `brick_game/` (logic) and `gui/cli/` (display)
-- **Time-based delays** for precise falling speed
-
-## License
-
-School 21 educational project
-
diff --git a/flake.lock b/flake.lock
deleted file mode 100644
index 4d1227e..0000000
--- a/flake.lock
+++ /dev/null
@@ -1,61 +0,0 @@
-{
- "nodes": {
- "flake-utils": {
- "inputs": {
- "systems": "systems"
- },
- "locked": {
- "lastModified": 1731533236,
- "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
- "owner": "numtide",
- "repo": "flake-utils",
- "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
- "type": "github"
- },
- "original": {
- "owner": "numtide",
- "repo": "flake-utils",
- "type": "github"
- }
- },
- "nixpkgs": {
- "locked": {
- "lastModified": 1758427187,
- "narHash": "sha256-pHpxZ/IyCwoTQPtFIAG2QaxuSm8jWzrzBGjwQZIttJc=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "554be6495561ff07b6c724047bdd7e0716aa7b46",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "ref": "nixos-unstable",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "root": {
- "inputs": {
- "flake-utils": "flake-utils",
- "nixpkgs": "nixpkgs"
- }
- },
- "systems": {
- "locked": {
- "lastModified": 1681028828,
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
- "owner": "nix-systems",
- "repo": "default",
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
- "type": "github"
- },
- "original": {
- "owner": "nix-systems",
- "repo": "default",
- "type": "github"
- }
- }
- },
- "root": "root",
- "version": 7
-}
diff --git a/flake.nix b/flake.nix
deleted file mode 100644
index a030f24..0000000
--- a/flake.nix
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- description = "C Project with Check, Valgrind, Gcov support";
-
- inputs = {
- nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
- flake-utils.url = "github:numtide/flake-utils";
- };
-
- outputs = { self, nixpkgs, flake-utils }:
- flake-utils.lib.eachDefaultSystem (system: let
- pkgs = nixpkgs.legacyPackages.${system};
-
- buildInputs = with pkgs; [
- check # для unit-тестов
- valgrind # для проверки утечек
- lcov # для отчетов gcov
- clang-tools # clang-format
- gcc # компилятор
- gnumake # make
- ncurses # для TUI/CLI приложений
- readline # для readline поддержки
- zlib # сжатие данных
- libxml2 # XML parsing
- curl # HTTP клиент
- openssl # криптография
- sqlite # база данных
- ];
- in {
- devShells.default = pkgs.mkShell {
- inherit buildInputs;
-
- shellHook = ''
- export PATH="$HOME/.cargo/bin:$PATH"
- echo "✅ C Project Dev Environment Loaded"
- echo "🔧 Available tools:"
- echo " - gcc, make, clang-format"
- echo " - checkmk, valgrind, lcov"
- echo "🚀 Run 'make test' to build and run tests"
- '';
- };
- });
-}
diff --git a/src/.gpskip b/src/.gpskip
deleted file mode 100644
index 2c29003..0000000
--- a/src/.gpskip
+++ /dev/null
@@ -1,72 +0,0 @@
-.git/
-.vscode/
-.idea/
-target/
-build/
-dist/
-node_modules/
-*.log
-*.tmp
-*.swp
-*.bak
-.DS_Store
-Thumbs.db
-
-# Prerequisites
-*.d
-
-# Object files
-*.o
-*.ko
-*.obj
-*.elf
-
-# Linker output
-*.ilk
-*.map
-*.exp
-
-# Precompiled Headers
-*.gch
-*.pch
-
-# Libraries
-*.lib
-*.a
-*.la
-*.lo
-
-# Shared objects (inc. Windows DLLs)
-*.dll
-*.so
-*.so.*
-*.dylib
-
-# Executables
-*.exe
-*.out
-*.app
-*.i*86
-*.x86_64
-*.hex
-
-# Debug files
-*.dSYM/
-*.su
-*.idb
-*.pdb
-
-# Kernel Module Compile Results
-*.mod*
-*.cmd
-.tmp_versions/
-modules.order
-Module.symvers
-Mkfile.old
-dkms.conf
-src/project.md
-src/tetris_bin
-src/.gpskip
-.gpskip
-ginpee.toml
-
diff --git a/src/Doxyfile b/src/Doxyfile
deleted file mode 100644
index 72a050c..0000000
--- a/src/Doxyfile
+++ /dev/null
@@ -1,23 +0,0 @@
-# Doxyfile для генерации документации
-PROJECT_NAME = "Tetris Game C API"
-PROJECT_NUMBER = "1.0"
-PROJECT_BRIEF = "Classic Tetris implementation with separated backend/frontend"
-OUTPUT_DIRECTORY = dvi
-INPUT = brick_game/tetris/00_tetris.h doc.md
-RECURSIVE = NO
-GENERATE_HTML = YES
-GENERATE_LATEX = NO
-EXTRACT_ALL = YES
-EXTRACT_PRIVATE = NO
-EXTRACT_STATIC = NO
-FILE_PATTERNS = *.h *.md
-HTML_OUTPUT = html
-USE_MDFILE_AS_MAINPAGE = doc.md
-JAVADOC_AUTOBRIEF = YES
-OPTIMIZE_OUTPUT_FOR_C = YES
-TYPEDEF_HIDES_STRUCT = YES
-SHOW_INCLUDE_FILES = YES
-SHOW_NAMESPACES = NO
-QUIET = YES
-WARNINGS = YES
-WARN_IF_UNDOCUMENTED = YES
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644
index e4afbbd..0000000
--- a/src/Makefile
+++ /dev/null
@@ -1,129 +0,0 @@
-.PHONY: all clean install uninstall test test_coverage gcov_report dvi dist run style format
-
-CC ?= gcc
-CFLAGS ?= -Wall -Wextra -Werror -std=c11 -g -D_POSIX_C_SOURCE=199309L -MMD -MP
-CHECK_CFLAGS ?= -I/usr/include/check
-
-# Проверяем наличие библиотек
-CHECK_LIBS_AVAILABLE := $(shell ldconfig -p 2>/dev/null | grep -q libsubunit && echo yes || echo no)
-
-ifeq ($(CHECK_LIBS_AVAILABLE),yes)
- LDFLAGS ?= -lcheck -lrt -lpthread -lm -lncurses -lsubunit
-else
- LDFLAGS ?= -lcheck -lrt -lpthread -lm -lncurses
-endif
-
-BUILDDIR = build
-TETRISDIR = brick_game/tetris
-CLIDIR = gui/cli
-TESTDIR = test
-GCOV_DIR = gcov_report
-DVI_DIR = dvi
-
-# Файлы
-TETRIS_SRC = $(shell find $(TETRISDIR) -name "*.c")
-TETRIS_OBJ = $(TETRIS_SRC:.c=.o)
-CLI_SRC = $(shell find $(CLIDIR) -name "*.c")
-CLI_OBJ = $(CLI_SRC:.c=.o)
-TEST_SRC = $(filter-out $(TESTDIR)/test.c, $(shell find $(TESTDIR) -name "*.c"))
-
-LIB_TETRIS = $(BUILDDIR)/libtetris.a
-TARGET = $(BUILDDIR)/tetris_bin.out
-TEST_TARGET = $(BUILDDIR)/test.out
-
-# Установка
-PREFIX ?= $(HOME)/.local
-BINDIR = $(PREFIX)/bin
-
-all: gcov_report dvi
-
-run: clean $(TARGET)
- ./$(TARGET)
-
-install: clean $(TARGET)
- mkdir -p $(BINDIR)
- install -m 755 $(TARGET) $(BINDIR)/tetris
- @echo "installed $(BINDIR)/tetris"
-
-uninstall:
- rm -f $(BINDIR)/tetris
- @echo "uninstalled $(BINDIR)/tetris"
-
-clean:
- rm -rf $(CLI_OBJ) $(TETRIS_OBJ) $(TARGET) $(LIB_TETRIS) $(TEST_TARGET)
- rm -rf $(TETRISDIR)/*.d $(CLIDIR)/*.d
- rm -rf $(TETRISDIR)/*.gcda $(TETRISDIR)/*.gcno $(TETRISDIR)/*.gcov
- rm -rf $(GCOV_DIR) $(DVI_DIR)
-
-test: clean $(LIB_TETRIS)
- $(CC) $(CFLAGS) $(TEST_SRC) -L$(BUILDDIR) -ltetris $(LDFLAGS) -o $(TEST_TARGET)
- ./$(TEST_TARGET)
-
-test_coverage: CFLAGS += --coverage
-test_coverage: LDFLAGS += --coverage
-test_coverage: clean $(LIB_TETRIS)
- $(CC) $(CFLAGS) $(TEST_SRC) -L$(BUILDDIR) -ltetris $(LDFLAGS) -o $(TEST_TARGET)
- ./$(TEST_TARGET)
-
-gcov_report: test_coverage
- @mkdir -p $(GCOV_DIR)/obj
- @find $(TETRISDIR) -name "*.gcda" -exec mv {} $(GCOV_DIR)/obj/ \;
- @find $(TETRISDIR) -name "*.gcno" -exec mv {} $(GCOV_DIR)/obj/ \;
- @cd $(GCOV_DIR) && for src in $(addprefix ../,$(TETRIS_SRC)); do \
- gcov $$src -o obj/ 2>/dev/null; \
- done
- lcov --capture --directory $(GCOV_DIR)/obj \
- --output-file $(GCOV_DIR)/coverage.info \
- --ignore-errors unused
- lcov --extract $(GCOV_DIR)/coverage.info '*/brick_game/tetris/*' \
- -o $(GCOV_DIR)/coverage.info \
- --ignore-errors unused
- genhtml $(GCOV_DIR)/coverage.info \
- --output-directory $(GCOV_DIR)
- @echo "Report: $(GCOV_DIR)/index.html"
- xdg-open $(GCOV_DIR)/index.html
-
-
-dvi:
- @mkdir -p $(DVI_DIR)
- @echo "Generating documentation with Doxygen..."
- @if command -v doxygen >/dev/null 2>&1; then \
- doxygen Doxyfile && echo "HTML docs: $(DVI_DIR)/html/index.html"; \
- else \
- echo "Error: Doxygen not found. Install: nix-shell -p doxygen"; \
- echo "Copying doc.md as fallback..."; \
- cp doc.md $(DVI_DIR)/; \
- fi
- xdg-open $(DVI_DIR)/html/index.html
-
-dist: clean
- tar -czf tetris.tar.gz Makefile $(TETRISDIR) $(CLIDIR) $(TESTDIR) ../README.md doc.md
-
-style:
- @if [ -f ../materials/linters/.clang-format ]; then \
- clang-format -n $(TETRIS_SRC) $(CLI_SRC) $(TEST_SRC); \
- else \
- echo ".clang-format not found"; \
- fi
-
-format:
- @if [ -f ../materials/linters/.clang-format ]; then \
- clang-format -i $(TETRIS_SRC) $(CLI_SRC) $(TEST_SRC); \
- else \
- echo ".clang-format not found"; \
- fi
-
-$(LIB_TETRIS): $(TETRIS_OBJ)
- mkdir -p $(BUILDDIR)
- ar rcs $@ $^
-
-$(TARGET): $(LIB_TETRIS) $(CLI_OBJ)
- $(CC) $(CLI_OBJ) -L$(BUILDDIR) -ltetris -o $@ $(LDFLAGS)
-
-brick_game/tetris/%.o: brick_game/tetris/%.c
- $(CC) $(CFLAGS) -c $< -o $@
-
-gui/cli/%.o: gui/cli/%.c
- $(CC) $(CFLAGS) -c $< -o $@
-
--include $(TETRIS_OBJ:.o=.d) $(CLI_OBJ:.o=.d)
diff --git a/src/brick_game/tetris/00_tetris.h b/src/brick_game/tetris/00_tetris.h
deleted file mode 100644
index 3e0624a..0000000
--- a/src/brick_game/tetris/00_tetris.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- * @file 00_tetris.h
- * @brief Public API for Tetris game backend
- *
- * This header defines the public interface for Tetris game logic.
- * The backend is completely separated from UI, allowing integration
- * with any frontend (ncurses, SDL, Qt, Rust, etc.).
- *
- * @note Thread safety: NOT thread-safe! Use from single thread only.
- * @note Memory management: All memory is managed internally. Call
- * userInput(Terminate, false) before exit to free resources.
- */
-
-#ifndef TETRIS_H
-#define TETRIS_H
-
-#include
-#include
-
-/**
- * @brief Width of the game field in blocks
- */
-#define FIELD_WIDTH 10
-
-/**
- * @brief Height of the game field in blocks
- */
-#define FIELD_HEIGHT 20
-
-/**
- * @enum UserAction_t
- * @brief User input actions
- *
- * Represents all possible user actions in the game.
- */
-typedef enum {
- Start, /**< Initialize or restart the game */
- Pause, /**< Toggle pause state */
- Terminate, /**< Quit game and free all resources */
- Left, /**< Move figure left */
- Right, /**< Move figure right */
- Up, /**< Release instant drop flag (for next drop) */
- Down, /**< Instant drop figure to bottom */
- Action /**< Rotate figure clockwise */
-} UserAction_t;
-
-/**
- * @struct GameInfo_t
- * @brief Game state information for rendering
- *
- * Contains all information needed to render the game state.
- * This structure is returned by updateCurrentState() every frame.
- *
- * @note Do not free the pointers - memory is managed internally
- */
-typedef struct {
- /**
- * @brief 2D game field [FIELD_HEIGHT][FIELD_WIDTH]
- *
- * Values:
- * - 0: Empty cell
- * - 1: Active (falling) figure
- * - 2: Placed (fixed) blocks
- */
- int **field;
-
- /**
- * @brief Preview of next figure [4][4]
- *
- * 4x4 matrix containing the next figure shape.
- * Values: 0 (empty) or 1 (figure block)
- */
- int **next;
-
- int score; /**< Current score */
- int high_score; /**< Best score (persistent across sessions) */
- int level; /**< Current level (1-10) */
- int speed; /**< Current speed multiplier */
- int pause; /**< Pause state: 0=playing, 1=paused */
-} GameInfo_t;
-
-/**
- * @brief Process user input and update game state
- *
- * This function handles all user actions and updates the internal FSM state.
- * Should be called when user performs an action (key press, button click, etc.).
- *
- * @param action User action from UserAction_t enum
- * @param hold Reserved for future use (currently ignored)
- *
- * @note Actions are blocked during pause (except Pause, Terminate, Start)
- * @note Actions are blocked during Attaching state (except Pause, Terminate, Start)
- * @note Down action requires key release between uses to prevent double-drop
- *
- * @par Example:
- * @code
- * // Handle left arrow key press
- * userInput(Left, false);
- *
- * // Start new game
- * userInput(Start, false);
- *
- * // Quit and cleanup
- * userInput(Terminate, false);
- * @endcode
- *
- * @par FSI Integration (Rust example):
- * @code{.rs}
- * extern "C" {
- * fn userInput(action: UserAction_t, hold: bool);
- * }
- *
- * unsafe {
- * userInput(UserAction_t::Left, false);
- * }
- * @endcode
- */
-void userInput(UserAction_t action, bool hold);
-
-/**
- * @brief Update game state and return current game information
- *
- * This function must be called every game loop iteration (recommended ~10ms).
- * It updates the internal FSM, handles timing, figure movement, collisions,
- * and returns the current state for rendering.
- *
- * @return GameInfo_t structure with current game state
- *
- * @note Call frequency: ~100 times per second (every 10ms)
- * @note Timing is handled internally using POSIX clock_gettime(CLOCK_MONOTONIC)
- * @note The returned structure contains pointers to internal memory - do not free!
- *
- * @par Example:
- * @code
- * // Game loop
- * while (running) {
- * GameInfo_t state = updateCurrentState();
- *
- * // Render game field
- * for (int i = 0; i < FIELD_HEIGHT; i++) {
- * for (int j = 0; j < FIELD_WIDTH; j++) {
- * if (state.field[i][j] == 1) {
- * draw_active_block(j, i);
- * } else if (state.field[i][j] == 2) {
- * draw_placed_block(j, i);
- * }
- * }
- * }
- *
- * // Display score
- * printf("Score: %d Level: %d\n", state.score, state.level);
- *
- * sleep_ms(10);
- * }
- * @endcode
- *
- * @par Timing behavior:
- * - Base fall delay: 1100ms - (speed × 100ms)
- * - Instant drop: 30ms per step
- * - Attach delay: 350ms (prevents instant spawn)
- * - Pause correctly compensates all timings
- */
-GameInfo_t updateCurrentState();
-
-#endif /* TETRIS_H */
diff --git a/src/brick_game/tetris/01_automato.h b/src/brick_game/tetris/01_automato.h
deleted file mode 100644
index 7551fda..0000000
--- a/src/brick_game/tetris/01_automato.h
+++ /dev/null
@@ -1,126 +0,0 @@
-#ifndef AUTOMATO_H
-#define AUTOMATO_H
-
-#define _POSIX_C_SOURCE 199309L
-
-#include "00_tetris.h"
-#include
-#include
-#include
-
-#define ATTACH_DELAY_MS 350
-#define INSTANT_DROP_DELAY_MS 30
-#define BASE_FALL_DELAY_MS 1100
-#define SPEED_MULTIPLIER_MS 100
-#define MAX_LEVEL 10
-
-#define SCORE_PER_LEVEL 600
-#define POINTS_ONE_LINE 100
-#define POINTS_TWO_LINES 300
-#define POINTS_THREE_LINES 700
-#define POINTS_FOUR_LINES 1500
-
-
-typedef enum {
- Init,
- Spawn,
- Moving,
- Move,
- Attaching,
- GameOver
-} Automato_t;
-
-typedef enum {
- RightDown,
- LeftDown,
- Rotate,
- ToDown,
- DoNothing
-} Moving_t;
-
-typedef enum {
- I = 0,
- J,
- L,
- O,
- S,
- T,
- Z,
- FIGURE_COUNT
-} Sprite_t;
-
-typedef struct {
- int x, y;
- int mtrx[4][4];
- Sprite_t sprite;
- int rotation;
-} Figure_t;
-
-typedef struct {
- Figure_t curr;
- Figure_t next;
- Automato_t state;
- Moving_t moving_type;
- int field[FIELD_HEIGHT][FIELD_WIDTH];
- GameInfo_t* info;
- long long last_move_time;
- long long pause_start_time;
- long long attach_start_time;
- int attach_completed;
- int down_key_was_released;
-} GameState_t;
-
-GameState_t* get_game_state(void);
-
-void do_init(void);
-int load_high_score();
-void save_high_score(int score);
-void generate_next_figure(void);
-void terminate_and_free(void);
-
-long long get_current_time_ms(void);
-
-void do_spawn(void);
-
-void do_move(void);
-
-void do_moving(void);
-
-void do_attaching(void);
-int check_collision();
-void place_figure();
-void clear_lines();
-
-void do_gameover(void);
-int is_game_over();
-
-const int (*get_figure_shape(Sprite_t sprite, int rotation))[4];
-
-const int (*i_fig_up())[4];
-const int (*i_fig_right())[4];
-const int (*i_fig_down())[4];
-const int (*i_fig_left())[4];
-const int (*o_fig())[4];
-const int (*t_fig_up())[4];
-const int (*t_fig_right())[4];
-const int (*t_fig_down())[4];
-const int (*t_fig_left())[4];
-const int (*l_fig_up())[4];
-const int (*l_fig_right())[4];
-const int (*l_fig_down())[4];
-const int (*l_fig_left())[4];
-const int (*j_fig_up())[4];
-const int (*j_fig_right())[4];
-const int (*j_fig_down())[4];
-const int (*j_fig_left())[4];
-const int (*s_fig_up())[4];
-const int (*s_fig_right())[4];
-const int (*s_fig_down())[4];
-const int (*s_fig_left())[4];
-const int (*z_fig_up())[4];
-const int (*z_fig_right())[4];
-const int (*z_fig_down())[4];
-const int (*z_fig_left())[4];
-const int (*empty_fig())[4];
-
-#endif
diff --git a/src/brick_game/tetris/02_tetris.c b/src/brick_game/tetris/02_tetris.c
deleted file mode 100644
index e8210c4..0000000
--- a/src/brick_game/tetris/02_tetris.c
+++ /dev/null
@@ -1,136 +0,0 @@
-#include "01_automato.h"
-
-void userInput(UserAction_t action, bool hold) {
- (void)hold;
- GameState_t *state = get_game_state();
-
- int should_process = 1;
-
- if (state->info->pause) {
- if (action == Left || action == Right || action == Down || action == Up ||
- action == Action || action == Start) {
- should_process = 0;
- }
- }
-
- if (state->state == Attaching && !state->attach_completed) {
- if (action == Left || action == Right || action == Down || action == Up ||
- action == Action) {
- should_process = 0;
- }
- }
-
- if (should_process) {
- switch (action) {
- case Start:
- if (state->info->score >= state->info->high_score) {
- state->info->high_score = state->info->score;
- save_high_score(state->info->high_score);
- }
- state->info->high_score = load_high_score();
- state->state = Init;
- state->down_key_was_released = 1;
- break;
- case Terminate:
- if (state->info->score > state->info->high_score) {
- state->info->high_score = state->info->score;
- save_high_score(state->info->high_score);
- }
- terminate_and_free();
- state->state = GameOver;
- break;
- case Left:
- state->state = Moving;
- state->moving_type = LeftDown;
- break;
- case Right:
- state->state = Moving;
- state->moving_type = RightDown;
- break;
- case Action:
- state->state = Moving;
- state->moving_type = Rotate;
- break;
- case Down:
- if (state->down_key_was_released) {
- state->state = Moving;
- state->moving_type = ToDown;
- state->down_key_was_released = 0;
- }
- break;
- case Up:
- state->down_key_was_released = 1;
- break;
- case Pause:
- if (!state->info->pause) {
- state->pause_start_time = get_current_time_ms();
- } else {
- long long pause_duration =
- get_current_time_ms() - state->pause_start_time;
- state->last_move_time += pause_duration;
- // Корректируем attach_start_time если мы в Attaching
- state->attach_start_time +=
- (state->state == Attaching) * pause_duration;
- }
- state->info->pause = !state->info->pause;
- break;
- default:
- break;
- }
- }
-}
-
-GameInfo_t updateCurrentState() {
- GameState_t *state = get_game_state();
-
- int should_update = (!state->info->pause || state->state == GameOver);
- if (should_update) {
- switch (state->state) {
- case Init:
- do_init();
- break;
- case Spawn:
- do_spawn();
- break;
- case Move:
- do_move();
- break;
- case Moving:
- do_moving();
- break;
- case Attaching:
- do_attaching();
- break;
- case GameOver:
- do_gameover();
- break;
- }
- }
-
- for (int i = 0; i < FIELD_HEIGHT; i++) {
- for (int j = 0; j < FIELD_WIDTH; j++) {
- state->info->field[i][j] = state->field[i][j];
- }
- }
-
- Figure_t *fig = &state->curr;
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- if (fig->mtrx[i][j]) {
- int x = fig->x + j;
- int y = fig->y + i;
- if (y >= 0 && y < FIELD_HEIGHT && x >= 0 && x < FIELD_WIDTH) {
- state->info->field[y][x] = 1;
- }
- }
- }
- }
-
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- state->info->next[i][j] = state->next.mtrx[i][j];
- }
- }
-
- return *state->info;
-}
diff --git a/src/brick_game/tetris/03_automato.c b/src/brick_game/tetris/03_automato.c
deleted file mode 100644
index ce84744..0000000
--- a/src/brick_game/tetris/03_automato.c
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "01_automato.h"
-
-long long get_current_time_ms(void) {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return ts.tv_sec * 1000LL + ts.tv_nsec / 1000000LL;
-}
-
-int load_high_score() {
- FILE *file = fopen("build/high_score.txt", "r");
- int high_score = 0;
- if (file) {
- if (fscanf(file, "%d", &high_score) != 1) {
- high_score = 0;
- }
- fclose(file);
- }
- return high_score;
-}
-
-void save_high_score(int score) {
- FILE *file = fopen("build/high_score.txt", "w");
- if (file) {
- fprintf(file, "%d", score);
- fclose(file);
- }
-}
-
-GameState_t *get_game_state(void) {
- static GameState_t state = {0};
- static int initialized = 0;
-
- if (!initialized) {
- state.info = malloc(sizeof(GameInfo_t));
- state.info->field = malloc(FIELD_HEIGHT * sizeof(int *));
- for (int i = 0; i < FIELD_HEIGHT; i++) {
- state.info->field[i] = malloc(FIELD_WIDTH * sizeof(int));
- }
-
- state.info->next = malloc(4 * sizeof(int *));
- for (int i = 0; i < 4; i++) {
- state.info->next[i] = malloc(4 * sizeof(int));
- }
- state.info->speed = 1;
- state.info->score = 0;
- state.info->level = 1;
- state.info->pause = 0;
- state.last_move_time = get_current_time_ms();
- state.pause_start_time = 0;
- state.attach_start_time = 0;
- state.attach_completed = 0;
- state.down_key_was_released = 1;
- state.info->high_score = load_high_score();
-
- state.state = GameOver;
- initialized = 1;
- }
-
- return &state;
-}
-
-void terminate_and_free() {
- GameState_t *state = get_game_state();
-
- if (state->info) {
- if (state->info->field != NULL) {
- for (int i = 0; i < FIELD_HEIGHT; i++) {
- if (state->info->field[i] != NULL) {
- free(state->info->field[i]);
- state->info->field[i] = NULL;
- }
- }
- free(state->info->field);
- state->info->field = NULL;
- }
-
- if (state->info->next != NULL) {
- for (int i = 0; i < 4; i++) {
- if (state->info->next[i] != NULL) {
- free(state->info->next[i]);
- state->info->next[i] = NULL;
- }
- }
- free(state->info->next);
- state->info->next = NULL;
- }
-
- free(state->info);
- state->info = NULL;
- }
-}
diff --git a/src/brick_game/tetris/04_init.c b/src/brick_game/tetris/04_init.c
deleted file mode 100644
index e32e6b3..0000000
--- a/src/brick_game/tetris/04_init.c
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "01_automato.h"
-
-void clear_field(void) {
- GameState_t *state = get_game_state();
- for (int i = 0; i < FIELD_HEIGHT; ++i)
- for (int j = 0; j < FIELD_WIDTH; ++j)
- state->field[i][j] = 0;
-}
-
-void reset_game_stats(void) {
- GameState_t *state = get_game_state();
- state->info->score = 0;
- state->info->level = 1;
- state->info->speed = 1;
- state->last_move_time = get_current_time_ms();
-}
-
-void do_init(void) {
- clear_field();
- reset_game_stats();
- generate_next_figure();
- get_game_state()->state = Spawn;
-}
diff --git a/src/brick_game/tetris/05_spawn.c b/src/brick_game/tetris/05_spawn.c
deleted file mode 100644
index 51e5874..0000000
--- a/src/brick_game/tetris/05_spawn.c
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "01_automato.h"
-
-void set_current_figure_from_next(void) {
- GameState_t *state = get_game_state();
- state->curr = state->next;
- state->curr.x = FIELD_WIDTH / 2 - 2;
- state->curr.y = 0;
- state->moving_type = DoNothing;
-}
-
-void generate_next_figure(void) {
- GameState_t *state = get_game_state();
- state->next.sprite = rand() % FIGURE_COUNT;
- state->next.rotation = 0;
- const int(*shape)[4] = get_figure_shape(state->next.sprite, 0);
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < 4; ++j) {
- state->next.mtrx[i][j] = shape[i][j];
- }
- }
-}
-
-void do_spawn(void) {
- GameState_t *state = get_game_state();
-
- set_current_figure_from_next();
- generate_next_figure();
-
- int has_collision = check_collision();
-
- if (has_collision) {
- state->state = GameOver;
- } else {
- state->state = Move;
- state->down_key_was_released = 1;
- }
-}
diff --git a/src/brick_game/tetris/06_move.c b/src/brick_game/tetris/06_move.c
deleted file mode 100644
index 89d92cf..0000000
--- a/src/brick_game/tetris/06_move.c
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "01_automato.h"
-
-int get_milliseconds_to_wait(void) {
- GameState_t *state = get_game_state();
- int result = 0;
-
- if (state->moving_type == ToDown) {
- result = INSTANT_DROP_DELAY_MS;
- } else {
- int base_delay =
- BASE_FALL_DELAY_MS - (state->info->speed * SPEED_MULTIPLIER_MS);
- result =
- (base_delay > SPEED_MULTIPLIER_MS) ? base_delay : SPEED_MULTIPLIER_MS;
- }
-
- return result;
-}
-
-void do_move(void) {
- GameState_t *state = get_game_state();
-
- long long current_time = get_current_time_ms();
- int ms_to_wait = get_milliseconds_to_wait();
-
- int should_move = (current_time - state->last_move_time >= ms_to_wait);
-
- if (should_move) {
- state->last_move_time = current_time;
-
- state->curr.y++;
- int has_collision = check_collision();
-
- if (has_collision) {
- state->curr.y--;
- state->state = Attaching;
- }
- }
-}
diff --git a/src/brick_game/tetris/07_moving.c b/src/brick_game/tetris/07_moving.c
deleted file mode 100644
index 14c8fe3..0000000
--- a/src/brick_game/tetris/07_moving.c
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "01_automato.h"
-
-void handle_move_direction(Moving_t direction) {
- GameState_t *state = get_game_state();
- switch (direction) {
- case LeftDown:
- state->curr.x--;
- break;
- case RightDown:
- state->curr.x++;
- break;
- default:
- break;
- }
-}
-
-void handle_rotate(void) {
- GameState_t *state = get_game_state();
- state->curr.rotation = (state->curr.rotation + 1) % 4;
- const int(*shape)[4] =
- get_figure_shape(state->curr.sprite, state->curr.rotation);
- for (int i = 0; i < 4; ++i)
- for (int j = 0; j < 4; ++j)
- state->curr.mtrx[i][j] = shape[i][j];
-}
-
-void handle_horizontal_rotate_move(void) {
- GameState_t *state = get_game_state();
- Figure_t old = state->curr;
-
- switch (state->moving_type) {
- case LeftDown:
- case RightDown:
- handle_move_direction(state->moving_type);
- break;
- case Rotate:
- handle_rotate();
- break;
- default:
- break;
- }
-
- if (check_collision()) {
- state->curr = old;
- }
- state->state = Move;
-}
-
-void handle_to_down_move(void) {
- GameState_t *state = get_game_state();
- while (!check_collision()) {
- state->curr.y++;
- }
- state->curr.y--;
- state->state = Attaching;
-}
-
-void do_moving(void) {
- GameState_t *state = get_game_state();
-
- switch (state->moving_type) {
- case LeftDown:
- case RightDown:
- case Rotate:
- handle_horizontal_rotate_move();
- break;
- case ToDown:
- handle_to_down_move();
- break;
- case DoNothing:
- state->state = Move;
- break;
- }
-}
\ No newline at end of file
diff --git a/src/brick_game/tetris/08_attaching.c b/src/brick_game/tetris/08_attaching.c
deleted file mode 100644
index 6d443a2..0000000
--- a/src/brick_game/tetris/08_attaching.c
+++ /dev/null
@@ -1,124 +0,0 @@
-#include "01_automato.h"
-
-void do_attaching(void) {
- GameState_t *state = get_game_state();
- long long current_time = get_current_time_ms();
-
- if (!state->attach_completed) {
- if (state->attach_start_time == 0) {
- place_figure();
- clear_lines();
- state->attach_start_time = current_time;
- state->attach_completed = 0;
- }
-
- if (current_time - state->attach_start_time >= ATTACH_DELAY_MS) {
- state->attach_completed = 1;
- state->attach_start_time = 0;
-
- int game_over = is_game_over();
-
- if (game_over) {
- state->state = GameOver;
- } else {
- state->state = Spawn;
- }
-
- state->attach_completed = 0;
- }
- }
-}
-
-int check_collision() {
- GameState_t *state = get_game_state();
- Figure_t *fig = &state->curr;
- int collision_detected = 0;
-
- for (int i = 0; i < 4 && !collision_detected; ++i) {
- for (int j = 0; j < 4 && !collision_detected; ++j) {
- if (fig->mtrx[i][j]) {
- int x = fig->x + j;
- int y = fig->y + i;
-
- int out_of_bounds = (x < 0 || x >= FIELD_WIDTH || y >= FIELD_HEIGHT);
- int hits_placed_block = (y >= 0 && state->field[y][x]);
-
- if (out_of_bounds || hits_placed_block) {
- collision_detected = 1;
- }
- }
- }
- }
-
- return collision_detected;
-}
-
-void place_figure() {
- GameState_t *state = get_game_state();
- Figure_t *fig = &state->curr;
-
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < 4; ++j) {
- if (fig->mtrx[i][j]) {
- int x = fig->x + j;
- int y = fig->y + i;
- if (y >= 0 && y < FIELD_HEIGHT && x >= 0 && x < FIELD_WIDTH) {
- state->field[y][x] = 2;
- }
- }
- }
- }
-}
-
-void shift_lines_down(int from_row) {
- GameState_t *state = get_game_state();
- for (int y = from_row; y > 0; --y) {
- for (int x = 0; x < FIELD_WIDTH; ++x) {
- state->field[y][x] = state->field[y - 1][x];
- }
- }
- for (int x = 0; x < FIELD_WIDTH; ++x) {
- state->field[0][x] = 0;
- }
-}
-
-void clear_lines() {
- GameState_t *state = get_game_state();
- int lines_cleared = 0;
-
- for (int i = FIELD_HEIGHT - 1; i >= 0; --i) {
- int full = 1;
-
- for (int j = 0; j < FIELD_WIDTH; ++j) {
- if (state->field[i][j] != 2) {
- full = 0;
- }
- }
-
- if (full) {
- shift_lines_down(i);
- lines_cleared++;
- i++;
- }
- }
-
- if (lines_cleared > 0) {
- int points[] = {0, POINTS_ONE_LINE, POINTS_TWO_LINES, POINTS_THREE_LINES,
- POINTS_FOUR_LINES};
- state->info->score += points[lines_cleared];
-
- if (state->info->score > state->info->high_score) {
- state->info->high_score = state->info->score;
- }
-
- int new_level = (state->info->score / SCORE_PER_LEVEL) + 1;
- if (new_level > MAX_LEVEL) {
- new_level = MAX_LEVEL;
- }
-
- if (new_level > state->info->level) {
- state->info->level = new_level;
- state->info->speed = new_level + (new_level / 2);
- }
- }
-}
diff --git a/src/brick_game/tetris/09_gameover.c b/src/brick_game/tetris/09_gameover.c
deleted file mode 100644
index 6c38c32..0000000
--- a/src/brick_game/tetris/09_gameover.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "01_automato.h"
-
-void do_gameover(void) {
- GameState_t *state = get_game_state();
-
- if (state->info->score > state->info->high_score) {
- state->info->high_score = state->info->score;
- save_high_score(state->info->high_score);
- }
-
- const int(*shape)[4] = empty_fig();
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < 4; ++j) {
- state->next.mtrx[i][j] = shape[i][j];
- }
- }
-}
-
-int is_game_over() {
- GameState_t *state = get_game_state();
- int game_over = 0;
-
- for (int j = 0; j < FIELD_WIDTH; ++j) {
- if (state->field[0][j] || state->field[1][j]) {
- game_over = 1;
- }
- }
-
- return game_over;
-}
diff --git a/src/brick_game/tetris/figure_sprites.c b/src/brick_game/tetris/figure_sprites.c
deleted file mode 100644
index 6803c60..0000000
--- a/src/brick_game/tetris/figure_sprites.c
+++ /dev/null
@@ -1,272 +0,0 @@
-#include "01_automato.h"
-
-const int (*get_figure_shape(Sprite_t sprite, int rotation))[4] {
- const int(*result)[4] = NULL;
- switch (sprite) {
- case I:
- switch (rotation % 4) {
- case 0:
- result = i_fig_up();
- break;
- case 1:
- result = i_fig_right();
- break;
- case 2:
- result = i_fig_down();
- break;
- case 3:
- result = i_fig_left();
- break;
- }
- break;
- case O:
- result = o_fig();
- break;
- case T:
- switch (rotation % 4) {
- case 0:
- result = t_fig_up();
- break;
- case 1:
- result = t_fig_right();
- break;
- case 2:
- result = t_fig_down();
- break;
- case 3:
- result = t_fig_left();
- break;
- }
- break;
- case L:
- switch (rotation % 4) {
- case 0:
- result = l_fig_up();
- break;
- case 1:
- result = l_fig_right();
- break;
- case 2:
- result = l_fig_down();
- break;
- case 3:
- result = l_fig_left();
- break;
- }
- break;
- case J:
- switch (rotation % 4) {
- case 0:
- result = j_fig_up();
- break;
- case 1:
- result = j_fig_right();
- break;
- case 2:
- result = j_fig_down();
- break;
- case 3:
- result = j_fig_left();
- break;
- }
- break;
- case S:
- switch (rotation % 4) {
- case 0:
- result = s_fig_up();
- break;
- case 1:
- result = s_fig_right();
- break;
- case 2:
- result = s_fig_down();
- break;
- case 3:
- result = s_fig_left();
- break;
- }
- break;
- case Z:
- switch (rotation % 4) {
- case 0:
- result = z_fig_up();
- break;
- case 1:
- result = z_fig_right();
- break;
- case 2:
- result = z_fig_down();
- break;
- case 3:
- result = z_fig_left();
- break;
- }
- break;
- default:
- result = NULL;
- }
- return result;
-}
-
-// I
-const int (*i_fig_up())[4] {
- static const int shape[4][4] = {
- {0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*i_fig_right())[4] {
- static const int shape[4][4] = {
- {0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*i_fig_down())[4] {
- static const int shape[4][4] = {
- {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*i_fig_left())[4] {
- static const int shape[4][4] = {
- {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-// O
-const int (*o_fig())[4] {
- static const int shape[4][4] = {
- {0, 1, 1, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-// T
-const int (*t_fig_up())[4] {
- static const int shape[4][4] = {
- {0, 1, 0, 0}, {1, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*t_fig_right())[4] {
- static const int shape[4][4] = {
- {0, 1, 0, 0}, {0, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*t_fig_down())[4] {
- static const int shape[4][4] = {
- {0, 0, 0, 0}, {1, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*t_fig_left())[4] {
- static const int shape[4][4] = {
- {0, 1, 0, 0}, {1, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-// L
-const int (*l_fig_up())[4] {
- static const int shape[4][4] = {
- {0, 0, 1, 0}, {1, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*l_fig_right())[4] {
- static const int shape[4][4] = {
- {1, 0, 0, 0}, {1, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*l_fig_down())[4] {
- static const int shape[4][4] = {
- {0, 0, 0, 0}, {1, 1, 1, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*l_fig_left())[4] {
- static const int shape[4][4] = {
- {1, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-// J
-const int (*j_fig_up())[4] {
- static const int shape[4][4] = {
- {1, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*j_fig_right())[4] {
- static const int shape[4][4] = {
- {0, 1, 1, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*j_fig_down())[4] {
- static const int shape[4][4] = {
- {0, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*j_fig_left())[4] {
- static const int shape[4][4] = {
- {0, 1, 0, 0}, {0, 1, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-// S
-const int (*s_fig_up())[4] {
- static const int shape[4][4] = {
- {0, 1, 1, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*s_fig_right())[4] {
- static const int shape[4][4] = {
- {0, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*s_fig_down())[4] {
- static const int shape[4][4] = {
- {0, 1, 1, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*s_fig_left())[4] {
- static const int shape[4][4] = {
- {0, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-// Z
-const int (*z_fig_up())[4] {
- static const int shape[4][4] = {
- {1, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*z_fig_right())[4] {
- static const int shape[4][4] = {
- {0, 0, 1, 0}, {0, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*z_fig_down())[4] {
- static const int shape[4][4] = {
- {1, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*z_fig_left())[4] {
- static const int shape[4][4] = {
- {0, 0, 1, 0}, {0, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
-
-const int (*empty_fig())[4] {
- static const int shape[4][4] = {
- {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
- return (const int(*)[4])shape;
-}
\ No newline at end of file
diff --git a/src/diagram.pdf b/src/diagram.pdf
deleted file mode 100644
index fc85637..0000000
Binary files a/src/diagram.pdf and /dev/null differ
diff --git a/src/diagram_also.pdf b/src/diagram_also.pdf
deleted file mode 100644
index 2d8ae01..0000000
Binary files a/src/diagram_also.pdf and /dev/null differ
diff --git a/src/doc.md b/src/doc.md
deleted file mode 100644
index 847cd44..0000000
--- a/src/doc.md
+++ /dev/null
@@ -1,218 +0,0 @@
-# Tetris Game API Documentation
-
-## Overview
-
-This is a Tetris game implementation following FSM (Finite State Machine) pattern.
-The backend is completely separated from frontend, allowing easy integration with any UI.
-
-## Public API
-
-### `void userInput(UserAction_t action, bool hold)`
-
-Processes user input and updates game state accordingly.
-
-**Parameters:**
-
-- `action`: User action from enum `UserAction_t`
- - `Start`: Initialize/restart game
- - `Pause`: Toggle pause state
- - `Terminate`: Quit game and free resources
- - `Left`: Move figure left
- - `Right`: Move figure right
- - `Up`: Release instant drop flag (for next drop)
- - `Down`: Instant drop figure
- - `Action`: Rotate figure
-- `hold`: Reserved for future use (currently ignored)
-
-**Behavior:**
-
-- Blocked during pause (except Pause/Terminate/Start)
-- Blocked during Attaching state (except Pause/Terminate/Start)
-- Down action requires key release between uses (prevents double-drop)
-
-**Example (Rust FFI):**
-
-```rust
-extern "C" {
- fn userInput(action: UserAction_t, hold: bool);
-}
-
-// In your Rust code:
-unsafe {
- userInput(UserAction_t::Left, false);
-}
-```
-
----
-
-### `GameInfo_t updateCurrentState()`
-
-Updates game state machine and returns current game information for rendering.
-
-**Returns:**
-
-- `GameInfo_t` structure containing:
- - `field`: 2D array `[FIELD_HEIGHT][FIELD_WIDTH]` representing game field
- - `0`: Empty cell
- - `1`: Active (falling) figure
- - `2`: Placed (fixed) blocks
- - `next`: 2D array `[4][4]` with next figure preview
- - `score`: Current score
- - `high_score`: Best score (persistent)
- - `level`: Current level (1-10)
- - `speed`: Current speed multiplier
- - `pause`: Pause state (0=playing, 1=paused)
-
-**Call frequency:**
-Should be called every game loop iteration (~10ms recommended).
-The function handles timing internally using `clock_gettime(CLOCK_MONOTONIC)`.
-
-**Example (Rust FFI):**
-
-```rust
-#[repr(C)]
-struct GameInfo_t {
- field: *mut *mut c_int,
- next: *mut *mut c_int,
- score: c_int,
- high_score: c_int,
- level: c_int,
- speed: c_int,
- pause: c_int,
-}
-
-extern "C" {
- fn updateCurrentState() -> GameInfo_t;
-}
-
-// In your game loop:
-loop {
- let state = unsafe { updateCurrentState() };
- render_game(state);
- std::thread::sleep(Duration::from_millis(10));
-}
-```
-
----
-
-## Game Constants
-
-```c
-#define FIELD_WIDTH 10
-#define FIELD_HEIGHT 20
-```
-
-## FSM States (Internal)
-
-- **Init**: Initialize game field and stats
-- **Spawn**: Create new figure at top
-- **Move**: Automatic figure falling
-- **Moving**: User-controlled movement/rotation
-- **Attaching**: Figure placement with delay
-- **GameOver**: Game ended, waiting for restart
-
-## Timing
-
-- Base fall delay: 1100ms - (speed × 100ms)
-- Instant drop delay: 30ms per step
-- Attach delay: 350ms (prevents double-spawn)
-- Pause compensates all timings correctly
-
-## Score System
-
-- 1 line: 100 points
-- 2 lines: 300 points
-- 3 lines: 700 points
-- 4 lines: 1500 points
-- Level up: every 600 points
-- Speed increases with level (max level 10)
-
-## Memory Management
-
-All memory is managed internally. Call `userInput(Terminate, false)` before exit
-to properly free resources.
-
-## Thread Safety
-
-**Not thread-safe!** Use from single thread only or add external synchronization.
-
----
-
-## Integration Example (Full Rust Frontend)
-
-```rust
-use std::ffi::c_int;
-use std::time::Duration;
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-enum UserAction_t {
- Start = 0,
- Pause,
- Terminate,
- Left,
- Right,
- Up,
- Down,
- Action,
-}
-
-#[repr(C)]
-struct GameInfo_t {
- field: *mut *mut c_int,
- next: *mut *mut c_int,
- score: c_int,
- high_score: c_int,
- level: c_int,
- speed: c_int,
- pause: c_int,
-}
-
-#[link(name = "tetris", kind = "static")]
-extern "C" {
- fn userInput(action: UserAction_t, hold: bool);
- fn updateCurrentState() -> GameInfo_t;
-}
-
-fn main() {
- // Initialize game
- unsafe { userInput(UserAction_t::Start, false); }
-
- // Game loop
- loop {
- // Get input (your input handling)
- let action = get_user_input();
- if let Some(a) = action {
- unsafe { userInput(a, false); }
- }
-
- // Update and render
- let state = unsafe { updateCurrentState() };
- render_with_your_ui(state);
-
- std::thread::sleep(Duration::from_millis(10));
- }
-}
-```
-
----
-
-## Building for FFI
-
-```sh
-# Build static library
-make
-
-# Use in your project
-gcc your_frontend.c -L./build -ltetris -lncurses -lm -lrt -lpthread -o game
-# or with Rust
-cargo build --release
-```
-
-## Notes for Frontend Developers
-
-1. **Field coordinates**: [y][x] where y=0 is top, x=0 is left
-2. **Next preview**: Always 4×4 matrix, figure may not fill entire space
-3. **High score**: Automatically saved to `build/high_score.txt`
-4. **No need to free** GameInfo_t - it's internal storage
-5. **Call sequence**: Start → (updateCurrentState + userInput)* → Terminate
diff --git a/src/gui/cli/display.c b/src/gui/cli/display.c
deleted file mode 100644
index 8b1bbf1..0000000
--- a/src/gui/cli/display.c
+++ /dev/null
@@ -1,41 +0,0 @@
-// src/gui/cli/display.c
-#include "../../brick_game/tetris/00_tetris.h"
-#include
-
-void display_game(GameInfo_t game_state) {
- clear();
-
- for (int i = 0; i < FIELD_HEIGHT; ++i) {
- for (int j = 0; j < FIELD_WIDTH; ++j) {
- if (game_state.field[i][j] == 2) {
- mvaddch(i + 1, j * 2 + 1, '#');
- } else if (game_state.field[i][j] == 1) {
- mvaddch(i + 1, j * 2 + 1, '$');
- } else {
- mvaddch(i + 1, j * 2 + 1, '.');
- }
- }
- }
-
- mvaddstr(1, FIELD_WIDTH * 2 + 5, "Next figure:");
-
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < 4; ++j) {
- if (game_state.next[i][j]) {
- mvaddch(i + 3, (FIELD_WIDTH * 2 + 5) + j * 2, '@');
- } else {
- mvaddch(i + 3, (FIELD_WIDTH * 2 + 5) + j * 2, ' ');
- }
- }
- }
-
- mvprintw(FIELD_HEIGHT + 2, 1, "Score: %d", game_state.score);
- mvprintw(FIELD_HEIGHT + 3, 1, "High Score: %d", game_state.high_score);
- mvprintw(FIELD_HEIGHT + 4, 1, "Level: %d", game_state.level);
- mvprintw(FIELD_HEIGHT + 5, 1, "Speed: %d", game_state.speed);
-
- if (game_state.pause) {
- mvprintw(FIELD_HEIGHT / 2, FIELD_WIDTH * 2 + 1, "PAUSED");
- }
- refresh();
-}
\ No newline at end of file
diff --git a/src/gui/cli/main.c b/src/gui/cli/main.c
deleted file mode 100644
index ca9ea44..0000000
--- a/src/gui/cli/main.c
+++ /dev/null
@@ -1,81 +0,0 @@
-#include "../../brick_game/tetris/00_tetris.h"
-#include
-#include
-#include
-#include
-
-void display_game(GameInfo_t game_state);
-
-int main() {
- srand(time(NULL));
-
- initscr();
- cbreak();
- noecho();
- keypad(stdscr, TRUE);
- nodelay(stdscr, FALSE);
- curs_set(0);
-
- mvprintw(FIELD_HEIGHT / 2, FIELD_WIDTH - 4, "Press F to Start");
- refresh();
-
- int ch = 0;
- int started = 0;
- while (!started) {
- ch = getch();
- if (ch == 'f' || ch == 'F') {
- userInput(Start, false);
- started = 1;
- }
- }
-
- nodelay(stdscr, TRUE);
- timeout(10);
-
- UserAction_t current_action = {0};
- bool action_valid = false;
- bool running = true;
-
- while (running) {
- ch = getch();
- action_valid = false;
-
- if (ch == 'q') {
- userInput(Terminate, false);
- running = false;
- } else if (ch == 'r' || ch == ' ') {
- current_action = Action;
- action_valid = true;
- } else if (ch == KEY_LEFT) {
- current_action = Left;
- action_valid = true;
- } else if (ch == KEY_RIGHT) {
- current_action = Right;
- action_valid = true;
- } else if (ch == KEY_DOWN) {
- current_action = Down;
- action_valid = true;
- } else if (ch == KEY_UP) {
- current_action = Up;
- action_valid = true;
- } else if (ch == 's' || ch == 'S') {
- current_action = Start;
- action_valid = true;
- } else if (ch == 'p' || ch == 'P') {
- current_action = Pause;
- action_valid = true;
- }
-
- if (action_valid) {
- userInput(current_action, false);
- }
-
- if (running) {
- GameInfo_t game_state = updateCurrentState();
- display_game(game_state);
- }
- }
-
- endwin();
- return 0;
-}
diff --git a/src/test/test.c b/src/test/test.c
deleted file mode 100644
index 74a7640..0000000
--- a/src/test/test.c
+++ /dev/null
@@ -1,86 +0,0 @@
-#include
-#include "../brick_game/tetris/01_automato.h"
-
-START_TEST(test_collision_bottom_boundary) {
- GameState_t* state = get_game_state();
- state->curr.y = FIELD_HEIGHT - 1;
- state->curr.x = 5;
- state->curr.mtrx[0][0] = 1;
- state->curr.y++;
- ck_assert_int_eq(check_collision(), 1);
-}
-END_TEST
-
-START_TEST(test_collision_left_boundary) {
- GameState_t* state = get_game_state();
- state->curr.x = -1;
- state->curr.mtrx[0][0] = 1;
- ck_assert_int_eq(check_collision(), 1);
-}
-END_TEST
-
-START_TEST(test_collision_with_placed_block) {
- GameState_t* state = get_game_state();
- state->field[10][5] = 2;
- state->curr.y = 10;
- state->curr.x = 5;
- state->curr.mtrx[0][0] = 1;
- ck_assert_int_eq(check_collision(), 1);
-}
-END_TEST
-
-START_TEST(test_no_collision) {
- GameState_t* state = get_game_state();
- state->curr.y = 5;
- state->curr.x = 5;
- state->curr.mtrx[0][0] = 1;
- ck_assert_int_eq(check_collision(), 0);
-}
-END_TEST
-
-START_TEST(test_game_over_detection) {
- GameState_t* state = get_game_state();
- state->field[0][5] = 2;
- ck_assert_int_eq(is_game_over(), 1);
-}
-END_TEST
-
-START_TEST(test_clear_single_line) {
- GameState_t* state = get_game_state();
- state->info->score = 0;
-
- // Заполняем нижнюю линию
- for (int j = 0; j < FIELD_WIDTH; j++) {
- state->field[FIELD_HEIGHT - 1][j] = 2;
- }
-
- clear_lines();
- ck_assert_int_eq(state->info->score, 100);
-}
-END_TEST
-
-Suite* tetris_suite(void) {
- Suite* s = suite_create("Tetris");
- TCase* tc_core = tcase_create("Core");
-
- tcase_add_test(tc_core, test_collision_bottom_boundary);
- tcase_add_test(tc_core, test_collision_left_boundary);
- tcase_add_test(tc_core, test_collision_with_placed_block);
- tcase_add_test(tc_core, test_no_collision);
- tcase_add_test(tc_core, test_game_over_detection);
- tcase_add_test(tc_core, test_clear_single_line);
-
- suite_add_tcase(s, tc_core);
- return s;
-}
-
-int main(void) {
- Suite* s = tetris_suite();
- SRunner* sr = srunner_create(s);
-
- srunner_run_all(sr, CK_NORMAL);
- int number_failed = srunner_ntests_failed(sr);
- srunner_free(sr);
-
- return (number_failed == 0) ? 0 : 1;
-}
diff --git a/src/test/test_collision.c b/src/test/test_collision.c
deleted file mode 100644
index 657639b..0000000
--- a/src/test/test_collision.c
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "test_helper.h"
-
-START_TEST(test_collision_bottom) {
- test_setup();
- GameState_t *state = get_game_state();
-
- state->curr.y = FIELD_HEIGHT;
- state->curr.x = 5;
- state->curr.mtrx[0][0] = 1;
-
- ck_assert_int_eq(check_collision(), 1);
-}
-END_TEST
-
-START_TEST(test_collision_left) {
- test_setup();
- GameState_t *state = get_game_state();
-
- state->curr.x = -1;
- state->curr.y = 5;
- state->curr.mtrx[0][0] = 1;
-
- ck_assert_int_eq(check_collision(), 1);
-}
-END_TEST
-
-START_TEST(test_collision_right) {
- test_setup();
- GameState_t *state = get_game_state();
-
- state->curr.x = FIELD_WIDTH;
- state->curr.y = 5;
- state->curr.mtrx[0][0] = 1;
-
- ck_assert_int_eq(check_collision(), 1);
-}
-END_TEST
-
-START_TEST(test_collision_placed_block) {
- test_setup();
- GameState_t *state = get_game_state();
-
- state->field[10][5] = 2;
- state->curr.y = 10;
- state->curr.x = 5;
- state->curr.mtrx[0][0] = 1;
-
- ck_assert_int_eq(check_collision(), 1);
-}
-END_TEST
-
-START_TEST(test_no_collision) {
- test_setup();
- GameState_t *state = get_game_state();
-
- state->curr.y = 5;
- state->curr.x = 5;
- state->curr.mtrx[0][0] = 1;
-
- ck_assert_int_eq(check_collision(), 0);
-}
-END_TEST
-
-Suite *collision_suite(void) {
- Suite *s = suite_create("Collision");
- TCase *tc = tcase_create("Core");
-
- tcase_add_test(tc, test_collision_bottom);
- tcase_add_test(tc, test_collision_left);
- tcase_add_test(tc, test_collision_right);
- tcase_add_test(tc, test_collision_placed_block);
- tcase_add_test(tc, test_no_collision);
-
- suite_add_tcase(s, tc);
- return s;
-}
diff --git a/src/test/test_figures.c b/src/test/test_figures.c
deleted file mode 100644
index c5fab70..0000000
--- a/src/test/test_figures.c
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "test_helper.h"
-
-START_TEST(test_generate_figure) {
- generate_next_figure();
- GameState_t *state = get_game_state();
-
- ck_assert(state->next.sprite >= I && state->next.sprite < FIGURE_COUNT);
- ck_assert_int_eq(state->next.rotation, 0);
-}
-END_TEST
-
-START_TEST(test_all_shapes_exist) {
- for (int sprite = I; sprite < FIGURE_COUNT; sprite++) {
- for (int rotation = 0; rotation < 4; rotation++) {
- const int(*shape)[4] = get_figure_shape(sprite, rotation);
- ck_assert_ptr_nonnull(shape);
- }
- }
-}
-END_TEST
-
-START_TEST(test_i_figure_horizontal) {
- const int(*shape)[4] = get_figure_shape(I, 0);
-
- int count = 0;
- for (int j = 0; j < 4; j++) {
- if (shape[1][j])
- count++;
- }
- ck_assert_int_eq(count, 4);
-}
-END_TEST
-
-START_TEST(test_o_figure_unchanged) {
- const int(*s1)[4] = get_figure_shape(O, 0);
- const int(*s2)[4] = get_figure_shape(O, 2);
-
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- ck_assert_int_eq(s1[i][j], s2[i][j]);
- }
- }
-}
-END_TEST
-
-Suite *figures_suite(void) {
- Suite *s = suite_create("Figures");
- TCase *tc = tcase_create("Core");
-
- tcase_add_test(tc, test_generate_figure);
- tcase_add_test(tc, test_all_shapes_exist);
- tcase_add_test(tc, test_i_figure_horizontal);
- tcase_add_test(tc, test_o_figure_unchanged);
-
- suite_add_tcase(s, tc);
- return s;
-}
diff --git a/src/test/test_fsm.c b/src/test/test_fsm.c
deleted file mode 100644
index 4ceb5f7..0000000
--- a/src/test/test_fsm.c
+++ /dev/null
@@ -1,354 +0,0 @@
-#include "test_helper.h"
-#include
-#include
-
-// ============================================================================
-// Тесты FSM и интеграции
-// ============================================================================
-
-START_TEST(test_game_start) {
- userInput(Start, false);
- GameInfo_t state = updateCurrentState();
-
- // После Start игра должна инициализироваться
- ck_assert_int_eq(state.level, 1);
- ck_assert_int_eq(state.score, 0);
- ck_assert_int_eq(state.pause, 0);
-}
-END_TEST
-
-START_TEST(test_pause_toggle) {
- userInput(Start, false);
- updateCurrentState();
-
- userInput(Pause, false);
- GameInfo_t state = updateCurrentState();
- ck_assert_int_eq(state.pause, 1);
-
- userInput(Pause, false);
- state = updateCurrentState();
- ck_assert_int_eq(state.pause, 0);
-}
-END_TEST
-
-START_TEST(test_field_initialization) {
- userInput(Start, false);
- GameInfo_t state = updateCurrentState();
-
- int non_zero_count = 0;
- for (int i = 0; i < FIELD_HEIGHT; i++) {
- for (int j = 0; j < FIELD_WIDTH; j++) {
- if (state.field[i][j] != 0) {
- non_zero_count++;
- }
- }
- }
-
- ck_assert_int_le(non_zero_count, 4);
-}
-END_TEST
-
-START_TEST(test_next_figure_exists) {
- userInput(Start, false);
- GameInfo_t state = updateCurrentState();
-
- int has_blocks = 0;
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- if (state.next[i][j]) {
- has_blocks = 1;
- }
- }
- }
- ck_assert_int_eq(has_blocks, 1);
-}
-END_TEST
-
-START_TEST(test_movement_left) {
- userInput(Start, false);
- updateCurrentState();
-
- userInput(Left, false);
- GameInfo_t state = updateCurrentState();
-
- ck_assert_int_eq(state.pause, 0);
-}
-END_TEST
-
-START_TEST(test_movement_right) {
- userInput(Start, false);
- updateCurrentState();
-
- userInput(Right, false);
- GameInfo_t state = updateCurrentState();
-
- ck_assert_int_eq(state.pause, 0);
- ck_assert_int_ge(state.level, 1);
-}
-END_TEST
-
-START_TEST(test_rotation) {
- userInput(Start, false);
- updateCurrentState();
-
- userInput(Action, false);
- GameInfo_t state = updateCurrentState();
-
- int has_figure = 0;
- for (int i = 0; i < FIELD_HEIGHT; i++) {
- for (int j = 0; j < FIELD_WIDTH; j++) {
- if (state.field[i][j] == 1) {
- has_figure = 1;
- }
- }
- }
- ck_assert_int_eq(has_figure, 1);
-}
-END_TEST
-
-START_TEST(test_user_input_actions) {
- userInput(Start, false);
- userInput(Left, false);
- userInput(Right, false);
- userInput(Action, false);
- userInput(Pause, false);
- userInput(Terminate, false);
-
- ck_assert_int_eq(1, 1);
-}
-END_TEST
-
-START_TEST(test_multiple_updates) {
- userInput(Start, false);
-
- for (int i = 0; i < 10; i++) {
- updateCurrentState();
- }
-
- GameInfo_t state = updateCurrentState();
- ck_assert_int_eq(state.level, 1);
-}
-END_TEST
-
-START_TEST(test_instant_drop_down_key) {
- userInput(Start, false);
- updateCurrentState();
-
- GameState_t *state = get_game_state();
- state->down_key_was_released = 1;
-
- userInput(Down, false);
-
- ck_assert_int_eq(state->down_key_was_released, 0);
-}
-END_TEST
-
-START_TEST(test_up_key_release) {
- userInput(Start, false);
- updateCurrentState();
-
- GameState_t *state = get_game_state();
- state->down_key_was_released = 0;
-
- userInput(Up, false);
- updateCurrentState();
-
- ck_assert_int_eq(state->down_key_was_released, 1);
-}
-END_TEST
-
-START_TEST(test_terminate_with_high_score) {
- userInput(Start, false);
- updateCurrentState();
-
- GameState_t *state = get_game_state();
- state->info->high_score = 100;
- state->info->score = 500;
-
- userInput(Terminate, false);
-
- int loaded = load_high_score();
- ck_assert_int_ge(loaded, 500);
-}
-END_TEST
-
-START_TEST(test_game_over_state) {
- userInput(Start, false);
- updateCurrentState();
-
- GameState_t *state = get_game_state();
-
- for (int i = 0; i < 2; i++) {
- for (int j = 0; j < FIELD_WIDTH; j++) {
- state->field[i][j] = 2;
- }
- }
-
- state->state = Spawn;
- updateCurrentState();
-
- ck_assert_int_eq(state->state, GameOver);
-}
-END_TEST
-
-START_TEST(test_do_gameover_clears_next) {
- userInput(Start, false);
- updateCurrentState();
-
- GameState_t *state = get_game_state();
-
- for (int i = 0; i < 2; i++) {
- for (int j = 0; j < FIELD_WIDTH; j++) {
- state->field[i][j] = 2;
- }
- }
-
- state->state = Spawn;
- updateCurrentState();
-
- state->state = GameOver;
- updateCurrentState();
-
- int has_blocks = 0;
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- if (state->next.mtrx[i][j]) {
- has_blocks = 1;
- }
- }
- }
- ck_assert_int_eq(has_blocks, 0);
-}
-END_TEST
-
-START_TEST(test_place_figure_directly) {
- GameState_t *state = get_game_state();
-
- for (int i = 0; i < FIELD_HEIGHT; i++)
- for (int j = 0; j < FIELD_WIDTH; j++)
- state->field[i][j] = 0;
-
- state->curr.x = 5;
- state->curr.y = 10;
- for (int i = 0; i < 4; i++)
- for (int j = 0; j < 4; j++)
- state->curr.mtrx[i][j] = 0;
- state->curr.mtrx[0][0] = 1;
- state->curr.mtrx[0][1] = 1;
-
- place_figure();
-
- ck_assert_int_eq(state->field[10][5], 2);
- ck_assert_int_eq(state->field[10][6], 2);
-}
-END_TEST
-
-START_TEST(test_attach_state_transition) {
- userInput(Start, false);
- updateCurrentState();
-
- GameState_t *state = get_game_state();
-
- state->state = Attaching;
- state->attach_start_time = 0;
- state->attach_completed = 0;
-
- updateCurrentState();
-
- ck_assert_int_gt(state->attach_start_time, 0);
-}
-END_TEST
-
-START_TEST(test_moving_to_down) {
- userInput(Start, false);
- updateCurrentState();
-
- GameState_t *state = get_game_state();
-
- state->curr.y = 5;
- state->curr.x = 5;
- for (int i = 0; i < 4; i++)
- for (int j = 0; j < 4; j++)
- state->curr.mtrx[i][j] = 0;
- state->curr.mtrx[0][0] = 1;
-
- int initial_y = state->curr.y;
-
- state->state = Moving;
- state->moving_type = ToDown;
-
- long long start = get_current_time_ms();
- updateCurrentState();
- long long elapsed = get_current_time_ms() - start;
-
- ck_assert(state->curr.y > initial_y || state->state == Attaching);
- ck_assert_int_lt(elapsed, 1000);
-}
-END_TEST
-
-START_TEST(test_moving_do_nothing) {
- userInput(Start, false);
- updateCurrentState();
-
- GameState_t *state = get_game_state();
-
- state->state = Moving;
- state->moving_type = DoNothing;
- updateCurrentState();
-
- ck_assert_int_eq(state->state, Move);
-}
-END_TEST
-
-START_TEST(test_automatic_falling) {
- userInput(Start, false);
- updateCurrentState();
-
- GameState_t *state = get_game_state();
-
- state->curr.y = 5;
- state->curr.x = 5;
- for (int i = 0; i < 4; i++)
- for (int j = 0; j < 4; j++)
- state->curr.mtrx[i][j] = 0;
- state->curr.mtrx[0][0] = 1;
-
- state->state = Move;
- state->last_move_time = 0;
-
- int initial_y = state->curr.y;
-
- updateCurrentState();
-
- ck_assert(state->curr.y > initial_y || state->state == Attaching);
-}
-END_TEST
-
-Suite *fsm_suite(void) {
- Suite *s = suite_create("FSM");
- TCase *tc = tcase_create("Core");
-
- tcase_add_test(tc, test_game_start);
- tcase_add_test(tc, test_pause_toggle);
- tcase_add_test(tc, test_field_initialization);
- tcase_add_test(tc, test_next_figure_exists);
- tcase_add_test(tc, test_movement_left);
- tcase_add_test(tc, test_movement_right);
- tcase_add_test(tc, test_rotation);
- tcase_add_test(tc, test_user_input_actions);
- tcase_add_test(tc, test_multiple_updates);
-
- tcase_add_test(tc, test_instant_drop_down_key);
- tcase_add_test(tc, test_up_key_release);
- tcase_add_test(tc, test_terminate_with_high_score);
- tcase_add_test(tc, test_game_over_state);
- tcase_add_test(tc, test_do_gameover_clears_next);
- tcase_add_test(tc, test_place_figure_directly);
- tcase_add_test(tc, test_attach_state_transition);
- tcase_add_test(tc, test_moving_to_down);
- tcase_add_test(tc, test_moving_do_nothing);
- tcase_add_test(tc, test_automatic_falling);
-
- suite_add_tcase(s, tc);
- return s;
-}
diff --git a/src/test/test_helper.h b/src/test/test_helper.h
deleted file mode 100644
index 75e1b72..0000000
--- a/src/test/test_helper.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef TEST_HELPER_H
-#define TEST_HELPER_H
-
-#include
-#include "../brick_game/tetris/01_automato.h"
-
-// Хелпер для очистки поля
-static inline void clear_test_field(void) {
- GameState_t* state = get_game_state();
- for (int i = 0; i < FIELD_HEIGHT; i++)
- for (int j = 0; j < FIELD_WIDTH; j++)
- state->field[i][j] = 0;
-}
-
-// Хелпер для очистки матрицы фигуры
-static inline void clear_figure_matrix(void) {
- GameState_t* state = get_game_state();
- for (int i = 0; i < 4; i++)
- for (int j = 0; j < 4; j++)
- state->curr.mtrx[i][j] = 0;
-}
-
-// Хелпер для заполнения линии
-static inline void fill_line(int row) {
- GameState_t* state = get_game_state();
- for (int j = 0; j < FIELD_WIDTH; j++) {
- state->field[row][j] = 2;
- }
-}
-
-// Setup функция (вызывается перед каждым тестом)
-static inline void test_setup(void) {
- GameState_t* state = get_game_state();
- clear_test_field();
- state->info->score = 0;
- state->info->level = 1;
- state->info->speed = 1;
-}
-
-#endif
diff --git a/src/test/test_lines.c b/src/test/test_lines.c
deleted file mode 100644
index 617790f..0000000
--- a/src/test/test_lines.c
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "test_helper.h"
-
-START_TEST(test_clear_one_line) {
- test_setup();
- fill_line(FIELD_HEIGHT - 1);
-
- clear_lines();
-
- ck_assert_int_eq(get_game_state()->info->score, 100);
-}
-END_TEST
-
-START_TEST(test_clear_two_lines) {
- test_setup();
- fill_line(FIELD_HEIGHT - 1);
- fill_line(FIELD_HEIGHT - 2);
-
- clear_lines();
-
- ck_assert_int_eq(get_game_state()->info->score, 300);
-}
-END_TEST
-
-START_TEST(test_clear_three_lines) {
- test_setup();
- fill_line(FIELD_HEIGHT - 1);
- fill_line(FIELD_HEIGHT - 2);
- fill_line(FIELD_HEIGHT - 3);
-
- clear_lines();
-
- ck_assert_int_eq(get_game_state()->info->score, 700);
-}
-END_TEST
-
-START_TEST(test_clear_four_lines) {
- test_setup();
- fill_line(FIELD_HEIGHT - 1);
- fill_line(FIELD_HEIGHT - 2);
- fill_line(FIELD_HEIGHT - 3);
- fill_line(FIELD_HEIGHT - 4);
-
- clear_lines();
-
- ck_assert_int_eq(get_game_state()->info->score, 1500);
-}
-END_TEST
-
-START_TEST(test_incomplete_line) {
- test_setup();
- GameState_t *state = get_game_state();
-
- // Не полная линия
- for (int j = 0; j < FIELD_WIDTH - 1; j++) {
- state->field[FIELD_HEIGHT - 1][j] = 2;
- }
-
- clear_lines();
-
- ck_assert_int_eq(state->info->score, 0);
-}
-END_TEST
-
-Suite *lines_suite(void) {
- Suite *s = suite_create("ClearLines");
- TCase *tc = tcase_create("Core");
-
- tcase_add_test(tc, test_clear_one_line);
- tcase_add_test(tc, test_clear_two_lines);
- tcase_add_test(tc, test_clear_three_lines);
- tcase_add_test(tc, test_clear_four_lines);
- tcase_add_test(tc, test_incomplete_line);
-
- suite_add_tcase(s, tc);
- return s;
-}
diff --git a/src/test/test_main.c b/src/test/test_main.c
deleted file mode 100644
index 3476cca..0000000
--- a/src/test/test_main.c
+++ /dev/null
@@ -1,21 +0,0 @@
-#include
-
-Suite *collision_suite(void);
-Suite *lines_suite(void);
-Suite *figures_suite(void);
-Suite *score_suite(void);
-Suite *fsm_suite(void);
-
-int main(void) {
- SRunner *sr = srunner_create(collision_suite());
- srunner_add_suite(sr, lines_suite());
- srunner_add_suite(sr, figures_suite());
- srunner_add_suite(sr, score_suite());
- srunner_add_suite(sr, fsm_suite());
-
- srunner_run_all(sr, CK_VERBOSE);
- int failed = srunner_ntests_failed(sr);
- srunner_free(sr);
-
- return (failed == 0) ? 0 : 1;
-}
diff --git a/src/test/test_score.c b/src/test/test_score.c
deleted file mode 100644
index 8bf2f91..0000000
--- a/src/test/test_score.c
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "test_helper.h"
-
-START_TEST(test_level_up) {
- test_setup();
- GameState_t *state = get_game_state();
-
- fill_line(FIELD_HEIGHT - 1);
- clear_lines();
-
- fill_line(FIELD_HEIGHT - 1);
- fill_line(FIELD_HEIGHT - 2);
- clear_lines();
-
- fill_line(FIELD_HEIGHT - 1);
- fill_line(FIELD_HEIGHT - 2);
- clear_lines();
-
- ck_assert_int_eq(state->info->level, 2);
-}
-END_TEST
-
-START_TEST(test_max_level) {
- test_setup();
- GameState_t *state = get_game_state();
-
- state->info->score = 10000;
- fill_line(FIELD_HEIGHT - 1);
- clear_lines();
-
- ck_assert_int_le(state->info->level, 10);
-}
-END_TEST
-
-START_TEST(test_high_score_save) {
- save_high_score(9999);
- ck_assert_int_eq(load_high_score(), 9999);
-}
-END_TEST
-
-START_TEST(test_game_over_top) {
- test_setup();
- GameState_t *state = get_game_state();
-
- state->field[0][5] = 2;
- ck_assert_int_eq(is_game_over(), 1);
-}
-END_TEST
-
-Suite *score_suite(void) {
- Suite *s = suite_create("Score");
- TCase *tc = tcase_create("Core");
-
- tcase_add_test(tc, test_level_up);
- tcase_add_test(tc, test_max_level);
- tcase_add_test(tc, test_high_score_save);
- tcase_add_test(tc, test_game_over_top);
-
- suite_add_tcase(s, tc);
- return s;
-}