From f8e1e664a7473ea150e264ad3e0645a9b28ef422 Mon Sep 17 00:00:00 2001 From: Rorikstr | Rust Dev Date: Mon, 29 Sep 2025 13:42:58 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D1=81=D1=91=20=D1=84=D0=B8=D0=B3=D0=BD?= =?UTF-8?q?=D1=8F,=20=D0=B4=D0=B0=D0=B2=D0=B0=D0=B9=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tetris/{tetris.h => 00_tetris.h} | 0 .../tetris/{game_logic.h => 01_automato.h} | 71 +++--- src/brick_game/tetris/02_automato.c | 12 + src/brick_game/tetris/03_tetris.c | 63 +++++ src/brick_game/tetris/04_start.c | 14 ++ src/brick_game/tetris/05_spawn.c | 26 ++ src/brick_game/tetris/06_move.c | 8 + src/brick_game/tetris/07_moving.c | 8 + src/brick_game/tetris/08_attaching.c | 19 ++ src/brick_game/tetris/09_gameover.c | 9 + .../tetris/{figures.c => figure_sprites.c} | 2 +- src/brick_game/tetris/game_logic.c | 228 ------------------ src/brick_game/tetris/tetris.c | 218 ----------------- 13 files changed, 199 insertions(+), 479 deletions(-) rename src/brick_game/tetris/{tetris.h => 00_tetris.h} (100%) rename src/brick_game/tetris/{game_logic.h => 01_automato.h} (62%) create mode 100644 src/brick_game/tetris/02_automato.c create mode 100644 src/brick_game/tetris/03_tetris.c create mode 100644 src/brick_game/tetris/04_start.c create mode 100644 src/brick_game/tetris/05_spawn.c create mode 100644 src/brick_game/tetris/06_move.c create mode 100644 src/brick_game/tetris/07_moving.c create mode 100644 src/brick_game/tetris/08_attaching.c create mode 100644 src/brick_game/tetris/09_gameover.c rename src/brick_game/tetris/{figures.c => figure_sprites.c} (99%) delete mode 100644 src/brick_game/tetris/game_logic.c delete mode 100644 src/brick_game/tetris/tetris.c diff --git a/src/brick_game/tetris/tetris.h b/src/brick_game/tetris/00_tetris.h similarity index 100% rename from src/brick_game/tetris/tetris.h rename to src/brick_game/tetris/00_tetris.h diff --git a/src/brick_game/tetris/game_logic.h b/src/brick_game/tetris/01_automato.h similarity index 62% rename from src/brick_game/tetris/game_logic.h rename to src/brick_game/tetris/01_automato.h index e9fe7f6..0ad1f85 100644 --- a/src/brick_game/tetris/game_logic.h +++ b/src/brick_game/tetris/01_automato.h @@ -1,10 +1,18 @@ -#ifndef GAME_LOGIC_H -#define GAME_LOGIC_H +#ifndef AUTOMATO_H +#define AUTOMATO_H +#include "00_tetris.h" #include -#include "tetris.h" // для использования GameInfo_t -// Типы фигур +typedef enum { + Init, + Spawn, + Moving, + Move, + Attaching, + GameOver +} Automato_t; + typedef enum { I = 0, J, @@ -16,47 +24,45 @@ 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; // Позиция фигуры на поле + int mtrx[4][4]; // сама матрица FigureType type; // Тип фигуры int rotation; // Поворот (0–3) } Figure; typedef struct { - int game_field[FIELD_HEIGHT][FIELD_WIDTH]; - Figure current_figure; - Figure next_figure; - FSMState_t state; + Figure curr; + Figure next; + Automato_t state; + int field[FIELD_HEIGHT][FIELD_WIDTH]; int score; int high_score; int level; - long long drop_time; - bool game_over; - bool paused; -} GameStateData; + int speed; + long long last_time; +} GameState_t; -// Внутренние функции -bool check_collision(void); -void init_game(void); -void fsm_transition(void); -int get_random_figure(void); +GameState_t* get_game_state(void); + +// Функции состояний +void do_start(void); +void do_spawn(void); +void do_move(void); +void do_moving(void); +void do_attaching(void); +void do_gameover(void); + +// Вспомогательные +void place_figure_on_field(); +int check_collision(); +void clear_lines(); +int is_game_over(); + +// Функции фигур 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]; const int (*i_fig_right())[4]; const int (*i_fig_down())[4]; @@ -84,4 +90,5 @@ const int (*z_fig_down())[4]; const int (*z_fig_left())[4]; const int (*empty_fig())[4]; + #endif \ No newline at end of file diff --git a/src/brick_game/tetris/02_automato.c b/src/brick_game/tetris/02_automato.c new file mode 100644 index 0000000..8517f8e --- /dev/null +++ b/src/brick_game/tetris/02_automato.c @@ -0,0 +1,12 @@ +#include "01_automato.h" + +static GameState_t g_state = {0}; + +GameState_t* get_game_state(void) { + static int initialized = 0; + if (!initialized) { + g_state.state = GameOver; + initialized = 1; + } + return &g_state; +} \ No newline at end of file diff --git a/src/brick_game/tetris/03_tetris.c b/src/brick_game/tetris/03_tetris.c new file mode 100644 index 0000000..41a3f32 --- /dev/null +++ b/src/brick_game/tetris/03_tetris.c @@ -0,0 +1,63 @@ +#include "01_automato.h" +#include + +void userInput(UserAction_t action, bool hold) { + GameState_t* state = get_game_state(); + + switch (action) { + case Start: + state->state = Start; + break; + case Terminate: + if (state->score > state->high_score) { + state->high_score = state->score; + } + exit(0); + break; + case Left: + case Right: + case Action: + state->state = Moving; + break; + case Down: + // Ускорение падения — будет обрабатываться в do_move + break; + case Pause: + // Обработка на UI + break; + } +} + +GameInfo_t updateCurrentState() { + GameState_t* state = get_game_state(); + switch (state->state) { + case Start: + do_start(); + 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; + } + + GameInfo_t info = {0}; + info.field = (int**)state->field; + info.next = (int**)state->next.mtrx; // теперь next.mtrx + info.score = state->score; + info.high_score = state->high_score; + info.level = state->level; + info.speed = state->speed; + info.pause = 0; + return info; +} \ No newline at end of file diff --git a/src/brick_game/tetris/04_start.c b/src/brick_game/tetris/04_start.c new file mode 100644 index 0000000..4f04158 --- /dev/null +++ b/src/brick_game/tetris/04_start.c @@ -0,0 +1,14 @@ +#include "01_automato.h" + +void do_start(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; + + state->score = 0; + state->level = 1; + state->speed = 1; + state->state = Spawn; +} \ No newline at end of file diff --git a/src/brick_game/tetris/05_spawn.c b/src/brick_game/tetris/05_spawn.c new file mode 100644 index 0000000..ba50526 --- /dev/null +++ b/src/brick_game/tetris/05_spawn.c @@ -0,0 +1,26 @@ +#include "01_automato.h" +#include + +void do_spawn(void) { + GameState_t* state = get_game_state(); + // Присваиваем curr = next + state->curr = state->next; + state->curr.x = FIELD_WIDTH / 2 - 2; + state->curr.y = 0; + + // Генерим следующую фигуру + state->next.type = rand() % FIGURE_COUNT; + state->next.rotation = 0; + const int (*shape)[4] = get_figure_shape(state->next.type, 0); + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + state->next.mtrx[i][j] = shape[i][j]; + + // Проверка на GameOver + if (check_collision()) { + state->state = GameOver; + return; + } + + state->state = Move; +} \ No newline at end of file diff --git a/src/brick_game/tetris/06_move.c b/src/brick_game/tetris/06_move.c new file mode 100644 index 0000000..078c851 --- /dev/null +++ b/src/brick_game/tetris/06_move.c @@ -0,0 +1,8 @@ +#include "01_automato.h" + +void do_move(void) { + GameState_t* state = get_game_state(); + // Проверка таймера и перемещение вниз + // Если hold Down — ускорение + // Если коллизия — переход в 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 new file mode 100644 index 0000000..1842b4f --- /dev/null +++ b/src/brick_game/tetris/07_moving.c @@ -0,0 +1,8 @@ +#include "01_automato.h" + +void do_moving(void) { + GameState_t* state = get_game_state(); + // Обработка Left/Right/Action + // Проверка коллизии + // Возврат в Move или остаться в Moving +} \ No newline at end of file diff --git a/src/brick_game/tetris/08_attaching.c b/src/brick_game/tetris/08_attaching.c new file mode 100644 index 0000000..2fa4c35 --- /dev/null +++ b/src/brick_game/tetris/08_attaching.c @@ -0,0 +1,19 @@ +#include "01_automato.h" + +void do_attaching(void) { + GameState_t* state = get_game_state(); + // Закрепляем фигуру на поле + place_figure_on_field(); + + // Удаляем линии + clear_lines(); + + // Проверяем GameOver + if (is_game_over()) { + state->state = GameOver; + return; + } + + // Переход в Spawn + state->state = Spawn; +} \ No newline at end of file diff --git a/src/brick_game/tetris/09_gameover.c b/src/brick_game/tetris/09_gameover.c new file mode 100644 index 0000000..58f515e --- /dev/null +++ b/src/brick_game/tetris/09_gameover.c @@ -0,0 +1,9 @@ +#include "01_automato.h" + +void do_gameover(void) { + GameState_t* state = get_game_state(); + // Сброс next в пустую фигуру + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + state->next.mtrx[i][j] = 0; +} \ No newline at end of file diff --git a/src/brick_game/tetris/figures.c b/src/brick_game/tetris/figure_sprites.c similarity index 99% rename from src/brick_game/tetris/figures.c rename to src/brick_game/tetris/figure_sprites.c index 8ee16a1..d22bd09 100644 --- a/src/brick_game/tetris/figures.c +++ b/src/brick_game/tetris/figure_sprites.c @@ -1,4 +1,4 @@ -#include "game_logic.h" +#include "01_automato.h" #include const int (*get_figure_shape(FigureType type, int rotation))[4] { diff --git a/src/brick_game/tetris/game_logic.c b/src/brick_game/tetris/game_logic.c deleted file mode 100644 index 77dcd04..0000000 --- a/src/brick_game/tetris/game_logic.c +++ /dev/null @@ -1,228 +0,0 @@ -#include "tetris.h" -#include "game_logic.h" -#include -#include -#include -#include - -static GameStateData* get_instance() { - static GameStateData instance = {0}; - static bool initialized_local = false; - - if (!initialized_local) { - // Инициализация экземпляра - for (int i = 0; i < FIELD_HEIGHT; i++) { - for (int j = 0; j < FIELD_WIDTH; j++) { - instance.game_field[i][j] = 0; - } - } - instance.score = 0; - instance.high_score = 0; - instance.level = 1; - instance.state = FSM_Start; - instance.game_over = false; - instance.paused = false; - initialized_local = true; - } - - return &instance; -} - -void init_game() { - GameStateData* state = get_instance(); - - // Инициализация игрового поля - for (int i = 0; i < FIELD_HEIGHT; i++) { - for (int j = 0; j < FIELD_WIDTH; j++) { - state->game_field[i][j] = 0; - } - } - - srand((unsigned int)time(NULL)); - - state->next_figure.type = get_random_figure(); - state->next_figure.x = 0; - state->next_figure.y = 0; - state->next_figure.rotation = 0; - - state->score = 0; - state->high_score = 0; - state->level = 1; - state->drop_time = time(NULL) * 1000; - state->game_over = false; - state->paused = false; - state->state = FSM_Start; // Начинаем в состоянии FSM_Start -} - -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(); - - // Спавним новую фигуру - 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; - } -} - -void clear_completed_lines() { - GameStateData* state = get_instance(); - int lines_cleared = 0; - int write_row = FIELD_HEIGHT - 1; - - // Проходим снизу вверх - for (int read_row = FIELD_HEIGHT - 1; read_row >= 0; read_row--) { - bool line_complete = true; - for (int j = 0; j < FIELD_WIDTH; j++) { - if (state->game_field[read_row][j] == 0) { - line_complete = false; - break; - } - } - - if (line_complete) { - lines_cleared++; - } else { - // Копируем неполные строки вниз - if (write_row != read_row) { - for (int j = 0; j < FIELD_WIDTH; j++) { - state->game_field[write_row][j] = state->game_field[read_row][j]; - } - } - write_row--; - } - } - - // Заполняем пустые строки наверху - for (int i = 0; i <= write_row; i++) { - for (int j = 0; j < FIELD_WIDTH; j++) { - state->game_field[i][j] = 0; - } - } - - if (lines_cleared > 0) { - // Обновляем счет в соответствии с количеством очищенных линий - int points[] = {0, 100, 300, 700, 1500}; // 0, 1, 2, 3, 4 линии - int score_points = 0; - if (lines_cleared <= 4) { - score_points = points[lines_cleared]; - } else { - score_points = points[4]; // для > 4 линий - } - state->score += score_points; // УБРАЛ УМНОЖЕНИЕ НА УРОВЕНЬ - - if (state->score > state->high_score) { - state->high_score = state->score; - } - - // Увеличиваем уровень каждые 600 очков, максимум 10 - int new_level = state->score / 600 + 1; - if (new_level > 10) new_level = 10; - if (new_level != state->level) { - state->level = new_level; - } - } -} - -int get_random_figure() { - return rand() % FIGURE_COUNT; -} - -bool check_collision() { - GameStateData* state = get_instance(); - Figure* f = &state->current_figure; - const int (*shape)[4] = get_figure_shape(f->type, f->rotation); - bool collision = false; - - for (int i = 0; !collision && i < 4; i++) { - for (int j = 0; !collision && j < 4; j++) { - if (shape[i][j]) { - int x = f->x + j; - int y = f->y + i; - - if (x < 0 || x >= FIELD_WIDTH || y >= FIELD_HEIGHT - || (y >= 0 && state->game_field[y][x] != 0)) { - collision = true; - } - } - } - } - return collision; -} - -void fsm_transition() { - GameStateData* state = get_instance(); - - switch (state->state) { - case FSM_Start: - // Ожидание начала игры - не делаем ничего, пока не будет нажата Start - 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; - } - 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(); - break; - - case FSM_GameOver: - break; - } -} - -GameStateData* get_game_state() { - return get_instance(); -} \ No newline at end of file diff --git a/src/brick_game/tetris/tetris.c b/src/brick_game/tetris/tetris.c deleted file mode 100644 index 466362b..0000000 --- a/src/brick_game/tetris/tetris.c +++ /dev/null @@ -1,218 +0,0 @@ -#include "tetris.h" -#include "game_logic.h" -#include -#include -#include -#include - -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++) { - instance.field[i] = malloc(FIELD_WIDTH * sizeof(int)); - } - - instance.next = malloc(4 * sizeof(int*)); - for (int i = 0; i < 4; i++) { - 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; -} - -void userInput(UserAction_t action, bool hold) { - GameStateData* state = get_game_state(); - GameInfo_t* game_info = get_game_info_instance(); - - switch (action) { - case Terminate: - // Освобождаем память при завершении - if (game_info->field != NULL) { - for (int i = 0; i < FIELD_HEIGHT; i++) { - if (game_info->field[i] != NULL) { - free(game_info->field[i]); - game_info->field[i] = NULL; - } - } - free(game_info->field); - game_info->field = NULL; - } - - if (game_info->next != NULL) { - for (int i = 0; i < 4; i++) { - if (game_info->next[i] != NULL) { - free(game_info->next[i]); - game_info->next[i] = NULL; - } - } - free(game_info->next); - game_info->next = NULL; - } - return; // ВАЖНО: выходим из функции, не делаем ничего после Terminate - - case Start: - if (state->state == FSM_GameOver) { - // Перезапуск игры после Game Over - int saved_high_score = state->high_score; - init_game(); - state->high_score = saved_high_score; - // Не меняем состояние, пусть остается в FSM_Start до следующего нажатия - } else if (state->state == FSM_Start) { - // Начинаем игру из состояния Start - state->state = FSM_Spawn; - } else { - // Для всех других состояний (Moving, Move) - ставим на паузу - state->paused = !state->paused; - } - break; - - case Pause: - state->paused = !state->paused; - break; - - default: - if (state->state == FSM_GameOver || state->paused || state->state == FSM_Start) { - 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() { - GameStateData* state = get_game_state(); - GameInfo_t* game_info = get_game_info_instance(); - - if (state->state != FSM_Start && !state->game_over && !state->paused) { - long long current_time = time(NULL) * 1000; - - // Определяем интервал падения в зависимости от уровня - 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 (но не в состоянии Start) - if (state->state != FSM_Start) { - 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]; - } - } - - // Добавляем активную фигуру на поле для отображения (если не в Start или GameOver) - 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++) { - for (int j = 0; j < 4; j++) { - if (shape[i][j]) { - int x = f->x + j; - int y = f->y + i; - // Проверяем границы перед записью - if (x >= 0 && x < FIELD_WIDTH && y >= 0 && y < FIELD_HEIGHT) { - // Не перезаписываем уже зафиксированные блоки - if (state->game_field[y][x] == 0) { - game_info->field[y][x] = 1; - } - } - } - } - } - } - - // Обновляем next - const int (*next_shape)[4]; - if (state->state == FSM_GameOver) { - // При game_over показываем пустую фигуру - next_shape = empty_fig(); - } else { - next_shape = get_figure_shape(state->next_figure.type, state->next_figure.rotation); - } - - 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->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; -} \ No newline at end of file