Всё фигня, давай по новой

This commit is contained in:
Rorikstr | Rust Dev 2025-09-29 13:42:58 +03:00
parent 485ac0ca40
commit f8e1e664a7
13 changed files with 199 additions and 479 deletions

View file

@ -1,10 +1,18 @@
#ifndef GAME_LOGIC_H
#define GAME_LOGIC_H
#ifndef AUTOMATO_H
#define AUTOMATO_H
#include "00_tetris.h"
#include <stdbool.h>
#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; // Поворот (03)
} 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

View file

@ -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;
}

View file

@ -0,0 +1,63 @@
#include "01_automato.h"
#include <stdlib.h>
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;
}

View file

@ -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;
}

View file

@ -0,0 +1,26 @@
#include "01_automato.h"
#include <stdlib.h>
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;
}

View file

@ -0,0 +1,8 @@
#include "01_automato.h"
void do_move(void) {
GameState_t* state = get_game_state();
// Проверка таймера и перемещение вниз
// Если hold Down — ускорение
// Если коллизия — переход в Attaching
}

View file

@ -0,0 +1,8 @@
#include "01_automato.h"
void do_moving(void) {
GameState_t* state = get_game_state();
// Обработка Left/Right/Action
// Проверка коллизии
// Возврат в Move или остаться в Moving
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -1,4 +1,4 @@
#include "game_logic.h"
#include "01_automato.h"
#include <string.h>
const int (*get_figure_shape(FigureType type, int rotation))[4] {

View file

@ -1,228 +0,0 @@
#include "tetris.h"
#include "game_logic.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
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();
}

View file

@ -1,218 +0,0 @@
#include "tetris.h"
#include "game_logic.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
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;
}