jag

snake_c

public

Small snake game in C

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