jag

tetoris_c

public

Small block game in C

Star0
Watch0
a5d2e11a0a023b800351d532b22e5b3b06bb8edc
1
ssh://git@git.jaglekon.de:30/jag/tetoris_c.git
main.cC612 lines12.4 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
        for (j = 0; j < current_piece.size; j++) {
162
          for (k = 0; k < current_piece.size; k++) {
163
164
            coord points = find_direction(current_piece, k, j);
165
166
            if (
167
              current_piece.piece[j][k]  && 
168
              playfield[current_piece.y + points.y][current_piece.x + points.x + move] != EMPTY
169
            ) {
170
              is_valid = false;
171
            }
172
          }
173
        }
174
175
        if (is_valid) {
176
          current_piece.x += move;
177
          draw = true;
178
        }
179
      }
180
    }
181
182
    /* Piece rotates */
183
184
    
185
    if (rotate) {
186
      
187
      piece rotated = current_piece;
188
189
      rotated.rotation = (current_piece.rotation + 1) % (LEFT + 1);
190
191
      bool is_valid = true;
192
193
      for(j = 0; j < rotated.size; j++) {
194
        for (k = 0; k < rotated.size; k++) {
195
196
          coord points = find_direction(rotated, k, j);
197
198
          if (
199
            rotated.piece[j][k] &&
200
            playfield[rotated.y + points.y][rotated.x + points.x] != EMPTY
201
          ) {
202
            is_valid = false;
203
          }
204
        }
205
      } 
206
      
207
      if (is_valid) {
208
        current_piece = rotated;
209
        draw = true;
210
      }
211
212
    }
213
    
214
215
    /* Piece falls */
216
217
    if (i == 0 || down) {
218
      current_piece.y++;
219
      i = 1;
220
      draw = true;
221
    }
222
223
224
    /* When a piece is stuck */
225
226
    for(j = 0; j < current_piece.size; j++) {
227
      for (k = 0; k < current_piece.size; k++) {
228
      
229
        coord points = find_direction(current_piece, k, j);
230
231
        uint8_t x = current_piece.x + points.x;
232
        uint8_t y = current_piece.y + points.y;
233
234
        if (
235
          y >= FULL_HEIGHT || 
236
          (playfield[y][x] != EMPTY && current_piece.piece[j][k])
237
        ) {
238
239
          uint8_t l, m;
240
241
          for (l = 0; l < current_piece.size; l++) {
242
            for (m = 0; m < current_piece.size; m++) {
243
              
244
              if (current_piece.piece[l][m]) {
245
                
246
                coord solid = find_direction(current_piece, m, l);
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
258
    /* Looks if there is a tetris */
259
260
    for (j = FIELD_HEIGHT + TOP_GAP - 1; j >= 0; j--) {
261
262
      uint8_t is_tetris = 0;
263
264
      for (k = 0; k < FIELD_WIDTH; k++) {
265
        if (playfield[j][k + BORDER_WIDTH] != EMPTY)
266
          is_tetris++;
267
      }
268
269
      if (is_tetris == 10) {
270
        
271
        for (k = 0; k < 4; k++) {
272
          
273
          draw_board(current_piece, (k % 2) ? -1 : j, is_piece);
274
275
          usleep(300000);
276
        }
277
278
        for (k = j; k > 0; k--) {
279
          
280
          int8_t l;
281
          
282
          for (l = 0; l < FIELD_WIDTH; l++) {
283
            playfield[k][l + BORDER_WIDTH] = playfield[k - 1][l + BORDER_WIDTH];
284
          }
285
        }
286
        
287
        j++;
288
      }
289
    }
290
291
292
    /* Looks for game over */
293
294
    for (j = 0; j < FIELD_WIDTH; j++) {
295
      if (playfield[TOP_GAP][j + BORDER_WIDTH] > 0) {
296
        draw_board(current_piece, -1, is_piece);
297
        sleep(2);
298
        printf("\033[2J\033[H");
299
        printf("HAHA LOSER\n");
300
        sleep(1);
301
        goto end;
302
      }
303
    }
304
305
306
    if (draw)
307
      draw_board(current_piece, -1, is_piece);
308
    usleep(10000);
309
  }
310
311
end:
312
313
  printf("\033[?1049l\033[?25h");
314
315
  tcsetattr(STDIN_FILENO, TCSANOW, &old);
316
317
  return 0;
318
}
319
320
321
322
void init_field() {
323
  uint8_t i, j;
324
  for(i = 0; i < FULL_HEIGHT; i++) {
325
    for (j = 0; j < FULL_WIDTH; j++) {
326
      
327
      if (
328
        j < BORDER_WIDTH || 
329
        j >= FULL_WIDTH - BORDER_WIDTH ||
330
        i >= FULL_HEIGHT - BORDER_WIDTH
331
      ) {
332
        playfield[i][j] = TETRIS; 
333
      } else {
334
        playfield[i][j] = EMPTY;
335
      }
336
    }
337
  }
338
}
339
340
341
342
#define RED "\033[38;2;255;0;0m"
343
#define GREEN "\033[38;2;0;255;0m"
344
#define BLUE "\033[38;2;0;0;255m"
345
#define YELLOW "\033[38;2;255;255;0m"
346
#define MAGENTA "\033[38;2;255;0;255m"
347
#define TEAL "\033[38;2;0;255;255m"
348
#define PURPLE "\033[38;2;127;0;255m"
349
#define WHITE "\033[38;2;255;255;255m"
350
351
void draw_color (tetromino current_square) {
352
  switch (current_square) {
353
    case I_PIECE:
354
      printf("%s", RED);
355
      break;
356
    case J_PIECE:
357
      printf("%s", GREEN);
358
      break;
359
    case L_PIECE:
360
      printf("%s", BLUE);
361
      break;
362
    case O_PIECE:
363
      printf("%s", YELLOW);
364
      break;
365
    case S_PIECE:
366
      printf("%s", MAGENTA);
367
      break;
368
    case T_PIECE:
369
      printf("%s", TEAL);
370
      break;
371
    case Z_PIECE:
372
      printf("%s", PURPLE);
373
      break;
374
  } 
375
}
376
377
378
void draw_board(piece current_piece, int8_t blink, bool is_piece) {
379
380
  uint8_t i, j;
381
382
383
  printf("\033[2J\033[H");
384
  printf("┏");
385
  for (i = 0; i < FIELD_WIDTH; i++)
386
    printf("━━");
387
  printf("┓\n");
388
389
390
  for (i = 0; i < FIELD_HEIGHT; i++) {
391
    
392
    printf("┃");
393
394
    for (j = 0; j < FIELD_WIDTH; j++) {
395
      
396
      if (i + TOP_GAP == blink) {
397
  
398
        printf("%s", WHITE);
399
      } else {
400
401
402
        tetromino current_square = current_square = playfield[i + TOP_GAP][j + BORDER_WIDTH];
403
404
        if (is_piece) {
405
406
          uint8_t k, l;
407
          
408
          for (k = 0; k < current_piece.size; k++) {
409
            for (l = 0; l < current_piece.size; l++) {
410
411
              coord points = find_direction(current_piece, l, k);
412
413
              if (
414
                i == current_piece.y + points.y - TOP_GAP && 
415
                j == current_piece.x + points.x - BORDER_WIDTH && 
416
                current_piece.piece[k][l]) 
417
              {
418
                current_square = current_piece.name;
419
              }
420
            }
421
          }
422
        }
423
424
        if (current_square <= EMPTY || current_square > Z_PIECE) {
425
          printf("· ");
426
          continue;
427
        }
428
429
        draw_color(current_square);
430
      }
431
        
432
      printf("██\033[0m");
433
    }
434
    printf("┃\n");
435
  }
436
437
  printf("┗");
438
  for (i = 0; i < FIELD_WIDTH; i++)
439
    printf("━━");
440
  printf("┛\n");
441
442
}
443
444
445
piece init_piece(tetromino new_piece) {
446
  
447
  piece current_piece = {};
448
449
  current_piece.name = new_piece;
450
451
  switch (current_piece.name) {
452
    case I_PIECE:
453
      current_piece.size = 4;
454
      
455
      current_piece.piece[0][0] = false;
456
      current_piece.piece[0][1] = false;
457
      current_piece.piece[0][2] = false;
458
      current_piece.piece[0][3] = false;
459
460
      current_piece.piece[1][0] = true;
461
      current_piece.piece[1][1] = true;
462
      current_piece.piece[1][2] = true;
463
      current_piece.piece[1][3] = true;
464
      
465
      current_piece.piece[2][0] = false;
466
      current_piece.piece[2][1] = false;
467
      current_piece.piece[2][2] = false;
468
      current_piece.piece[2][3] = false;
469
      
470
      current_piece.piece[3][0] = false;
471
      current_piece.piece[3][1] = false;
472
      current_piece.piece[3][2] = false;
473
      current_piece.piece[3][3] = false;
474
      
475
      break;
476
477
    case J_PIECE:
478
      current_piece.size = 3;
479
  
480
      current_piece.piece[0][0] = true;
481
      current_piece.piece[0][1] = false;
482
      current_piece.piece[0][2] = false;
483
      
484
      current_piece.piece[1][0] = true;
485
      current_piece.piece[1][1] = true;
486
      current_piece.piece[1][2] = true;
487
488
      current_piece.piece[2][0] = false;
489
      current_piece.piece[2][1] = false;
490
      current_piece.piece[2][2] = false;
491
492
      break;
493
494
    case L_PIECE:
495
      current_piece.size = 3;
496
  
497
      current_piece.piece[0][0] = false;
498
      current_piece.piece[0][1] = false;
499
      current_piece.piece[0][2] = true;
500
      
501
      current_piece.piece[1][0] = true;
502
      current_piece.piece[1][1] = true;
503
      current_piece.piece[1][2] = true;
504
505
      current_piece.piece[2][0] = false;
506
      current_piece.piece[2][1] = false;
507
      current_piece.piece[2][2] = false;
508
509
      break;
510
511
    case O_PIECE:
512
      current_piece.size = 2;
513
  
514
      current_piece.piece[0][0] = true;
515
      current_piece.piece[0][1] = true;
516
517
      current_piece.piece[1][0] = true;
518
      current_piece.piece[1][1] = true;
519
      
520
      break;
521
522
    case S_PIECE:
523
      current_piece.size = 3;
524
  
525
      current_piece.piece[0][0] = false;
526
      current_piece.piece[0][1] = true;
527
      current_piece.piece[0][2] = true;
528
529
      current_piece.piece[1][0] = true;
530
      current_piece.piece[1][1] = true;
531
      current_piece.piece[1][2] = false;
532
 
533
      current_piece.piece[2][0] = false;
534
      current_piece.piece[2][1] = false;
535
      current_piece.piece[2][2] = false;
536
537
      break;
538
539
    case T_PIECE:
540
      current_piece.size = 3;
541
  
542
      current_piece.piece[0][0] = false;
543
      current_piece.piece[0][1] = true;
544
      current_piece.piece[0][2] = false;
545
      
546
      current_piece.piece[1][0] = true;
547
      current_piece.piece[1][1] = true;
548
      current_piece.piece[1][2] = true;
549
      
550
      current_piece.piece[2][0] = false;
551
      current_piece.piece[2][1] = false;
552
      current_piece.piece[2][2] = false;
553
554
      break;
555
    case Z_PIECE:
556
      current_piece.size = 3;
557
  
558
      current_piece.piece[0][0] = true;
559
      current_piece.piece[0][1] = true;
560
      current_piece.piece[0][2] = false;
561
562
      current_piece.piece[1][0] = false;
563
      current_piece.piece[1][1] = true;
564
      current_piece.piece[1][2] = true;
565
566
      current_piece.piece[2][0] = false;
567
      current_piece.piece[2][1] = false;
568
      current_piece.piece[2][2] = false;
569
570
      break;
571
    default:
572
      perror("Fuck you\n");
573
      break;
574
  }
575
576
  current_piece.x = (FIELD_WIDTH >> 1) - (current_piece.size >> 1) + BORDER_WIDTH;
577
  current_piece.y = TOP_GAP;
578
  current_piece.rotation = TOP;
579
580
  return current_piece;
581
}
582
583
coord find_direction(piece current_piece, uint8_t x, uint8_t y) {
584
585
  coord points = {
586
    x,
587
    y
588
  };
589
590
  switch (current_piece.rotation) {
591
    case TOP:
592
      /*
593
      x = k;
594
      y = j;
595
      */
596
      break;
597
    case RIGHT:
598
      points.x = y;
599
      points.y = current_piece.size - (x + 1);
600
      break;
601
    case BOTTOM:
602
      points.x = current_piece.size - (x + 1);
603
      points.y = current_piece.size - (y + 1);
604
      break;
605
    case LEFT:
606
      points.x = current_piece.size - (y + 1);
607
      points.y = x;
608
      break;
609
  }
610
611
  return points;
612
}