jag

snake_c

public

Small snake game in C

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