jag

snake_c

public

Small snake game in C

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