jag

snake_c

public

Small snake game in C

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