jag

snake_c

public

Small snake game in C

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