it's working but should fix return issue
This commit is contained in:
parent
1911d23459
commit
9de308925c
7 changed files with 609 additions and 212 deletions
|
|
@ -1,4 +1,64 @@
|
||||||
#include "tetris.h"
|
#include "game_logic.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
const int (*get_figure_shape(FigureType type, int rotation))[4] {
|
||||||
|
const int (*result)[4] = NULL;
|
||||||
|
switch (type) {
|
||||||
|
case I:
|
||||||
|
switch (rotation % 4) {
|
||||||
|
case 0: result = i_fig_up(); break;
|
||||||
|
case 1: result = i_fig_right(); break;
|
||||||
|
case 2: result = i_fig_down(); break;
|
||||||
|
case 3: result = i_fig_left(); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case O:
|
||||||
|
result = o_fig();
|
||||||
|
break;
|
||||||
|
case T:
|
||||||
|
switch (rotation % 4) {
|
||||||
|
case 0: result = t_fig_up(); break;
|
||||||
|
case 1: result = t_fig_right(); break;
|
||||||
|
case 2: result = t_fig_down(); break;
|
||||||
|
case 3: result = t_fig_left(); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case L:
|
||||||
|
switch (rotation % 4) {
|
||||||
|
case 0: result = l_fig_up(); break;
|
||||||
|
case 1: result = l_fig_right(); break;
|
||||||
|
case 2: result = l_fig_down(); break;
|
||||||
|
case 3: result = l_fig_left(); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case J:
|
||||||
|
switch (rotation % 4) {
|
||||||
|
case 0: result = j_fig_up(); break;
|
||||||
|
case 1: result = j_fig_right(); break;
|
||||||
|
case 2: result = j_fig_down(); break;
|
||||||
|
case 3: result = j_fig_left(); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case S:
|
||||||
|
switch (rotation % 4) {
|
||||||
|
case 0: result = s_fig_up(); break;
|
||||||
|
case 1: result = s_fig_right(); break;
|
||||||
|
case 2: result = s_fig_down(); break;
|
||||||
|
case 3: result = s_fig_left(); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Z:
|
||||||
|
switch (rotation % 4) {
|
||||||
|
case 0: result = z_fig_up(); break;
|
||||||
|
case 1: result = z_fig_right(); break;
|
||||||
|
case 2: result = z_fig_down(); break;
|
||||||
|
case 3: result = z_fig_left(); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: result = NULL;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// I
|
// I
|
||||||
const int (*i_fig_up())[4] {
|
const int (*i_fig_up())[4] {
|
||||||
|
|
@ -256,3 +316,13 @@ const int (*z_fig_left())[4] {
|
||||||
};
|
};
|
||||||
return (const int (*)[4])shape;
|
return (const int (*)[4])shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int (*empty_fig())[4] {
|
||||||
|
static const int shape[4][4] = {
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
return (const int (*)[4])shape;
|
||||||
|
}
|
||||||
216
src/brick_game/tetris/game_logic.c
Normal file
216
src/brick_game/tetris/game_logic.c
Normal file
|
|
@ -0,0 +1,216 @@
|
||||||
|
#include "tetris.h"
|
||||||
|
#include "game_logic.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
// static bool initialized = false;
|
||||||
|
|
||||||
|
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.speed = 1000;
|
||||||
|
instance.figure_active = false;
|
||||||
|
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->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->level = 1;
|
||||||
|
state->speed = 1000;
|
||||||
|
state->drop_time = time(NULL) * 1000;
|
||||||
|
state->game_over = false;
|
||||||
|
state->paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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 * state->level;
|
||||||
|
|
||||||
|
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;
|
||||||
|
// Увеличиваем скорость (уменьшаем время падения)
|
||||||
|
state->speed = 1000 - (state->level - 1) * 50;
|
||||||
|
if (state->speed < 100) state->speed = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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->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();
|
||||||
|
} else {
|
||||||
|
state->drop_time = current_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GameStateData* get_game_state() {
|
||||||
|
return get_instance();
|
||||||
|
}
|
||||||
77
src/brick_game/tetris/game_logic.h
Normal file
77
src/brick_game/tetris/game_logic.h
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
#ifndef GAME_LOGIC_H
|
||||||
|
#define GAME_LOGIC_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "tetris.h" // для использования GameInfo_t
|
||||||
|
|
||||||
|
// Типы фигур
|
||||||
|
typedef enum {
|
||||||
|
I = 0,
|
||||||
|
J,
|
||||||
|
L,
|
||||||
|
O,
|
||||||
|
S,
|
||||||
|
T,
|
||||||
|
Z,
|
||||||
|
FIGURE_COUNT
|
||||||
|
} FigureType;
|
||||||
|
|
||||||
|
// Структура фигуры
|
||||||
|
typedef struct {
|
||||||
|
int x, y; // Позиция фигуры на поле
|
||||||
|
FigureType type; // Тип фигуры
|
||||||
|
int rotation; // Поворот (0–3)
|
||||||
|
} Figure;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int game_field[FIELD_HEIGHT][FIELD_WIDTH];
|
||||||
|
Figure current_figure;
|
||||||
|
Figure next_figure;
|
||||||
|
bool figure_active;
|
||||||
|
int score;
|
||||||
|
int high_score;
|
||||||
|
int level;
|
||||||
|
int speed;
|
||||||
|
long long drop_time;
|
||||||
|
bool game_over;
|
||||||
|
bool paused;
|
||||||
|
} GameStateData;
|
||||||
|
|
||||||
|
// Внутренние функции
|
||||||
|
bool check_collision(void);
|
||||||
|
void init_game(void);
|
||||||
|
void update_game_state(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);
|
||||||
|
|
||||||
|
// Фигуры
|
||||||
|
const int (*i_fig_up())[4];
|
||||||
|
const int (*i_fig_right())[4];
|
||||||
|
const int (*i_fig_down())[4];
|
||||||
|
const int (*i_fig_left())[4];
|
||||||
|
const int (*o_fig())[4];
|
||||||
|
const int (*t_fig_up())[4];
|
||||||
|
const int (*t_fig_right())[4];
|
||||||
|
const int (*t_fig_down())[4];
|
||||||
|
const int (*t_fig_left())[4];
|
||||||
|
const int (*l_fig_up())[4];
|
||||||
|
const int (*l_fig_right())[4];
|
||||||
|
const int (*l_fig_down())[4];
|
||||||
|
const int (*l_fig_left())[4];
|
||||||
|
const int (*j_fig_up())[4];
|
||||||
|
const int (*j_fig_right())[4];
|
||||||
|
const int (*j_fig_down())[4];
|
||||||
|
const int (*j_fig_left())[4];
|
||||||
|
const int (*s_fig_up())[4];
|
||||||
|
const int (*s_fig_right())[4];
|
||||||
|
const int (*s_fig_down())[4];
|
||||||
|
const int (*s_fig_left())[4];
|
||||||
|
const int (*z_fig_up())[4];
|
||||||
|
const int (*z_fig_right())[4];
|
||||||
|
const int (*z_fig_down())[4];
|
||||||
|
const int (*z_fig_left())[4];
|
||||||
|
const int (*empty_fig())[4];
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,126 +1,191 @@
|
||||||
// src/brick_game/tetris/tetris.c
|
// src/brick_game/tetris/tetris.c
|
||||||
#include "tetris.h"
|
#include "tetris.h"
|
||||||
|
#include "game_logic.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
static GameStateData game_state = {0};
|
static GameInfo_t game_info = {0};
|
||||||
static bool initialized = false;
|
static bool initialized = false; // Добавляем эту переменную
|
||||||
|
|
||||||
const int (*get_figure_shape(FigureType type, int rotation))[4] {
|
void init_game_info() {
|
||||||
const int (*result)[4] = NULL;
|
if (game_info.field == NULL) {
|
||||||
switch (type) {
|
game_info.field = malloc(FIELD_HEIGHT * sizeof(int*));
|
||||||
case I:
|
for (int i = 0; i < FIELD_HEIGHT; i++) {
|
||||||
switch (rotation % 4) {
|
game_info.field[i] = malloc(FIELD_WIDTH * sizeof(int));
|
||||||
case 0: result = i_fig_up(); break;
|
|
||||||
case 1: result = i_fig_right(); break;
|
|
||||||
case 2: result = i_fig_down(); break;
|
|
||||||
case 3: result = i_fig_left(); break;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case O:
|
|
||||||
result = o_fig();
|
|
||||||
break;
|
|
||||||
case T:
|
|
||||||
switch (rotation % 4) {
|
|
||||||
case 0: result = t_fig_up(); break;
|
|
||||||
case 1: result = t_fig_right(); break;
|
|
||||||
case 2: result = t_fig_down(); break;
|
|
||||||
case 3: result = t_fig_left(); break;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case L:
|
if (game_info.next == NULL) {
|
||||||
switch (rotation % 4) {
|
game_info.next = malloc(4 * sizeof(int*));
|
||||||
case 0: result = l_fig_up(); break;
|
for (int i = 0; i < 4; i++) {
|
||||||
case 1: result = l_fig_right(); break;
|
game_info.next[i] = malloc(4 * sizeof(int));
|
||||||
case 2: result = l_fig_down(); break;
|
|
||||||
case 3: result = l_fig_left(); break;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case J:
|
|
||||||
switch (rotation % 4) {
|
|
||||||
case 0: result = j_fig_up(); break;
|
|
||||||
case 1: result = j_fig_right(); break;
|
|
||||||
case 2: result = j_fig_down(); break;
|
|
||||||
case 3: result = j_fig_left(); break;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case S:
|
// Инициализация полей
|
||||||
switch (rotation % 4) {
|
for (int i = 0; i < FIELD_HEIGHT; i++) {
|
||||||
case 0: result = s_fig_up(); break;
|
for (int j = 0; j < FIELD_WIDTH; j++) {
|
||||||
case 1: result = s_fig_right(); break;
|
game_info.field[i][j] = 0;
|
||||||
case 2: result = s_fig_down(); break;
|
|
||||||
case 3: result = s_fig_left(); break;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case Z:
|
|
||||||
switch (rotation % 4) {
|
|
||||||
case 0: result = z_fig_up(); break;
|
|
||||||
case 1: result = z_fig_right(); break;
|
|
||||||
case 2: result = z_fig_down(); break;
|
|
||||||
case 3: result = z_fig_left(); break;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default: result = NULL;
|
for (int i = 0; i < 4; i++) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
game_info.next[i][j] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void user_input(UserAction_t action) {
|
void userInput(UserAction_t action, bool hold) {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
memset(&game_state, 0, sizeof(game_state));
|
init_game_info();
|
||||||
|
init_game();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Figure1 <= action && action <= Figure7) {
|
GameStateData* state = get_game_state();
|
||||||
FigureType type = (FigureType)(action - Figure1);
|
|
||||||
game_state.current_figure.type = type;
|
if (action == Start) {
|
||||||
game_state.current_figure.x = FIELD_WIDTH / 2 - 2;
|
if (state->game_over) {
|
||||||
game_state.current_figure.y = 0;
|
init_game(); // Перезапуск игры
|
||||||
game_state.current_figure.rotation = 0;
|
} else if (!state->paused) {
|
||||||
game_state.figure_active = true;
|
state->paused = true;
|
||||||
|
} else {
|
||||||
|
state->paused = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game_state.figure_active) {
|
if (action == Pause) {
|
||||||
int old_x = game_state.current_figure.x;
|
state->paused = !state->paused;
|
||||||
int old_y = game_state.current_figure.y;
|
return;
|
||||||
int old_rot = game_state.current_figure.rotation;
|
}
|
||||||
|
|
||||||
if (action == Left) game_state.current_figure.x--;
|
if (action == Terminate) {
|
||||||
if (action == Right) game_state.current_figure.x++;
|
return;
|
||||||
if (action == Down) game_state.current_figure.y++;
|
}
|
||||||
if (action == Up) game_state.current_figure.y--;
|
|
||||||
if (action == Rotate) {
|
if (state->game_over || state->paused) {
|
||||||
game_state.current_figure.rotation = (game_state.current_figure.rotation + 1) % 4;
|
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;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_collision()) {
|
if (check_collision()) {
|
||||||
// Возвращаем старые значения
|
// Возвращаем старые значения
|
||||||
game_state.current_figure.x = old_x;
|
state->current_figure.x = old_x;
|
||||||
game_state.current_figure.y = old_y;
|
state->current_figure.y = old_y;
|
||||||
game_state.current_figure.rotation = old_rot;
|
state->current_figure.rotation = old_rot;
|
||||||
|
} else if (action == Down) {
|
||||||
|
// Если перемещение вниз успешно, обновляем время падения
|
||||||
|
state->drop_time = time(NULL) * 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GameStateData* get_game_state() {
|
GameInfo_t updateCurrentState() {
|
||||||
return &game_state;
|
if (!initialized) {
|
||||||
}
|
init_game_info();
|
||||||
|
init_game();
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool check_collision() {
|
GameStateData* state = get_game_state();
|
||||||
Figure* f = &game_state.current_figure;
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем 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_over)
|
||||||
|
if (state->figure_active && !state->game_over) {
|
||||||
|
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++) {
|
||||||
for (int j = 0; j < 4; j++) {
|
for (int j = 0; j < 4; j++) {
|
||||||
if (shape[i][j]) {
|
if (shape[i][j]) {
|
||||||
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 >= FIELD_HEIGHT) {
|
if (x >= 0 && x < FIELD_WIDTH && y >= 0 && y < FIELD_HEIGHT) {
|
||||||
return true; // Коллизия
|
game_info.field[y][x] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false; // Нет коллизии
|
}
|
||||||
|
|
||||||
|
// Обновляем next
|
||||||
|
const int (*next_shape)[4];
|
||||||
|
if (state->game_over) {
|
||||||
|
// При 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 = state->speed;
|
||||||
|
game_info.pause = state->paused ? 1 : 0;
|
||||||
|
|
||||||
|
return game_info;
|
||||||
}
|
}
|
||||||
|
|
@ -1,41 +1,12 @@
|
||||||
// src/brick_game/tetris/tetris.h
|
|
||||||
#ifndef TETRIS_H
|
#ifndef TETRIS_H
|
||||||
#define TETRIS_H
|
#define TETRIS_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
// Константы
|
|
||||||
#define FIELD_WIDTH 10
|
#define FIELD_WIDTH 10
|
||||||
#define FIELD_HEIGHT 20
|
#define FIELD_HEIGHT 20
|
||||||
|
|
||||||
// Типы фигур
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
I = 0,
|
|
||||||
J,
|
|
||||||
L,
|
|
||||||
O,
|
|
||||||
S,
|
|
||||||
T,
|
|
||||||
Z,
|
|
||||||
FIGURE_COUNT
|
|
||||||
} FigureType;
|
|
||||||
|
|
||||||
// Структура фигуры
|
|
||||||
typedef struct {
|
|
||||||
int x, y; // Позиция фигуры на поле
|
|
||||||
FigureType type; // Тип фигуры
|
|
||||||
int rotation; // Поворот (0–3)
|
|
||||||
} Figure;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int field[FIELD_HEIGHT][FIELD_WIDTH]; // Игровое поле
|
|
||||||
Figure current_figure; // Текущая фигура
|
|
||||||
bool figure_active; // Есть активная фигура?
|
|
||||||
} GameStateData;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
Undefined = -1,
|
|
||||||
Start,
|
Start,
|
||||||
Pause,
|
Pause,
|
||||||
Terminate,
|
Terminate,
|
||||||
|
|
@ -43,53 +14,20 @@ typedef enum {
|
||||||
Right,
|
Right,
|
||||||
Up,
|
Up,
|
||||||
Down,
|
Down,
|
||||||
Rotate,
|
Action
|
||||||
Action,
|
|
||||||
Figure1,
|
|
||||||
Figure2,
|
|
||||||
Figure3,
|
|
||||||
Figure4,
|
|
||||||
Figure5,
|
|
||||||
Figure6,
|
|
||||||
Figure7
|
|
||||||
} UserAction_t;
|
} UserAction_t;
|
||||||
|
|
||||||
void user_input(UserAction_t action);
|
typedef struct {
|
||||||
static bool check_collision();
|
int **field;
|
||||||
GameStateData* get_game_state(void);
|
int **next;
|
||||||
|
int score;
|
||||||
|
int high_score;
|
||||||
|
int level;
|
||||||
|
int speed;
|
||||||
|
int pause;
|
||||||
|
} GameInfo_t;
|
||||||
|
|
||||||
const int (*get_figure_shape(FigureType type, int rotation))[4];
|
void userInput(UserAction_t action, bool hold);
|
||||||
|
GameInfo_t updateCurrentState();
|
||||||
const int (*i_fig_up())[4];
|
|
||||||
const int (*i_fig_right())[4];
|
|
||||||
const int (*i_fig_down())[4];
|
|
||||||
const int (*i_fig_left())[4];
|
|
||||||
|
|
||||||
const int (*o_fig())[4];
|
|
||||||
|
|
||||||
const int (*t_fig_up())[4];
|
|
||||||
const int (*t_fig_right())[4];
|
|
||||||
const int (*t_fig_down())[4];
|
|
||||||
const int (*t_fig_left())[4];
|
|
||||||
|
|
||||||
const int (*l_fig_up())[4];
|
|
||||||
const int (*l_fig_right())[4];
|
|
||||||
const int (*l_fig_down())[4];
|
|
||||||
const int (*l_fig_left())[4];
|
|
||||||
|
|
||||||
const int (*j_fig_up())[4];
|
|
||||||
const int (*j_fig_right())[4];
|
|
||||||
const int (*j_fig_down())[4];
|
|
||||||
const int (*j_fig_left())[4];
|
|
||||||
|
|
||||||
const int (*s_fig_up())[4];
|
|
||||||
const int (*s_fig_right())[4];
|
|
||||||
const int (*s_fig_down())[4];
|
|
||||||
const int (*s_fig_left())[4];
|
|
||||||
|
|
||||||
const int (*z_fig_up())[4];
|
|
||||||
const int (*z_fig_right())[4];
|
|
||||||
const int (*z_fig_down())[4];
|
|
||||||
const int (*z_fig_left())[4];
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -5,30 +5,39 @@
|
||||||
void display_game() {
|
void display_game() {
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
GameStateData* state = get_game_state();
|
GameInfo_t game_state = updateCurrentState();
|
||||||
|
|
||||||
// Очистка поля
|
// Отображение игрового поля
|
||||||
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++) {
|
||||||
mvaddch(i + 1, j * 2 + 1, '.');
|
if (game_state.field[i][j] != 0) {
|
||||||
|
mvaddch(i + 1, j * 2 + 1, '#'); // Заполненные блоки
|
||||||
|
} else {
|
||||||
|
mvaddch(i + 1, j * 2 + 1, '.'); // Пустые ячейки
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если фигура активна — отображаем её
|
// Отображение следующей фигуры
|
||||||
if (state->figure_active) {
|
mvaddstr(1, FIELD_WIDTH * 2 + 5, "Next figure:");
|
||||||
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 i = 0; i < 4; i++) {
|
||||||
for (int j = 0; j < 4; j++) {
|
for (int j = 0; j < 4; j++) {
|
||||||
if (shape[i][j]) {
|
if (game_state.next[i][j]) {
|
||||||
int x = f->x + j;
|
mvaddch(i + 3, (FIELD_WIDTH * 2 + 5) + j * 2, '@');
|
||||||
int y = f->y + i;
|
} else {
|
||||||
if (x >= 0 && x < FIELD_WIDTH && y >= 0 && y < FIELD_HEIGHT) {
|
mvaddch(i + 3, (FIELD_WIDTH * 2 + 5) + j * 2, ' ');
|
||||||
mvaddch(y + 1, x * 2 + 1, '$');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mvprintw(FIELD_HEIGHT + 2, 1, "Score: %d", game_state.score);
|
||||||
|
mvprintw(FIELD_HEIGHT + 3, 1, "High Score: %d", game_state.high_score);
|
||||||
|
mvprintw(FIELD_HEIGHT + 4, 1, "Level: %d", game_state.level);
|
||||||
|
mvprintw(FIELD_HEIGHT + 5, 1, "Speed: %d", game_state.speed);
|
||||||
|
|
||||||
|
if (game_state.pause) {
|
||||||
|
mvprintw(FIELD_HEIGHT / 2, FIELD_WIDTH - 4, "PAUSED");
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
|
||||||
|
|
@ -14,35 +14,57 @@ int main() {
|
||||||
nodelay(stdscr, TRUE);
|
nodelay(stdscr, TRUE);
|
||||||
curs_set(0);
|
curs_set(0);
|
||||||
|
|
||||||
timeout(100); // Таймаут для getch()
|
timeout(100);
|
||||||
|
|
||||||
int ch;
|
int ch;
|
||||||
UserAction_t action = Undefined;
|
UserAction_t current_action;
|
||||||
while (Terminate != action) {
|
bool action_valid = false;
|
||||||
|
bool running = true;
|
||||||
|
|
||||||
|
while (running) {
|
||||||
ch = getch();
|
ch = getch();
|
||||||
|
action_valid = false;
|
||||||
|
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'q': action = Terminate; break;
|
case 'q':
|
||||||
case '1': action = Figure1; break;
|
userInput(Terminate, false);
|
||||||
case '2': action = Figure2; break;
|
running = false;
|
||||||
case '3': action = Figure3; break;
|
break;
|
||||||
case '4': action = Figure4; break;
|
case 'r': case ' ':
|
||||||
case '5': action = Figure5; break;
|
current_action = Action;
|
||||||
case '6': action = Figure6; break;
|
action_valid = true;
|
||||||
case '7': action = Figure7; break;
|
break;
|
||||||
case 'r': action = Rotate; break;
|
case KEY_LEFT:
|
||||||
case ' ': action = Rotate; break;
|
current_action = Left;
|
||||||
case KEY_LEFT: action = Left; break;
|
action_valid = true;
|
||||||
case KEY_RIGHT: action = Right; break;
|
break;
|
||||||
case KEY_DOWN: action = Down; break;
|
case KEY_RIGHT:
|
||||||
case KEY_UP: action = Up; break;
|
current_action = Right;
|
||||||
default: action = Undefined;
|
action_valid = true;
|
||||||
|
break;
|
||||||
|
case KEY_DOWN:
|
||||||
|
current_action = Down;
|
||||||
|
action_valid = true;
|
||||||
|
break;
|
||||||
|
case KEY_UP:
|
||||||
|
current_action = Up;
|
||||||
|
action_valid = true;
|
||||||
|
break;
|
||||||
|
case 's': case 'S':
|
||||||
|
current_action = Start;
|
||||||
|
action_valid = true;
|
||||||
|
break;
|
||||||
|
case 'p': case 'P':
|
||||||
|
current_action = Pause;
|
||||||
|
action_valid = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action != Undefined) {
|
if (action_valid) {
|
||||||
user_input(action);
|
userInput(current_action, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateCurrentState();
|
||||||
display_game();
|
display_game();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue