jag

snake_c

public

Small snake game in C

Star0
Watch0
9357e6aedfb433987568f86b8ff19611c93ee1a9
2
ssh://git@git.jaglekon.de:30/jag/snake_c.git
snake.cC372 lines8.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
  /* 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
      if (change_menu) {
143
        
144
        draw_settings(selected_setting, game_speed, game_size);
145
146
        change_menu = false;
147
      }
148
149
150
      if (pressed_key < DIRECTIONS) {
151
152
        switch (pressed_key) {
153
          case UP:
154
            selected_setting = (selected_setting + (SETTINGS_MENU - 1)) % SETTINGS_MENU;
155
            break;
156
          case DOWN:
157
            selected_setting = (selected_setting + 1) % SETTINGS_MENU;
158
            break;
159
          case LEFT:
160
            switch (selected_setting) {
161
              case SPEED_S:
162
                if (game_speed < SPEED_L - 1)
163
                  game_speed++;
164
                break;
165
              case WIDTH_S:
166
                if (game_size.x > MIN_SIZE)
167
                  game_size.x--;
168
                break;
169
              case HEIGHT_S:
170
                if (game_size.y > MIN_SIZE)
171
                  game_size.y--;
172
                break;
173
              default:
174
                break;
175
            }
176
            break;
177
          case RIGHT:
178
            switch (selected_setting) {
179
              case SPEED_S:
180
                if (game_speed)
181
                  game_speed--;
182
                break;
183
              case WIDTH_S:
184
                if (game_size.x < MAX_SIZE)
185
                  game_size.x++;
186
                break;
187
              case HEIGHT_S:
188
                if (game_size.y < MAX_SIZE)
189
                  game_size.y++;
190
                break;
191
              default:
192
                break;
193
            }
194
            break;
195
          default:
196
            break;
197
        }
198
        change_menu = true;
199
200
      }
201
202
      if (pressed_key == ENTER) {
203
        switch (selected_setting) {
204
          case BACK_S:
205
            setting = false;
206
            break;
207
          default:
208
            break;
209
        }
210
        change_menu = true;
211
      }
212
    }
213
214
  nanosleep(&mili100, NULL);
215
  }
216
217
218
  /* initialisation of the game */
219
220
  snake my_snake;
221
  my_snake.length = STARTING_LENGTH;
222
223
  my_snake.position = malloc((my_snake.length + 1) * sizeof(snake_part));
224
225
  direction shift = RIGHT;
226
  direction tmp_direction = shift;
227
228
  {
229
    uint16_t i;
230
231
    my_snake.position[0].coords.y = game_size.y >> 1;
232
    my_snake.position[0].coords.x = game_size.x >> 1;
233
    my_snake.position[0].directions[0] = shift;
234
    my_snake.position[0].directions[1] = shift;
235
    for (i = 1; i <= my_snake.length; i++) {
236
      my_snake.position[i].coords.y = -1;
237
      my_snake.position[i].coords.x = -1;
238
    }
239
  }
240
241
  board_state **board = malloc(game_size.y * sizeof(board_state*));
242
  board_state *values = calloc(game_size.y * game_size.x, sizeof(board_state));
243
244
  if(board == NULL || values == NULL)
245
    goto end_of_program;
246
247
  {
248
    uint8_t i;
249
250
    for (i = 0; i < game_size.y; i++)
251
      board[i] = values + i * game_size.x;
252
  }
253
254
  update_field(my_snake, game_size, board, true);
255
256
  bool paused = false;
257
258
  if ((too_small = draw_board(game_size, board, current_size, true, false)))
259
    paused = true;
260
261
262
  struct timespec milispeed = {0, 1000000 * (SPEED + game_speed * 25)};
263
264
  bool run = true;
265
266
  /* start of the actual game loop */
267
268
  while (run) {
269
270
    /* input handling  */
271
272
    
273
      char string[STRING_LEN] = {};
274
      ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
275
276
      if (length_string > 0) {
277
        string[length_string] = '\0';
278
279
        if (!too_small) {
280
          if (toupper(string[0]) == 'P' || string[0] == '\n') {
281
            paused = !paused;
282
            if (paused)
283
              draw_board(game_size, board, current_size, true, paused);
284
          }
285
286
          if (!paused)
287
            tmp_direction = read_direction(string, true);
288
        }
289
290
        if (toupper(string[0]) == 'Q')
291
          run = false;
292
      }
293
    
294
295
    /* game logic */
296
297
    /*
298
    struct timeval current;
299
    gettimeofday(&current, NULL);*/
300
301
    if (/*time_diff >= SPEED * 1000*/ true) {
302
303
      ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
304
305
      if (current_size.ws_col != init_size.ws_col ||
306
          current_size.ws_row != init_size.ws_row) {
307
        init_size = current_size;
308
309
        paused = true;
310
        too_small = draw_board(game_size, board, current_size, true, paused);
311
      }
312
313
      if (!paused) {
314
315
        if (tmp_direction < DIRECTIONS &&
316
            ((shift + (DIRECTIONS >> 1)) % DIRECTIONS) != tmp_direction) {
317
          shift = tmp_direction;
318
        }
319
320
        uint8_t update = update_snake(game_size ,&my_snake, shift);
321
322
        uint8_t head_y = my_snake.position[0].coords.y;
323
        uint8_t head_x = my_snake.position[0].coords.x;
324
325
        if (board[head_y][head_x] == FRUIT) {
326
          my_snake.length++;
327
          snake_part *tmp = realloc(my_snake.position, (my_snake.length + 1) * sizeof(snake_part));
328
          if (tmp == NULL)
329
            goto end_free;
330
          my_snake.position = tmp;
331
332
          my_snake.position[my_snake.length].coords.y = -1;
333
          my_snake.position[my_snake.length].coords.x = -1;
334
        }
335
336
        update_field(my_snake, game_size, board, false);
337
338
        draw_board(game_size, board, current_size, false, false);
339
340
        switch (update) {
341
          case 0: 
342
            printf("\nERROR\n");
343
            goto end_free;
344
            break;
345
          case 2:
346
            printf("\nHAHA LOST, LOSER");
347
            goto end_free;
348
            break;
349
          default:
350
            break;
351
        }
352
353
      }
354
    }
355
    nanosleep(&milispeed, NULL);
356
  }
357
358
end_free:
359
360
    free(my_snake.position);
361
    free(board);
362
    free(values);
363
364
end_of_program:
365
366
  tcsetattr(STDIN_FILENO, TCSANOW, &old);
367
  printf("\033[?25h\n\n");
368
369
370
  printf("\033[32m");
371
  return 0;
372
}