jag

snake_c

public

Small snake game in C

Star0
Watch0

Commit c28baaaf800e

tarball

Jaglekon <58274093+Jaglekon@users.noreply.github.com>2026-04-03 20:49 UTC

3 files changed+327-266
integrating everything
Modified file: drawing.c+44 additions−13 deletions
@@ -402,9 +402,10 @@ bool draw_board(
402402
403403 uint8_t i, j;
404404
405- uint8_t actual_height = game_size.y + 2 + 2;
405+ uint8_t actual_height = game_size.y + 2 + 2 + 1;
406406 uint8_t actual_width = game_size.x * 2 + 1 + 2;
407407
408+
408409 if (actual_width > window_size.ws_col || actual_height > window_size.ws_row) {
409410
410411 printf("\033[2J\033[HToo small\n");
@@ -466,11 +467,21 @@ bool draw_board(
466467 printf("╰");
467468 for (i = 0; i < game_size.x; i++)
468469 printf("──");
469- printf("─╯");
470+ printf("─╯\n");
471+
472+ if (paused) {
473+ if (paused == 2) {
474+ char *game_over = "Press ENTER to continue";
475+
476+ for (i = 0; i < (window_size.ws_col - strlen(game_over)) >> 1; i++)
477+ printf(" ");
478+ printf("%s", game_over);
479+ }
470480
471- if (paused)
472481 draw_pause(window_size, actual_width, actual_height, paused);
473482
483+ }
484+
474485 fflush(stdout);
475486
476487 return false;
@@ -483,14 +494,14 @@ void draw_pause(struct winsize window_size, uint8_t field_width,
483494 const uint8_t width = 14;
484495
485496 const char *pause_menu[] = {
486- "+------------+",
487- "| |",
488- "| PAUSED |",
489- "| |",
490- "+------------+"
497+ "┌────────────┐",
498+ "│ │",
499+ "│ PAUSED │",
500+ "│ │",
501+ "└────────────┘"
491502 };
492503
493- const char *game_over_menu = "| GAME OVER |";
504+ const char *game_over_menu = "│ GAME OVER │";
494505
495506 const uint8_t going_up = (field_height + height) >> 1;
496507 const uint8_t going_right = (field_width - width) >> 1;
@@ -523,20 +534,40 @@ void draw_pause(struct winsize window_size, uint8_t field_width,
523534 fflush(stdout);
524535 }
525536
526-void draw_highscore (int16_t new_score, score leader_board[SCORE_NUMBER]) {
537+void draw_highscore (int16_t new_score, score leader_board[SCORE_NUMBER], struct winsize window_size) {
527538
528539 uint8_t i;
529540
530541 printf("\033[2J\033[H");
531542
543+ printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
544+
545+ printf("┃ RANK NAME SCORE ┃\n");
546+ printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
547+
548+
532549 for (i = 0; i < SCORE_NUMBER; i++) {
533- printf("%-10s %05d\n", leader_board[i].name, leader_board[i].points);
550+
551+ char *ordinals[] = {
552+ "st", "nd", "rd", "th"
553+ };
554+
555+ printf("┃ %2d%-4s %-9s %06d ┃\n",i + 1, (i >= 3) ? ordinals[3] : ordinals[i] ,leader_board[i].name, leader_board[i].points);
556+ printf("┃ ┃\n");
557+
534558 }
559+
560+ printf("\033[%c┗━━━━━━━━━━━━━━━━━━━━━━━━━━┛", UP_CODE);
561+
562+ fflush(stdout);
535563
536564 if (new_score != -1) {
537- for (i = SCORE_NUMBER - new_score; i > 0; i--)
565+ for (i = (SCORE_NUMBER - new_score) * 2 - 1; i > 0; i--)
538566 printf("\033[%c", UP_CODE);
539- printf("\r_");
567+ printf("\r");
568+ for (i = 0; i < 8 + 1; i++)
569+ printf("\033[%c", RIGHT_CODE);
570+ printf("_");
540571 fflush(stdout);
541572
542573 uint8_t number_characters = 0;
Modified file: snake.c+282 additions−252 deletions
@@ -35,7 +35,6 @@ int main(int argc, char *args[]) {
3535 selected game_size = {WIDTH, HEIGHT};
3636
3737 score leader_board[SCORE_NUMBER] = {};
38- uint16_t score = 0;
3938
4039 /* Handling Files to store settings & highscores */
4140
@@ -158,10 +157,11 @@ int main(int argc, char *args[]) {
158157 current_size = init_size;
159158 bool too_small = false;
160159
160+ bool gameloop = true;
161161
162162 /* Animation at the start */
163163 if (!start_animation())
164- goto end_of_program;
164+ gameloop = false;
165165
166166
167167 /* Menu */
@@ -173,362 +173,392 @@ int main(int argc, char *args[]) {
173173 main_menu selected_menu = NEW_GAME;
174174 settings selected_setting = HEIGHT_S;
175175
176- struct timespec mili100 = {0, 1000000 * 100};
176+ uint16_t score = 0;
177+
178+ snake my_snake;
179+ my_snake.position = NULL;
177180
178-menu_start:
181+ board_state **board = NULL;
182+ board_state *values = NULL;
179183
180- while (menu) {
181184
182- direction pressed_key = DIRECTIONS;
185+ struct timespec mili100 = {0, 1000000 * 100};
183186
184- char string[STRING_LEN] = {};
185- ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
187+ while(gameloop) {
186188
189+ bool play_game = false;
187190
188- ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
191+ while (menu) {
189192
190- if (current_size.ws_col != init_size.ws_col ||
191- current_size.ws_row != init_size.ws_row) {
192- init_size = current_size;
193- change_menu = true;
194- }
193+ direction pressed_key = DIRECTIONS;
195194
195+ char string[STRING_LEN] = {};
196+ ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
196197
197- if (length_string > 0) {
198- string[length_string] = '\0';
199198
200- pressed_key = read_direction(string, false);
201- }
199+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
202200
203- if (pressed_key == Q_KEY || pressed_key == ESCAPE)
204- goto end_of_program;
201+ if (current_size.ws_col != init_size.ws_col ||
202+ current_size.ws_row != init_size.ws_row) {
203+ init_size = current_size;
204+ change_menu = true;
205+ }
205206
206207
207- if (setting == MAIN_MENU) {
208-
209- if (!too_small) {
210- switch (pressed_key) {
211- case UP:
212- selected_menu = (selected_menu + (MAIN_MENU - 1)) % MAIN_MENU;
213- change_menu = true;
214- break;
215- case DOWN:
216- selected_menu = (selected_menu + 1) % MAIN_MENU;
217- change_menu = true;
218- break;
219- default:
220- break;
221- }
222- }
208+ if (length_string > 0) {
209+ string[length_string] = '\0';
223210
224- if (change_menu) {
225- too_small = draw_menu(selected_menu, init_size);
226- change_menu = false;
211+ pressed_key = read_direction(string, false);
227212 }
228213
229- if (pressed_key == ENTER && !too_small) {
230- switch (selected_menu) {
231- case NEW_GAME:
232- menu = false;
233- break;
234- case SETTINGS:
235- setting = SETTINGS;
236- break;
237- case HIGHSCORE:
238- setting = HIGHSCORE;
239- break;
240- default:
241- goto end_of_program;
214+ if (pressed_key == Q_KEY || pressed_key == ESCAPE) {
215+ if (setting != MAIN_MENU) {
216+ setting = MAIN_MENU;
217+ change_menu = true;
218+ } else {
219+ gameloop = false;
242220 break;
243221 }
244- change_menu = true;
245222 }
246223
247- } else if (setting == SETTINGS) {
248224
249- /* Settings Menu */
250-
251- if (change_menu) {
225+ if (setting == MAIN_MENU) {
252226
253- draw_settings(selected_setting, game_speed, game_size, init_size);
254-
255- change_menu = false;
256- }
257-
258-
259- if (pressed_key < DIRECTIONS) {
227+ if (!too_small) {
228+ switch (pressed_key) {
229+ case UP:
230+ selected_menu = (selected_menu + (MAIN_MENU - 1)) % MAIN_MENU;
231+ change_menu = true;
232+ break;
233+ case DOWN:
234+ selected_menu = (selected_menu + 1) % MAIN_MENU;
235+ change_menu = true;
236+ break;
237+ default:
238+ break;
239+ }
240+ }
260241
261- int8_t setting_change = 0;
242+ if (change_menu) {
243+ too_small = draw_menu(selected_menu, init_size);
244+ change_menu = false;
245+ }
262246
263- switch (pressed_key) {
264- case UP:
265- selected_setting = (selected_setting + (SETTINGS_MENU - 1)) % SETTINGS_MENU;
266- break;
267- case DOWN:
268- selected_setting = (selected_setting + 1) % SETTINGS_MENU;
247+ if (pressed_key == ENTER && !too_small) {
248+ switch (selected_menu) {
249+ case NEW_GAME:
250+ menu = false;
251+ play_game = true;
269252 break;
270- case LEFT:
271- setting_change = 1;
253+ case SETTINGS:
254+ setting = SETTINGS;
272255 break;
273- case RIGHT:
274- setting_change = -1;
256+ case HIGHSCORE:
257+ setting = HIGHSCORE;
275258 break;
276259 default:
260+ menu = false;
261+ gameloop = false;
277262 break;
263+ }
264+ change_menu = true;
278265 }
279-
280- if (setting_change) {
281- switch (selected_setting) {
282- case SPEED_S:
283- if (
284- game_speed + setting_change <= SPEED_L - 1 &&
285- ((int8_t) game_speed) + setting_change >= 0
286- ) {
287- game_speed += setting_change;
288- }
266+
267+ } else if (setting == SETTINGS) {
268+
269+ /* Settings Menu */
270+
271+ if (change_menu) {
272+
273+ draw_settings(selected_setting, game_speed, game_size, init_size);
274+
275+ change_menu = false;
276+ }
277+
278+
279+ if (pressed_key < DIRECTIONS) {
280+
281+ int8_t setting_change = 0;
282+
283+ switch (pressed_key) {
284+ case UP:
285+ selected_setting = (selected_setting + (SETTINGS_MENU - 1)) % SETTINGS_MENU;
289286 break;
290- case WIDTH_S:
291- if (
292- game_size.x - setting_change >= MIN_SIZE &&
293- game_size.x - setting_change <= MAX_SIZE
294- ) {
295- game_size.x -= setting_change;
296- }
287+ case DOWN:
288+ selected_setting = (selected_setting + 1) % SETTINGS_MENU;
297289 break;
298- case HEIGHT_S:
299- if (
300- game_size.y - setting_change >= MIN_SIZE &&
301- game_size.y - setting_change <= MAX_SIZE
302- ) {
303- game_size.y -= setting_change;
304- }
290+ case LEFT:
291+ setting_change = 1;
292+ break;
293+ case RIGHT:
294+ setting_change = -1;
305295 break;
306296 default:
307297 break;
308298 }
309- }
310-
311- change_menu = true;
299+
300+ if (setting_change) {
301+ switch (selected_setting) {
302+ case SPEED_S:
303+ if (
304+ game_speed + setting_change <= SPEED_L - 1 &&
305+ ((int8_t) game_speed) + setting_change >= 0
306+ ) {
307+ game_speed += setting_change;
308+ }
309+ break;
310+ case WIDTH_S:
311+ if (
312+ game_size.x - setting_change >= MIN_SIZE &&
313+ game_size.x - setting_change <= MAX_SIZE
314+ ) {
315+ game_size.x -= setting_change;
316+ }
317+ break;
318+ case HEIGHT_S:
319+ if (
320+ game_size.y - setting_change >= MIN_SIZE &&
321+ game_size.y - setting_change <= MAX_SIZE
322+ ) {
323+ game_size.y -= setting_change;
324+ }
325+ break;
326+ default:
327+ break;
328+ }
329+ }
312330
313- }
331+ change_menu = true;
314332
315- if (pressed_key == ENTER) {
316- switch (selected_setting) {
317- case BACK_S:
318- setting = MAIN_MENU;
319- break;
320- default:
321- break;
322333 }
323- change_menu = true;
324- }
325- } else if (setting == HIGHSCORE) {
326-
327- if(change_menu) {
328- uint8_t i;
329334
330- for (i = 0; i < SCORE_NUMBER; i++) {
331- if (score > leader_board[i].points)
332- break;
335+ if (pressed_key == ENTER) {
336+ switch (selected_setting) {
337+ case BACK_S:
338+ setting = MAIN_MENU;
339+ break;
340+ default:
341+ break;
342+ }
343+ change_menu = true;
333344 }
345+ } else if (setting == HIGHSCORE) {
346+
347+ if(change_menu) {
348+ uint8_t i;
349+
350+ for (i = 0; i < SCORE_NUMBER; i++) {
351+ if (score > leader_board[i].points)
352+ break;
353+ }
354+
355+ if (i < SCORE_NUMBER) {
334356
335- if (i < SCORE_NUMBER) {
357+ uint8_t new_score = i;
336358
337- uint8_t new_score = i;
359+ for (i = SCORE_NUMBER - 1; i > new_score; i--) {
360+ leader_board[i] = leader_board[i -1];
361+ }
362+
363+ leader_board[new_score].points = score;
364+ leader_board[new_score].name[0] = '\0';
365+
366+ draw_highscore (new_score, leader_board, init_size);
367+
368+ score = 0;
369+ change_menu = true;
338370
339- for (i = SCORE_NUMBER - 1; i > new_score; i--) {
340- leader_board[i] = leader_board[i -1];
371+ } else {
372+ if (change_menu)
373+ draw_highscore (-1, leader_board, init_size);
341374 }
375+ }
342376
343- leader_board[new_score].points = score;
344- leader_board[new_score].name[0] = '\0';
377+ if(pressed_key == ENTER) {
378+ setting = MAIN_MENU;
379+ change_menu = true;
380+ }
381+ } else if(setting == NEW_GAME) {
345382
346- draw_highscore (new_score, leader_board);
347-
348- score = 0;
383+ if (change_menu)
384+ draw_board(game_size, board, init_size, true, 2, score);
385+
386+ if(pressed_key == ENTER) {
387+ setting = HIGHSCORE;
349388 change_menu = true;
350389
351- } else {
352- draw_highscore (-1, leader_board);
390+ free(my_snake.position);
391+ free(board);
392+ free(values);
393+ my_snake.position = NULL;
394+ board = NULL;
395+ values = NULL;
353396 }
354397 }
355398
356- if(pressed_key == ENTER) {
357- setting = MAIN_MENU;
358- change_menu = true;
359- }
360- } else if(setting == NEW_GAME) {
361-
362- if(pressed_key == ENTER) {
363- setting = HIGHSCORE;
364- change_menu = true;
365- }
399+ nanosleep(&mili100, NULL);
366400 }
367401
368- nanosleep(&mili100, NULL);
369- }
402+ menu = true;
370403
371- menu = true;
404+ /* initialisation of the game */
405+
406+ if(play_game) {
372407
373- /* initialisation of the game */
408+ bool run = true;
374409
375- snake my_snake;
376- my_snake.length = STARTING_LENGTH;
410+ my_snake.length = STARTING_LENGTH;
377411
378- my_snake.position = malloc((my_snake.length + 1) * sizeof(snake_part));
412+ my_snake.position = malloc((my_snake.length + 1) * sizeof(snake_part));
379413
380- direction shift = RIGHT;
381- direction tmp_direction = shift;
414+ direction shift = RIGHT;
415+ direction tmp_direction = shift;
382416
383- {
384- uint16_t i;
385-
386- my_snake.position[0].coords.y = game_size.y >> 1;
387- my_snake.position[0].coords.x = game_size.x >> 1;
388- my_snake.position[0].directions[0] = shift;
389- my_snake.position[0].directions[1] = shift;
390- for (i = 1; i <= my_snake.length; i++) {
391- my_snake.position[i].coords.y = -1;
392- my_snake.position[i].coords.x = -1;
393- }
394- }
417+ {
418+ uint16_t i;
395419
396- board_state **board = malloc(game_size.y * sizeof(board_state*));
397- board_state *values = calloc(game_size.y * game_size.x, sizeof(board_state));
420+ my_snake.position[0].coords.y = game_size.y >> 1;
421+ my_snake.position[0].coords.x = game_size.x >> 1;
422+ my_snake.position[0].directions[0] = shift;
423+ my_snake.position[0].directions[1] = shift;
424+ for (i = 1; i <= my_snake.length; i++) {
425+ my_snake.position[i].coords.y = -1;
426+ my_snake.position[i].coords.x = -1;
427+ }
428+ }
398429
399- if(board == NULL || values == NULL)
400- goto end_of_program;
430+ board = malloc(game_size.y * sizeof(board_state*));
431+ values = calloc(game_size.y * game_size.x, sizeof(board_state));
401432
402- {
403- uint8_t i;
433+ if(board == NULL || values == NULL)
434+ run = false;
404435
405- for (i = 0; i < game_size.y; i++)
406- board[i] = values + i * game_size.x;
407- }
436+ {
437+ uint8_t i;
408438
409- update_field(my_snake, game_size, board, true);
439+ for (i = 0; i < game_size.y; i++)
440+ board[i] = values + i * game_size.x;
441+ }
410442
411- uint8_t paused = 0;
443+ update_field(my_snake, game_size, board, true);
412444
445+ uint8_t paused = 0;
413446
414- if ((too_small = draw_board(game_size, board, current_size, true, 0, score)))
415- paused = 1;
416447
417- bool run = true;
448+ if ((too_small = draw_board(game_size, board, current_size, true, 0, score)))
449+ paused = 1;
418450
419- struct timespec milispeed = {0, 1000000 * (SPEED + game_speed * 25)};
420451
421- /* start of the actual game loop */
452+ struct timespec milispeed = {0, 1000000 * (SPEED + game_speed * 25)};
422453
423- while (run) {
454+ /* start of the actual game loop */
424455
425- /* input handling */
456+ while (run) {
426457
458+ /* input handling */
427459
428- direction pressed_key;
429460
430- char string[STRING_LEN] = {};
431- ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
461+ direction pressed_key;
432462
433- if (length_string > 0) {
434- string[length_string] = '\0';
463+ char string[STRING_LEN] = {};
464+ ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
435465
436- pressed_key = read_direction(string, true);
466+ if (length_string > 0) {
467+ string[length_string] = '\0';
437468
438- if (!too_small) {
439- if (pressed_key == P_KEY || pressed_key == ENTER) {
440- paused = (paused == 1) ? 0 : 1;
441- if (paused)
442- draw_board(game_size, board, current_size, true, paused, score);
443- }
469+ pressed_key = read_direction(string, true);
444470
445- if (!paused && pressed_key < DIRECTIONS)
446- tmp_direction = pressed_key;
447- }
471+ if (!too_small) {
472+ if (pressed_key == P_KEY || pressed_key == ENTER) {
473+ paused = (paused == 1) ? 0 : 1;
474+ if (paused)
475+ draw_board(game_size, board, current_size, true, paused, score);
476+ }
448477
449- if (pressed_key == Q_KEY || pressed_key == ESCAPE)
450- run = false;
451- }
452-
478+ if (!paused && pressed_key < DIRECTIONS)
479+ tmp_direction = pressed_key;
480+ }
453481
454- /* game logic */
482+ if (pressed_key == Q_KEY || pressed_key == ESCAPE)
483+ run = false;
484+ }
455485
456- /*
457- struct timeval current;
458- gettimeofday(&current, NULL);*/
486+ /* game logic */
459487
460- if (/*time_diff >= SPEED * 1000*/ true) {
488+ /*
489+ struct timeval current;
490+ gettimeofday(&current, NULL);*/
461491
462- ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
492+ if (/*time_diff >= SPEED * 1000*/ true) {
463493
464- if (current_size.ws_col != init_size.ws_col ||
465- current_size.ws_row != init_size.ws_row) {
466- init_size = current_size;
494+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
467495
468- paused = 1;
469- too_small = draw_board(game_size, board, current_size, true, paused, score);
470- }
496+ if (current_size.ws_col != init_size.ws_col ||
497+ current_size.ws_row != init_size.ws_row) {
498+ init_size = current_size;
471499
472- if (!paused) {
500+ paused = 1;
501+ too_small = draw_board(game_size, board, current_size, true, paused, score);
502+ }
473503
474- if (tmp_direction < DIRECTIONS &&
475- ((shift + (DIRECTIONS >> 1)) % DIRECTIONS) != tmp_direction) {
476- shift = tmp_direction;
477- }
504+ if (!paused) {
478505
479- uint8_t update = update_snake(game_size ,&my_snake, shift);
506+ if (tmp_direction < DIRECTIONS &&
507+ ((shift + (DIRECTIONS >> 1)) % DIRECTIONS) != tmp_direction) {
508+ shift = tmp_direction;
509+ }
480510
481- uint8_t head_y = my_snake.position[0].coords.y;
482- uint8_t head_x = my_snake.position[0].coords.x;
511+ uint8_t update = update_snake(game_size ,&my_snake, shift);
512+
513+ uint8_t head_y = my_snake.position[0].coords.y;
514+ uint8_t head_x = my_snake.position[0].coords.x;
515+
516+ if (board[head_y][head_x] == FRUIT) {
517+ my_snake.length++;
518+ score += 1 +
519+ (((MAX_SIZE >> 1) / (game_size.y) +
520+ (MAX_SIZE >> 1) / (game_size.x) +
521+ (my_snake.length - STARTING_LENGTH)) / (game_speed + 1));
522+ snake_part *tmp = realloc(my_snake.position, (my_snake.length + 1) * sizeof(snake_part));
523+ if (tmp == NULL) {
524+ gameloop = false;
525+ break;
526+ }
527+ my_snake.position = tmp;
483528
484- if (board[head_y][head_x] == FRUIT) {
485- my_snake.length++;
486- score += 1 +
487- (((MAX_SIZE >> 1) / (game_size.y) +
488- (MAX_SIZE >> 1) / (game_size.x) +
489- (my_snake.length - STARTING_LENGTH)) / (game_speed + 1));
490- snake_part *tmp = realloc(my_snake.position, (my_snake.length + 1) * sizeof(snake_part));
491- if (tmp == NULL)
492- goto end_free;
493- my_snake.position = tmp;
529+ my_snake.position[my_snake.length].coords.y = -1;
530+ my_snake.position[my_snake.length].coords.x = -1;
531+ }
494532
495- my_snake.position[my_snake.length].coords.y = -1;
496- my_snake.position[my_snake.length].coords.x = -1;
497- }
533+ update_field(my_snake, game_size, board, false);
498534
499- update_field(my_snake, game_size, board, false);
535+ draw_board(game_size, board, current_size, false, 0, score);
500536
501- draw_board(game_size, board, current_size, false, 0, score);
537+ switch (update) {
538+ case 0:
539+ gameloop = false;
540+ break;
541+ case 2:
542+ setting = NEW_GAME;
543+ run = false;
544+ break;
545+ default:
546+ break;
547+ }
502548
503- switch (update) {
504- case 0:
505- goto end_free;
506- break;
507- case 2:
508- setting = NEW_GAME;
509- draw_board(game_size, board, current_size, false, 2, score);
510- goto end_free;
511- break;
512- default:
513- break;
549+ }
514550 }
515-
551+ nanosleep(&milispeed, NULL);
516552 }
517553 }
518- nanosleep(&milispeed, NULL);
519554 }
520555
521-end_free:
556+
522557
523558 free(my_snake.position);
524559 free(board);
525560 free(values);
526561
527- if (setting == NEW_GAME)
528- goto menu_start;
529-
530-end_of_program:
531-
532562 tcsetattr(STDIN_FILENO, TCSANOW, &old);
533563 printf("\033[?25h\n\n");
534564
Modified file: snake.h+1 additions−1 deletions
@@ -99,7 +99,7 @@ bool draw_board(selected game_size, board_state **board,
9999 void draw_pause(struct winsize window_size,
100100 uint8_t field_width,uint8_t field_height, uint8_t game_over);
101101 void draw_highscore (int16_t new_score,
102- score leader_board[SCORE_NUMBER]);
102+ score leader_board[SCORE_NUMBER], struct winsize window_size);
103103
104104 bool print_buffer(uint16_t width, char *string);