jag

tetoris_c

public

Small block game in C

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