jag

snake_c

public

Small snake game in C

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