From 6b80483129a2415abc7f82aa841da414f4229963 Mon Sep 17 00:00:00 2001 From: Rorikstr | Rust Dev Date: Sun, 28 Sep 2025 17:01:42 +0300 Subject: [PATCH] works but with leaks --- src/Makefile | 25 ++- src/brick_game/tetris/game_logic.c | 139 ++++++++------- src/brick_game/tetris/game_logic.h | 16 +- src/brick_game/tetris/tetris.c | 276 ++++++++++++++--------------- 4 files changed, 231 insertions(+), 225 deletions(-) diff --git a/src/Makefile b/src/Makefile index 4e687e7..d2cc7b0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -24,23 +24,30 @@ CLIDIR = gui/cli # Файлы TETRIS_SRC = $(shell find $(TETRISDIR) -name "*.c") +TETRIS_OBJ = $(TETRIS_SRC:.c=.o) CLI_SRC = $(shell find $(CLIDIR) -name "*.c") -ALL_SRC = $(TETRIS_SRC) $(CLI_SRC) -OBJ = $(ALL_SRC:.c=.o) +CLI_OBJ = $(CLI_SRC:.c=.o) -# Имя исполняемого файла -TARGET = tetris_bin +LIB_TETRIS = $(BUILDDIR)/libtetris.a +TARGET = $(BUILDDIR)/tetris_bin.out # Установка PREFIX ?= /usr/local BINDIR = $(PREFIX)/bin -all: $(TARGET) +all: clean $(TARGET) -$(TARGET): $(OBJ) - $(CC) $(OBJ) -o $@ $(LDFLAGS) +$(LIB_TETRIS): $(TETRIS_OBJ) + mkdir -p $(BUILDDIR) + ar rcs $@ $^ -%.o: %.c +$(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 $@ install: $(TARGET) @@ -50,7 +57,7 @@ uninstall: rm -f $(BINDIR)/$(TARGET) clean: - rm -f $(OBJ) $(TARGET) *.gcda *.gcno *.gcov + rm -f $(CLI_OBJ) $(TETRIS_OBJ) $(TARGET) *.gcda *.gcno *.gcov test: @echo "Running tests..." diff --git a/src/brick_game/tetris/game_logic.c b/src/brick_game/tetris/game_logic.c index 6ff4473..9789c7b 100644 --- a/src/brick_game/tetris/game_logic.c +++ b/src/brick_game/tetris/game_logic.c @@ -5,8 +5,6 @@ #include #include -// static bool initialized = false; - static GameStateData* get_instance() { static GameStateData instance = {0}; static bool initialized_local = false; @@ -21,8 +19,7 @@ static GameStateData* get_instance() { instance.score = 0; instance.high_score = 0; instance.level = 1; - instance.speed = 1000; - instance.figure_active = false; + instance.state = FSM_Start; instance.game_over = false; instance.paused = false; initialized_local = true; @@ -43,27 +40,45 @@ void init_game() { srand((unsigned int)time(NULL)); - state->current_figure.type = get_random_figure(); - state->current_figure.x = FIELD_WIDTH / 2 - 2; - state->current_figure.y = 0; - state->current_figure.rotation = 0; - state->next_figure.type = get_random_figure(); state->next_figure.x = 0; state->next_figure.y = 0; state->next_figure.rotation = 0; - state->figure_active = true; state->score = 0; - state->high_score = 0; // Позже можно загружать из файла + state->high_score = 0; state->level = 1; - state->speed = 1000; state->drop_time = time(NULL) * 1000; state->game_over = false; state->paused = false; + state->state = FSM_Start; } -static void clear_completed_lines() { +void place_figure() { + GameStateData* state = get_instance(); + Figure* f = &state->current_figure; + const int (*shape)[4] = get_figure_shape(f->type, f->rotation); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + if (shape[i][j]) { + int x = f->x + j; + int y = f->y + i; + if (y >= 0 && y < FIELD_HEIGHT && x >= 0 && x < FIELD_WIDTH) { + state->game_field[y][x] = 1; + } + } + } + } + + // Проверяем и удаляем заполненные строки + clear_completed_lines(); + + // Меняем состояние на Spawn для генерации новой фигуры + state->state = FSM_Spawn; +} + +void clear_completed_lines() { GameStateData* state = get_instance(); int lines_cleared = 0; int write_row = FIELD_HEIGHT - 1; @@ -118,9 +133,6 @@ static void clear_completed_lines() { if (new_level > 10) new_level = 10; if (new_level != state->level) { state->level = new_level; - // Увеличиваем скорость (уменьшаем время падения) - state->speed = 1000 - (state->level - 1) * 50; - if (state->speed < 100) state->speed = 100; } } } @@ -151,63 +163,50 @@ bool check_collision() { return collision; } -void place_figure() { +void fsm_transition() { GameStateData* state = get_instance(); - Figure* f = &state->current_figure; - const int (*shape)[4] = get_figure_shape(f->type, f->rotation); - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - if (shape[i][j]) { - int x = f->x + j; - int y = f->y + i; - if (y >= 0 && y < FIELD_HEIGHT && x >= 0 && x < FIELD_WIDTH) { - state->game_field[y][x] = 1; - } + + switch (state->state) { + case FSM_Start: + state->state = FSM_Spawn; + break; + + case FSM_Spawn: + state->current_figure = state->next_figure; + state->current_figure.x = FIELD_WIDTH / 2 - 2; + state->current_figure.y = 0; + state->current_figure.rotation = 0; + + state->next_figure.type = get_random_figure(); + state->next_figure.rotation = 0; + + if (check_collision()) { + state->state = FSM_GameOver; + } else { + state->state = FSM_Moving; } - } - } - - // Проверяем и удаляем заполненные строки - clear_completed_lines(); - - // Сгенерировать новую текущую фигуру из следующей - state->current_figure = state->next_figure; - state->current_figure.x = FIELD_WIDTH / 2 - 2; - state->current_figure.y = 0; - state->current_figure.rotation = 0; - - // Сгенерировать новую следующую фигуру - state->next_figure.type = get_random_figure(); - state->next_figure.rotation = 0; - - // Проверить, возможно ли размещение новой фигуры - if (check_collision()) { - state->game_over = true; - } -} - -void update_game_state() { - GameStateData* state = get_instance(); - - if (state->game_over) { - return; - } - - // Проверка времени для автоматического падения - long long current_time = time(NULL) * 1000; // в миллисекундах - if (current_time - state->drop_time >= state->speed) { - // Попробовать сдвинуть фигуру вниз - int old_y = state->current_figure.y; - state->current_figure.y++; - - if (check_collision()) { - // Если столкновение, вернуть позицию и зафиксировать фигуру - state->current_figure.y = old_y; + break; + + case FSM_Moving: + break; + + case FSM_Move: + state->current_figure.y++; + if (check_collision()) { + state->current_figure.y--; + state->state = FSM_Attaching; + } else { + state->state = FSM_Moving; + } + break; + + case FSM_Attaching: place_figure(); - } else { - state->drop_time = current_time; - } + state->state = FSM_Spawn; + break; + + case FSM_GameOver: + break; } } diff --git a/src/brick_game/tetris/game_logic.h b/src/brick_game/tetris/game_logic.h index 3096a44..e9fe7f6 100644 --- a/src/brick_game/tetris/game_logic.h +++ b/src/brick_game/tetris/game_logic.h @@ -16,6 +16,16 @@ typedef enum { FIGURE_COUNT } FigureType; +// FSM состояния +typedef enum { + FSM_Start, + FSM_Spawn, + FSM_Moving, + FSM_Move, + FSM_Attaching, + FSM_GameOver +} FSMState_t; + // Структура фигуры typedef struct { int x, y; // Позиция фигуры на поле @@ -27,11 +37,10 @@ typedef struct { int game_field[FIELD_HEIGHT][FIELD_WIDTH]; Figure current_figure; Figure next_figure; - bool figure_active; + FSMState_t state; int score; int high_score; int level; - int speed; long long drop_time; bool game_over; bool paused; @@ -40,11 +49,12 @@ typedef struct { // Внутренние функции bool check_collision(void); void init_game(void); -void update_game_state(void); +void fsm_transition(void); int get_random_figure(void); const int (*get_figure_shape(FigureType type, int rotation))[4]; GameStateData* get_game_state(void); void place_figure(void); +void clear_completed_lines(void); // Фигуры const int (*i_fig_up())[4]; diff --git a/src/brick_game/tetris/tetris.c b/src/brick_game/tetris/tetris.c index 535c119..aac41b7 100644 --- a/src/brick_game/tetris/tetris.c +++ b/src/brick_game/tetris/tetris.c @@ -5,172 +5,162 @@ #include #include -static GameInfo_t game_info = {0}; - -void init_game_info() { - if (game_info.field == NULL) { - game_info.field = malloc(FIELD_HEIGHT * sizeof(int*)); +static GameInfo_t* get_game_info_instance() { + static GameInfo_t instance = {0}; + static bool initialized = false; + + if (!initialized) { + instance.field = malloc(FIELD_HEIGHT * sizeof(int*)); for (int i = 0; i < FIELD_HEIGHT; i++) { - game_info.field[i] = malloc(FIELD_WIDTH * sizeof(int)); + instance.field[i] = malloc(FIELD_WIDTH * sizeof(int)); } - } - - if (game_info.next == NULL) { - game_info.next = malloc(4 * sizeof(int*)); + + instance.next = malloc(4 * sizeof(int*)); for (int i = 0; i < 4; i++) { - game_info.next[i] = malloc(4 * sizeof(int)); + instance.next[i] = malloc(4 * sizeof(int)); } + + // Инициализация полей + for (int i = 0; i < FIELD_HEIGHT; i++) { + for (int j = 0; j < FIELD_WIDTH; j++) { + instance.field[i][j] = 0; + } + } + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + instance.next[i][j] = 0; + } + } + + initialized = true; } - // Инициализация полей - for (int i = 0; i < FIELD_HEIGHT; i++) { - for (int j = 0; j < FIELD_WIDTH; j++) { - game_info.field[i][j] = 0; - } - } - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - game_info.next[i][j] = 0; - } - } -} - -bool is_game_info_initialized() { - // Проверяем, инициализирован ли game_info - if (game_info.field == NULL) return false; - if (game_info.next == NULL) return false; - return true; + return &instance; } void userInput(UserAction_t action, bool hold) { - if (!is_game_info_initialized()) { - init_game_info(); - init_game(); - } - GameStateData* state = get_game_state(); + GameInfo_t* game_info = get_game_info_instance(); - if (action == Start) { - if (state->game_over) { - int saved_high_score = state->high_score; // Сохраняем high_score - init_game(); // Перезапуск игры - state->high_score = saved_high_score; // Восстанавливаем high_score - } else if (!state->paused) { - state->paused = true; - } else { - state->paused = false; - } - return; - } - - if (action == Pause) { - state->paused = !state->paused; - return; - } - - if (action == Terminate) { - // Освобождаем память при завершении - if (game_info.field != NULL) { - for (int i = 0; i < FIELD_HEIGHT; i++) { - free(game_info.field[i]); + switch (action) { + case Start: + if (state->state == FSM_GameOver) { + // Перезапуск игры + int saved_high_score = state->high_score; + init_game(); + state->high_score = saved_high_score; + state->state = FSM_Spawn; + } else { + state->paused = !state->paused; } - free(game_info.field); - game_info.field = NULL; - } - - if (game_info.next != NULL) { - for (int i = 0; i < 4; i++) { - free(game_info.next[i]); - } - free(game_info.next); - game_info.next = NULL; - } - return; - } - - if (state->game_over || state->paused) { - return; - } - - if (state->figure_active && !state->game_over) { - int old_x = state->current_figure.x; - int old_y = state->current_figure.y; - int old_rot = state->current_figure.rotation; - - switch (action) { - case Left: - state->current_figure.x--; - break; - case Right: - state->current_figure.x++; - break; - case Down: - state->current_figure.y++; - break; - case Up: - state->current_figure.rotation = (state->current_figure.rotation + 1) % 4; - break; - case Action: // Это может быть вращение или сброс - if (hold) { - // Быстрый сброс вниз - while (!check_collision()) { - state->current_figure.y++; - } - state->current_figure.y--; // Вернуть на место перед столкновением - place_figure(); - } else { - state->current_figure.rotation = (state->current_figure.rotation + 1) % 4; + break; + + case Pause: + state->paused = !state->paused; + break; + + case Terminate: + // Освобождаем память при завершении + if (game_info->field != NULL) { + for (int i = 0; i < FIELD_HEIGHT; i++) { + free(game_info->field[i]); } - break; - default: - break; - } + free(game_info->field); + game_info->field = NULL; + } + + if (game_info->next != NULL) { + for (int i = 0; i < 4; i++) { + free(game_info->next[i]); + } + free(game_info->next); + game_info->next = NULL; + } + break; - if (check_collision()) { - // Возвращаем старые значения - state->current_figure.x = old_x; - state->current_figure.y = old_y; - state->current_figure.rotation = old_rot; - } else if (action == Down) { - // Если перемещение вниз успешно, обновляем время падения - state->drop_time = time(NULL) * 1000; - } + default: + if (state->state == FSM_GameOver || state->paused) { + break; + } + + if ((state->state == FSM_Moving || state->state == FSM_Move) && !state->game_over) { + int old_x = state->current_figure.x; + int old_y = state->current_figure.y; + int old_rot = state->current_figure.rotation; + + switch (action) { + case Left: + state->current_figure.x--; + break; + case Right: + state->current_figure.x++; + break; + case Down: + state->current_figure.y++; + break; + case Up: + state->current_figure.rotation = (state->current_figure.rotation + 1) % 4; + break; + case Action: + if (hold) { + // Быстрый сброс вниз + while (!check_collision()) { + state->current_figure.y++; + } + state->current_figure.y--; // Вернуть на место перед столкновением + state->state = FSM_Attaching; + } else { + state->current_figure.rotation = (state->current_figure.rotation + 1) % 4; + } + break; + default: + break; + } + + if (check_collision()) { + // Возвращаем старые значения + state->current_figure.x = old_x; + state->current_figure.y = old_y; + state->current_figure.rotation = old_rot; + } else if (action == Down) { + // Если перемещение вниз успешно, обновляем время падения + state->drop_time = time(NULL) * 1000; + } + } + break; } } GameInfo_t updateCurrentState() { - if (!is_game_info_initialized()) { - init_game_info(); - init_game(); - } - GameStateData* state = get_game_state(); + GameInfo_t* game_info = get_game_info_instance(); if (!state->game_over && !state->paused) { long long current_time = time(NULL) * 1000; - if (current_time - state->drop_time >= state->speed) { - int old_y = state->current_figure.y; - state->current_figure.y++; - - if (check_collision()) { - state->current_figure.y = old_y; - place_figure(); - } else { - state->drop_time = current_time; - } + + // Определяем интервал падения в зависимости от уровня + int drop_interval = 1000 - (state->level - 1) * 50; + if (drop_interval < 100) drop_interval = 100; // Минимальный интервал + + if (current_time - state->drop_time >= drop_interval) { + state->state = FSM_Move; // Переходим к автоматическому движению + state->drop_time = current_time; } } + // Выполняем переходы FSM + fsm_transition(); + // Обновляем game_info.field из state->game_field for (int i = 0; i < FIELD_HEIGHT; i++) { for (int j = 0; j < FIELD_WIDTH; j++) { - game_info.field[i][j] = state->game_field[i][j]; + game_info->field[i][j] = state->game_field[i][j]; } } // Добавляем активную фигуру на поле для отображения (если не game_over) - if (state->figure_active && !state->game_over) { + if ((state->state == FSM_Moving || state->state == FSM_Move) && !state->game_over) { Figure* f = &state->current_figure; const int (*shape)[4] = get_figure_shape(f->type, f->rotation); for (int i = 0; i < 4; i++) { @@ -179,7 +169,7 @@ GameInfo_t updateCurrentState() { int x = f->x + j; int y = f->y + i; if (x >= 0 && x < FIELD_WIDTH && y >= 0 && y < FIELD_HEIGHT) { - game_info.field[y][x] = 1; + game_info->field[y][x] = 1; } } } @@ -188,7 +178,7 @@ GameInfo_t updateCurrentState() { // Обновляем next const int (*next_shape)[4]; - if (state->game_over) { + if (state->state == FSM_GameOver) { // При game_over показываем пустую фигуру next_shape = empty_fig(); } else { @@ -197,16 +187,16 @@ GameInfo_t updateCurrentState() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { - game_info.next[i][j] = next_shape[i][j]; + game_info->next[i][j] = next_shape[i][j]; } } // Обновляем остальные поля - game_info.score = state->score; - game_info.high_score = state->high_score; - game_info.level = state->level; - game_info.speed = state->speed; - game_info.pause = state->paused ? 1 : 0; + game_info->score = state->score; + game_info->high_score = state->high_score; + game_info->level = state->level; + game_info->speed = 0; // Заглушка + game_info->pause = state->paused ? 1 : 0; - return game_info; + return *game_info; } \ No newline at end of file