jag

tetoris_c

public

Small block game in C

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