jag

tetoris_c

public

Small block game in C

Star0
Watch0
aa1476b5d15caadac6c4ff31dd865322ace0005b
1
ssh://git@git.jaglekon.de:30/jag/tetoris_c.git
source/main.cC420 lines7.8 KB
1
#include "main.h"
2
#include <jag_parse.h>
3
4
5
tetromino playfield[FULL_HEIGHT][FULL_WIDTH] = {};
6
7
uint8_t level = 0;
8
uint16_t all_cleared_lines = 0;
9
uint32_t score = 0;
10
11
12
int main () {
13
  
14
15
  /* Setting up the seed */
16
  
17
  srand(time(NULL));
18
19
20
  /* Setting up terminal with termios */
21
  
22
  struct termios old, new;
23
24
  tcgetattr(STDIN_FILENO, &old);
25
  new = old;
26
27
  new.c_lflag &= ~(ECHO | ICANON);
28
  new.c_cc[VMIN] = 0;
29
  new.c_cc[VTIME] = 0;
30
31
  tcsetattr(STDIN_FILENO, TCSANOW, &new);
32
33
  struct winsize init_size, current_size;
34
  ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);
35
  current_size = init_size;
36
37
  printf("\033[?25l\033[?1049h\033[2J\033[H");
38
39
40
  /* Polling */
41
42
  struct pollfd polling[1];
43
44
  polling[0].fd = STDIN_FILENO;
45
  polling[0].events = POLLIN;
46
47
48
  /* Init */
49
50
  init_field();
51
 
52
  bool blink[FIELD_HEIGHT] = {};
53
54
  bool is_piece = false;
55
56
  bool paused = false;
57
  
58
  uint16_t lines_to_level = 0;
59
60
  uint16_t pot_score_lines = 0;
61
  uint16_t pot_score_fall = 0;
62
63
  bool add_score = false;
64
65
  piece current_piece = {};
66
  piece next_piece = init_piece((rand() % Z_PIECE) + 1);
67
68
69
  /* Time keeping */
70
71
  struct timespec start, current;
72
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
73
74
  uint64_t time_acc = 0;
75
76
  uint16_t tick_fall = 0;
77
  uint16_t tick_blink = 0;
78
  uint16_t tick_spin = 0;
79
  uint16_t tick_fall_prot = 0;
80
  uint16_t tick_wait_key = 0;
81
82
  uint8_t i = 0;
83
  bool is_tetris = false;
84
85
  /* Game Loop */
86
87
  while (true) {
88
89
    int8_t move = 0;
90
    bool down = false;
91
    bool draw = false;
92
    bool rotate = false;
93
    bool key_lock = false;
94
95
96
    /* Polling for input */
97
98
    int is_poll = poll(polling, 1, paused ? -1 : 17);
99
100
101
    /* Dealing with Ticks */
102
103
    uint16_t ticks = 0;
104
105
    {
106
107
      clock_gettime(CLOCK_MONOTONIC_RAW, &current);
108
109
      uint64_t time_diff = 
110
        (current.tv_sec - start.tv_sec) * 1000000000ULL + 
111
        (current.tv_nsec - start.tv_nsec) + time_acc;
112
113
      if (!paused) {
114
115
        while (time_diff >= FRAME) {
116
117
          time_diff -= FRAME;
118
          ticks++;
119
        }
120
121
        time_acc = time_diff;
122
      }
123
124
      start = current;
125
    }
126
127
128
    /* Take care of the input */
129
130
    if (is_poll > 0 && polling[0].revents & POLLIN) {
131
132
      uint8_t buf[256] = {};
133
134
      int read_len;
135
136
      if ((read_len = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
137
138
        if (add_to_buf(buf, read_len)) {
139
140
          keypress input = UNKNOWN;
141
142
          while (input != NO_KEY) {
143
144
            input = parse_input();
145
 
146
            if (input == INCOMPLETE) {
147
              if(tick_wait_key >= KEY_PROTECTION) {
148
                break_unfinished();
149
              } else {
150
                key_lock = true;
151
                break;
152
              }
153
154
            } else {
155
156
              switch (input) {
157
                case P_KEY:
158
                  paused = !paused; 
159
                  break;
160
                case Q_KEY:
161
                  goto end;
162
                  break;
163
                case UP_ARROW:
164
                  rotate = true; 
165
                  break;
166
                case DOWN_ARROW:
167
                  down = true;
168
                  break;
169
                case RIGHT_ARROW:
170
                  move = 1;
171
                  break;
172
                case LEFT_ARROW:
173
                  move = -1;
174
                  break;
175
                default:
176
                  break;
177
              }
178
179
              tick_wait_key = 0;
180
            }
181
          }
182
        } else {
183
          break; /* TODO error */
184
        }
185
      }
186
    }
187
188
    if (key_lock)
189
      tick_wait_key++;
190
191
    if (paused) {
192
      draw_pause();
193
      continue;
194
    }
195
196
197
    /* Dealing with cleared Lines */
198
199
    if (is_tetris) {
200
201
202
      tick_blink += ticks;
203
204
      while (tick_blink >= BLINK && i < 4) { 
205
 
206
        tick_blink -= BLINK;
207
208
        draw_board(current_piece, next_piece, (i % 2 == 0) ? blink : NULL, is_piece);
209
        i++;
210
      }
211
      
212
213
      if (i == 4) {
214
      
215
        int8_t j, k;
216
217
        for (j = 0; j < FIELD_HEIGHT; j++) {
218
          if (blink[j]) {
219
220
            for (k = j; k > 0; k--) {
221
              int8_t l;
222
          
223
              for (l = 0; l < FIELD_WIDTH; l++) {
224
                playfield[k][l + BORDER_WIDTH] = playfield[k - 1][l + BORDER_WIDTH];
225
              }
226
            }
227
228
            all_cleared_lines++;
229
          }
230
        }
231
232
        i = 0;
233
        is_tetris = false;
234
      }
235
236
      continue;
237
    }
238
239
    /* Adding score */
240
241
    if (add_score) {
242
      score += pot_score_fall + pot_score_lines;
243
      pot_score_fall = pot_score_lines = 0;
244
      add_score = false;
245
    }
246
247
    /* Creates new piece */
248
249
    if (!is_piece) {
250
      current_piece = next_piece;
251
      next_piece = init_piece((rand() % Z_PIECE) + 1);
252
      is_piece = true;
253
      draw = true;
254
255
      tick_fall = tick_blink = tick_spin = tick_fall_prot = 0;
256
    }
257
258
259
    /* Move piece */
260
261
    if (move != 0) {
262
      if (
263
        current_piece.x + move >= 0 && 
264
        current_piece.x + move + current_piece.size <= FULL_WIDTH
265
      ) {
266
       
267
        piece moved_piece = current_piece;
268
        moved_piece.x += move;
269
270
        if (valid_position(moved_piece)) {
271
          current_piece.x += move;
272
          draw = true;
273
        }
274
      }
275
    }
276
277
278
    /* Piece rotates */
279
280
    if (tick_spin < SPIN)
281
      tick_spin += ticks;
282
283
    if (rotate && current_piece.positions > 1 && tick_spin >= SPIN) {
284
      
285
      piece rotated = current_piece;
286
287
      rotated.rotation = (current_piece.rotation + 1) % (current_piece.positions);
288
      
289
      if (valid_position(rotated)) {
290
        current_piece = rotated;
291
        draw = true;
292
      }
293
294
      tick_spin = 0;
295
    } 
296
    
297
298
    /* Piece falls */
299
300
    tick_fall += ticks;
301
  
302
    if (tick_fall_prot < FALL_PROTECTION)
303
      tick_fall_prot += ticks;
304
305
    uint8_t fall_delay = level_delay(); 
306
307
    while (tick_fall >= fall_delay || (down && tick_fall_prot >= FALL_PROTECTION)) {
308
      current_piece.y++;
309
      
310
311
      if (tick_fall >= fall_delay) {
312
        tick_fall -= fall_delay;
313
      } else {
314
        tick_fall = 0;
315
      }
316
317
      if (down) {
318
        pot_score_fall++;
319
      } else {
320
        pot_score_fall = 0;
321
      }
322
      
323
      down = false;
324
325
      draw = true;
326
    }
327
    
328
329
330
    /* Looks for stuck piece */
331
332
    if (!valid_position(current_piece)) {
333
334
      int8_t j, k;
335
336
      for (j = 0; j < current_piece.size; j++) {
337
        for (k = 0; k < current_piece.size; k++) {
338
          
339
          if (current_piece.piece[j][k]) {
340
            
341
            coord solid = find_direction(current_piece, k, j);
342
343
            playfield[current_piece.y - 1 + solid.y][current_piece.x + solid.x] = current_piece.name;
344
          }
345
        }
346
      }
347
      is_piece = false;
348
      draw = true;
349
350
      add_score = true;
351
    }
352
353
354
    /* Looks for cleared line */
355
356
    int8_t j, k;
357
    
358
    {
359
360
      uint8_t current_lines = 0;
361
      
362
      for (j = FIELD_HEIGHT - 1; j >= 0; j--) {
363
364
        uint8_t blocked = 0;
365
366
367
        for (k = 0; k < FIELD_WIDTH; k++) {
368
          if (playfield[j][k + BORDER_WIDTH] != EMPTY)
369
            blocked++;
370
        }
371
372
        if (blocked == 10) {
373
          
374
          blink[j] = true;
375
          is_tetris = true;
376
          lines_to_level++;
377
          current_lines++;
378
        } else {
379
          blink[j] = false;
380
        }
381
      }
382
383
      if (current_lines > 0) {
384
        pot_score_lines += line_points_add(current_lines, level);
385
        add_score = true;
386
      }
387
    }
388
389
    /* Level up */
390
391
    if (lines_to_level >= LINES_LEVEL && level < MAX_LEVEL) {
392
      level++;
393
      lines_to_level -= LINES_LEVEL;
394
    }
395
396
    /* Looks for game over */
397
398
    for (j = 0; j < FIELD_WIDTH; j++) {
399
      if (playfield[TOP_GAP][j + BORDER_WIDTH] > 0) {
400
        draw_board(current_piece, next_piece, NULL, is_piece);
401
        sleep(2);
402
        printf("\033[2J\033[H");
403
        printf("HAHA LOSER\n");
404
        sleep(1);
405
        goto end;
406
      }
407
    }
408
409
    if (draw)
410
      draw_board(current_piece, next_piece, NULL, is_piece);
411
  }
412
413
end:
414
415
  printf("\033[?1049l\033[?25h");
416
417
  tcsetattr(STDIN_FILENO, TCSANOW, &old);
418
419
  return 0;
420
}