jag

tetoris_c

public

Small block game in C

Star0
Watch0
1bbe0c4bb29c60c578100244b5f1f693102c2b9a
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
    /* Looks for game over */
302
303
    for (j = 0; j < FIELD_WIDTH; j++) {
304
      if (playfield[TOP_GAP][j + BORDER_WIDTH] > 0) {
305
        draw_board(current_piece, NULL, is_piece);
306
        sleep(2);
307
        printf("\033[2J\033[H");
308
        printf("HAHA LOSER\n");
309
        sleep(1);
310
        goto end;
311
      }
312
    }
313
314
315
    if (draw)
316
      draw_board(current_piece, NULL, is_piece);
317
    usleep(10000);
318
  }
319
320
end:
321
322
  printf("\033[?1049l\033[?25h");
323
324
  tcsetattr(STDIN_FILENO, TCSANOW, &old);
325
326
  return 0;
327
}
328
329
330
331
void init_field() {
332
  uint8_t i, j;
333
  for(i = 0; i < FULL_HEIGHT; i++) {
334
    for (j = 0; j < FULL_WIDTH; j++) {
335
      
336
      if (
337
        j < BORDER_WIDTH || 
338
        j >= FULL_WIDTH - BORDER_WIDTH ||
339
        i >= FULL_HEIGHT - BORDER_WIDTH
340
      ) {
341
        playfield[i][j] = TETRIS; 
342
      } else {
343
        playfield[i][j] = EMPTY;
344
      }
345
    }
346
  }
347
}
348
349
350
351
#define RED "\033[38;2;255;0;0m"
352
#define GREEN "\033[38;2;0;255;0m"
353
#define BLUE "\033[38;2;0;0;255m"
354
#define YELLOW "\033[38;2;255;255;0m"
355
#define MAGENTA "\033[38;2;255;0;255m"
356
#define TEAL "\033[38;2;0;255;255m"
357
#define PURPLE "\033[38;2;127;0;255m"
358
#define WHITE "\033[38;2;255;255;255m"
359
360
void draw_color (tetromino current_square) {
361
  switch (current_square) {
362
    case I_PIECE:
363
      printf("%s", RED);
364
      break;
365
    case J_PIECE:
366
      printf("%s", GREEN);
367
      break;
368
    case L_PIECE:
369
      printf("%s", BLUE);
370
      break;
371
    case O_PIECE:
372
      printf("%s", YELLOW);
373
      break;
374
    case S_PIECE:
375
      printf("%s", MAGENTA);
376
      break;
377
    case T_PIECE:
378
      printf("%s", TEAL);
379
      break;
380
    case Z_PIECE:
381
      printf("%s", PURPLE);
382
      break;
383
  } 
384
}
385
386
387
void draw_board(piece current_piece, bool *blink, bool is_piece) {
388
389
  uint8_t i, j;
390
391
392
  printf("\033[H");
393
  printf("┏");
394
  for (i = 0; i < FIELD_WIDTH; i++)
395
    printf("━━");
396
  printf("┓\n");
397
398
399
  for (i = 0; i < FIELD_HEIGHT; i++) {
400
    
401
    printf("┃");
402
403
    for (j = 0; j < FIELD_WIDTH; j++) {
404
      
405
      if ( blink != NULL && blink[i]) {
406
  
407
        printf("%s", WHITE);
408
      } else {
409
410
411
        tetromino current_square = current_square = playfield[i][j + BORDER_WIDTH];
412
413
        if (is_piece) {
414
415
          uint8_t k, l;
416
          
417
          for (k = 0; k < current_piece.size; k++) {
418
            for (l = 0; l < current_piece.size; l++) {
419
420
              coord points = find_direction(current_piece, l, k);
421
422
              if (
423
                i == current_piece.y + points.y && 
424
                j == current_piece.x + points.x - BORDER_WIDTH && 
425
                current_piece.piece[k][l]) 
426
              {
427
                current_square = current_piece.name;
428
              }
429
            }
430
          }
431
        }
432
433
        if (current_square <= EMPTY || current_square > Z_PIECE) {
434
          printf("· ");
435
          continue;
436
        }
437
438
        draw_color(current_square);
439
      }
440
        
441
      printf("██\033[0m");
442
    }
443
    printf("┃\n");
444
  }
445
446
  printf("┗");
447
  for (i = 0; i < FIELD_WIDTH; i++)
448
    printf("━━");
449
  printf("┛\n");
450
451
}
452
453
454
piece init_piece(tetromino new_piece) {
455
  
456
  piece current_piece = {};
457
458
  current_piece.name = new_piece;
459
460
  current_piece.y = TOP_GAP;
461
  current_piece.rotation = TOP;
462
463
464
  switch (current_piece.name) {
465
    case I_PIECE:
466
      current_piece.size = 4;
467
      current_piece.positions = 2;
468
469
      current_piece.piece[0][0] = false;
470
      current_piece.piece[0][1] = false;
471
      current_piece.piece[0][2] = false;
472
      current_piece.piece[0][3] = false;
473
474
      current_piece.piece[1][0] = true;
475
      current_piece.piece[1][1] = true;
476
      current_piece.piece[1][2] = true;
477
      current_piece.piece[1][3] = true;
478
      
479
      current_piece.piece[2][0] = false;
480
      current_piece.piece[2][1] = false;
481
      current_piece.piece[2][2] = false;
482
      current_piece.piece[2][3] = false;
483
      
484
      current_piece.piece[3][0] = false;
485
      current_piece.piece[3][1] = false;
486
      current_piece.piece[3][2] = false;
487
      current_piece.piece[3][3] = false;
488
      
489
      current_piece.y = 0;
490
491
      break;
492
493
    case J_PIECE:
494
      current_piece.size = 3;
495
      current_piece.positions = 4;
496
  
497
      current_piece.piece[0][0] = true;
498
      current_piece.piece[0][1] = false;
499
      current_piece.piece[0][2] = false;
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 L_PIECE:
512
      current_piece.size = 3;
513
      current_piece.positions = 4;
514
  
515
      current_piece.piece[0][0] = false;
516
      current_piece.piece[0][1] = false;
517
      current_piece.piece[0][2] = true;
518
      
519
      current_piece.piece[1][0] = true;
520
      current_piece.piece[1][1] = true;
521
      current_piece.piece[1][2] = true;
522
523
      current_piece.piece[2][0] = false;
524
      current_piece.piece[2][1] = false;
525
      current_piece.piece[2][2] = false;
526
527
      break;
528
529
    case O_PIECE:
530
      current_piece.size = 2;
531
      current_piece.positions = 1;
532
  
533
      current_piece.piece[0][0] = true;
534
      current_piece.piece[0][1] = true;
535
536
      current_piece.piece[1][0] = true;
537
      current_piece.piece[1][1] = true;
538
      
539
      break;
540
541
    case S_PIECE:
542
      current_piece.size = 3;
543
      current_piece.positions = 2;
544
  
545
      current_piece.piece[0][0] = false;
546
      current_piece.piece[0][1] = true;
547
      current_piece.piece[0][2] = true;
548
549
      current_piece.piece[1][0] = true;
550
      current_piece.piece[1][1] = true;
551
      current_piece.piece[1][2] = false;
552
 
553
      current_piece.piece[2][0] = false;
554
      current_piece.piece[2][1] = false;
555
      current_piece.piece[2][2] = false;
556
557
      break;
558
559
    case T_PIECE:
560
      current_piece.size = 3;
561
      current_piece.positions = 4;
562
  
563
      current_piece.piece[0][0] = false;
564
      current_piece.piece[0][1] = true;
565
      current_piece.piece[0][2] = false;
566
      
567
      current_piece.piece[1][0] = true;
568
      current_piece.piece[1][1] = true;
569
      current_piece.piece[1][2] = true;
570
      
571
      current_piece.piece[2][0] = false;
572
      current_piece.piece[2][1] = false;
573
      current_piece.piece[2][2] = false;
574
575
      break;
576
    case Z_PIECE:
577
      current_piece.size = 3;
578
      current_piece.positions = 2;
579
  
580
      current_piece.piece[0][0] = true;
581
      current_piece.piece[0][1] = true;
582
      current_piece.piece[0][2] = false;
583
584
      current_piece.piece[1][0] = false;
585
      current_piece.piece[1][1] = true;
586
      current_piece.piece[1][2] = true;
587
588
      current_piece.piece[2][0] = false;
589
      current_piece.piece[2][1] = false;
590
      current_piece.piece[2][2] = false;
591
592
      break;
593
    default:
594
      perror("Fuck you\n");
595
      break;
596
  }
597
598
  current_piece.x = ((FULL_WIDTH >> 1) - (current_piece.size >> 1));
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
}