jag

tetoris_c

public

Small block game in C

Star0
Watch0

Commit 77d71a4dea0a

tarball

Jaglekon <58274093+Jaglekon@users.noreply.github.com>2026-07-20 21:14 UTC

12 files changed+804-684
restructure + preview + pause
Modified file: Makefile+11 additions−2 deletions
@@ -1,2 +1,11 @@
1-tetoris: main.c draw.c logic.c
2- gcc -Wall -Wextra -o tetoris main.c draw.c logic.c
1+CC = gcc
2+CFLAGS = -Wall -Wextra
3+
4+SRC = $(wildcard source/*.c)
5+OBJ = $(SRC:.c=.o)
6+
7+tetoris: $(OBJ)
8+ $(CC) $(CFLAGS) -o $@ $(OBJ)
9+
10+clean:
11+ rm -f $(OBJ) tetoris
Deleted file: draw.c−106 deletions
@@ -1,106 +0,0 @@
1-#include "main.h"
2-
3-
4-#define RED "\033[38;2;255;0;0m"
5-#define GREEN "\033[38;2;0;255;0m"
6-#define BLUE "\033[38;2;0;0;255m"
7-#define YELLOW "\033[38;2;255;255;0m"
8-#define MAGENTA "\033[38;2;255;0;255m"
9-#define TEAL "\033[38;2;0;255;255m"
10-#define PURPLE "\033[38;2;127;0;255m"
11-#define WHITE "\033[38;2;255;255;255m"
12-
13-
14-void draw_color (tetromino current_square) {
15- switch (current_square) {
16- case I_PIECE:
17- printf("%s", RED);
18- break;
19- case J_PIECE:
20- printf("%s", GREEN);
21- break;
22- case L_PIECE:
23- printf("%s", BLUE);
24- break;
25- case O_PIECE:
26- printf("%s", YELLOW);
27- break;
28- case S_PIECE:
29- printf("%s", MAGENTA);
30- break;
31- case T_PIECE:
32- printf("%s", TEAL);
33- break;
34- case Z_PIECE:
35- printf("%s", PURPLE);
36- break;
37- default:
38- break;
39- }
40-}
41-
42-
43-void draw_board(piece current_piece, bool *blink, bool is_piece) {
44-
45- uint8_t i, j;
46-
47-
48- printf("\033[H");
49- printf("┏");
50- for (i = 0; i < FIELD_WIDTH; i++)
51- printf("━━");
52- printf("┓\n");
53-
54-
55- for (i = 0; i < FIELD_HEIGHT; i++) {
56-
57- printf("┃");
58-
59- for (j = 0; j < FIELD_WIDTH; j++) {
60-
61- if ( blink != NULL && blink[i]) {
62-
63- printf("%s", WHITE);
64- } else {
65-
66-
67- tetromino current_square = current_square = playfield[i][j + BORDER_WIDTH];
68-
69- if (is_piece) {
70-
71- uint8_t k, l;
72-
73- for (k = 0; k < current_piece.size; k++) {
74- for (l = 0; l < current_piece.size; l++) {
75-
76- coord points = find_direction(current_piece, l, k);
77-
78- if (
79- i == current_piece.y + points.y &&
80- j == current_piece.x + points.x - BORDER_WIDTH &&
81- current_piece.piece[k][l])
82- {
83- current_square = current_piece.name;
84- }
85- }
86- }
87- }
88-
89- if (current_square <= EMPTY || current_square > Z_PIECE) {
90- printf("· ");
91- continue;
92- }
93-
94- draw_color(current_square);
95- }
96-
97- printf("██\033[0m");
98- }
99- printf("┃\n");
100- }
101-
102- printf("┗");
103- for (i = 0; i < FIELD_WIDTH; i++)
104- printf("━━");
105- printf("┛\n");
106-}
Deleted file: logic.c−224 deletions
@@ -1,224 +0,0 @@
1-#include "main.h"
2-
3-void init_field() {
4- uint8_t i, j;
5- for(i = 0; i < FULL_HEIGHT; i++) {
6- for (j = 0; j < FULL_WIDTH; j++) {
7-
8- if (
9- j < BORDER_WIDTH ||
10- j >= FULL_WIDTH - BORDER_WIDTH ||
11- i >= FULL_HEIGHT - BORDER_WIDTH
12- ) {
13- playfield[i][j] = TETRIS;
14- } else {
15- playfield[i][j] = EMPTY;
16- }
17- }
18- }
19-}
20-
21-
22-piece init_piece(tetromino new_piece) {
23-
24- piece current_piece = {};
25-
26- current_piece.name = new_piece;
27-
28- current_piece.y = TOP_GAP;
29- current_piece.rotation = TOP;
30-
31-
32- switch (current_piece.name) {
33- case I_PIECE:
34- current_piece.size = 4;
35- current_piece.positions = 2;
36-
37- current_piece.piece[0][0] = false;
38- current_piece.piece[0][1] = false;
39- current_piece.piece[0][2] = false;
40- current_piece.piece[0][3] = false;
41-
42- current_piece.piece[1][0] = true;
43- current_piece.piece[1][1] = true;
44- current_piece.piece[1][2] = true;
45- current_piece.piece[1][3] = true;
46-
47- current_piece.piece[2][0] = false;
48- current_piece.piece[2][1] = false;
49- current_piece.piece[2][2] = false;
50- current_piece.piece[2][3] = false;
51-
52- current_piece.piece[3][0] = false;
53- current_piece.piece[3][1] = false;
54- current_piece.piece[3][2] = false;
55- current_piece.piece[3][3] = false;
56-
57- current_piece.y = 0;
58-
59- break;
60-
61- case J_PIECE:
62- current_piece.size = 3;
63- current_piece.positions = 4;
64-
65- current_piece.piece[0][0] = true;
66- current_piece.piece[0][1] = false;
67- current_piece.piece[0][2] = false;
68-
69- current_piece.piece[1][0] = true;
70- current_piece.piece[1][1] = true;
71- current_piece.piece[1][2] = true;
72-
73- current_piece.piece[2][0] = false;
74- current_piece.piece[2][1] = false;
75- current_piece.piece[2][2] = false;
76-
77- break;
78-
79- case L_PIECE:
80- current_piece.size = 3;
81- current_piece.positions = 4;
82-
83- current_piece.piece[0][0] = false;
84- current_piece.piece[0][1] = false;
85- current_piece.piece[0][2] = true;
86-
87- current_piece.piece[1][0] = true;
88- current_piece.piece[1][1] = true;
89- current_piece.piece[1][2] = true;
90-
91- current_piece.piece[2][0] = false;
92- current_piece.piece[2][1] = false;
93- current_piece.piece[2][2] = false;
94-
95- break;
96-
97- case O_PIECE:
98- current_piece.size = 2;
99- current_piece.positions = 1;
100-
101- current_piece.piece[0][0] = true;
102- current_piece.piece[0][1] = true;
103-
104- current_piece.piece[1][0] = true;
105- current_piece.piece[1][1] = true;
106-
107- break;
108-
109- case S_PIECE:
110- current_piece.size = 3;
111- current_piece.positions = 2;
112-
113- current_piece.piece[0][0] = false;
114- current_piece.piece[0][1] = true;
115- current_piece.piece[0][2] = true;
116-
117- current_piece.piece[1][0] = true;
118- current_piece.piece[1][1] = true;
119- current_piece.piece[1][2] = false;
120-
121- current_piece.piece[2][0] = false;
122- current_piece.piece[2][1] = false;
123- current_piece.piece[2][2] = false;
124-
125- break;
126-
127- case T_PIECE:
128- current_piece.size = 3;
129- current_piece.positions = 4;
130-
131- current_piece.piece[0][0] = false;
132- current_piece.piece[0][1] = true;
133- current_piece.piece[0][2] = false;
134-
135- current_piece.piece[1][0] = true;
136- current_piece.piece[1][1] = true;
137- current_piece.piece[1][2] = true;
138-
139- current_piece.piece[2][0] = false;
140- current_piece.piece[2][1] = false;
141- current_piece.piece[2][2] = false;
142-
143- break;
144- case Z_PIECE:
145- current_piece.size = 3;
146- current_piece.positions = 2;
147-
148- current_piece.piece[0][0] = true;
149- current_piece.piece[0][1] = true;
150- current_piece.piece[0][2] = false;
151-
152- current_piece.piece[1][0] = false;
153- current_piece.piece[1][1] = true;
154- current_piece.piece[1][2] = true;
155-
156- current_piece.piece[2][0] = false;
157- current_piece.piece[2][1] = false;
158- current_piece.piece[2][2] = false;
159-
160- break;
161- default:
162- perror("Fuck you\n");
163- break;
164- }
165-
166- current_piece.x = ((FULL_WIDTH >> 1) - (current_piece.size >> 1));
167-
168- return current_piece;
169-}
170-
171-
172-
173-coord find_direction(piece current_piece, uint8_t x, uint8_t y) {
174-
175- coord points = {
176- x,
177- y
178- };
179-
180- switch (current_piece.rotation) {
181- case TOP:
182- /*
183- x = k;
184- y = j;
185- */
186- break;
187- case RIGHT:
188- points.x = y;
189- points.y = current_piece.size - (x + 1);
190- break;
191- case BOTTOM:
192- points.x = current_piece.size - (x + 1);
193- points.y = current_piece.size - (y + 1);
194- break;
195- case LEFT:
196- points.x = current_piece.size - (y + 1);
197- points.y = x;
198- break;
199- }
200-
201- return points;
202-}
203-
204-
205-bool valid_position(piece current_piece) {
206-
207- uint8_t i, j;
208-
209- for(i = 0; i < current_piece.size; i++) {
210- for (j = 0; j < current_piece.size; j++) {
211-
212- coord points = find_direction(current_piece, j, i);
213-
214- if (
215- current_piece.piece[i][j] &&
216- playfield[current_piece.y + points.y][current_piece.x + points.x] != EMPTY
217- ) {
218- return false;
219- }
220- }
221- }
222-
223- return true;
224-}
Deleted file: main.c−277 deletions
@@ -1,277 +0,0 @@
1-#include "main.h"
2-
3-
4-tetromino playfield[FULL_HEIGHT][FULL_WIDTH] = {};
5-
6-
7-int main () {
8-
9-
10- /* Setting up the seed */
11-
12- srand(time(NULL));
13-
14-
15- /* Setting up terminal with termios */
16-
17- struct termios old, new;
18-
19- tcgetattr(STDIN_FILENO, &old);
20- new = old;
21-
22- new.c_lflag &= ~(ECHO | ICANON);
23- new.c_cc[VMIN] = 0;
24- new.c_cc[VTIME] = 0;
25-
26- tcsetattr(STDIN_FILENO, TCSANOW, &new);
27-
28- struct winsize init_size, current_size;
29- ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);
30- current_size = init_size;
31-
32- printf("\033[?25l\033[?1049h");
33-
34-
35- /* Polling */
36-
37- struct pollfd polling[1];
38-
39- polling[0].fd = 0;
40- polling[0].events = POLLIN;
41-
42-
43- /* Init */
44-
45- init_field();
46-
47- bool blink[FIELD_HEIGHT] = {};
48-
49- bool is_piece = false;
50-
51- piece current_piece = {};
52-
53-
54- /* Game Loop */
55-
56- struct timespec start, current;
57- clock_gettime(CLOCK_MONOTONIC_RAW, &start);
58-
59-
60- uint8_t i;
61- bool is_tetris = false;
62-
63- while (true) {
64-
65- int8_t move = 0;
66- bool down = false;
67- bool draw = false;
68- bool rotate = false;
69-
70- clock_gettime(CLOCK_MONOTONIC_RAW, &current);
71-
72- uint64_t time_diff = (current.tv_sec - start.tv_sec) * 1000000000ULL + (current.tv_nsec - start.tv_nsec);
73-
74-
75- /* Take care of the input */
76-
77- int is_poll = poll(polling, 1, 17);
78-
79- if (is_poll > 0 && polling[0].revents & POLLIN) {
80-
81- char buf[10] = {};
82-
83- if (read(STDIN_FILENO, buf, sizeof(buf)) > 0) {
84- if (buf[0] == 'q') {
85- break;
86- }
87-
88- if (buf[0] == '\033' && buf[1] == '[') {
89-
90- switch (buf[2]) {
91- case 'A':
92- rotate = true;
93- break;
94- case 'B':
95- down = true;
96- break;
97- case 'C':
98- move = 1;
99- break;
100- case 'D':
101- move = -1;
102- break;
103- default:
104- break;
105- }
106- }
107- }
108- }
109-
110-
111- if (is_tetris) {
112-
113- if (time_diff >= FRAME * BLINK && i < 4) {
114-
115- time_diff -= time_diff % (FRAME * BLINK);
116- start.tv_sec += time_diff / 1000000000ULL;
117- start.tv_nsec += time_diff % 1000000000ULL;
118-
119- draw_board(current_piece, (i % 2 == 0) ? NULL : blink, is_piece);
120- i++;
121- }
122-
123- if (i == 4) {
124-
125- int8_t j, k;
126-
127- for (j = 0; j < FIELD_HEIGHT; j++) {
128- if (blink[j]) {
129-
130- for (k = j; k > 0; k--) {
131- int8_t l;
132-
133- for (l = 0; l < FIELD_WIDTH; l++) {
134- playfield[k][l + BORDER_WIDTH] = playfield[k - 1][l + BORDER_WIDTH];
135- }
136- }
137- }
138- }
139-
140- i = 0;
141- is_tetris = false;
142- }
143-
144- continue;
145- }
146-
147-
148-
149- /* Creates new piece */
150-
151-
152- if (!is_piece) {
153- current_piece = init_piece((rand() % Z_PIECE) + 1);
154- is_piece = true;
155- draw = true;
156- }
157-
158-
159- /* Move piece */
160-
161- if (move != 0) {
162- if (
163- current_piece.x + move >= 0 &&
164- current_piece.x + move + current_piece.size <= FULL_WIDTH
165- ) {
166-
167- piece moved_piece = current_piece;
168- moved_piece.x += move;
169-
170- if (valid_position(moved_piece)) {
171- current_piece.x += move;
172- draw = true;
173- }
174- }
175- }
176-
177-
178- /* Piece rotates */
179-
180- if (rotate && current_piece.positions > 1) {
181-
182- piece rotated = current_piece;
183-
184- rotated.rotation = (current_piece.rotation + 1) % (current_piece.positions);
185-
186- if (valid_position(rotated)) {
187- current_piece = rotated;
188- draw = true;
189- }
190- }
191-
192-
193- /* Piece falls */
194-
195- if (time_diff >= (FRAME * FALL) || down) {
196- current_piece.y++;
197-
198- time_diff -= time_diff % (FRAME * FALL);
199- start.tv_sec += time_diff / 1000000000ULL;
200- start.tv_nsec += time_diff % 1000000000ULL;
201-
202-
203- draw = true;
204- }
205-
206-
207- /* When a piece is stuck */
208-
209- if (!valid_position(current_piece)) {
210-
211- int8_t j, k;
212-
213- for (j = 0; j < current_piece.size; j++) {
214- for (k = 0; k < current_piece.size; k++) {
215-
216- if (current_piece.piece[j][k]) {
217-
218- coord solid = find_direction(current_piece, k, j);
219-
220- playfield[current_piece.y - 1 + solid.y][current_piece.x + solid.x] = current_piece.name;
221- }
222- }
223- }
224- is_piece = false;
225- draw = true;
226- }
227-
228-
229- /* Looks if there is a tetris */
230-
231- int8_t j, k;
232-
233- for (j = FIELD_HEIGHT - 1; j >= 0; j--) {
234-
235- uint8_t blocked = 0;
236-
237- for (k = 0; k < FIELD_WIDTH; k++) {
238- if (playfield[j][k + BORDER_WIDTH] != EMPTY)
239- blocked++;
240- }
241-
242- if (blocked == 10) {
243-
244- blink[j] = true;
245- is_tetris = true;
246- } else {
247- blink[j] = false;
248- }
249- }
250-
251-
252- /* Looks for game over */
253-
254- for (j = 0; j < FIELD_WIDTH; j++) {
255- if (playfield[TOP_GAP][j + BORDER_WIDTH] > 0) {
256- draw_board(current_piece, NULL, is_piece);
257- sleep(2);
258- printf("\033[2J\033[H");
259- printf("HAHA LOSER\n");
260- sleep(1);
261- goto end;
262- }
263- }
264-
265-
266- if (draw)
267- draw_board(current_piece, NULL, is_piece);
268- }
269-
270-end:
271-
272- printf("\033[?1049l\033[?25h");
273-
274- tcsetattr(STDIN_FILENO, TCSANOW, &old);
275-
276- return 0;
277-}
Deleted file: main.h−75 deletions
@@ -1,75 +0,0 @@
1-#ifndef MAIN_C
2-
3-#define MAIN_C
4-
5-#include <stdio.h>
6-#include <stdint.h>
7-#include <stdbool.h>
8-#include <stdlib.h>
9-
10-#include <termios.h>
11-#include <sys/ioctl.h>
12-#include <sys/stat.h>
13-
14-#include <time.h>
15-#include <unistd.h>
16-#include <poll.h>
17-
18-#include <errno.h>
19-
20-
21-#define FIELD_WIDTH 10
22-#define FIELD_HEIGHT 21
23-
24-#define BORDER_WIDTH 2
25-#define TOP_GAP 1
26-
27-#define FULL_WIDTH (FIELD_WIDTH + BORDER_WIDTH * 2)
28-#define FULL_HEIGHT (FIELD_HEIGHT + BORDER_WIDTH)
29-
30-#define FRAME 17000000
31-
32-#define FALL 58
33-#define BLINK 12
34-
35-typedef enum {
36- EMPTY, I_PIECE, J_PIECE, L_PIECE, O_PIECE, S_PIECE, T_PIECE, Z_PIECE, TETRIS
37-} tetromino;
38-
39-typedef enum {
40- TOP, RIGHT, BOTTOM, LEFT
41-} direction;
42-
43-typedef struct {
44- tetromino name;
45- bool piece[4][4];
46- int8_t x;
47- int8_t y;
48- uint8_t size;
49- direction rotation;
50- uint8_t positions;
51-} piece;
52-
53-typedef struct {
54- int8_t x;
55- int8_t y;
56-} coord;
57-
58-
59-
60-void init_field();
61-
62-void draw_board(piece current_piece, bool *blink, bool is_piece);
63-
64-piece init_piece(tetromino new_piece);
65-
66-coord find_direction(piece current_piece, uint8_t x, uint8_t y);
67-
68-bool valid_position(piece current_piece);
69-
70-
71-
72-extern tetromino playfield[FULL_HEIGHT][FULL_WIDTH];
73-
74-
75-#endif
Added file: source/draw.c+171 additions
@@ -0,0 +1,171 @@
1+#include "main.h"
2+
3+
4+#define RED "\033[38;2;255;0;0m"
5+#define GREEN "\033[38;2;0;255;0m"
6+#define BLUE "\033[38;2;0;0;255m"
7+#define YELLOW "\033[38;2;255;255;0m"
8+#define MAGENTA "\033[38;2;255;0;255m"
9+#define TEAL "\033[38;2;0;255;255m"
10+#define PURPLE "\033[38;2;127;0;255m"
11+#define WHITE "\033[38;2;255;255;255m"
12+
13+
14+void draw_color (tetromino current_square) {
15+ switch (current_square) {
16+ case I_PIECE:
17+ printf("%s", RED);
18+ break;
19+ case J_PIECE:
20+ printf("%s", GREEN);
21+ break;
22+ case L_PIECE:
23+ printf("%s", BLUE);
24+ break;
25+ case O_PIECE:
26+ printf("%s", YELLOW);
27+ break;
28+ case S_PIECE:
29+ printf("%s", MAGENTA);
30+ break;
31+ case T_PIECE:
32+ printf("%s", TEAL);
33+ break;
34+ case Z_PIECE:
35+ printf("%s", PURPLE);
36+ break;
37+ default:
38+ break;
39+ }
40+}
41+
42+
43+void draw_board(piece current_piece, piece next_piece, bool *blink, bool is_piece) {
44+
45+ int8_t i, j;
46+
47+
48+ printf("\033[H");
49+ printf("┏");
50+ for (i = 0; i < FIELD_WIDTH; i++)
51+ printf("━━");
52+ printf("┓\n");
53+
54+
55+ for (i = 0; i < FIELD_HEIGHT; i++) {
56+
57+ printf("┃");
58+
59+ for (j = 0; j < FIELD_WIDTH; j++) {
60+
61+ if ( blink != NULL && blink[i]) {
62+
63+ printf("%s", WHITE);
64+ } else {
65+
66+
67+ tetromino current_square = playfield[i][j + BORDER_WIDTH];
68+
69+ if (is_piece) {
70+
71+ uint8_t k, l;
72+
73+ for (k = 0; k < current_piece.size; k++) {
74+ for (l = 0; l < current_piece.size; l++) {
75+
76+ coord points = find_direction(current_piece, l, k);
77+
78+ if (
79+ i == current_piece.y + points.y &&
80+ j == current_piece.x + points.x - BORDER_WIDTH &&
81+ current_piece.piece[k][l])
82+ {
83+ current_square = current_piece.name;
84+ }
85+ }
86+ }
87+ }
88+
89+ if (current_square <= EMPTY || current_square > Z_PIECE) {
90+ printf("· ");
91+ continue;
92+ }
93+
94+ draw_color(current_square);
95+ }
96+
97+ printf("██\033[0m");
98+ }
99+ printf("┃\n");
100+ }
101+
102+ printf("┗");
103+ for (i = 0; i < FIELD_WIDTH; i++)
104+ printf("━━");
105+ printf("┛\n");
106+
107+
108+ /* next piece preview */
109+
110+ printf("\033[H");
111+ printf("\033[%iC", FIELD_WIDTH * 2 + 1);
112+ printf("┱──────────┐\n");
113+
114+ for (i = 4; i > 0; i--) {
115+ printf("\033[%iC", FIELD_WIDTH * 2 + 1);
116+ printf("┃");
117+
118+ draw_color(next_piece.name);
119+
120+ for (j = 4; j >= 0; j--) {
121+
122+ int8_t y = next_piece.size - i;
123+ int8_t x = next_piece.size - j;
124+
125+
126+ if (
127+ y < next_piece.size && x < next_piece.size &&
128+ y >= 0 && x >= 0 &&
129+ next_piece.piece[y][x]
130+ ) {
131+ printf("██");
132+ continue;
133+ }
134+
135+ printf(" ");
136+ }
137+
138+ printf("\033[0m│\n");
139+ }
140+
141+ printf("\033[%iC", FIELD_WIDTH * 2 + 1);
142+ printf("┠──────────┘\n");
143+
144+}
145+
146+void draw_pause () {
147+
148+ uint8_t i;
149+
150+ printf("\033[H");
151+ printf("\033[%iB", 1 + (FIELD_HEIGHT >> 2));
152+
153+ for (i = 0; i < 3; i++) {
154+
155+ printf("\033[%iC", 1 + (((FIELD_WIDTH * 2 - 7) >> 1)));
156+
157+ switch(i) {
158+ case 0:
159+ printf("╭─────╮");
160+ break;
161+ case 1:
162+ printf("│PAUSE│");
163+ break;
164+ case 2:
165+ printf("╰─────╯");
166+ break;
167+ }
168+
169+ printf("\n");
170+ }
171+}
Binary file: source/draw.obinary

Binary file not shown.

Added file: source/logic.c+238 additions
@@ -0,0 +1,238 @@
1+#include "main.h"
2+
3+void init_field() {
4+ uint8_t i, j;
5+ for(i = 0; i < FULL_HEIGHT; i++) {
6+ for (j = 0; j < FULL_WIDTH; j++) {
7+
8+ if (
9+ j < BORDER_WIDTH ||
10+ j >= FULL_WIDTH - BORDER_WIDTH ||
11+ i >= FULL_HEIGHT - BORDER_WIDTH
12+ ) {
13+ playfield[i][j] = TETRIS;
14+ } else {
15+ playfield[i][j] = EMPTY;
16+ }
17+ }
18+ }
19+}
20+
21+
22+piece init_piece(tetromino new_piece) {
23+
24+ piece current_piece = {};
25+
26+ current_piece.name = new_piece;
27+
28+ current_piece.y = TOP_GAP;
29+ current_piece.rotation = TOP;
30+
31+
32+ switch (current_piece.name) {
33+ case I_PIECE:
34+ current_piece.size = 4;
35+ current_piece.positions = 2;
36+
37+ current_piece.piece[0][0] = false;
38+ current_piece.piece[0][1] = false;
39+ current_piece.piece[0][2] = false;
40+ current_piece.piece[0][3] = false;
41+
42+ current_piece.piece[1][0] = true;
43+ current_piece.piece[1][1] = true;
44+ current_piece.piece[1][2] = true;
45+ current_piece.piece[1][3] = true;
46+
47+ current_piece.piece[2][0] = false;
48+ current_piece.piece[2][1] = false;
49+ current_piece.piece[2][2] = false;
50+ current_piece.piece[2][3] = false;
51+
52+ current_piece.piece[3][0] = false;
53+ current_piece.piece[3][1] = false;
54+ current_piece.piece[3][2] = false;
55+ current_piece.piece[3][3] = false;
56+
57+ current_piece.y = 0;
58+
59+ break;
60+
61+ case J_PIECE:
62+ current_piece.size = 3;
63+ current_piece.positions = 4;
64+
65+ current_piece.piece[0][0] = true;
66+ current_piece.piece[0][1] = false;
67+ current_piece.piece[0][2] = false;
68+
69+ current_piece.piece[1][0] = true;
70+ current_piece.piece[1][1] = true;
71+ current_piece.piece[1][2] = true;
72+
73+ current_piece.piece[2][0] = false;
74+ current_piece.piece[2][1] = false;
75+ current_piece.piece[2][2] = false;
76+
77+ break;
78+
79+ case L_PIECE:
80+ current_piece.size = 3;
81+ current_piece.positions = 4;
82+
83+ current_piece.piece[0][0] = false;
84+ current_piece.piece[0][1] = false;
85+ current_piece.piece[0][2] = true;
86+
87+ current_piece.piece[1][0] = true;
88+ current_piece.piece[1][1] = true;
89+ current_piece.piece[1][2] = true;
90+
91+ current_piece.piece[2][0] = false;
92+ current_piece.piece[2][1] = false;
93+ current_piece.piece[2][2] = false;
94+
95+ break;
96+
97+ case O_PIECE:
98+ current_piece.size = 4;
99+ current_piece.positions = 1;
100+
101+ current_piece.piece[0][0] = false;
102+ current_piece.piece[0][1] = false;
103+ current_piece.piece[0][2] = false;
104+ current_piece.piece[0][3] = false;
105+
106+ current_piece.piece[1][0] = false;
107+ current_piece.piece[1][1] = true;
108+ current_piece.piece[1][2] = true;
109+ current_piece.piece[1][3] = false;
110+
111+ current_piece.piece[2][0] = false;
112+ current_piece.piece[2][1] = true;
113+ current_piece.piece[2][2] = true;
114+ current_piece.piece[2][3] = false;
115+
116+ current_piece.piece[3][0] = false;
117+ current_piece.piece[3][1] = false;
118+ current_piece.piece[3][2] = false;
119+ current_piece.piece[3][3] = false;
120+
121+ break;
122+
123+ case S_PIECE:
124+ current_piece.size = 3;
125+ current_piece.positions = 2;
126+
127+ current_piece.piece[0][0] = false;
128+ current_piece.piece[0][1] = true;
129+ current_piece.piece[0][2] = true;
130+
131+ current_piece.piece[1][0] = true;
132+ current_piece.piece[1][1] = true;
133+ current_piece.piece[1][2] = false;
134+
135+ current_piece.piece[2][0] = false;
136+ current_piece.piece[2][1] = false;
137+ current_piece.piece[2][2] = false;
138+
139+ break;
140+
141+ case T_PIECE:
142+ current_piece.size = 3;
143+ current_piece.positions = 4;
144+
145+ current_piece.piece[0][0] = false;
146+ current_piece.piece[0][1] = true;
147+ current_piece.piece[0][2] = false;
148+
149+ current_piece.piece[1][0] = true;
150+ current_piece.piece[1][1] = true;
151+ current_piece.piece[1][2] = true;
152+
153+ current_piece.piece[2][0] = false;
154+ current_piece.piece[2][1] = false;
155+ current_piece.piece[2][2] = false;
156+
157+ break;
158+ case Z_PIECE:
159+ current_piece.size = 3;
160+ current_piece.positions = 2;
161+
162+ current_piece.piece[0][0] = true;
163+ current_piece.piece[0][1] = true;
164+ current_piece.piece[0][2] = false;
165+
166+ current_piece.piece[1][0] = false;
167+ current_piece.piece[1][1] = true;
168+ current_piece.piece[1][2] = true;
169+
170+ current_piece.piece[2][0] = false;
171+ current_piece.piece[2][1] = false;
172+ current_piece.piece[2][2] = false;
173+
174+ break;
175+ default:
176+ perror("Fuck you\n");
177+ break;
178+ }
179+
180+ current_piece.x = ((FULL_WIDTH >> 1) - (current_piece.size >> 1));
181+
182+ return current_piece;
183+}
184+
185+
186+
187+coord find_direction(piece current_piece, uint8_t x, uint8_t y) {
188+
189+ coord points = {
190+ x,
191+ y
192+ };
193+
194+ switch (current_piece.rotation) {
195+ case TOP:
196+ /*
197+ x = k;
198+ y = j;
199+ */
200+ break;
201+ case RIGHT:
202+ points.x = y;
203+ points.y = current_piece.size - (x + 1);
204+ break;
205+ case BOTTOM:
206+ points.x = current_piece.size - (x + 1);
207+ points.y = current_piece.size - (y + 1);
208+ break;
209+ case LEFT:
210+ points.x = current_piece.size - (y + 1);
211+ points.y = x;
212+ break;
213+ }
214+
215+ return points;
216+}
217+
218+
219+bool valid_position(piece current_piece) {
220+
221+ uint8_t i, j;
222+
223+ for(i = 0; i < current_piece.size; i++) {
224+ for (j = 0; j < current_piece.size; j++) {
225+
226+ coord points = find_direction(current_piece, j, i);
227+
228+ if (
229+ current_piece.piece[i][j] &&
230+ playfield[current_piece.y + points.y][current_piece.x + points.x] != EMPTY
231+ ) {
232+ return false;
233+ }
234+ }
235+ }
236+
237+ return true;
238+}
Binary file: source/logic.obinary

Binary file not shown.

Added file: source/main.c+305 additions
@@ -0,0 +1,305 @@
1+#include "main.h"
2+
3+
4+tetromino playfield[FULL_HEIGHT][FULL_WIDTH] = {};
5+
6+
7+int main () {
8+
9+
10+ /* Setting up the seed */
11+
12+ srand(time(NULL));
13+
14+
15+ /* Setting up terminal with termios */
16+
17+ struct termios old, new;
18+
19+ tcgetattr(STDIN_FILENO, &old);
20+ new = old;
21+
22+ new.c_lflag &= ~(ECHO | ICANON);
23+ new.c_cc[VMIN] = 0;
24+ new.c_cc[VTIME] = 0;
25+
26+ tcsetattr(STDIN_FILENO, TCSANOW, &new);
27+
28+ struct winsize init_size, current_size;
29+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);
30+ current_size = init_size;
31+
32+ printf("\033[?25l\033[?1049h");
33+
34+
35+ /* Polling */
36+
37+ struct pollfd polling[1];
38+
39+ polling[0].fd = 0;
40+ polling[0].events = POLLIN;
41+
42+
43+ /* Init */
44+
45+ init_field();
46+
47+ bool blink[FIELD_HEIGHT] = {};
48+
49+ bool is_piece = false;
50+
51+ bool paused = false;
52+
53+ uint64_t paused_diff;
54+
55+ piece current_piece = {};
56+ piece next_piece = init_piece((rand() % Z_PIECE) + 1);
57+
58+ /* Game Loop */
59+
60+ struct timespec start, current;
61+ clock_gettime(CLOCK_MONOTONIC_RAW, &start);
62+
63+
64+ uint8_t i;
65+ bool is_tetris = false;
66+
67+ while (true) {
68+
69+ int8_t move = 0;
70+ bool down = false;
71+ bool draw = false;
72+ bool rotate = false;
73+
74+ clock_gettime(CLOCK_MONOTONIC_RAW, &current);
75+
76+ uint64_t time_diff =
77+ (current.tv_sec - start.tv_sec) * 1000000000ULL +
78+ (current.tv_nsec - start.tv_nsec);
79+
80+
81+ /* Take care of the input */
82+
83+ int is_poll = poll(polling, 1, paused ? -1 : 17);
84+
85+ if (is_poll > 0 && polling[0].revents & POLLIN) {
86+
87+ char buf[10] = {};
88+
89+ if (read(STDIN_FILENO, buf, sizeof(buf)) > 0) {
90+
91+ if (tolower(buf[0]) == 'q') {
92+ break;
93+ }
94+
95+ if (tolower(buf[0]) == 'p') {
96+ if (paused) {
97+
98+ time_diff = paused_diff;
99+
100+ paused = false;
101+ } else {
102+
103+ paused_diff = time_diff;
104+
105+ draw_pause();
106+
107+ paused = true;
108+ }
109+ }
110+
111+
112+ if (buf[0] == '\033' && buf[1] == '[') {
113+
114+ switch (buf[2]) {
115+ case 'A':
116+ rotate = true;
117+ break;
118+ case 'B':
119+ down = true;
120+ break;
121+ case 'C':
122+ move = 1;
123+ break;
124+ case 'D':
125+ move = -1;
126+ break;
127+ default:
128+ break;
129+ }
130+ }
131+ }
132+ }
133+
134+
135+ if (is_tetris) {
136+
137+ if (time_diff >= FRAME * BLINK && i < 4) {
138+
139+ time_diff -= time_diff % (FRAME * BLINK);
140+ start.tv_sec += time_diff / 1000000000ULL;
141+ start.tv_nsec += time_diff % 1000000000ULL;
142+
143+ draw_board(current_piece, (i % 2 == 0) ? NULL : blink, is_piece);
144+ i++;
145+ }
146+
147+ if (i == 4) {
148+
149+ int8_t j, k;
150+
151+ for (j = 0; j < FIELD_HEIGHT; j++) {
152+ if (blink[j]) {
153+
154+ for (k = j; k > 0; k--) {
155+ int8_t l;
156+
157+ for (l = 0; l < FIELD_WIDTH; l++) {
158+ playfield[k][l + BORDER_WIDTH] = playfield[k - 1][l + BORDER_WIDTH];
159+ }
160+ }
161+ }
162+ }
163+
164+ i = 0;
165+ is_tetris = false;
166+ }
167+
168+ continue;
169+ }
170+
171+
172+ if (paused)
173+ continue;
174+
175+
176+ /* Creates new piece */
177+
178+
179+ if (!is_piece) {
180+ current_piece = next_piece;
181+ next_piece = init_piece((rand() % Z_PIECE) + 1);
182+ is_piece = true;
183+ draw = true;
184+ }
185+
186+
187+ /* Move piece */
188+
189+ if (move != 0) {
190+ if (
191+ current_piece.x + move >= 0 &&
192+ current_piece.x + move + current_piece.size <= FULL_WIDTH
193+ ) {
194+
195+ piece moved_piece = current_piece;
196+ moved_piece.x += move;
197+
198+ if (valid_position(moved_piece)) {
199+ current_piece.x += move;
200+ draw = true;
201+ }
202+ }
203+ }
204+
205+
206+ /* Piece rotates */
207+
208+ if (rotate && current_piece.positions > 1) {
209+
210+ piece rotated = current_piece;
211+
212+ rotated.rotation = (current_piece.rotation + 1) % (current_piece.positions);
213+
214+ if (valid_position(rotated)) {
215+ current_piece = rotated;
216+ draw = true;
217+ }
218+ }
219+
220+
221+ /* Piece falls */
222+
223+ if (time_diff >= (FRAME * FALL) || down) {
224+ current_piece.y++;
225+
226+ time_diff -= time_diff % (FRAME * FALL);
227+ start.tv_sec += time_diff / 1000000000ULL;
228+ start.tv_nsec += time_diff % 1000000000ULL;
229+
230+
231+ draw = true;
232+ }
233+
234+
235+ /* When a piece is stuck */
236+
237+ if (!valid_position(current_piece)) {
238+
239+ int8_t j, k;
240+
241+ for (j = 0; j < current_piece.size; j++) {
242+ for (k = 0; k < current_piece.size; k++) {
243+
244+ if (current_piece.piece[j][k]) {
245+
246+ coord solid = find_direction(current_piece, k, j);
247+
248+ playfield[current_piece.y - 1 + solid.y][current_piece.x + solid.x] = current_piece.name;
249+ }
250+ }
251+ }
252+ is_piece = false;
253+ draw = true;
254+ }
255+
256+
257+ /* Looks if there is a tetris */
258+
259+ int8_t j, k;
260+
261+ for (j = FIELD_HEIGHT - 1; j >= 0; j--) {
262+
263+ uint8_t blocked = 0;
264+
265+ for (k = 0; k < FIELD_WIDTH; k++) {
266+ if (playfield[j][k + BORDER_WIDTH] != EMPTY)
267+ blocked++;
268+ }
269+
270+ if (blocked == 10) {
271+
272+ blink[j] = true;
273+ is_tetris = true;
274+ } else {
275+ blink[j] = false;
276+ }
277+ }
278+
279+
280+ /* Looks for game over */
281+
282+ for (j = 0; j < FIELD_WIDTH; j++) {
283+ if (playfield[TOP_GAP][j + BORDER_WIDTH] > 0) {
284+ draw_board(current_piece, NULL, is_piece);
285+ sleep(2);
286+ printf("\033[2J\033[H");
287+ printf("HAHA LOSER\n");
288+ sleep(1);
289+ goto end;
290+ }
291+ }
292+
293+
294+ if (draw)
295+ draw_board(current_piece, NULL, is_piece);
296+ }
297+
298+end:
299+
300+ printf("\033[?1049l\033[?25h");
301+
302+ tcsetattr(STDIN_FILENO, TCSANOW, &old);
303+
304+ return 0;
305+}
Added file: source/main.h+79 additions
@@ -0,0 +1,79 @@
1+#ifndef MAIN_C
2+
3+#define MAIN_C
4+
5+#include <stdio.h>
6+#include <stdint.h>
7+#include <stdbool.h>
8+#include <stdlib.h>
9+
10+#include <termios.h>
11+#include <sys/ioctl.h>
12+#include <sys/stat.h>
13+
14+#include <time.h>
15+#include <unistd.h>
16+#include <poll.h>
17+
18+#include <ctype.h>
19+
20+#include <errno.h>
21+
22+
23+#define FIELD_WIDTH 10
24+#define FIELD_HEIGHT 21
25+
26+#define BORDER_WIDTH 2
27+#define TOP_GAP 1
28+
29+#define FULL_WIDTH (FIELD_WIDTH + BORDER_WIDTH * 2)
30+#define FULL_HEIGHT (FIELD_HEIGHT + BORDER_WIDTH)
31+
32+#define FRAME 17000000
33+
34+#define FALL 58
35+#define BLINK 12
36+
37+typedef enum {
38+ EMPTY, I_PIECE, J_PIECE, L_PIECE, O_PIECE, S_PIECE, T_PIECE, Z_PIECE, TETRIS
39+} tetromino;
40+
41+typedef enum {
42+ TOP, RIGHT, BOTTOM, LEFT
43+} direction;
44+
45+typedef struct {
46+ tetromino name;
47+ bool piece[4][4];
48+ int8_t x;
49+ int8_t y;
50+ uint8_t size;
51+ direction rotation;
52+ uint8_t positions;
53+} piece;
54+
55+typedef struct {
56+ int8_t x;
57+ int8_t y;
58+} coord;
59+
60+
61+
62+void init_field();
63+
64+void draw_board(piece current_piece, piece next_piece, bool *blink, bool is_piece);
65+void draw_pause();
66+
67+
68+piece init_piece(tetromino new_piece);
69+
70+coord find_direction(piece current_piece, uint8_t x, uint8_t y);
71+
72+bool valid_position(piece current_piece);
73+
74+
75+
76+extern tetromino playfield[FULL_HEIGHT][FULL_WIDTH];
77+
78+
79+#endif
Binary file: source/main.obinary

Binary file not shown.