jag

tetoris_c

public

Small block game in C

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