#include <windows.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
#define GRID_WIDTH 10
#define GRID_HEIGHT 20
#define CELL_SIZE 30
typedef struct {
int x, y;
} Point;
typedef struct {
Point blocks[4];
Point position;
COLORREF color;
} Tetromino;
Tetromino tetrominoes[7] = {
{{ {0, 0}, {0, 1}, {0, 2}, {0, 3} }, {GRID_WIDTH / 2 - 2, 0}, RGB(0, 255, 255)}, // I
{{ {0, 0}, {0, 1}, {1, 0}, {1, 1} }, {GRID_WIDTH / 2 - 1, 0}, RGB(255, 255, 0)}, // O
{{ {0, 0}, {1, 0}, {2, 0}, {1, 1} }, {GRID_WIDTH / 2 - 1, 0}, RGB(128, 0, 128)}, // T
{{ {0, 1}, {1, 1}, {1, 0}, {2, 0} }, {GRID_WIDTH / 2 - 1, 0}, RGB(0, 255, 0)}, // S
{{ {0, 0}, {1, 0}, {1, 1}, {2, 1} }, {GRID_WIDTH / 2 - 1, 0}, RGB(255, 0, 0)}, // Z
{{ {0, 1}, {1, 1}, {2, 1}, {2, 0} }, {GRID_WIDTH / 2 - 1, 0}, RGB(0, 0, 255)}, // J
{{ {0, 0}, {0, 1}, {1, 1}, {2, 1} }, {GRID_WIDTH / 2 - 1, 0}, RGB(255, 165, 0)} // L
};
int grid[GRID_HEIGHT][GRID_WIDTH] = {0};
bool check_collision(Tetromino *tetromino, int offsetX, int offsetY) {
for (int i = 0; i < 4; i++) {
int newX = tetromino->blocks[i].x + tetromino->position.x + offsetX;
int newY = tetromino->blocks[i].y + tetromino->position.y + offsetY;
if (newX < 0 || newX >= GRID_WIDTH || newY < 0 || newY >= GRID_HEIGHT || grid[newY][newX]) {
return true;
}
}
return false;
}
void lock_tetromino(Tetromino *tetromino) {
for (int i = 0; i < 4; i++) {
int x = tetromino->blocks[i].x + tetromino->position.x;
int y = tetromino->blocks[i].y + tetromino->position.y;
grid[y][x] = 1;
}
}
void render_grid(HDC hdc) {
for (int y = 0; y < GRID_HEIGHT; y++) {
for (int x = 0; x < GRID_WIDTH; x++) {
if (grid[y][x]) {
RECT cell = { x * CELL_SIZE, y * CELL_SIZE, (x + 1) * CELL_SIZE, (y + 1) * CELL_SIZE };
FillRect(hdc, &cell, (HBRUSH)(COLOR_WINDOW+1));
}
}
}
}
void render_tetromino(HDC hdc, Tetromino *tetromino) {
HBRUSH brush = CreateSolidBrush(tetromino->color);
HBRUSH oldBrush = SelectObject(hdc, brush);
for (int i = 0; i < 4; i++) {
RECT block = { (tetromino->blocks[i].x + tetromino->position.x) * CELL_SIZE,
(tetromino->blocks[i].y + tetromino->position.y) * CELL_SIZE,
(tetromino->blocks[i].x + tetromino->position.x + 1) * CELL_SIZE,
(tetromino->blocks[i].y + tetromino->position.y + 1) * CELL_SIZE };
FillRect(hdc, &block, brush);
}
SelectObject(hdc, oldBrush);
DeleteObject(brush);
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static Tetromino current_tetromino;
static UINT_PTR timer;
switch (uMsg) {
case WM_CREATE:
srand((unsigned int)time(NULL));
current_tetromino = tetrominoes[rand() % 7];
timer = SetTimer(hwnd, 1, 500, NULL);
break;
case WM_KEYDOWN:
switch (wParam) {
case VK_LEFT:
if (!check_collision(¤t_tetromino, -1, 0)) {
current_tetromino.position.x -= 1;
}
break;
case VK_RIGHT:
if (!check_collision(¤t_tetromino, 1, 0)) {
current_tetromino.position.x += 1;
}
break;
case VK_DOWN:
if (!check_collision(¤t_tetromino, 0, 1)) {
current_tetromino.position.y += 1;
}
break;
}
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_TIMER:
if (!check_collision(¤t_tetromino, 0, 1)) {
current_tetromino.position.y += 1;
} else {
lock_tetromino(¤t_tetromino);
current_tetromino = tetrominoes[rand() % 7];
if (check_collision(¤t_tetromino, 0, 0)) {
KillTimer(hwnd, timer);
MessageBox(hwnd, L"Game Over", L"Tetris", MB_OK);
DestroyWindow(hwnd);
}
}
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
render_grid(hdc);
render_tetromino(hdc, ¤t_tetromino);
EndPaint(hwnd, &ps);
break;
}
case WM_DESTROY:
KillTimer(hwnd, timer);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
const wchar_t CLASS_NAME[] = L"TetrisWindowClass";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0,
CLASS_NAME,
L"Tetris",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, GRID_WIDTH * CELL_SIZE, GRID_HEIGHT * CELL_SIZE,
NULL,
NULL,
hInstance,
NULL
);
if (hwnd == NULL) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}