jag

snake_c

public

Small snake game in C

Star0
Watch0

Commit 96e537edae6d

tarball

Jaglekon <58274093+Jaglekon@users.noreply.github.com>2026-08-31 22:28 UTC

8 files changed+265-171
ffs incorporated anim into jag_parse
Modified file: CMakeLists.txt+1 additions
@@ -26,6 +26,7 @@ add_executable(snake
2626 source/draw.c
2727 source/parameters.c
2828 source/settings_file/settings_file.c
29+ source/animation/animation.c
2930 )
3031
3132 target_link_libraries(snake PRIVATE
Modified file: flake.nix+1 additions
@@ -33,6 +33,7 @@
3333 logic.c \
3434 parameters.c \
3535 settings_file/settings_file.c \
36+ animation/animation.c \
3637 -I${jag-parse-lib}/include \
3738 -L${jag-parse-lib}/lib \
3839 -ljag_parse \
Added file: source/animation/animation.c+166 additions
@@ -0,0 +1,166 @@
1+#include "../snake.h"
2+#include <jag_parse.h>
3+#include <poll.h>
4+
5+#define DELAY 10
6+
7+#define KEY_PROTECTION 2
8+
9+bool start_animation() {
10+
11+ struct winsize current_size;
12+ if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size)) == -1)
13+ return false;
14+
15+
16+ char *snake[3][7] = {
17+ {
18+ " ________ ",
19+ " _/ _ \\_",
20+ "________/ (_) ",
21+ "\\ \\ ",
22+ "/_______/ _ ",
23+ " \\_ (_) _",
24+ " \\________/ "
25+ },
26+ {
27+ " ",
28+ "________",
29+ "\\ /",
30+ " \\ / ",
31+ " \\ / ",
32+ "___\\/___",
33+ " ",
34+ },
35+ {
36+ " ",
37+ "__ ",
38+ " \\_____ ",
39+ " \\",
40+ " _____/",
41+ "__/ ",
42+ " "
43+ }
44+ };
45+
46+ uint8_t width_snake[3] = {};
47+
48+ {
49+ uint8_t i;
50+
51+ for (i = 0; i < 3; i++)
52+ width_snake[i] = (uint8_t) strlen(snake[i][0]);
53+ }
54+
55+ uint8_t middle;
56+
57+ uint8_t height = 7;
58+ uint8_t width = width_snake[0] + width_snake[1] + width_snake[2];
59+
60+ for (middle = 1; width < current_size.ws_col; middle++)
61+ width += width_snake[1];
62+
63+ int32_t end = current_size.ws_col + width;
64+
65+
66+ struct pollfd polling[1];
67+
68+ polling[0].fd = STDIN_FILENO;
69+ polling[0].events = POLLIN;
70+
71+ uint8_t wait_incomplete = 0;
72+ bool key_lock = false;
73+
74+ while (end >= 0) {
75+
76+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
77+
78+ int32_t start = end - width;
79+
80+ int32_t i, j;
81+
82+ printf("\033[2J\033[H");
83+
84+ for (j = 0; j < ((height > current_size.ws_row) ? current_size.ws_row : height) ; j++) {
85+
86+ for (i = 0; i < current_size.ws_col; i++) {
87+ int32_t index = i - start;
88+
89+ if (index >= 0 && index < width) {
90+ if (index < width_snake[0]) {
91+ printf("%c", snake[0][j][index]);
92+ } else if (index < width_snake[0] + width_snake[1] * middle) {
93+ printf("%c", snake[1][j][(index - width_snake[0]) % width_snake[1]]);
94+ } else {
95+ printf("%c", snake[2][j][index - (width_snake[0] + width_snake[1] * middle)]);
96+ }
97+ } else {
98+ printf(" ");
99+ }
100+ }
101+
102+ printf("\n");
103+
104+ }
105+ fflush(stdout);
106+
107+ end--;
108+
109+ keypress input[20] = {};
110+ size_t input_len = 0;
111+
112+ if (key_lock)
113+ wait_incomplete++;
114+
115+ if (wait_incomplete >= KEY_PROTECTION) {
116+ input[0] = ESCAPE_KEY;
117+ input_len++;
118+
119+ key_lock = false;
120+ wait_incomplete = 0;
121+
122+ break_unfinished();
123+ }
124+
125+ int is_poll = poll(polling, 1, DELAY);
126+
127+ char string[256] = {};
128+ ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
129+
130+ add_to_buf(string, length_string);
131+
132+
133+ size_t k = input_len;
134+
135+ input[k] = UNKNOWN;
136+
137+ while (input[k] != NO_KEY) {
138+
139+ input[k] = parse_input();
140+
141+ if (input[k] == INCOMPLETE) {
142+ key_lock = true;
143+ break;
144+ }
145+
146+ k++;
147+ }
148+
149+ input_len = k;
150+
151+ for (k = 0; k < input_len; k++) {
152+ switch (input[k]) {
153+ case Q_KEY:
154+ case ESCAPE_KEY:
155+ return false;
156+ break;
157+ case ENTER_KEY:
158+ end = -1;
159+ break;
160+ default:
161+ break;
162+ }
163+ }
164+ }
165+ return true;
166+}
Modified file: source/draw.c+1 additions−114 deletions
@@ -1,118 +1,5 @@
11 #include "snake.h"
22
3-bool start_animation() {
4-
5- struct winsize current_size;
6- ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
7-
8- struct timespec mili10 = {0, 1000000 * 10};
9-
10- char *snake[3][7] = {
11- {
12- " ________ ",
13- " _/ _ \\_",
14- "________/ (_) ",
15- "\\ \\ ",
16- "/_______/ _ ",
17- " \\_ (_) _",
18- " \\________/ "
19- },
20- {
21- " ",
22- "________",
23- "\\ /",
24- " \\ / ",
25- " \\ / ",
26- "___\\/___",
27- " ",
28- },
29- {
30- " ",
31- "__ ",
32- " \\_____ ",
33- " \\",
34- " _____/",
35- "__/ ",
36- " "
37- }
38- };
39-
40- uint8_t width_snake[3] = {};
41-
42- {
43- uint8_t i;
44-
45- for (i = 0; i < 3; i++)
46- width_snake[i] = (uint8_t) strlen(snake[i][0]);
47- }
48-
49- uint8_t middle;
50-
51- uint8_t height = 7;
52- uint8_t width = width_snake[0] + width_snake[1] + width_snake[2];
53-
54- for (middle = 1; width < current_size.ws_col; middle++)
55- width += width_snake[1];
56-
57- int32_t end = current_size.ws_col + width;
58-
59-
60-
61- while (end >= 0) {
62-
63- ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
64-
65- int32_t start = end - width;
66-
67- int32_t i, j;
68-
69- char string[1000] = {};
70- ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
71-
72- if (length_string > 0) {
73-
74- string[length_string] = '\0';
75-
76- direction pressed_key = read_direction(string, true);
77-
78- if (pressed_key == Q_BUTTON || pressed_key == ESCAPE)
79- return false;
80-
81- if (pressed_key == ENTER)
82- break;
83- }
84-
85- printf("\033[2J\033[H");
86-
87- for (j = 0; j < ((height > current_size.ws_row) ? current_size.ws_row : height) ; j++) {
88-
89- for (i = 0; i < current_size.ws_col; i++) {
90- int32_t index = i - start;
91-
92- if (index >= 0 && index < width) {
93- if (index < width_snake[0]) {
94- printf("%c", snake[0][j][index]);
95- } else if (index < width_snake[0] + width_snake[1] * middle) {
96- printf("%c", snake[1][j][(index - width_snake[0]) % width_snake[1]]);
97- } else {
98- printf("%c", snake[2][j][index - (width_snake[0] + width_snake[1] * middle)]);
99- }
100- } else {
101- printf(" ");
102- }
103- }
104-
105- printf("\n");
106-
107- }
108- fflush(stdout);
109-
110- end--;
111-
112- nanosleep(&mili10, NULL);
113- }
114- return true;
115-}
1163
1174 bool draw_menu(main_menu menu, struct winsize window_size) {
1185
@@ -534,7 +421,7 @@ void draw_pause(struct winsize window_size, uint8_t field_width,
534421 fflush(stdout);
535422 }
536423
537-void draw_highscore (int16_t new_score, score leader_board[SCORE_NUMBER], struct winsize window_size) {
424+void draw_highscore (int16_t new_score, highscore leader_board[SCORE_NUMBER], struct winsize window_size) {
538425
539426 uint8_t i, j;
540427
Modified file: source/main.c+35 additions−49 deletions
@@ -8,17 +8,7 @@ int main(int argc, char *args[]) {
88 if (!parse_parameters(argc, args))
99 return 0;
1010
11- printf("\033[?1049h");
1211
13-
14- /* game settings */
15-
16- speed game_speed = AVERAGE;
17- selected game_size = {WIDTH, HEIGHT};
18-
19- score leader_board[SCORE_NUMBER] = {};
20-
21-
2212 /* Handling Files to store settings & highscores */
2313
2414
@@ -29,22 +19,33 @@ int main(int argc, char *args[]) {
2919 {
3020 char xdg_path[PATH_MAX];
3121
32- if (!create_xdg_folder(xdg_path))
22+ if (!create_xdg_folder(xdg_path)) {
23+ printf("Failed to create folder\n");
3324 return 1;
25+ }
3426
35- if(!create_folder(SETTINGS_FOLDER ,xdg_path, folder_path))
27+ if(!create_folder(SETTINGS_FOLDER ,xdg_path, folder_path)) {
28+ printf("Failed to create Folder\n");
3629 return 1;
30+ }
3731 }
3832
3933
4034 /* Settings file */
41-
42- char *settings_path = malloc(strlen(folder_path) + strlen(SETTINGS_FILE) + 2);
35+
36+
37+ speed game_speed;
38+ selected game_size;
39+
40+
41+ char *settings_path;
42+ if (!(settings_path = malloc(strlen(folder_path) + strlen(SETTINGS_FILE) + 2))) {
43+ printf("alloc_issue");
44+ }
4345 sprintf(settings_path, "%s/%s", folder_path, SETTINGS_FILE);
4446
4547
4648 {
47-
4849 setting_arr settings;
4950
5051 if (!init_setting_arr(&settings, 3)) {
@@ -52,12 +53,12 @@ int main(int argc, char *args[]) {
5253 return 1;
5354 }
5455
55- settings.settings[0] = game_speed;
56- settings.settings[1] = game_size.x;
57- settings.settings[2] = game_size.y;
56+ settings.settings[0] = AVERAGE;
57+ settings.settings[1] = WIDTH;
58+ settings.settings[2] = HEIGHT;
5859
5960 if (!read_settings(settings_path, settings)) {
60- printf("Issue reading files\n");
61+ printf("Issue reading settings file\n");
6162 return 1;
6263 }
6364
@@ -71,38 +72,21 @@ int main(int argc, char *args[]) {
7172
7273 /* Highscore file */
7374
74- char *score_path = malloc(strlen(folder_path) + strlen(SCORE_FILE) + 2);
75- sprintf(score_path, "%s/%s", folder_path, SCORE_FILE);
76-
77- {
78- FILE* score_file = fopen(score_path, "r");
79-
80- if (score_file != NULL) {
81-
82- char score_input[STRING_LEN];
75+ highscore leader_board[SCORE_NUMBER] = {};
8376
84- uint8_t i;
85- for (i = 0; i < SCORE_NUMBER; i++) {
86- fgets(score_input, sizeof(score_input), score_file);
87- if (score_input[0] != '\0') {
88- uint8_t j;
89- for(j = 0; score_input[j] != '\0'; j++) {
90- if (score_input[j] == '\n' || score_input[j] == ' ' || score_input[j] == '\t') {
91- score_input[j] = '\0';
92- break;
93- }
94- }
95- score_input[SCORE_LEN - 1] = '\0';
96- strncpy(leader_board[i].name, score_input, SCORE_LEN);
97- }
77+ char *score_path;
78+ if (!(score_path = malloc(strlen(folder_path) + strlen(SCORE_FILE) + 2))) {
79+ printf("alloc issue");
80+ return 1;
81+ }
82+ sprintf(score_path, "%s/%s", folder_path, SCORE_FILE);
9883
99- fgets(score_input, sizeof(score_input), score_file);
100- leader_board[i].points = string_to_num(score_input, 0, UINT16_MAX, leader_board[i].points);
101- }
102- fclose(score_file);
103- }
84+ if (!get_highscore(score_path, leader_board, SCORE_NUMBER)){
85+ printf("Issue reading highscore file\n");
86+ return 1;
10487 }
10588
89+
10690 /* Setting up terminal with termios */
10791
10892 struct termios old, new;
@@ -119,8 +103,7 @@ int main(int argc, char *args[]) {
119103
120104 srand(time(NULL));
121105
122- printf("\033[?25l");
123-
106+ /* Get windowssize */
124107 struct winsize init_size, current_size;
125108 ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);
126109 current_size = init_size;
@@ -128,6 +111,9 @@ int main(int argc, char *args[]) {
128111
129112 bool gameloop = true;
130113
114+ /* Termianl in alternate buffer and hide cursor */
115+ printf("\033[?1049h\033[?25l");
116+
131117 /* Animation at the start */
132118 if (!start_animation())
133119 gameloop = false;
Modified file: source/settings_file/settings_file.c+46 additions−1 deletions
@@ -4,8 +4,13 @@ uint8_t read_settings(char *settings_path, setting_arr set_arr) {
44
55 FILE* setting_file = fopen(settings_path, "r");
66
7- if (setting_file == NULL)
7+ if (setting_file == NULL) {
8+ if (errno == ENOENT)
9+ return 1;
10+
11+ free(set_arr.settings);
812 return 0;
13+ }
914
1015 char setting_input[SET_LEN];
1116
@@ -41,3 +46,43 @@ void free_setting_arr(setting_arr set_arr) {
4146 free(set_arr.settings);
4247 }
4348
49+
50+
51+
52+uint8_t get_highscore(char *highscore_path, highscore *leader_board, size_t score_ammount) {
53+
54+ FILE* highscore_file = fopen(highscore_path, "r");
55+
56+ if(highscore_file == NULL)
57+ return (errno == ENOENT) ? 1 : 0;
58+
59+ char score_name[SCORE_LEN];
60+ char score_value[SCORE_LEN];
61+
62+ uint8_t i;
63+ for (i = 0; i < score_ammount; i++) {
64+
65+ fgets(score_name, sizeof(score_name), highscore_file);
66+
67+ fgets(score_value, sizeof(score_value), highscore_file);
68+ leader_board[i].points = strtoll(score_value, NULL, 10);;
69+
70+ if (score_name[0] == '\0')
71+ continue;
72+
73+ uint8_t j;
74+ for(j = 0; score_name[j] != '\0'; j++) {
75+ if (score_name[j] == '\n' || score_name[j] == ' ' || score_name[j] == '\t') {
76+ score_name[j] = '\0';
77+ break;
78+ }
79+ }
80+
81+ score_name[SCORE_LEN - 1] = '\0';
82+ strncpy(leader_board[i].name, score_name, SCORE_LEN);
83+ }
84+
85+ fclose(highscore_file);
86+
87+ return 1;
88+}
Modified file: source/settings_file/settings_file.h+14 additions−1 deletions
@@ -4,20 +4,33 @@
44 #include <stdio.h>
55 #include <stdint.h>
66 #include <stdlib.h>
7+#include <string.h>
8+
9+#include <errno.h>
710
811 #define SET_LEN 10
912
13+#define SCORE_LEN 10
14+
15+
1016 typedef struct {
1117 int64_t *settings;
1218 size_t length;
1319 } setting_arr;
1420
21+typedef struct score {
22+ char name[SCORE_LEN];
23+ uint16_t points;
24+} highscore;
1525
16-uint8_t read_settings(char *settings_file, setting_arr set_arr);
1726
27+uint8_t read_settings(char *settings_file, setting_arr set_arr);
1828
1929 uint8_t init_setting_arr(setting_arr *set_arr, size_t length);
2030
2131 void free_setting_arr(setting_arr set_arr);
2232
33+
34+uint8_t get_highscore(char *highscore_path, highscore *leader_board, size_t score_ammount);
35+
2336 #endif
Modified file: source/snake.h+1 additions−6 deletions
@@ -42,7 +42,6 @@
4242
4343 #define STRING_LEN 20
4444
45-#define SCORE_LEN 10
4645 #define SCORE_NUMBER 10
4746
4847 #define SPEED 50 /* in ms */
@@ -85,10 +84,6 @@ typedef struct snake {
8584 uint16_t length;
8685 } snake;
8786
88-typedef struct score {
89- char name[SCORE_LEN];
90- uint16_t points;
91-} score;
9287
9388 uint8_t update_snake (selected game_size, snake *my_snake, direction shift);
9489 void update_field(snake my_snake, selected game_size,
@@ -106,7 +101,7 @@ bool draw_board(selected game_size, board_state **board,
106101 void draw_pause(struct winsize window_size,
107102 uint8_t field_width,uint8_t field_height, uint8_t game_over);
108103 void draw_highscore (int16_t new_score,
109- score leader_board[SCORE_NUMBER], struct winsize window_size);
104+ highscore leader_board[SCORE_NUMBER], struct winsize window_size);
110105
111106 bool print_buffer(uint16_t width, char *string);