jag

snake_c

public

Small snake game in C

Star0
Watch0

Commit 7b30379da950

tarball

Jaglekon <58274093+Jaglekon@users.noreply.github.com>2026-03-21 22:51 UTC

2 files changed+214-87
variable term size
Added file: art.txt+9 additions
@@ -0,0 +1,9 @@
1+
2+
3+ ________
4+ _/ _ \_________
5+ ________/ (_) \ /
6+ \ \ \ /
7+ /_______/ _ \ /
8+ \_ (_) ____\/___
9+ \________/
Modified file: snake.c+205 additions−87 deletions
@@ -1,22 +1,31 @@
11 #include <unistd.h>
2+#include <termios.h>
23
34 #include <stdio.h>
45 #include <stdint.h>
56 #include <stdbool.h>
67 #include <stdlib.h>
78
9+#include <sys/ioctl.h>
10+
811 #include <time.h>
912 #include <sys/time.h>
13+
1014 #include <string.h>
11-#include <termios.h>
15+#include <ctype.h>
16+
1217
1318 #define RIGHT_CODE 'C'
1419 #define LEFT_CODE 'D'
1520 #define UP_CODE 'A'
1621 #define DOWN_CODE 'B'
1722
18-#define WIDTH 50
19-#define HEIGHT 50
23+#define WIDTH 20
24+#define HEIGHT 20
25+
26+#define ACTUAL_WIDTH ((WIDTH * 2) + 2)
27+#define ACTUAL_HEIGHT (HEIGHT + 2)
28+
2029
2130 #define SPEED 100 /* in ms */
2231
@@ -42,16 +51,43 @@ typedef struct snake {
4251 } snake;
4352
4453
54+
55+
4556 void update_field(snake my_snake, field_content board[HEIGHT][WIDTH], selected fruit);
4657 selected new_fruit(field_content board[HEIGHT][WIDTH]);
4758
48-void draw_board(field_content board[HEIGHT][WIDTH], bool redraw);
49-void draw_pause();
59+void draw_board(field_content board[HEIGHT][WIDTH], struct winsize window_size);
60+void draw_pause(struct winsize window_size);
5061
5162 direction read_direction (char* string, direction shift);
5263
5364
54-int main() {
65+
66+int main(int argc, char *args[]) {
67+
68+ if (argc > 1) {
69+ if (argc > 2) {
70+ printf("Too many parameters\n");
71+ goto help_info;
72+ }
73+
74+ if (!strcmp(args[1], "-h") || !strcmp(args[1], "--help")) {
75+
76+ printf("This is a small TUI snake game.\n\n");
77+ printf("Controls: Arrow keys/WASD to control the snake.\n");
78+ printf(" P to pause.\n");
79+ printf(" Q to quit.\n");
80+
81+ return 0;
82+ }
83+
84+ printf("Argument \"%s\" not recognized\n", args[1]);
85+
86+ help_info:
87+ printf("Try \"-h\" or \"--help\" for more information\n");
88+ return 1;
89+ }
90+
5591
5692 struct termios old, new;
5793
@@ -67,6 +103,8 @@ int main() {
67103
68104 srand(time(NULL));
69105
106+ printf("\033[?25l");
107+
70108
71109 snake my_snake = {};
72110 my_snake.length = STARTING_LENGTH;
@@ -83,112 +121,158 @@ int main() {
83121 }
84122
85123 selected fruit = {-1, -1};
86-
87124 field_content board[HEIGHT][WIDTH] = {0};
88-
125+
89126 update_field(my_snake, board, fruit);
90-
127+
91128 fruit = new_fruit(board);
92129 update_field(my_snake, board, fruit);
93130
131+ bool paused = false;
94132
95- printf("\033[?25l");
96- draw_board(board, false);
97133
134+ struct winsize init_size, current_size;
135+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);
136+ current_size = init_size;
137+
138+ bool too_small = false;
139+
140+
141+ if (current_size.ws_row < ACTUAL_HEIGHT || current_size.ws_col < ACTUAL_WIDTH) {
142+ too_small = true;
143+ paused = true;
144+
145+ printf("\033[2J\033[HToo small\n");
146+ printf("Min width: %i & Min height: %i\n", ACTUAL_WIDTH, ACTUAL_WIDTH);
147+ } else {
148+ draw_board(board, current_size);
149+ }
98150
99151 direction shift = RIGHT;
152+ direction tmp_direction = shift;
100153
101154
102155 struct timeval start, current;
103-
104156 gettimeofday(&start, NULL);
105157
106158
107- bool paused = false;
108159 bool run = true;
109160
110161 while (run) {
111162
112- char string[1000] = {};
113- ssize_t n = read(STDIN_FILENO, string, sizeof(string) - 1);
114-
115- gettimeofday(&current, NULL);
116163
117- if (n > 0) {
118- string[n] = '\0';
119-
120- if (!paused)
121- shift = read_direction(string, shift);
164+ if (!too_small) {
165+ char string[1000] = {};
166+ ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
167+
168+ gettimeofday(&current, NULL);
169+
170+ if (length_string > 0) {
171+ string[length_string] = '\0';
172+
173+ if (!paused)
174+ tmp_direction = read_direction(string, shift);
122175
123- if (strstr(string, "q"))
124- run = false;
176+ if (toupper(string[0]) == 'Q')
177+ run = false;
125178
126- if (strstr(string, "p")) {
127- paused = !paused;
128- if (paused)
129- draw_pause();
179+ if (toupper(string[0]) == 'P') {
180+ paused = !paused;
181+ }
130182 }
183+
184+ if (paused)
185+ draw_pause(current_size);
131186 }
132187
188+
133189 uint64_t time_diff = (current.tv_sec - start.tv_sec) * 1000000 + (current.tv_usec - start.tv_usec);
134190
135- if ((time_diff >= SPEED * 1000) && !paused) {
191+ if (time_diff >= SPEED * 1000) {
192+
193+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
136194
137- start = current;
138-
139- uint16_t i;
140- for (i = my_snake.length; i > 0; i--) {
141- my_snake.position[i].y = my_snake.position[i - 1].y;
142- my_snake.position[i].x = my_snake.position[i - 1].x;
195+ if (
196+ current_size.ws_col != init_size.ws_col ||
197+ current_size.ws_row != init_size.ws_row
198+ ) {
199+ init_size = current_size;
200+
201+ paused = true;
202+
203+ if (current_size.ws_row < ACTUAL_HEIGHT || current_size.ws_col < ACTUAL_WIDTH) {
204+ too_small = true;
205+
206+ printf("\033[2J\033[HToo small\n");
207+ printf("Min width: %i & Min height: %i\n", ACTUAL_WIDTH, ACTUAL_WIDTH);
208+ } else {
209+ too_small = false;
210+
211+ draw_board(board, current_size);
212+ }
143213 }
144214
145- switch (shift) {
146- case UP:
147- my_snake.position[0].y = ((my_snake.position[0].y + HEIGHT - 1) % HEIGHT);
148- break;
149- case DOWN:
150- my_snake.position[0].y = ((my_snake.position[0].y + 1) % HEIGHT);
151- break;
152- case LEFT:
153- my_snake.position[0].x = ((my_snake.position[0].x + WIDTH - 1) % WIDTH);
154- break;
155- case RIGHT:
156- my_snake.position[0].x = ((my_snake.position[0].x + 1) % WIDTH);
157- break;
158- }
159-
160- uint8_t head_y = my_snake.position[0].y;
161- uint8_t head_x = my_snake.position[0].x;
162-
163- for (i = 1; i < my_snake.length; i++) {
164- int16_t x = my_snake.position[i].x;
165- int16_t y = my_snake.position[i].y;
166-
167- if (
168- x == head_x &&
169- y == head_y
170- ) {
171- printf("\n\nHAHA LOST!!!\n\n");
172- goto end_of_program;
215+ if (!paused) {
216+
217+ start = current;
218+ shift = tmp_direction;
219+
220+ uint16_t i;
221+ for (i = my_snake.length; i > 0; i--) {
222+ my_snake.position[i].y = my_snake.position[i - 1].y;
223+ my_snake.position[i].x = my_snake.position[i - 1].x;
173224 }
174- }
175225
176- if (head_y == fruit.y && head_x == fruit.x) {
177- my_snake.length++;
178- fruit = new_fruit(board);
179- }
226+ switch (shift) {
227+ case UP:
228+ my_snake.position[0].y = ((my_snake.position[0].y + HEIGHT - 1) % HEIGHT);
229+ break;
230+ case DOWN:
231+ my_snake.position[0].y = ((my_snake.position[0].y + 1) % HEIGHT);
232+ break;
233+ case LEFT:
234+ my_snake.position[0].x = ((my_snake.position[0].x + WIDTH - 1) % WIDTH);
235+ break;
236+ case RIGHT:
237+ my_snake.position[0].x = ((my_snake.position[0].x + 1) % WIDTH);
238+ break;
239+ }
240+
241+ uint8_t head_y = my_snake.position[0].y;
242+ uint8_t head_x = my_snake.position[0].x;
243+
244+ for (i = 1; i < my_snake.length; i++) {
245+ int16_t x = my_snake.position[i].x;
246+ int16_t y = my_snake.position[i].y;
247+
248+ if (
249+ x == head_x &&
250+ y == head_y
251+ ) {
252+ printf("\n\nHAHA LOST!!!\n\n");
253+ goto end_of_program;
254+ }
255+ }
180256
257+ update_field(my_snake, board, fruit);
181258
182- update_field(my_snake, board, fruit);
183- draw_board(board, true);
184- }
259+ if (head_y == fruit.y && head_x == fruit.x) {
260+ my_snake.length++;
261+ fruit = new_fruit(board);
262+ }
185263
264+
265+ update_field(my_snake, board, fruit);
266+ draw_board(board, current_size);
267+ }
268+ }
186269 }
187270
271+
188272 end_of_program:
189273
190274 tcsetattr(STDIN_FILENO, TCSANOW, &old);
191- printf("\033[?25h");
275+ printf("\033[?25h\n\n");
192276
193277 return 0;
194278 }
@@ -233,22 +317,26 @@ selected new_fruit(field_content board[HEIGHT][WIDTH]) {
233317 found_space = true;
234318 }
235319
236- selected fruit = {y, x};
320+ selected fruit = {x, y};
237321
238322 return fruit;
239323 }
240324
241325
242-void draw_board (field_content board[HEIGHT][WIDTH], bool redraw) {
326+void draw_board (field_content board[HEIGHT][WIDTH], struct winsize window_size) {
243327
244328 uint8_t i, j;
245329
246- if (redraw) {
247- for (i = 0; i < HEIGHT + 2; i++)
248- printf("\033[A");
249- printf("\r");
250- }
330+ uint8_t vertical_buffer = (window_size.ws_row - ACTUAL_HEIGHT) >> 1;
331+ uint8_t horizontal_buffer = (window_size.ws_col - ACTUAL_WIDTH) >> 1;
332+
333+ printf("\033[2J\033[H");
251334
335+ for (i = 0; i < vertical_buffer; i++)
336+ printf("\n");
337+
338+ for (i = 0; i < horizontal_buffer; i++)
339+ printf(" ");
252340 printf("+");
253341 for (i = 0; i < WIDTH; i++)
254342 printf("--");
@@ -256,6 +344,9 @@ void draw_board (field_content board[HEIGHT][WIDTH], bool redraw) {
256344
257345 for ( i = 0; i < HEIGHT; i++) {
258346
347+ for (j = 0; j < horizontal_buffer; j++)
348+ printf(" ");
349+
259350 printf("|");
260351
261352 for (j = 0; j < WIDTH; j++) {
@@ -278,19 +369,22 @@ void draw_board (field_content board[HEIGHT][WIDTH], bool redraw) {
278369 printf("|\n");
279370 }
280371
281- printf("+");
372+ for (i = 0; i < horizontal_buffer; i++)
373+ printf(" ");
374+ printf("+");
282375 for (i = 0; i < WIDTH; i++)
283376 printf("--");
284- printf("+\n");
377+ printf("+");
378+ fflush(stdout);
285379 }
286380
287381
288-void draw_pause() {
382+void draw_pause(struct winsize window_size) {
289383
290384 const uint8_t height = 5;
291385 const uint8_t width = 14;
292386
293- char *pause_menu [] = {
387+ const char *pause_menu [] = {
294388 "+------------+",
295389 "| |",
296390 "| PAUSED |",
@@ -298,16 +392,21 @@ void draw_pause() {
298392 "+------------+"
299393 };
300394
301- uint8_t i, j;
302395
303- const uint8_t going_up = (HEIGHT + height) >> 1;
304- const uint8_t going_right = (WIDTH * 2 - width) >> 1;
396+ const uint8_t going_up = (ACTUAL_HEIGHT + height) >> 1;
397+ const uint8_t going_right = (ACTUAL_WIDTH - width) >> 1;
398+
399+ const uint8_t horizontal_buffer = (window_size.ws_col - ACTUAL_WIDTH) >> 1;
400+
401+ uint8_t i, j;
305402
306403 for (i = 0; i < going_up; i++)
307404 printf("\033[%c", UP_CODE);
308405 printf("\r");
309406
310407 for (i = 0; i < height; i++) {
408+ for (j = 0; j < horizontal_buffer; j++)
409+ printf("\033[%c", RIGHT_CODE);
311410 for (j = 0; j < going_right; j++)
312411 printf("\033[%c", RIGHT_CODE);
313412
@@ -321,7 +420,6 @@ void draw_pause() {
321420 fflush(stdout);
322421 }
323422
324-
325423 direction read_direction (char* string, direction shift) {
326424
327425 if (strlen(string) >= 3) {
@@ -350,5 +448,25 @@ direction read_direction (char* string, direction shift) {
350448 }
351449 }
352450
451+
452+ switch (toupper(string[0])) {
453+ case 'A':
454+ if (shift != RIGHT)
455+ return LEFT;
456+ break;
457+ case 'D':
458+ if (shift != LEFT)
459+ return RIGHT;
460+ break;
461+ case 'W':
462+ if (shift != DOWN)
463+ return UP;
464+ break;
465+ case 'S':
466+ if (shift != UP)
467+ return DOWN;
468+ break;
469+ }
470+
353471 return shift;
354472 }