works but with leaks

This commit is contained in:
Rorikstr | Rust Dev 2025-09-28 17:01:42 +03:00
parent eaafb06836
commit 6b80483129
4 changed files with 231 additions and 225 deletions

View file

@ -24,23 +24,30 @@ CLIDIR = gui/cli
# Файлы # Файлы
TETRIS_SRC = $(shell find $(TETRISDIR) -name "*.c") TETRIS_SRC = $(shell find $(TETRISDIR) -name "*.c")
TETRIS_OBJ = $(TETRIS_SRC:.c=.o)
CLI_SRC = $(shell find $(CLIDIR) -name "*.c") CLI_SRC = $(shell find $(CLIDIR) -name "*.c")
ALL_SRC = $(TETRIS_SRC) $(CLI_SRC) CLI_OBJ = $(CLI_SRC:.c=.o)
OBJ = $(ALL_SRC:.c=.o)
# Имя исполняемого файла LIB_TETRIS = $(BUILDDIR)/libtetris.a
TARGET = tetris_bin TARGET = $(BUILDDIR)/tetris_bin.out
# Установка # Установка
PREFIX ?= /usr/local PREFIX ?= /usr/local
BINDIR = $(PREFIX)/bin BINDIR = $(PREFIX)/bin
all: $(TARGET) all: clean $(TARGET)
$(TARGET): $(OBJ) $(LIB_TETRIS): $(TETRIS_OBJ)
$(CC) $(OBJ) -o $@ $(LDFLAGS) 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 $@ $(CC) $(CFLAGS) -c $< -o $@
install: $(TARGET) install: $(TARGET)
@ -50,7 +57,7 @@ uninstall:
rm -f $(BINDIR)/$(TARGET) rm -f $(BINDIR)/$(TARGET)
clean: clean:
rm -f $(OBJ) $(TARGET) *.gcda *.gcno *.gcov rm -f $(CLI_OBJ) $(TETRIS_OBJ) $(TARGET) *.gcda *.gcno *.gcov
test: test:
@echo "Running tests..." @echo "Running tests..."

View file

@ -5,8 +5,6 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
// static bool initialized = false;
static GameStateData* get_instance() { static GameStateData* get_instance() {
static GameStateData instance = {0}; static GameStateData instance = {0};
static bool initialized_local = false; static bool initialized_local = false;
@ -21,8 +19,7 @@ static GameStateData* get_instance() {
instance.score = 0; instance.score = 0;
instance.high_score = 0; instance.high_score = 0;
instance.level = 1; instance.level = 1;
instance.speed = 1000; instance.state = FSM_Start;
instance.figure_active = false;
instance.game_over = false; instance.game_over = false;
instance.paused = false; instance.paused = false;
initialized_local = true; initialized_local = true;
@ -43,27 +40,45 @@ void init_game() {
srand((unsigned int)time(NULL)); 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.type = get_random_figure();
state->next_figure.x = 0; state->next_figure.x = 0;
state->next_figure.y = 0; state->next_figure.y = 0;
state->next_figure.rotation = 0; state->next_figure.rotation = 0;
state->figure_active = true;
state->score = 0; state->score = 0;
state->high_score = 0; // Позже можно загружать из файла state->high_score = 0;
state->level = 1; state->level = 1;
state->speed = 1000;
state->drop_time = time(NULL) * 1000; state->drop_time = time(NULL) * 1000;
state->game_over = false; state->game_over = false;
state->paused = 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(); GameStateData* state = get_instance();
int lines_cleared = 0; int lines_cleared = 0;
int write_row = FIELD_HEIGHT - 1; 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 > 10) new_level = 10;
if (new_level != state->level) { if (new_level != state->level) {
state->level = new_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; return collision;
} }
void place_figure() { void fsm_transition() {
GameStateData* state = get_instance(); 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++) { switch (state->state) {
for (int j = 0; j < 4; j++) { case FSM_Start:
if (shape[i][j]) { state->state = FSM_Spawn;
int x = f->x + j; break;
int y = f->y + i;
if (y >= 0 && y < FIELD_HEIGHT && x >= 0 && x < FIELD_WIDTH) { case FSM_Spawn:
state->game_field[y][x] = 1; 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;
} }
} break;
}
// Проверяем и удаляем заполненные строки case FSM_Moving:
clear_completed_lines(); break;
// Сгенерировать новую текущую фигуру из следующей case FSM_Move:
state->current_figure = state->next_figure; state->current_figure.y++;
state->current_figure.x = FIELD_WIDTH / 2 - 2; if (check_collision()) {
state->current_figure.y = 0; state->current_figure.y--;
state->current_figure.rotation = 0; state->state = FSM_Attaching;
} else {
state->state = FSM_Moving;
}
break;
// Сгенерировать новую следующую фигуру case FSM_Attaching:
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;
place_figure(); place_figure();
} else { state->state = FSM_Spawn;
state->drop_time = current_time; break;
}
case FSM_GameOver:
break;
} }
} }

View file

@ -16,6 +16,16 @@ typedef enum {
FIGURE_COUNT FIGURE_COUNT
} FigureType; } FigureType;
// FSM состояния
typedef enum {
FSM_Start,
FSM_Spawn,
FSM_Moving,
FSM_Move,
FSM_Attaching,
FSM_GameOver
} FSMState_t;
// Структура фигуры // Структура фигуры
typedef struct { typedef struct {
int x, y; // Позиция фигуры на поле int x, y; // Позиция фигуры на поле
@ -27,11 +37,10 @@ typedef struct {
int game_field[FIELD_HEIGHT][FIELD_WIDTH]; int game_field[FIELD_HEIGHT][FIELD_WIDTH];
Figure current_figure; Figure current_figure;
Figure next_figure; Figure next_figure;
bool figure_active; FSMState_t state;
int score; int score;
int high_score; int high_score;
int level; int level;
int speed;
long long drop_time; long long drop_time;
bool game_over; bool game_over;
bool paused; bool paused;
@ -40,11 +49,12 @@ typedef struct {
// Внутренние функции // Внутренние функции
bool check_collision(void); bool check_collision(void);
void init_game(void); void init_game(void);
void update_game_state(void); void fsm_transition(void);
int get_random_figure(void); int get_random_figure(void);
const int (*get_figure_shape(FigureType type, int rotation))[4]; const int (*get_figure_shape(FigureType type, int rotation))[4];
GameStateData* get_game_state(void); GameStateData* get_game_state(void);
void place_figure(void); void place_figure(void);
void clear_completed_lines(void);
// Фигуры // Фигуры
const int (*i_fig_up())[4]; const int (*i_fig_up())[4];

View file

@ -5,172 +5,162 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
static GameInfo_t game_info = {0}; static GameInfo_t* get_game_info_instance() {
static GameInfo_t instance = {0};
static bool initialized = false;
void init_game_info() { if (!initialized) {
if (game_info.field == NULL) { instance.field = malloc(FIELD_HEIGHT * sizeof(int*));
game_info.field = malloc(FIELD_HEIGHT * sizeof(int*));
for (int i = 0; i < FIELD_HEIGHT; i++) { 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) { instance.next = malloc(4 * sizeof(int*));
game_info.next = malloc(4 * sizeof(int*));
for (int i = 0; i < 4; i++) { 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;
} }
// Инициализация полей return &instance;
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;
} }
void userInput(UserAction_t action, bool hold) { void userInput(UserAction_t action, bool hold) {
if (!is_game_info_initialized()) {
init_game_info();
init_game();
}
GameStateData* state = get_game_state(); GameStateData* state = get_game_state();
GameInfo_t* game_info = get_game_info_instance();
if (action == Start) { switch (action) {
if (state->game_over) { case Start:
int saved_high_score = state->high_score; // Сохраняем high_score if (state->state == FSM_GameOver) {
init_game(); // Перезапуск игры // Перезапуск игры
state->high_score = saved_high_score; // Восстанавливаем high_score int saved_high_score = state->high_score;
} else if (!state->paused) { init_game();
state->paused = true; state->high_score = saved_high_score;
} else { state->state = FSM_Spawn;
state->paused = false; } else {
} state->paused = !state->paused;
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]);
} }
free(game_info.field); break;
game_info.field = NULL;
}
if (game_info.next != NULL) { case Pause:
for (int i = 0; i < 4; i++) { state->paused = !state->paused;
free(game_info.next[i]); break;
}
free(game_info.next);
game_info.next = NULL;
}
return;
}
if (state->game_over || state->paused) { case Terminate:
return; // Освобождаем память при завершении
} if (game_info->field != NULL) {
for (int i = 0; i < FIELD_HEIGHT; i++) {
if (state->figure_active && !state->game_over) { free(game_info->field[i]);
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; free(game_info->field);
default: game_info->field = NULL;
break; }
}
if (check_collision()) { if (game_info->next != NULL) {
// Возвращаем старые значения for (int i = 0; i < 4; i++) {
state->current_figure.x = old_x; free(game_info->next[i]);
state->current_figure.y = old_y; }
state->current_figure.rotation = old_rot; free(game_info->next);
} else if (action == Down) { game_info->next = NULL;
// Если перемещение вниз успешно, обновляем время падения }
state->drop_time = time(NULL) * 1000; break;
}
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() { GameInfo_t updateCurrentState() {
if (!is_game_info_initialized()) {
init_game_info();
init_game();
}
GameStateData* state = get_game_state(); GameStateData* state = get_game_state();
GameInfo_t* game_info = get_game_info_instance();
if (!state->game_over && !state->paused) { if (!state->game_over && !state->paused) {
long long current_time = time(NULL) * 1000; 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; int drop_interval = 1000 - (state->level - 1) * 50;
place_figure(); if (drop_interval < 100) drop_interval = 100; // Минимальный интервал
} else {
state->drop_time = current_time; 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 // Обновляем game_info.field из state->game_field
for (int i = 0; i < FIELD_HEIGHT; i++) { for (int i = 0; i < FIELD_HEIGHT; i++) {
for (int j = 0; j < FIELD_WIDTH; j++) { 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) // Добавляем активную фигуру на поле для отображения (если не 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; Figure* f = &state->current_figure;
const int (*shape)[4] = get_figure_shape(f->type, f->rotation); const int (*shape)[4] = get_figure_shape(f->type, f->rotation);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -179,7 +169,7 @@ GameInfo_t updateCurrentState() {
int x = f->x + j; int x = f->x + j;
int y = f->y + i; int y = f->y + i;
if (x >= 0 && x < FIELD_WIDTH && y >= 0 && y < FIELD_HEIGHT) { 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 // Обновляем next
const int (*next_shape)[4]; const int (*next_shape)[4];
if (state->game_over) { if (state->state == FSM_GameOver) {
// При game_over показываем пустую фигуру // При game_over показываем пустую фигуру
next_shape = empty_fig(); next_shape = empty_fig();
} else { } else {
@ -197,16 +187,16 @@ GameInfo_t updateCurrentState() {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) { 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->score = state->score;
game_info.high_score = state->high_score; game_info->high_score = state->high_score;
game_info.level = state->level; game_info->level = state->level;
game_info.speed = state->speed; game_info->speed = 0; // Заглушка
game_info.pause = state->paused ? 1 : 0; game_info->pause = state->paused ? 1 : 0;
return game_info; return *game_info;
} }