133 lines
3.6 KiB
C
133 lines
3.6 KiB
C
|
#include <stdlib.h>
|
||
|
#include <time.h>
|
||
|
#include <string.h>
|
||
|
#include "world.h"
|
||
|
|
||
|
#define EMPTY ' '
|
||
|
#define BLOCK '#'
|
||
|
#define WIDTH 10
|
||
|
#define HEIGHT 20
|
||
|
|
||
|
typedef struct {
|
||
|
int x, y;
|
||
|
} Point;
|
||
|
|
||
|
typedef struct {
|
||
|
Point blocks[4];
|
||
|
Point position;
|
||
|
} Tetromino;
|
||
|
|
||
|
typedef struct {
|
||
|
char grid[HEIGHT][WIDTH];
|
||
|
Tetromino current;
|
||
|
} GameState;
|
||
|
|
||
|
Tetromino tetrominoes[] = {
|
||
|
{{{0,0}, {1,0}, {0,1}, {1,1}}, {0,0}}, // Square
|
||
|
{{{0,0}, {1,0}, {2,0}, {3,0}}, {0,0}}, // Line
|
||
|
{{{0,0}, {1,0}, {1,1}, {2,1}}, {0,0}}, // Z shape
|
||
|
{{{1,0}, {2,0}, {0,1}, {1,1}}, {0,0}}, // S shape
|
||
|
{{{0,0}, {1,0}, {2,0}, {1,1}}, {0,0}}, // T shape
|
||
|
{{{0,0}, {0,1}, {1,1}, {2,1}}, {0,0}}, // L shape
|
||
|
{{{2,0}, {0,1}, {1,1}, {2,1}}, {0,0}} // J shape
|
||
|
};
|
||
|
|
||
|
void draw_grid(GameState* game) {
|
||
|
clear_world();
|
||
|
for (int y = 0; y < HEIGHT; ++y) {
|
||
|
for (int x = 0; x < WIDTH; ++x) {
|
||
|
draw_character(game->grid[y][x], x, y);
|
||
|
}
|
||
|
}
|
||
|
for (int i = 0; i < 4; ++i) {
|
||
|
draw_character(BLOCK, game->current.position.x + game->current.blocks[i].x,
|
||
|
game->current.position.y + game->current.blocks[i].y);
|
||
|
}
|
||
|
refresh_world();
|
||
|
}
|
||
|
|
||
|
int is_valid_position(GameState* game, Point new_position) {
|
||
|
for (int i = 0; i < 4; ++i) {
|
||
|
int new_x = new_position.x + game->current.blocks[i].x;
|
||
|
int new_y = new_position.y + game->current.blocks[i].y;
|
||
|
if (new_x < 0 || new_x >= WIDTH || new_y < 0 || new_y >= HEIGHT || game->grid[new_y][new_x] != EMPTY) {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
void place_tetromino(GameState* game) {
|
||
|
for (int i = 0; i < 4; ++i) {
|
||
|
int x = game->current.position.x + game->current.blocks[i].x;
|
||
|
int y = game->current.position.y + game->current.blocks[i].y;
|
||
|
game->grid[y][x] = BLOCK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void clear_lines(GameState* game) {
|
||
|
for (int y = HEIGHT - 1; y >= 0; --y) {
|
||
|
int full = 1;
|
||
|
for (int x = 0; x < WIDTH; ++x) {
|
||
|
if (game->grid[y][x] == EMPTY) {
|
||
|
full = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (full) {
|
||
|
for (int row = y; row > 0; --row) {
|
||
|
memcpy(game->grid[row], game->grid[row - 1], WIDTH);
|
||
|
}
|
||
|
memset(game->grid[0], EMPTY, WIDTH);
|
||
|
y++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void* init_game() {
|
||
|
srand(time(NULL));
|
||
|
GameState* game = malloc(sizeof(GameState));
|
||
|
memset(game->grid, EMPTY, sizeof(game->grid));
|
||
|
game->current = tetrominoes[rand() % 7];
|
||
|
game->current.position.x = WIDTH / 2 - 1;
|
||
|
game->current.position.y = 0;
|
||
|
return game;
|
||
|
}
|
||
|
|
||
|
int game_event(struct event* event, void* state) {
|
||
|
GameState* game = (GameState*)state;
|
||
|
Point new_position = game->current.position;
|
||
|
|
||
|
if (event->key) {
|
||
|
switch (event->key) {
|
||
|
case 'a': new_position.x -= 1; break;
|
||
|
case 'd': new_position.x += 1; break;
|
||
|
case 's': new_position.y += 1; break;
|
||
|
case 'w': // TODO: Implement rotation
|
||
|
break;
|
||
|
case 27: return 1; // Escape key
|
||
|
}
|
||
|
if (is_valid_position(game, new_position)) {
|
||
|
game->current.position = new_position;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
new_position.y += 1;
|
||
|
if (!is_valid_position(game, new_position)) {
|
||
|
place_tetromino(game);
|
||
|
clear_lines(game);
|
||
|
game->current = tetrominoes[rand() % 7];
|
||
|
game->current.position.x = WIDTH / 2 - 1;
|
||
|
game->current.position.y = 0;
|
||
|
if (!is_valid_position(game, game->current.position)) {
|
||
|
return 1; // Game over
|
||
|
}
|
||
|
} else {
|
||
|
game->current.position = new_position;
|
||
|
}
|
||
|
|
||
|
draw_grid(game);
|
||
|
return 0;
|
||
|
}
|
||
|
|