jag

tetoris_c

public

Small block game in C

Star0
Watch0
b7c05d8620b059a69bf69dec38a8c71d5d8d608c
1
ssh://git@git.jaglekon.de:30/jag/tetoris_c.git
source/main.cC407 lines7.5 KB
1
#include "main.h"
2
#include "parser.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 = 0;
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;
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 PAUSE:
158
                  paused = !paused; 
159
                  break;
160
                case QUIT:
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
      tick_blink += ticks;
202
203
      while (tick_blink >= BLINK && i < 4) { 
204
 
205
        tick_blink -= BLINK;
206
207
        draw_board(current_piece, next_piece, (i % 2 == 0) ? blink : NULL, is_piece);
208
        i++;
209
      }
210
      
211
212
      if (i == 4) {
213
      
214
        int8_t j, k;
215
216
        for (j = 0; j < FIELD_HEIGHT; j++) {
217
          if (blink[j]) {
218
219
            for (k = j; k > 0; k--) {
220
              int8_t l;
221
          
222
              for (l = 0; l < FIELD_WIDTH; l++) {
223
                playfield[k][l + BORDER_WIDTH] = playfield[k - 1][l + BORDER_WIDTH];
224
              }
225
            }
226
227
            all_cleared_lines++;
228
          }
229
        }
230
231
        i = 0;
232
        is_tetris = false;
233
      }
234
235
      continue;
236
    }
237
238
    /* Adding score */
239
240
    if (add_score) {
241
      score += pot_score_fall + pot_score_lines;
242
      pot_score_fall = pot_score_lines = 0;
243
      add_score = false;
244
    }
245
246
    /* Creates new piece */
247
248
    if (!is_piece) {
249
      current_piece = next_piece;
250
      next_piece = init_piece((rand() % Z_PIECE) + 1);
251
      is_piece = true;
252
      draw = true;
253
254
      tick_fall = tick_blink = tick_spin = tick_fall_prot = 0;
255
    }
256
257
258
    /* Move piece */
259
260
    if (move != 0) {
261
      if (
262
        current_piece.x + move >= 0 && 
263
        current_piece.x + move + current_piece.size <= FULL_WIDTH
264
      ) {
265
       
266
        piece moved_piece = current_piece;
267
        moved_piece.x += move;
268
269
        if (valid_position(moved_piece)) {
270
          current_piece.x += move;
271
          draw = true;
272
        }
273
      }
274
    }
275
276
277
    /* Piece rotates */
278
279
    if (tick_spin < SPIN)
280
      tick_spin += ticks;
281
282
    if (rotate && current_piece.positions > 1 && tick_spin >= SPIN) {
283
      
284
      piece rotated = current_piece;
285
286
      rotated.rotation = (current_piece.rotation + 1) % (current_piece.positions);
287
      
288
      if (valid_position(rotated)) {
289
        current_piece = rotated;
290
        draw = true;
291
      }
292
293
      tick_spin = 0;
294
    } 
295
    
296
297
    /* Piece falls */
298
299
    tick_fall += ticks;
300
  
301
    if (tick_fall_prot < FALL_PROTECTION)
302
      tick_fall_prot += ticks;
303
304
    uint8_t fall_delay = level_delay(); 
305
306
    while (tick_fall >= fall_delay || (down && tick_fall_prot >= FALL_PROTECTION)) {
307
      current_piece.y++;
308
      
309
310
      if (tick_fall >= fall_delay) {
311
        tick_fall -= fall_delay;
312
      } else {
313
        tick_fall = 0;
314
      }
315
316
      if (down) {
317
        pot_score_fall++;
318
      } else {
319
        pot_score_fall = 0;
320
      }
321
      
322
      down = false;
323
324
      draw = true;
325
    }
326
    
327
328
329
    /* Looks for stuck piece */
330
331
    if (!valid_position(current_piece)) {
332
333
      int8_t j, k;
334
335
      for (j = 0; j < current_piece.size; j++) {
336
        for (k = 0; k < current_piece.size; k++) {
337
          
338
          if (current_piece.piece[j][k]) {
339
            
340
            coord solid = find_direction(current_piece, k, j);
341
342
            playfield[current_piece.y - 1 + solid.y][current_piece.x + solid.x] = current_piece.name;
343
          }
344
        }
345
      }
346
      is_piece = false;
347
      draw = true;
348
349
      add_score = true;
350
    }
351
352
353
    /* Looks for cleared line */
354
355
    int8_t j, k;
356
357
    for (j = FIELD_HEIGHT - 1; j >= 0; j--) {
358
359
      uint8_t blocked = 0;
360
361
      for (k = 0; k < FIELD_WIDTH; k++) {
362
        if (playfield[j][k + BORDER_WIDTH] != EMPTY)
363
          blocked++;
364
      }
365
366
      if (blocked == 10) {
367
        
368
        blink[j] = true;
369
        is_tetris = true;
370
        lines_to_level++;
371
      } else {
372
        blink[j] = false;
373
      }
374
    }
375
376
    /* Level up */
377
378
    if (lines_to_level >= LINES_LEVEL && level < MAX_LEVEL) {
379
      level++;
380
      lines_to_level -= LINES_LEVEL;
381
    }
382
383
    /* Looks for game over */
384
385
    for (j = 0; j < FIELD_WIDTH; j++) {
386
      if (playfield[TOP_GAP][j + BORDER_WIDTH] > 0) {
387
        draw_board(current_piece, next_piece, NULL, is_piece);
388
        sleep(2);
389
        printf("\033[2J\033[H");
390
        printf("HAHA LOSER\n");
391
        sleep(1);
392
        goto end;
393
      }
394
    }
395
396
    if (draw)
397
      draw_board(current_piece, next_piece, NULL, is_piece);
398
  }
399
400
end:
401
402
  printf("\033[?1049l\033[?25h");
403
404
  tcsetattr(STDIN_FILENO, TCSANOW, &old);
405
406
  return 0;
407
}