jag

tetoris_c

public

Small block game in C

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