jag

snake_c

public

Small snake game in C

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