diff --git a/src/Makefile b/src/Makefile index d2cc7b0..244b2fd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -43,6 +43,7 @@ $(LIB_TETRIS): $(TETRIS_OBJ) $(TARGET): $(LIB_TETRIS) $(CLI_OBJ) $(CC) $(CLI_OBJ) -L$(BUILDDIR) -ltetris -o $@ $(LDFLAGS) + rm -f $(CLI_OBJ) $(TETRIS_OBJ) brick_game/tetris/%.o: brick_game/tetris/%.c $(CC) $(CFLAGS) -c $< -o $@ @@ -57,7 +58,7 @@ uninstall: rm -f $(BINDIR)/$(TARGET) clean: - rm -f $(CLI_OBJ) $(TETRIS_OBJ) $(TARGET) *.gcda *.gcno *.gcov + rm -f $(CLI_OBJ) $(TETRIS_OBJ) $(TARGET) $(LIB_TETRIS) *.gcda *.gcno *.gcov test: @echo "Running tests..." diff --git a/src/brick_game/tetris/01_automato.h b/src/brick_game/tetris/01_automato.h index f8aef55..83b2569 100644 --- a/src/brick_game/tetris/01_automato.h +++ b/src/brick_game/tetris/01_automato.h @@ -17,6 +17,7 @@ typedef enum { RightDown, LeftDown, Rotate, + ToDown, DoNothing } Moving_t; @@ -44,29 +45,36 @@ typedef struct { Automato_t state; Moving_t moving_type; int field[FIELD_HEIGHT][FIELD_WIDTH]; - // int score; // НЕ НУЖЕН, это уже есть в GameInfo_t - // int high_score; // НЕ НУЖЕН, это уже есть в GameInfo_t - // int level; // НЕ НУЖЕН, это уже есть в GameInfo_t - // int speed; // НЕ НУЖЕН, это уже есть в GameInfo_t - long long last_time; // нужно пояснение для чего это + GameInfo_t info; + long long last_time; } GameState_t; GameState_t* get_game_state(void); // Функции состояний +// init void do_init(void); -void do_spawn(void); -void do_move(void); -void do_moving(void); -void do_attaching(void); -void do_gameover(void); -// Вспомогательные -void place_figure(); +// spawn +void do_spawn(void); + +// move +void do_move(void); + +//moving +void do_moving(void); + +// attaching +void do_attaching(void); int check_collision(); +void place_figure(); void clear_lines(); + +// gameover +void do_gameover(void); int is_game_over(); + // Функции фигур const int (*get_figure_shape(Sprite_t sprite, int rotation))[4]; diff --git a/src/brick_game/tetris/02_tetris.c b/src/brick_game/tetris/02_tetris.c index 5da7688..c7ea2fc 100644 --- a/src/brick_game/tetris/02_tetris.c +++ b/src/brick_game/tetris/02_tetris.c @@ -1,43 +1,47 @@ #include "01_automato.h" -#include void userInput(UserAction_t action, bool hold) { - GameState_t* g_state = get_game_state(); - GameInfo_t* g_info = get_info_state(); + (void)hold; // заглушка + GameState_t* state = get_game_state(); switch (action) { case Start: - g_state->state = Init; + state->state = Init; break; case Terminate: - if (g_info->score > g_info->high_score) { - g_info->high_score = g_info->score; + if (state->info.score > state->info.high_score) { + state->info.high_score = state->info.score; } + state->state = GameOver; break; case Left: - g_state->state = Moving; - g_state->moving_type = LeftDown; + state->state = Moving; + state->moving_type = LeftDown; break; case Right: - g_state->state = Moving; - g_state->moving_type = RightDown; + state->state = Moving; + state->moving_type = RightDown; break; case Action: - g_state->state = Moving; - g_state->moving_type = Rotate; + state->state = Moving; + state->moving_type = Rotate; break; case Down: - // Ускорение падения — будет обрабатываться в do_move + state->state = Moving; + state->moving_type = ToDown; + break; + case Pause: + state->info.pause = !state->info.pause; break; default: - break; // pause и down - не нужны в backend логике. + break; } } GameInfo_t updateCurrentState() { - GameState_t* g_state = get_game_state(); - switch (g_state->state) { - case Start: + GameState_t* state = get_game_state(); + switch (state->state) { + case Init: do_init(); break; case Spawn: @@ -57,9 +61,9 @@ GameInfo_t updateCurrentState() { break; } - GameInfo_t info = {0}; - info.field = (int**)g_state->field; - info.next = (int**)g_state->next.mtrx; // теперь next.mtrx + GameInfo_t info = state->info; + info.field = (int**)state->field; + info.next = (int**)state->next.mtrx; info.pause = 0; return info; } \ No newline at end of file diff --git a/src/brick_game/tetris/03_automato.c b/src/brick_game/tetris/03_automato.c index d8c9e42..82f10c2 100644 --- a/src/brick_game/tetris/03_automato.c +++ b/src/brick_game/tetris/03_automato.c @@ -1,13 +1,11 @@ #include "01_automato.h" -static GameState_t g_state = {0}; - GameState_t* get_game_state(void) { - static GameInfo_t instance = {0}; - static int is_init = 0; - if (!is_init) { - g_state.state = GameOver; - is_init = 1; + static GameState_t state = {0}; + static int initialized = 0; + if (!initialized) { + state.state = GameOver; + initialized = 1; } - return &g_state; + return &state; } \ No newline at end of file diff --git a/src/brick_game/tetris/04_init.c b/src/brick_game/tetris/04_init.c index 5497e2c..7391594 100644 --- a/src/brick_game/tetris/04_init.c +++ b/src/brick_game/tetris/04_init.c @@ -1,15 +1,14 @@ #include "01_automato.h" void do_init(void) { - GameState_t* g_state = get_game_state(); - GameInfo_t* g_info = get_game_info(); + GameState_t* state = get_game_state(); // Очистка поля for (int i = 0; i < FIELD_HEIGHT; ++i) for (int j = 0; j < FIELD_WIDTH; ++j) - g_state->field[i][j] = 0; + state->field[i][j] = 0; - g_info->score = 0; - g_info->level = 1; - g_info->speed = 1; - g_state->state = Spawn; + state->info.score = 0; + state->info.level = 1; + state->info.speed = 1; + state->state = Spawn; } \ No newline at end of file diff --git a/src/brick_game/tetris/06_move.c b/src/brick_game/tetris/06_move.c index 078c851..8f20f01 100644 --- a/src/brick_game/tetris/06_move.c +++ b/src/brick_game/tetris/06_move.c @@ -1,8 +1,26 @@ +#include #include "01_automato.h" +long long get_time_ms() { + return (long long)time(NULL) * 1000; +} + void do_move(void) { GameState_t* state = get_game_state(); - // Проверка таймера и перемещение вниз - // Если hold Down — ускорение - // Если коллизия — переход в Attaching + + long long current_time = get_time_ms(); + + int delay = (state->moving_type == ToDown) ? 50 : (1000 / state->info.speed); + + if (current_time - state->last_time < delay) { + return; // ещё не время + } + state->last_time = current_time; + + // Двигаем вниз + state->curr.y++; + if (check_collision()) { + state->curr.y--; // откат + state->state = Attaching; // переход в Attaching + } } \ No newline at end of file diff --git a/src/brick_game/tetris/07_moving.c b/src/brick_game/tetris/07_moving.c index 1842b4f..39381b0 100644 --- a/src/brick_game/tetris/07_moving.c +++ b/src/brick_game/tetris/07_moving.c @@ -2,7 +2,47 @@ void do_moving(void) { GameState_t* state = get_game_state(); - // Обработка Left/Right/Action - // Проверка коллизии - // Возврат в Move или остаться в Moving + + switch (state->moving_type) { + case LeftDown: + case RightDown: + case Rotate: + // Обработка движения/поворота + Figure_t old = state->curr; + switch (state->moving_type) { + case LeftDown: + state->curr.x--; + break; + case RightDown: + state->curr.x++; + break; + case Rotate: + 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]; + break; + default: + break; + } + if (check_collision()) { + state->curr = old; // откат + } + state->state = Move; + break; + + case ToDown: + // Мгновенное падение: двигаем вниз, пока не упрёмся + do { + state->curr.y++; + } while (!check_collision()); + state->curr.y--; // откат на 1 назад + state->state = Attaching; // сразу в Attaching + 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 index 33c02f4..ade7674 100644 --- a/src/brick_game/tetris/08_attaching.c +++ b/src/brick_game/tetris/08_attaching.c @@ -15,4 +15,82 @@ void do_attaching(void) { } else { state->state = Spawn; } +} + +int check_collision() { + 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 (x < 0 || x >= FIELD_WIDTH || y >= FIELD_HEIGHT) { + return 1; // коллизия + } + if (y >= 0 && state->field[y][x]) { + return 1; // коллизия с другой фигурой + } + } + } + } + return 0; // нет коллизии +} + +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 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; + break; + } + } + if (full) { + // Сдвигаем строки вниз + for (int y = i; 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; + } + lines_cleared++; + i++; // проверяем эту строку снова + } + } + + // Начисление очков + if (lines_cleared > 0) { + int points[] = {0, 100, 300, 700, 1500}; + state->info.score += points[lines_cleared]; + if (state->info.score / 600 > state->info.level - 1) { + state->info.level++; + state->info.speed = state->info.level; + } + } } \ No newline at end of file diff --git a/src/brick_game/tetris/09_gameover.c b/src/brick_game/tetris/09_gameover.c index 719d9e9..1358668 100644 --- a/src/brick_game/tetris/09_gameover.c +++ b/src/brick_game/tetris/09_gameover.c @@ -3,7 +3,19 @@ void do_gameover(void) { GameState_t* state = get_game_state(); // Сброс next в пустую фигуру + 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] = 0; + state->next.mtrx[i][j] = shape[i][j]; +} + +int is_game_over() { + GameState_t* state = get_game_state(); + // Проверяем, есть ли блоки в верхних рядах + for (int j = 0; j < FIELD_WIDTH; ++j) { + if (state->field[0][j] || state->field[1][j]) { + return 1; + } + } + return 0; } \ No newline at end of file diff --git a/src/gui/cli/display.c b/src/gui/cli/display.c index e6f576d..f9b8813 100644 --- a/src/gui/cli/display.c +++ b/src/gui/cli/display.c @@ -3,13 +3,23 @@ #include "../../brick_game/tetris/00_tetris.h" void display_game() { - printf("DEBUG: display_game called\n"); clear(); GameInfo_t game_state = updateCurrentState(); - printf("DEBUG: Got game state, field: %p, next: %p\n", - game_state.field, game_state.next); + // Проверяем, является ли состояние GameOver + if (game_state.next[0][0] == 0 && game_state.next[0][1] == 0 && game_state.next[0][2] == 0 && game_state.next[0][3] == 0) { + mvprintw(FIELD_HEIGHT / 2, FIELD_WIDTH - 4, "GAME OVER"); + refresh(); + return; + } + + // Проверяем pause + if (game_state.pause) { + mvprintw(FIELD_HEIGHT / 2, FIELD_WIDTH - 4, "PAUSED"); + refresh(); + return; + } // Отображение игрового поля for (int i = 0; i < FIELD_HEIGHT; ++i) {