Small snake game in C
Commit 7b30379da950
tarball2 files changed+214-87
@@ -0,0 +1,9 @@
1
+2
+3
+ ________4
+ _/ _ \_________5
+ ________/ (_) \ /6
+ \ \ \ / 7
+ /_______/ _ \ / 8
+ \_ (_) ____\/___9
+ \________/@@ -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 5019
-#define HEIGHT 5023
+#define WIDTH 2024
+#define HEIGHT 2025
+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(¤t, 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(¤t, 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, ¤t_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_row198
+ ) {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_y170
- ) {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_y251
+ ) {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
}