jag

snake_c

public

Small snake game in C

Star0
Watch0
893f512567ee1b182a798d5ded71155349362ae6
2
ssh://git@git.jaglekon.de:30/jag/snake_c.git
source/main.cC554 lines13.3 KB
1
#include "snake.h"
2
3
4
int main(int argc, char *args[]) {
5
6
  /* command argument handling */
7
  if (!parse_parameters(argc, args))
8
    return 0;
9
10
  printf("\033[?1049h");
11
  
12
  /* game settings */
13
14
  speed game_speed = AVERAGE;
15
  selected game_size = {WIDTH, HEIGHT};
16
17
  score leader_board[SCORE_NUMBER] = {};
18
19
  /* Handling Files to store settings & highscores */
20
21
22
  /* Making sure all the folders on the path exist */
23
24
  char folder_path[PATH_MAX];
25
  
26
  {
27
    char xdg_path[PATH_MAX];
28
29
    if (!create_xdg_folder(xdg_path))
30
      return 1;
31
32
    if(!create_folder(SETTINGS_FOLDER ,xdg_path, folder_path))
33
      return 1;
34
  }
35
  
36
  char *settings_path = malloc(strlen(folder_path) + strlen(SETTINGS_FILE) + 2); 
37
  sprintf(settings_path, "%s/%s", folder_path, SETTINGS_FILE);
38
39
40
  /* Reading the files */
41
42
  {
43
    FILE* setting_file = fopen(settings_path, "r");
44
45
    if (setting_file != NULL) {
46
      
47
      char setting_input[STRING_LEN]; 
48
49
      fgets(setting_input, sizeof(setting_input), setting_file);
50
      game_speed = string_to_num(setting_input, 0, SPEED_L - 1, game_speed);
51
52
      fgets(setting_input, sizeof(setting_input), setting_file);
53
      game_size.x = string_to_num(setting_input, MIN_SIZE, MAX_SIZE, game_size.x);
54
55
      fgets(setting_input, sizeof(setting_input), setting_file);
56
      game_size.y = string_to_num(setting_input, MIN_SIZE, MAX_SIZE, game_size.y);
57
58
      fclose(setting_file);
59
    }
60
  }
61
62
  char *score_path = malloc(strlen(folder_path) + strlen(SCORE_FILE) + 2);
63
  sprintf(score_path, "%s/%s", folder_path, SCORE_FILE);
64
65
66
  {
67
    FILE* score_file = fopen(score_path, "r");
68
69
    if (score_file != NULL) {
70
71
      char score_input[STRING_LEN];
72
73
      uint8_t i;
74
      for (i = 0; i < SCORE_NUMBER; i++) {
75
        fgets(score_input, sizeof(score_input), score_file);
76
        if (score_input[0] != '\0') {
77
          uint8_t j;
78
          for(j = 0; score_input[j] != '\0'; j++) {
79
            if (score_input[j] == '\n' || score_input[j] == ' ' || score_input[j] == '\t') {
80
              score_input[j] = '\0';
81
              break;
82
            }
83
          }
84
          score_input[SCORE_LEN - 1] = '\0';
85
          strncpy(leader_board[i].name, score_input, SCORE_LEN);
86
        }
87
88
        fgets(score_input, sizeof(score_input), score_file);
89
        leader_board[i].points = string_to_num(score_input, 0, UINT16_MAX, leader_board[i].points);
90
      }
91
      fclose(score_file);
92
    }
93
  }
94
95
  /* Setting up terminal with termios */
96
97
  struct termios old, new;
98
99
  tcgetattr(STDIN_FILENO, &old);
100
101
  new = old;
102
103
  new.c_lflag &= ~(ECHO | ICANON);
104
  new.c_cc[VMIN] = 0;
105
  new.c_cc[VTIME] = 0;
106
107
  tcsetattr(STDIN_FILENO, TCSANOW, &new);
108
109
  srand(time(NULL));
110
111
  printf("\033[?25l");
112
113
  struct winsize init_size, current_size;
114
  ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);
115
  current_size = init_size;
116
  bool too_small = false;
117
118
  bool gameloop = true;
119
120
  /* Animation at the start */
121
  if (!start_animation())
122
    gameloop = false;
123
124
125
  /* Menu */
126
127
  bool menu = true;
128
  main_menu setting = MAIN_MENU;
129
  bool change_menu = true;
130
131
  main_menu selected_menu = NEW_GAME;
132
  settings selected_setting = HEIGHT_S;
133
134
  uint16_t score = 0;
135
  
136
  snake my_snake;
137
  my_snake.position = NULL;
138
139
  board_state **board = NULL;
140
  board_state *values = NULL;
141
142
143
  struct timespec mili100 = {0, 1000000 * 100};
144
145
  while(gameloop) {
146
147
    bool play_game = false;
148
149
    while (menu) {
150
151
      direction pressed_key = DIRECTIONS;
152
153
      char string[STRING_LEN] = {};
154
      ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
155
156
157
      ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
158
159
      if (current_size.ws_col != init_size.ws_col ||
160
          current_size.ws_row != init_size.ws_row) {
161
        init_size = current_size;
162
        change_menu = true;
163
      }
164
165
166
      if (length_string > 0) {
167
        string[length_string] = '\0';
168
169
        pressed_key = read_direction(string, false);
170
      }
171
172
      if (pressed_key == Q_BUTTON || pressed_key == ESCAPE) {
173
        if (setting != MAIN_MENU) {
174
          setting = MAIN_MENU;
175
          change_menu = true;
176
        } else {
177
          gameloop = false;
178
          break;
179
        }
180
      }
181
182
183
      if (setting == MAIN_MENU) {
184
        
185
        if (!too_small) {
186
          switch (pressed_key) {
187
            case UP:
188
              selected_menu = (selected_menu + (MAIN_MENU - 1)) % MAIN_MENU;
189
              change_menu = true;
190
              break;
191
            case DOWN:
192
              selected_menu = (selected_menu + 1) % MAIN_MENU;
193
              change_menu = true;
194
              break;
195
            default:
196
              break;
197
          }
198
        }
199
200
        if (change_menu) {
201
          too_small = draw_menu(selected_menu, init_size);
202
          change_menu = false;
203
        }
204
205
        if (pressed_key == ENTER && !too_small) {
206
          switch (selected_menu) {
207
          case NEW_GAME:
208
            menu = false;
209
            play_game = true;
210
            break;
211
          case SETTINGS:
212
            setting = SETTINGS;
213
            break;
214
          case HIGHSCORE:
215
            setting = HIGHSCORE;
216
            break;
217
          default:
218
            menu = false;
219
            gameloop = false; 
220
            break;
221
          }
222
          change_menu = true;
223
        }
224
225
      } else if (setting == SETTINGS) {
226
227
        /* Settings Menu */
228
229
        if (change_menu) {
230
          
231
          draw_settings(selected_setting, game_speed, game_size, init_size);
232
233
          change_menu = false;
234
        }
235
236
237
        if (pressed_key < DIRECTIONS) {
238
239
          int8_t setting_change = 0;
240
241
          switch (pressed_key) {
242
            case UP:
243
              selected_setting = (selected_setting + (SETTINGS_MENU - 1)) % SETTINGS_MENU;
244
              break;
245
            case DOWN:
246
              selected_setting = (selected_setting + 1) % SETTINGS_MENU;
247
              break;
248
            case LEFT:
249
              setting_change = 1;
250
              break;
251
            case RIGHT:
252
              setting_change = -1;
253
              break;
254
            default:
255
              break;
256
          }
257
          
258
          if (setting_change) {
259
            switch (selected_setting) {
260
              case SPEED_S:
261
                if (
262
                  game_speed + setting_change <= SPEED_L - 1 &&
263
                  ((int8_t) game_speed) + setting_change >= 0 
264
                ) {
265
                  game_speed += setting_change;
266
                }
267
                break;
268
              case WIDTH_S:
269
                if (
270
                  game_size.x - setting_change >= MIN_SIZE &&
271
                  game_size.x - setting_change <= MAX_SIZE
272
                ) {
273
                  game_size.x -= setting_change;
274
                }
275
                break;
276
              case HEIGHT_S:
277
                if (
278
                  game_size.y - setting_change >= MIN_SIZE &&
279
                  game_size.y - setting_change <= MAX_SIZE
280
                ) {
281
                  game_size.y -= setting_change;
282
                }
283
                break;
284
              default:
285
                break;
286
            }
287
          }
288
289
          change_menu = true;
290
291
        }
292
293
        if (pressed_key == ENTER) {
294
          switch (selected_setting) {
295
            case BACK_S:
296
              setting = MAIN_MENU;
297
              break;
298
            default:
299
              break;
300
          }
301
          change_menu = true;
302
        }
303
      } else if (setting == HIGHSCORE) {
304
        
305
        if(change_menu) {
306
          uint8_t i;
307
308
          for (i = 0; i < SCORE_NUMBER; i++) {
309
            if (score > leader_board[i].points)
310
              break;
311
          }
312
313
          if (i < SCORE_NUMBER) {
314
315
            uint8_t new_score = i;
316
317
            for (i = SCORE_NUMBER - 1; i > new_score; i--) {
318
              leader_board[i] = leader_board[i -1]; 
319
            }
320
321
            leader_board[new_score].points = score;
322
            leader_board[new_score].name[0] = '\0';
323
324
            draw_highscore (new_score, leader_board, init_size);
325
      
326
            score = 0;
327
            change_menu = true;
328
329
          } else {
330
            if (change_menu) {
331
              draw_highscore (-1, leader_board, init_size);
332
              change_menu = false;
333
            }
334
          }
335
        }
336
337
        if(pressed_key == ENTER) {
338
          setting = MAIN_MENU;
339
          change_menu = true;
340
        }
341
      } else if(setting == NEW_GAME) {
342
343
        if (change_menu) {
344
          draw_board(game_size, board, init_size, true, 2, score);
345
          change_menu = false;
346
        }
347
348
        if(pressed_key == ENTER) {
349
          setting = HIGHSCORE;
350
          change_menu = true;
351
352
          free(my_snake.position);
353
          free(board);
354
          free(values);
355
          my_snake.position = NULL;
356
          board = NULL;
357
          values = NULL;
358
        }
359
      }
360
361
      nanosleep(&mili100, NULL);
362
    }
363
364
    menu = true;
365
366
    /* initialisation of the game */
367
  
368
    if(play_game) {
369
370
      bool run = true;
371
372
      my_snake.length = STARTING_LENGTH;
373
374
      my_snake.position = malloc((my_snake.length + 1) * sizeof(snake_part));
375
376
      direction shift = RIGHT;
377
      direction tmp_direction = shift;
378
379
      {
380
        uint16_t i;
381
382
        my_snake.position[0].coords.y = game_size.y >> 1;
383
        my_snake.position[0].coords.x = game_size.x >> 1;
384
        my_snake.position[0].directions[0] = shift;
385
        my_snake.position[0].directions[1] = shift;
386
        for (i = 1; i <= my_snake.length; i++) {
387
          my_snake.position[i].coords.y = -1;
388
          my_snake.position[i].coords.x = -1;
389
        }
390
      }
391
392
      board = malloc(game_size.y * sizeof(board_state*));
393
      values = calloc(game_size.y * game_size.x, sizeof(board_state));
394
395
      if(board == NULL || values == NULL)
396
        run = false;  
397
398
      {
399
        uint8_t i;
400
401
        for (i = 0; i < game_size.y; i++)
402
          board[i] = values + i * game_size.x;
403
      }
404
405
      update_field(my_snake, game_size, board, true);
406
407
      uint8_t paused = 0;
408
409
410
      if ((too_small = draw_board(game_size, board, current_size, true, 0, score)))
411
        paused = 1;
412
413
414
      struct timespec milispeed = {0, 1000000 * (SPEED + game_speed * 25)};
415
416
      /* start of the actual game loop */
417
418
      while (run) {
419
420
        /* input handling  */
421
422
423
        direction pressed_key;
424
425
        char string[STRING_LEN] = {};
426
        ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
427
428
        if (length_string > 0) {
429
          string[length_string] = '\0';
430
431
          pressed_key = read_direction(string, true);
432
433
          if (!too_small) {
434
            if (pressed_key == P_BUTTON || pressed_key == ENTER) {
435
              paused = (paused == 1) ? 0 : 1;
436
              if (paused)
437
                draw_board(game_size, board, current_size, true, paused, score);
438
            } 
439
440
            if (!paused && pressed_key < DIRECTIONS)
441
              tmp_direction = pressed_key;
442
          }
443
444
          if (pressed_key == Q_BUTTON || pressed_key == ESCAPE)
445
            run = false;
446
        }
447
448
        /* game logic */
449
450
        /*
451
        struct timeval current;
452
        gettimeofday(&current, NULL);*/
453
454
        if (/*time_diff >= SPEED * 1000*/ true) {
455
456
          ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
457
458
          if (current_size.ws_col != init_size.ws_col ||
459
              current_size.ws_row != init_size.ws_row) {
460
            init_size = current_size;
461
462
            paused = 1;
463
            too_small = draw_board(game_size, board, current_size, true, paused, score);
464
          }
465
466
          if (!paused) {
467
468
            if (tmp_direction < DIRECTIONS &&
469
                ((shift + (DIRECTIONS >> 1)) % DIRECTIONS) != tmp_direction) {
470
              shift = tmp_direction;
471
            }
472
473
            uint8_t update = update_snake(game_size ,&my_snake, shift);
474
475
            uint8_t head_y = my_snake.position[0].coords.y;
476
            uint8_t head_x = my_snake.position[0].coords.x;
477
478
            if (board[head_y][head_x] == FRUIT) {
479
              my_snake.length++;
480
              score += 1 + 
481
                (((MAX_SIZE >> 1) / (game_size.y) + 
482
                (MAX_SIZE >> 1) / (game_size.x) + 
483
                (my_snake.length - STARTING_LENGTH)) / (game_speed + 1));
484
              snake_part *tmp = realloc(my_snake.position, (my_snake.length + 1) * sizeof(snake_part));
485
              if (tmp == NULL) {
486
                gameloop = false;
487
                break;
488
              }
489
              my_snake.position = tmp;
490
491
              my_snake.position[my_snake.length].coords.y = -1;
492
              my_snake.position[my_snake.length].coords.x = -1;
493
            }
494
495
            update_field(my_snake, game_size, board, false);
496
497
            draw_board(game_size, board, current_size, false, 0, score);
498
499
            switch (update) {
500
              case 0: 
501
                gameloop = false;
502
                break;
503
              case 2:
504
                setting = NEW_GAME;
505
                run = false;
506
                break;
507
              default:
508
                break;
509
            }
510
511
          }
512
        }
513
        nanosleep(&milispeed, NULL);
514
      }
515
    }
516
  }
517
518
519
520
  free(my_snake.position);
521
  free(board);
522
  free(values);
523
524
  tcsetattr(STDIN_FILENO, TCSANOW, &old);
525
  printf("\033[?25h\n\n");
526
527
  printf("\033[32m");
528
  printf("\033[2J\033[H");
529
530
531
  {
532
    FILE* setting_file = fopen(settings_path, "w");
533
534
    fprintf(setting_file, "%d\n%d\n%d", game_speed, game_size.x, game_size.y);
535
    
536
    fclose(setting_file);
537
538
    FILE* score_file = fopen(score_path, "w");
539
    uint8_t i;
540
541
    for (i = 0; i < SCORE_NUMBER; i++) {
542
      fprintf(score_file, "%s\n%i\n", leader_board[i].name, leader_board[i].points);
543
    }
544
545
    fclose(score_file);
546
  }
547
548
  free(settings_path);
549
  free(score_path);
550
551
  printf("\033[?1049l");
552
  
553
  return 0;
554
}