jag

snake_c

public

Small snake game in C

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