jag

snake_c

public

Small snake game in C

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