jag

tetoris_c

public

Small block game in C

Star0
Watch0

Commit 376b4433efc1

tarball

Jaglekon <58274093+Jaglekon@users.noreply.github.com>2026-07-17 13:57 UTC

5 files changed+604-0
First commitment
Added file: Makefile+2 additions
@@ -0,0 +1,2 @@
1+tetoris: main.c
2+ gcc -Wall -Wextra -o tetoris main.c
Added file: README.mdAdded file
Binary file: mainbinary

Binary file not shown.

Added file: main.c+602 additions
@@ -0,0 +1,602 @@
1+#include <stdio.h>
2+#include <stdint.h>
3+#include <stdbool.h>
4+#include <stdlib.h>
5+
6+#include <termios.h>
7+#include <sys/ioctl.h>
8+#include <sys/stat.h>
9+
10+#include <time.h>
11+#include <unistd.h>
12+
13+#include <errno.h>
14+
15+
16+/* TETRIS */
17+
18+#define FIELD_WIDTH 10
19+#define FIELD_HEIGHT 20
20+
21+#define BORDER_WIDTH 2
22+#define TOP_GAP 1
23+
24+#define FULL_WIDTH (FIELD_WIDTH + BORDER_WIDTH * 2)
25+#define FULL_HEIGHT (FIELD_HEIGHT + BORDER_WIDTH + TOP_GAP)
26+
27+typedef enum {
28+ EMPTY, I_PIECE, J_PIECE, L_PIECE, O_PIECE, S_PIECE, T_PIECE, Z_PIECE, TETRIS
29+} tetromino;
30+
31+typedef enum {
32+ TOP, RIGHT, BOTTOM, LEFT
33+} direction;
34+
35+typedef struct {
36+ tetromino name;
37+ bool piece[4][4];
38+ int8_t x;
39+ int8_t y;
40+ uint8_t width;
41+ uint8_t height;
42+ direction rotation;
43+} piece;
44+
45+typedef struct {
46+ int8_t x;
47+ int8_t y;
48+} coord;
49+
50+tetromino playfield[FULL_HEIGHT][FULL_WIDTH] = {};
51+
52+
53+void init_field();
54+
55+void draw_board(piece current_piece, int8_t blink, bool is_piece);
56+
57+piece init_piece(tetromino new_piece);
58+
59+coord find_direction(piece current_piece, uint8_t x, uint8_t y);
60+
61+
62+int main () {
63+
64+
65+ /* Setting up the seed */
66+
67+ srand(time(NULL));
68+
69+
70+ /* Setting up terminal with termios */
71+
72+ struct termios old, new;
73+
74+ tcgetattr(STDIN_FILENO, &old);
75+ new = old;
76+
77+ new.c_lflag &= ~(ECHO | ICANON);
78+ new.c_cc[VMIN] = 0;
79+ new.c_cc[VTIME] = 0;
80+
81+ tcsetattr(STDIN_FILENO, TCSANOW, &new);
82+
83+ struct winsize init_size, current_size;
84+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);
85+ current_size = init_size;
86+
87+ printf("\033[?25l\033[?1049h");
88+
89+ /* Init */
90+
91+ init_field();
92+
93+
94+ /* Init the first piece */
95+
96+ bool is_piece = true;
97+
98+ piece current_piece = init_piece((rand() % Z_PIECE) + 1);
99+
100+
101+ uint16_t i;
102+
103+ for (i = 0;; i = (i + 1) % 20) {
104+
105+ int8_t move = 0;
106+ bool down = false;
107+ bool draw = false;
108+ bool rotate = false;
109+
110+ int8_t j, k;
111+
112+ if (!is_piece) {
113+ current_piece = init_piece((rand() % Z_PIECE) + 1);
114+ current_piece.rotation = rand() % (LEFT + 1);
115+ is_piece = true;
116+ draw = true;
117+ }
118+
119+
120+ /* Take care of the input */
121+
122+
123+ char buf[10] = {};
124+
125+ if (read(STDIN_FILENO, buf, sizeof(buf)) > 0) {
126+ if (buf[0] == 'q') {
127+ break;
128+ }
129+
130+ if (buf[0] == '\033' && buf[1] == '[') {
131+
132+ switch (buf[2]) {
133+ case 'A':
134+ rotate = true;
135+ break;
136+ case 'B':
137+ down = true;
138+ break;
139+ case 'C':
140+ move = 1;
141+ break;
142+ case 'D':
143+ move = -1;
144+ break;
145+ default:
146+ break;
147+ }
148+ }
149+ }
150+
151+
152+ /* Move piece */
153+
154+ if (move != 0) {
155+ if (
156+ current_piece.x + move >= 0 &&
157+ current_piece.x + move + ((current_piece.rotation % 2) ? current_piece.height : current_piece.width) <= FULL_WIDTH
158+ ) {
159+
160+ bool is_valid = true;
161+
162+
163+ for (j = 0; j < current_piece.height; j++) {
164+ for (k = 0; k < current_piece.width; k++) {
165+
166+ coord points = find_direction(current_piece, k, j);
167+
168+ if (
169+ current_piece.piece[j][k] &&
170+ playfield[current_piece.y + points.y][current_piece.x + points.x + move] != EMPTY
171+ ) {
172+ is_valid = false;
173+ }
174+ }
175+ }
176+
177+ if (is_valid) {
178+ current_piece.x += move;
179+ draw = true;
180+ }
181+ }
182+ }
183+
184+ /* Piece rotates */
185+
186+ /*
187+ if (rotate) {
188+
189+ direction new_direction = (current_piece.rotation + 1) % (LEFT + 1);
190+
191+ uint8_t actual_width = (new_direction % 2) ? current_piece.height : current_piece.width;
192+ uint8_t actual_height = (new_direction % 2) ? current_piece.width : current_piece.height;
193+
194+ coord new_position = {
195+
196+ }
197+ }
198+ */
199+
200+ /* Piece falls */
201+
202+ if (i == 0 || down) {
203+ current_piece.y++;
204+ i = 1;
205+ draw = true;
206+ }
207+
208+
209+ /* When a piece is stuck */
210+
211+ for(j = 0; j < current_piece.height; j++) {
212+ for (k = 0; k < current_piece.width; k++) {
213+
214+ coord points = find_direction(current_piece, k, j);
215+
216+ uint8_t x = current_piece.x + points.x;
217+ uint8_t y = current_piece.y + points.y;
218+
219+ if (
220+ y >= FULL_HEIGHT ||
221+ (playfield[y][x] != EMPTY && current_piece.piece[j][k])
222+ ) {
223+
224+ uint8_t l, m;
225+
226+ for (l = 0; l < current_piece.height; l++) {
227+ for (m = 0; m < current_piece.width; m++) {
228+
229+ if (current_piece.piece[l][m]) {
230+
231+ coord solid = find_direction(current_piece, m, l);
232+
233+ playfield[current_piece.y - 1 + solid.y][current_piece.x + solid.x] = current_piece.name;
234+ }
235+ }
236+ }
237+ is_piece = false;
238+ draw = true;
239+ }
240+ }
241+ }
242+
243+ /* Looks if there is a tetris */
244+
245+ for (j = FIELD_HEIGHT + TOP_GAP - 1; j >= 0; j--) {
246+
247+ uint8_t is_tetris = 0;
248+
249+ for (k = 0; k < FIELD_WIDTH; k++) {
250+ if (playfield[j][k + BORDER_WIDTH] != EMPTY)
251+ is_tetris++;
252+ }
253+
254+ if (is_tetris == 10) {
255+
256+ for (k = 0; k < 4; k++) {
257+
258+ draw_board(current_piece, (k % 2) ? -1 : j, is_piece);
259+
260+ usleep(300000);
261+ }
262+
263+ for (k = j; k > 0; k--) {
264+
265+ int8_t l;
266+
267+ for (l = 0; l < FIELD_WIDTH; l++) {
268+ playfield[k][l + BORDER_WIDTH] = playfield[k - 1][l + BORDER_WIDTH];
269+ }
270+ }
271+
272+ j++;
273+ }
274+ }
275+
276+
277+ /* Looks for game over */
278+
279+ for (j = 0; j < FIELD_WIDTH; j++) {
280+ if (playfield[TOP_GAP][j + BORDER_WIDTH] > 0) {
281+ printf("\033[2J\033[H");
282+ printf("HAHA LOSER\n");
283+ sleep(3);
284+ goto end;
285+ }
286+ }
287+
288+
289+ if (draw)
290+ draw_board(current_piece, -1, is_piece);
291+ usleep(10000);
292+ }
293+
294+end:
295+
296+ printf("\033[?1049l\033[?25h");
297+
298+ tcsetattr(STDIN_FILENO, TCSANOW, &old);
299+
300+ return 0;
301+}
302+
303+
304+
305+void init_field() {
306+ uint8_t i, j;
307+ for(i = 0; i < FULL_HEIGHT; i++) {
308+ for (j = 0; j < FULL_WIDTH; j++) {
309+
310+ if (
311+ j < BORDER_WIDTH ||
312+ j >= FULL_WIDTH - BORDER_WIDTH ||
313+ i >= FULL_HEIGHT - BORDER_WIDTH
314+ ) {
315+ playfield[i][j] = TETRIS;
316+ } else {
317+ playfield[i][j] = EMPTY;
318+ }
319+ }
320+ }
321+}
322+
323+
324+
325+#define RED "\033[38;2;255;0;0m"
326+#define GREEN "\033[38;2;0;255;0m"
327+#define BLUE "\033[38;2;0;0;255m"
328+#define YELLOW "\033[38;2;255;255;0m"
329+#define MAGENTA "\033[38;2;255;0;255m"
330+#define TEAL "\033[38;2;0;255;255m"
331+#define PURPLE "\033[38;2;127;0;255m"
332+#define WHITE "\033[38;2;255;255;255m"
333+
334+void draw_color (tetromino current_square) {
335+ switch (current_square) {
336+ case I_PIECE:
337+ printf("%s", RED);
338+ break;
339+ case J_PIECE:
340+ printf("%s", GREEN);
341+ break;
342+ case L_PIECE:
343+ printf("%s", BLUE);
344+ break;
345+ case O_PIECE:
346+ printf("%s", YELLOW);
347+ break;
348+ case S_PIECE:
349+ printf("%s", MAGENTA);
350+ break;
351+ case T_PIECE:
352+ printf("%s", TEAL);
353+ break;
354+ case Z_PIECE:
355+ printf("%s", PURPLE);
356+ break;
357+ }
358+}
359+
360+
361+void draw_board(piece current_piece, int8_t blink, bool is_piece) {
362+
363+ uint8_t i, j;
364+
365+
366+ printf("\033[2J\033[H");
367+ printf("┏");
368+ for (i = 0; i < FIELD_WIDTH; i++)
369+ printf("━━");
370+ printf("┓\n");
371+
372+
373+ for (i = 0; i < FIELD_HEIGHT; i++) {
374+
375+ printf("┃");
376+
377+ for (j = 0; j < FIELD_WIDTH; j++) {
378+
379+ if (i + TOP_GAP == blink) {
380+
381+ printf("%s", WHITE);
382+ } else {
383+
384+
385+ tetromino current_square = current_square = playfield[i + TOP_GAP][j + BORDER_WIDTH];
386+
387+ if (is_piece) {
388+
389+ uint8_t k, l;
390+
391+ for (k = 0; k < current_piece.height; k++) {
392+ for (l = 0; l < current_piece.width; l++) {
393+
394+ coord points = find_direction(current_piece, l, k);
395+
396+ if (
397+ i == current_piece.y + points.y - TOP_GAP &&
398+ j == current_piece.x + points.x - BORDER_WIDTH &&
399+ current_piece.piece[k][l])
400+ {
401+ current_square = current_piece.name;
402+ }
403+ }
404+ }
405+ }
406+
407+ if (current_square <= EMPTY || current_square > Z_PIECE) {
408+ printf("· ");
409+ continue;
410+ }
411+
412+ draw_color(current_square);
413+ }
414+
415+ printf("██\033[0m");
416+ }
417+ printf("┃\n");
418+ }
419+
420+ printf("┗");
421+ for (i = 0; i < FIELD_WIDTH; i++)
422+ printf("━━");
423+ printf("┛\n");
424+
425+}
426+
427+
428+piece init_piece(tetromino new_piece) {
429+
430+ piece current_piece = {};
431+
432+ current_piece.name = new_piece;
433+
434+ switch (current_piece.name) {
435+ case I_PIECE:
436+ current_piece.width = 4;
437+ current_piece.height = 4;
438+
439+ current_piece.piece[0][0] = false;
440+ current_piece.piece[0][1] = false;
441+ current_piece.piece[0][2] = false;
442+ current_piece.piece[0][3] = false;
443+
444+ current_piece.piece[1][0] = true;
445+ current_piece.piece[1][1] = true;
446+ current_piece.piece[1][2] = true;
447+ current_piece.piece[1][3] = true;
448+
449+ current_piece.piece[2][0] = false;
450+ current_piece.piece[2][1] = false;
451+ current_piece.piece[2][2] = false;
452+ current_piece.piece[2][3] = false;
453+
454+ current_piece.piece[3][0] = false;
455+ current_piece.piece[3][1] = false;
456+ current_piece.piece[3][2] = false;
457+ current_piece.piece[3][3] = false;
458+
459+ break;
460+
461+ case J_PIECE:
462+ current_piece.width = 3;
463+ current_piece.height = 3;
464+
465+ current_piece.piece[0][0] = true;
466+ current_piece.piece[0][1] = false;
467+ current_piece.piece[0][2] = false;
468+
469+ current_piece.piece[1][0] = true;
470+ current_piece.piece[1][1] = true;
471+ current_piece.piece[1][2] = true;
472+
473+ current_piece.piece[2][0] = false;
474+ current_piece.piece[2][1] = false;
475+ current_piece.piece[2][2] = false;
476+
477+ break;
478+
479+ case L_PIECE:
480+ current_piece.width = 3;
481+ current_piece.height = 3;
482+
483+ current_piece.piece[0][0] = false;
484+ current_piece.piece[0][1] = false;
485+ current_piece.piece[0][2] = true;
486+
487+ current_piece.piece[1][0] = true;
488+ current_piece.piece[1][1] = true;
489+ current_piece.piece[1][2] = true;
490+
491+ current_piece.piece[2][0] = false;
492+ current_piece.piece[2][1] = false;
493+ current_piece.piece[2][2] = false;
494+
495+ break;
496+
497+ case O_PIECE:
498+ current_piece.width = 2;
499+ current_piece.height = 2;
500+
501+ current_piece.piece[0][0] = true;
502+ current_piece.piece[0][1] = true;
503+
504+ current_piece.piece[1][0] = true;
505+ current_piece.piece[1][1] = true;
506+
507+ break;
508+
509+ case S_PIECE:
510+ current_piece.width = 3;
511+ current_piece.height = 3;
512+
513+ current_piece.piece[0][0] = false;
514+ current_piece.piece[0][1] = true;
515+ current_piece.piece[0][2] = true;
516+
517+ current_piece.piece[1][0] = true;
518+ current_piece.piece[1][1] = true;
519+ current_piece.piece[1][2] = false;
520+
521+ current_piece.piece[2][0] = false;
522+ current_piece.piece[2][1] = false;
523+ current_piece.piece[2][2] = false;
524+
525+ break;
526+
527+ case T_PIECE:
528+ current_piece.width = 3;
529+ current_piece.height = 3;
530+
531+ current_piece.piece[0][0] = false;
532+ current_piece.piece[0][1] = true;
533+ current_piece.piece[0][2] = false;
534+
535+ current_piece.piece[1][0] = true;
536+ current_piece.piece[1][1] = true;
537+ current_piece.piece[1][2] = true;
538+
539+ current_piece.piece[2][0] = false;
540+ current_piece.piece[2][1] = false;
541+ current_piece.piece[2][2] = false;
542+
543+ break;
544+ case Z_PIECE:
545+ current_piece.width = 3;
546+ current_piece.height = 3;
547+
548+ current_piece.piece[0][0] = true;
549+ current_piece.piece[0][1] = true;
550+ current_piece.piece[0][2] = false;
551+
552+ current_piece.piece[1][0] = false;
553+ current_piece.piece[1][1] = true;
554+ current_piece.piece[1][2] = true;
555+
556+ current_piece.piece[2][0] = false;
557+ current_piece.piece[2][1] = false;
558+ current_piece.piece[2][2] = false;
559+
560+ break;
561+ default:
562+ perror("Fuck you\n");
563+ break;
564+ }
565+
566+ current_piece.x = (FIELD_WIDTH >> 1) - (current_piece.width >> 1) + BORDER_WIDTH;
567+ current_piece.y = TOP_GAP;
568+ current_piece.rotation = TOP;
569+
570+ return current_piece;
571+}
572+
573+coord find_direction(piece current_piece, uint8_t x, uint8_t y) {
574+
575+ coord points = {
576+ x,
577+ y
578+ };
579+
580+ switch (current_piece.rotation) {
581+ case TOP:
582+ /*
583+ x = k;
584+ y = j;
585+ */
586+ break;
587+ case RIGHT:
588+ points.x = y;
589+ points.y = current_piece.width - (x + 1);
590+ break;
591+ case BOTTOM:
592+ points.x = current_piece.width - (x + 1);
593+ points.y = current_piece.height - (y + 1);
594+ break;
595+ case LEFT:
596+ points.x = current_piece.height - (y + 1);
597+ points.y = x;
598+ break;
599+ }
600+
601+ return points;
602+}
Binary file: tetorisbinary

Binary file not shown.