jag

snake_c

public

Small snake game in C

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