jag

snake_c

public

Small snake game in C

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