jag

snake_c

public

Small snake game in C

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