Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
zmx0142857
GitHub Repository: zmx0142857/mini-games
Path: blob/master/c/tetris/tetris.c
363 views
1
#include "game.h"
2
#include <time.h> // time()
3
#include <string.h> // memset()
4
#include <ctype.h> // tolower()
5
6
#define WIDTH 10
7
#define HEIGHT 20
8
#define BUFFER_HEIGHT 4
9
bool map[HEIGHT+BUFFER_HEIGHT+1][WIDTH];
10
11
#define MOVE_LEFT -1
12
#define MOVE_DOWN 0
13
#define MOVE_RIGHT 1
14
#define ROTATE90 90
15
#define ROTATE180 180
16
#define ROTATE270 270
17
#define msglog(s...)\
18
do {\
19
mvprint(13,2*WIDTH+3, " ");\
20
mvprint(13, 2*WIDTH+3, s);\
21
} while (false)
22
23
// debug only
24
void map_dump()
25
{
26
for (int i = 0; cursor_goto(i, 40), i < HEIGHT+BUFFER_HEIGHT; ++i)
27
for (int j = 0; j < WIDTH; ++j)
28
putchar('0' + map[i][j]);
29
}
30
31
typedef struct {
32
int x[4];
33
int y[4];
34
} Tetris;
35
36
// coordinate y increases
37
Tetris tetris[7] = {
38
// parenthesis indicates the pivot
39
{ {-1, 0, 1, 2}, {0, 0, 0, 0} }, // []()[][]
40
41
{ {0, -1, 0, 1}, {-1, 0, 0, 0} }, // []
42
// []()[]
43
44
{ {0, 1, -1, 0}, {-1, -1, 0, 0} }, // [][]
45
// []()
46
47
{ {-1, 0, 0, 1}, {-1, -1, 0, 0} }, // [][]
48
// ()[]
49
50
{ {-1, 0, 1, 1}, {0, 0, 0, 1} }, // []()[]
51
// []
52
53
{ {-1, 0, 1, 0}, {0, 0, 0, 1} }, // []()[]
54
// []
55
// this one doesn't rotate
56
{ {0, 1, 0, 1}, {0, 0, 1, 1} } // [][]
57
// [][]
58
};
59
60
Tetris tetNext, tetCurrent;
61
62
int score = 0;
63
bool is_game_ready = false;
64
bool is_game_over = false;
65
bool need_new = true;
66
int tet_x, tet_y;
67
68
void score_print()
69
{
70
mvprint(3, 2*WIDTH+4, "%6d", score);
71
}
72
73
void tet_eliminate()
74
{
75
static int ys[4];
76
int cnt = 0;
77
bool ok;
78
// find rows to emilinate
79
for (int i = 0; i < 4; ++i) {
80
ok = true;
81
int y = ys[cnt] = tet_y + tetCurrent.y[i];
82
for (int j = 0; j < cnt; ++j) {
83
if (ys[j] == y) {
84
ok = false;
85
break;
86
}
87
}
88
if (ok) {
89
for (int x = 0; x < WIDTH; ++x) {
90
if (!map[y][x]) {
91
ok = false;
92
break;
93
}
94
}
95
}
96
if (ok) ++cnt;
97
}
98
99
// insertion-sort the ys
100
if (cnt) {
101
for (int i = 1; i < cnt; ++i) {
102
int j;
103
int tmp = ys[i];
104
for (j = i; j > 0 && ys[j-1] > tmp; --j)
105
ys[j] = ys[j-1];
106
ys[j] = tmp;
107
}
108
}
109
110
// eliminate
111
for (int i = 0; i < cnt; ++i) {
112
int y = ys[i];
113
while (y > 0) {
114
for (int x = 0; x < WIDTH; ++x) {
115
if (map[y][x] != map[y-1][x]) {
116
map[y][x] = map[y-1][x];
117
mvprint(y-BUFFER_HEIGHT, x*2+1,
118
map[y][x] ? SYM_BLOCK_BRACK : SYM_BLANK);
119
}
120
}
121
--y;
122
}
123
for (int x = 0; x < WIDTH; ++x) {
124
map[0][x] = false;
125
mvprint(0, x*2+1, SYM_BLANK);
126
}
127
}
128
129
if (cnt) {
130
score += cnt*(100+(cnt-1)*50);
131
score_print();
132
}
133
}
134
135
void tet_rotate(Tetris *tet, int angle)
136
{
137
int tmp;
138
for (int rotated = 0; rotated < angle; rotated += 90) {
139
for (int i = 0; i < 4; ++i) {
140
tmp = tet->y[i];
141
tet->y[i] = tet->x[i];
142
tet->x[i] = -tmp;
143
}
144
}
145
}
146
147
void tet_print(const Tetris *tet, int y, int x, bool visible)
148
{
149
for (int i = 0; i < 4; ++i) {
150
int new_x = x + tet->x[i];
151
int new_y = y + tet->y[i];
152
if (new_y >= 0)
153
mvprint(new_y, new_x*2 + 1,
154
visible ? SYM_BLOCK_BRACK : SYM_BLANK);
155
}
156
}
157
158
void tet_copy(Tetris *dest, const Tetris *src)
159
{
160
for (int i = 0; i < 4; ++i) {
161
dest->x[i] = src->x[i];
162
dest->y[i] = src->y[i];
163
}
164
}
165
166
void tet_set(bool visible)
167
{
168
for (int i = 0; i < 4; ++i) {
169
int new_x = tet_x + tetCurrent.x[i];
170
int new_y = tet_y + tetCurrent.y[i];
171
map[new_y][new_x] = visible;
172
}
173
tet_print(&tetCurrent, tet_y-BUFFER_HEIGHT, tet_x, visible);
174
}
175
176
bool tet_chk(int movement)
177
{
178
tet_set(false);
179
if (movement == MOVE_LEFT || movement == MOVE_RIGHT) {
180
for (int i = 0; i < 4; ++i) {
181
int new_x = tet_x + tetCurrent.x[i] + movement;
182
int new_y = tet_y + tetCurrent.y[i];
183
if (new_x < 0 || new_x >= WIDTH || map[new_y][new_x] == true)
184
{
185
tet_set(true);
186
return false;
187
}
188
}
189
} else if (movement == MOVE_DOWN) {
190
for (int i = 0; i < 4; ++i) {
191
int new_x = tet_x + tetCurrent.x[i];
192
int new_y = tet_y + tetCurrent.y[i] + 1;
193
if (map[new_y][new_x] == true) {
194
tet_set(true);
195
tet_eliminate();
196
for (int j = 0; j < 4; ++j) {
197
if (tet_y + tetCurrent.y[j] < BUFFER_HEIGHT)
198
is_game_over = true;
199
}
200
need_new = true;
201
return false;
202
}
203
}
204
} else { // rotate
205
Tetris tmp;
206
tet_copy(&tmp, &tetCurrent);
207
tet_rotate(&tmp, movement);
208
int tmp_x = tet_x, tmp_y = tet_y;
209
for (int i = 0; i < 4; ++i) {
210
int new_x = tmp_x + tmp.x[i];
211
int new_y = tmp_y + tmp.y[i];
212
if (new_x < 0)
213
tmp_x += -new_x;
214
else if (new_x >= WIDTH)
215
tmp_x -= new_x - WIDTH + 1;
216
else if (new_y >= HEIGHT+BUFFER_HEIGHT)
217
tmp_y -= new_y - HEIGHT - BUFFER_HEIGHT + 1;
218
}
219
for (int i = 0; i < 4; ++i) {
220
int new_x = tmp_x + tmp.x[i];
221
int new_y = tmp_y + tmp.y[i];
222
if (map[new_y][new_x] == true) {
223
tet_set(true);
224
return false;
225
}
226
}
227
tet_x = tmp_x, tet_y = tmp_y;
228
}
229
return true;
230
}
231
232
void tet_generate()
233
{
234
if (!is_game_ready) {
235
srand(time(NULL));
236
} else {
237
tet_copy(&tetCurrent, &tetNext);
238
tet_set(true);
239
tet_print(&tetNext, 9, WIDTH+3, false);
240
}
241
tet_copy(&tetNext, tetris + rand() % 7);
242
tet_rotate(&tetNext, rand() % 4 * 90);
243
tet_print(&tetNext, 9, WIDTH+3, true);
244
if (!is_game_ready) {
245
is_game_ready = true;
246
tet_generate();
247
}
248
}
249
250
bool tet_move(int movement)
251
{
252
if (tet_chk(movement)) {
253
if (movement == MOVE_LEFT || movement == MOVE_RIGHT)
254
tet_x += movement;
255
else if (movement == MOVE_DOWN)
256
++tet_y;
257
else
258
tet_rotate(&tetCurrent, movement);
259
tet_set(true);
260
return true;
261
}
262
return false;
263
}
264
265
void control()
266
{
267
is_game_ready = false;
268
is_game_over = false;
269
need_new = true;
270
while (!is_game_over) {
271
if (need_new) {
272
need_new = false;
273
tet_x = 5;
274
tet_y = 1;
275
tet_generate();
276
} else
277
tet_move(MOVE_DOWN);
278
279
while (!need_new && kbhit()) {
280
char key = tolower(getch());
281
switch (key) {
282
case 'a': case '4':
283
if (tet_move(MOVE_LEFT)) Sleep(30); break;
284
case 'd': case '6':
285
if (tet_move(MOVE_RIGHT)) Sleep(30); break;
286
case 'w': case '2':
287
if (tet_move(ROTATE90)) Sleep(30); break;
288
case 's': case '5':
289
while (tet_move(MOVE_DOWN)) {} break;
290
case KEY_ESC:
291
msglog("paused");
292
key = getch();
293
if (key == KEY_ESC) {
294
return;
295
} else {
296
msglog(" ");
297
}
298
}
299
}
300
Sleep(300);
301
}
302
}
303
304
void scene_play()
305
{
306
screen_clear();
307
for (int i = 0; i < HEIGHT; ++i) {
308
mvprint(i, 0, "|");
309
mvprint(i, 2*WIDTH+1, "|");
310
}
311
mvprint(HEIGHT, 0, "+--------------------+");
312
mvprint(1, 2*WIDTH+4, " score");
313
mvprint(5, 2*WIDTH+4, " next");
314
score = 0;
315
score_print();
316
memset(map, 0, sizeof(map));
317
memset(map+HEIGHT+BUFFER_HEIGHT, 1, WIDTH);
318
319
control();
320
321
mvprint(13, 2*WIDTH+3, "game over!");
322
getch();
323
}
324
325
void scene_high_score()
326
{
327
screen_clear();
328
printf("zmx: 99999\n\n"
329
"any key to return\n");
330
getch();
331
}
332
333
void scene_help()
334
{
335
screen_clear();
336
printf("w, 2 - rotate\n"
337
"a, 4 - move left\n"
338
"d, 6 - move right\n"
339
"s, 5 - drop\n"
340
"<esc> - pause; when paused,\n"
341
" press <esc> again to exit\n\n"
342
"any key to return\n");
343
getch();
344
}
345
346
void scene_welcome()
347
{
348
while (true) {
349
screen_clear();
350
printf(
351
"\n\n\n\n\n\n"
352
" Welcome to Tetris!\n\n\n"
353
" 1. play\n"
354
" 2. high score\n"
355
" 3. help\n"
356
" 4. exit\n");
357
switch (getch()) {
358
case '1': scene_play(); break;
359
case '2': scene_high_score(); break;
360
case '3': scene_help(); break;
361
case '4': case KEY_ESC: return;
362
}
363
}
364
}
365
366
int main()
367
{
368
game_init();
369
screen_size(21, 32);
370
scene_welcome();
371
game_over();
372
return 0;
373
}
374
375