jag

snake_c

public

Small snake game in C

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