Small snake game in C
Commit d82b79536846
tarball2 files changed+344-80
@@ -1,9 +1,9 @@
11
22
33
________4
- _/ _ \_________5
- ________/ (_) \ /6
- \ \ \ / 7
- /_______/ _ \ / 8
- \_ (_) ____\/___4
+ _/ _ \_ ________ _________ _5
+ ________/ (_) \ / \ / \_____6
+ \ \ \ / \ / \7
+ /_______/ _ \ / \ / _____/8
+ \_ (_) _ ___\/___ ___\/____ _/ 99
\________/@@ -23,6 +23,8 @@
2323
#define WIDTH 202424
#define HEIGHT 202525
26
+#define STRING_LEN 2027
+2628
#define ACTUAL_WIDTH ((WIDTH * 2) + 2)2729
#define ACTUAL_HEIGHT (HEIGHT + 2)2830
@@ -33,12 +35,12 @@
3335
#define STARTING_LENGTH 23436
3537
typedef enum {36
- UP, DOWN, LEFT, RIGHT37
-} direction;38
+ NEW_GAME, SETTINGS, EXIT, MAIN_MENU39
+} main_menu;3840
3941
typedef enum {40
- EMPTY, SNAKE, HEAD, FRUIT41
-} field_content;42
+ NO_DIRECTION = -1, UP = 0, LEFT = 1, DOWN = 2, RIGHT = 3, DIRECTIONS = 443
+} direction;4244
4345
typedef struct selected {4446
int16_t x;@@ -52,19 +54,22 @@ typedef struct snake {
5254
5355
5456
57
+void update_field(snake my_snake, char board[HEIGHT][WIDTH][STRING_LEN], selected fruit);58
+selected new_fruit(char board[HEIGHT][WIDTH][STRING_LEN]);5559
56
-void update_field(snake my_snake, field_content board[HEIGHT][WIDTH], selected fruit);57
-selected new_fruit(field_content board[HEIGHT][WIDTH]);60
+bool draw_menu(main_menu menu, struct winsize window_size);5861
59
-void draw_board(field_content board[HEIGHT][WIDTH], struct winsize window_size);62
+void draw_board(char board[HEIGHT][WIDTH][STRING_LEN], struct winsize window_size);6063
void draw_pause(struct winsize window_size);6164
62
-direction read_direction (char* string, direction shift);65
+direction read_direction (char* string, bool wasd_enabled);6366
6467
6568
6669
int main(int argc, char *args[]) {6770
71
+ /* command argument handling */72
+ 6873
if (argc > 1) {6974
if (argc > 2) {7075
printf("Too many parameters\n");@@ -88,6 +93,8 @@ int main(int argc, char *args[]) {
8893
return 1;8994
}9095
96
+ 97
+ /* Setting up terminal with termios */9198
9299
struct termios old, new;93100
@@ -105,6 +112,103 @@ int main(int argc, char *args[]) {
105112
106113
printf("\033[?25l");107114
115
+ 116
+ struct winsize init_size, current_size;117
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);118
+ current_size = init_size;119
+ bool too_small = false;120
+121
+122
+123
+ struct timeval start;124
+ gettimeofday(&start, NULL);125
+126
+127
+ /* Menu */128
+129
+ bool menu = true;130
+131
+ main_menu selected_menu = NEW_GAME;132
+ 133
+ too_small =draw_menu(selected_menu, init_size);134
+135
+ while (menu) {136
+137
+ bool pressed_enter = false;138
+ bool changed_selected = false;139
+140
+ char string[1000] = {};141
+ ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);142
+ 143
+ direction arrow = NO_DIRECTION;144
+145
+146
+ /* Check for window size */147
+ 148
+ struct timeval current;149
+ gettimeofday(¤t, NULL);150
+151
+ uint64_t time_diff = (current.tv_sec - start.tv_sec) * 1000000 + (current.tv_usec - start.tv_usec);152
+153
+ if (time_diff >= SPEED * 1000) {154
+ 155
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, ¤t_size);156
+157
+ if (158
+ current_size.ws_col != init_size.ws_col || 159
+ current_size.ws_row != init_size.ws_row160
+ ) {161
+ init_size = current_size;162
+ changed_selected = true;163
+ }164
+ }165
+166
+167
+ if (length_string > 0 && !too_small) {168
+ string[length_string] = '\0';169
+ 170
+ arrow = read_direction(string, false);171
+ 172
+173
+ switch (arrow) {174
+ case UP:175
+ selected_menu = (selected_menu + (MAIN_MENU - 1)) % MAIN_MENU;176
+ changed_selected = true;177
+ break;178
+ case DOWN:179
+ selected_menu = (selected_menu + 1) % MAIN_MENU;180
+ changed_selected = true;181
+ break;182
+ default:183
+ break;184
+ }185
+ 186
+ if (toupper(string[0]) == 'Q')187
+ goto end_of_program;188
+ 189
+ if (string[0] == '\n')190
+ pressed_enter = true;191
+ }192
+ 193
+194
+ if (changed_selected)195
+ too_small = draw_menu(selected_menu, init_size);196
+ 197
+ if (pressed_enter) {198
+ switch (selected_menu) {199
+ case NEW_GAME:200
+ menu = false;201
+ break;202
+ default:203
+ goto end_of_program;204
+ break;205
+ }206
+ }207
+ }208
+209
+210
+211
+ /* initialisation of the game */108212
109213
snake my_snake = {}; 110214
my_snake.length = STARTING_LENGTH;@@ -112,8 +216,8 @@ int main(int argc, char *args[]) {
112216
{113217
uint16_t i;114218
115
- my_snake.position[0].x = HEIGHT >> 1;116
- my_snake.position[0].y = WIDTH >> 1;219
+ my_snake.position[0].y = HEIGHT >> 1;220
+ my_snake.position[0].x = WIDTH >> 1;117221
for (i = 1; i < my_snake.length; i++) {118222
my_snake.position[i].y = -1;119223
my_snake.position[i].x = -1;@@ -121,22 +225,15 @@ int main(int argc, char *args[]) {
121225
}122226
123227
selected fruit = {-1, -1};124
- field_content board[HEIGHT][WIDTH] = {0};228
+ char board[HEIGHT][WIDTH][STRING_LEN] = {};125229
126230
update_field(my_snake, board, fruit);127231
128232
fruit = new_fruit(board);129233
update_field(my_snake, board, fruit);130234
131
- bool paused = false;132
-133
-134
- struct winsize init_size, current_size;135
- ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);136
- current_size = init_size;137235
138
- bool too_small = false;139
- 236
+ bool paused = false;140237
141238
if (current_size.ws_row < ACTUAL_HEIGHT || current_size.ws_col < ACTUAL_WIDTH) {142239
too_small = true;@@ -152,26 +249,24 @@ int main(int argc, char *args[]) {
152249
direction tmp_direction = shift;153250
154251
155
- struct timeval start, current;156
- gettimeofday(&start, NULL);157
-158
-159252
bool run = true;160253
254
+ /* start of the actual game loop */255
+161256
while (run) {162257
163258
259
+ /* input handling */260
+164261
if (!too_small) {165262
char string[1000] = {};166263
ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);167
- 168
- gettimeofday(¤t, NULL);169
-264
+ 170265
if (length_string > 0) {171266
string[length_string] = '\0';172267
173268
if (!paused)174
- tmp_direction = read_direction(string, shift);269
+ tmp_direction = read_direction(string, true);175270
176271
if (toupper(string[0]) == 'Q')177272
run = false;@@ -186,6 +281,11 @@ int main(int argc, char *args[]) {
186281
}187282
188283
284
+ /* game logic */285
+286
+ struct timeval current;287
+ gettimeofday(¤t, NULL);288
+189289
uint64_t time_diff = (current.tv_sec - start.tv_sec) * 1000000 + (current.tv_usec - start.tv_usec);190290
191291
if (time_diff >= SPEED * 1000) {@@ -215,7 +315,12 @@ int main(int argc, char *args[]) {
215315
if (!paused) {216316
217317
start = current;218
- shift = tmp_direction;318
+ if (319
+ tmp_direction != NO_DIRECTION &&320
+ ((shift + (DIRECTIONS >> 1)) % DIRECTIONS) != tmp_direction321
+ ) {322
+ shift = tmp_direction;323
+ }219324
220325
uint16_t i;221326
for (i = my_snake.length; i > 0; i--) {@@ -236,6 +341,9 @@ int main(int argc, char *args[]) {
236341
case RIGHT:237342
my_snake.position[0].x = ((my_snake.position[0].x + 1) % WIDTH);238343
break;344
+ default:345
+ goto end_of_program;346
+ break;239347
}240348
241349
uint8_t head_y = my_snake.position[0].y;@@ -281,29 +389,84 @@ end_of_program:
281389
282390
283391
284
-void update_field(snake my_snake, field_content board[HEIGHT][WIDTH], selected fruit) {392
+void update_field(snake my_snake, char board[HEIGHT][WIDTH][STRING_LEN], selected fruit) {285393
286394
uint8_t i, j;287395
288396
for (i = 0; i < HEIGHT; i++) {289397
for (j = 0; j < WIDTH; j++) { 290
- board[i][j] = EMPTY;398
+ strcpy(board[i][j], " ");291399
}292400
}293401
402
+403
+294404
for (i = 0; i < my_snake.length; i++) {295405
int16_t x = my_snake.position[i].x;296406
int16_t y = my_snake.position[i].y;297
- if (x >= 0 && y >= 0)298
- board[y][x] = (i == 0) ? HEAD : SNAKE;407
+ if (x >= 0 && y >= 0) {408
+ if (!i) {409
+ if (x == (my_snake.position[i + 1].x + 1) % WIDTH) {410
+ strcpy(board[y][x], "\033[33m═\033[31mX\033[0m");411
+ } else {412
+ strcpy(board[y][x], "\033[31m X\033[0m");413
+ }414
+ } else if (i == my_snake.length - 1) {415
+ if (x != my_snake.position[i - 1].x) {416
+ strcpy(board[y][x], "\033[33m══\033[0m"); 417
+ } else {418
+ strcpy(board[y][x], "\033[33m ║\033[0m"); 419
+ }420
+ } else {421
+ if (422
+ x != my_snake.position[i - 1].x && 423
+ x != my_snake.position[i + 1].x424
+ ) {425
+ strcpy(board[y][x], "\033[33m══\033[0m"); 426
+ } else if (427
+ y != my_snake.position[i - 1].y && 428
+ y != my_snake.position[i + 1].y429
+ ) {430
+ strcpy(board[y][x], "\033[33m ║\033[0m");431
+ } else if (432
+ (y == (my_snake.position[i - 1].y + 1) % HEIGHT && 433
+ x == (my_snake.position[i + 1].x + 1) % WIDTH) ||434
+ (y == (my_snake.position[i + 1].y + 1) % HEIGHT && 435
+ x == (my_snake.position[i - 1].x + 1) % WIDTH)436
+ ) {437
+ strcpy(board[y][x], "\033[33m═╝\033[0m");438
+ } else if (439
+ (y == (my_snake.position[i - 1].y + (HEIGHT - 1)) % HEIGHT && 440
+ x == (my_snake.position[i + 1].x + 1) % WIDTH) ||441
+ (y == (my_snake.position[i + 1].y + HEIGHT - 1) % HEIGHT && 442
+ x == (my_snake.position[i - 1].x + 1) % WIDTH)443
+ ) {444
+ strcpy(board[y][x], "\033[33m═╗\033[0m");445
+ } else if (446
+ (y == (my_snake.position[i - 1].y + 1) % HEIGHT && 447
+ x == (my_snake.position[i + 1].x + (WIDTH - 1)) % WIDTH) ||448
+ (y == (my_snake.position[i + 1].y + 1) % HEIGHT && 449
+ x == (my_snake.position[i - 1].x + (WIDTH - 1)) % WIDTH)450
+ ) {451
+ strcpy(board[y][x], "\033[33m ╚\033[0m");452
+ } else if (453
+ (y == (my_snake.position[i - 1].y + (HEIGHT - 1)) % HEIGHT && 454
+ x == (my_snake.position[i + 1].x + (WIDTH - 1)) % WIDTH) ||455
+ (y == (my_snake.position[i + 1].y + (HEIGHT - 1)) % HEIGHT && 456
+ x == (my_snake.position[i - 1].x + (WIDTH - 1)) % WIDTH)457
+ ) {458
+ strcpy(board[y][x], "\033[33m ╔\033[0m");459
+ }460
+ }461
+ }299462
}300463
301464
if (fruit.x >= 0 && fruit.y >= 0)302
- board[fruit.y][fruit.x] = FRUIT;465
+ strcpy(board[fruit.y][fruit.x], "\033[32m O\033[0m");303466
}304467
305468
306
-selected new_fruit(field_content board[HEIGHT][WIDTH]) {469
+selected new_fruit(char board[HEIGHT][WIDTH][STRING_LEN]) {307470
bool found_space = false;308471
309472
uint8_t x;@@ -313,7 +476,7 @@ selected new_fruit(field_content board[HEIGHT][WIDTH]) {
313476
x = rand() % WIDTH;314477
y = rand() % HEIGHT;315478
316
- if (board[y][x] == EMPTY)479
+ if (!strcmp(board[y][x], " "))317480
found_space = true;318481
}319482
@@ -323,7 +486,128 @@ selected new_fruit(field_content board[HEIGHT][WIDTH]) {
323486
}324487
325488
326
-void draw_board (field_content board[HEIGHT][WIDTH], struct winsize window_size) {489
+bool draw_menu(main_menu menu, struct winsize window_size) {490
+491
+ char *menu_string[] = {492
+ "NEW GAME",493
+ "SETTINGS",494
+ "EXIT"495
+ };496
+ 497
+ const uint8_t width_small = 8;498
+ const uint8_t height_small = 5;499
+500
+501
+ 502
+ char *menu_string_big[][6] = {503
+ {504
+ "███╗ ██╗███████╗██╗ ██╗ ██████╗ █████╗ ███╗ ███╗███████╗",505
+ "████╗ ██║██╔════╝██║ ██║ ██╔════╝ ██╔══██╗████╗ ████║██╔════╝",506
+ "██╔██╗ ██║█████╗ ██║ █╗ ██║ ██║ ███╗███████║██╔████╔██║█████╗ ",507
+ "██║╚██╗██║██╔══╝ ██║███╗██║ ██║ ██║██╔══██║██║╚██╔╝██║██╔══╝ ",508
+ "██║ ╚████║███████╗╚███╔███╔╝ ╚██████╔╝██║ ██║██║ ╚═╝ ██║███████╗",509
+ "╚═╝ ╚═══╝╚══════╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝"510
+ },511
+ {512
+ "███████╗███████╗████████╗████████╗██╗███╗ ██╗ ██████╗ ███████╗",513
+ "██╔════╝██╔════╝╚══██╔══╝╚══██╔══╝██║████╗ ██║██╔════╝ ██╔════╝",514
+ "███████╗█████╗ ██║ ██║ ██║██╔██╗ ██║██║ ███╗███████╗",515
+ "╚════██║██╔══╝ ██║ ██║ ██║██║╚██╗██║██║ ██║╚════██║",516
+ "███████║███████╗ ██║ ██║ ██║██║ ╚████║╚██████╔╝███████║",517
+ "╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝"518
+ },519
+ {520
+ "███████╗██╗ ██╗██╗████████╗",521
+ "██╔════╝╚██╗██╔╝██║╚══██╔══╝",522
+ "█████╗ ╚███╔╝ ██║ ██║ ",523
+ "██╔══╝ ██╔██╗ ██║ ██║ ",524
+ "███████╗██╔╝ ██╗██║ ██║ ",525
+ "╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ "526
+ }527
+ };528
+529
+ const uint8_t height_line = 6;530
+ const uint8_t height_space = 2;531
+532
+ const uint8_t width_text[] = {68, 64, 28};533
+534
+ const uint8_t width_big = width_text[0] /*strlen(menu_string_big[0][0])*/;535
+ const uint8_t height_big = 536
+ height_line * MAIN_MENU + height_space * (MAIN_MENU - 1);537
+538
+539
+ printf("\033[2J\033[H");540
+541
+ if (window_size.ws_row < height_small || window_size.ws_col < width_small) {542
+ printf("Too small\n");543
+ return true;544
+ }545
+546
+547
+ uint8_t i, j, k;548
+549
+ if (window_size.ws_row >= height_big && window_size.ws_col >= width_big) {550
+ 551
+ uint8_t vertical_buffer = (window_size.ws_row - height_big) >> 1;552
+ 553
+ for (i = 0; i < vertical_buffer; i++)554
+ printf("\n");555
+556
+ for (i = 0; i < MAIN_MENU; i++) {557
+ 558
+ if (i == menu)559
+ printf("\033[32m");560
+561
+ uint8_t horizontal_buffer = 562
+ (window_size.ws_col - width_text[i]) >> 1;563
+564
+ for (j = 0; j < height_line; j++) {565
+ 566
+ for (k = 0; k < horizontal_buffer; k++)567
+ printf(" ");568
+569
+ printf("%s", menu_string_big[i][j]);570
+571
+ if (j != height_line - 1)572
+ printf("\n");573
+ }574
+ 575
+ printf("\033[0m");576
+577
+ if (i != MAIN_MENU - 1)578
+ printf("\n\n");579
+ }580
+581
+ } else {582
+583
+ uint8_t vertical_buffer = (window_size.ws_row - height_small) >> 1;584
+585
+ for (i = 0; i < vertical_buffer; i++)586
+ printf("\n");587
+588
+ for (i = 0; i < MAIN_MENU; i++) {589
+ if (i == menu)590
+ printf("\033[32m");591
+592
+ uint8_t horizontal_buffer = 593
+ (window_size.ws_col - strlen(menu_string[i])) >> 1;594
+595
+ for (j = 0; j < horizontal_buffer; j++)596
+ printf(" ");597
+598
+ printf("%s\033[0m", menu_string[i]);599
+600
+ if (i != MAIN_MENU - 1)601
+ printf("\n\n");602
+ }603
+ }604
+605
+ fflush(stdout);606
+ return false;607
+}608
+609
+610
+void draw_board (char board[HEIGHT][WIDTH][STRING_LEN], struct winsize window_size) {327611
328612
uint8_t i, j; 329613
@@ -349,23 +633,9 @@ void draw_board (field_content board[HEIGHT][WIDTH], struct winsize window_size)
349633
350634
printf("|");351635
352
- for (j = 0; j < WIDTH; j++) {636
+ for (j = 0; j < WIDTH; j++)637
+ printf("%s", board[i][j]);353638
354
- switch (board[i][j]) {355
- case EMPTY:356
- printf(" ");357
- break;358
- case SNAKE:359
- printf("\033[31m x\033[0m");360
- break;361
- case HEAD:362
- printf("\033[32m X\033[0m");363
- break;364
- case FRUIT:365
- printf("\033[34m O\033[0m");366
- break;367
- }368
- }369639
printf("|\n");370640
}371641
@@ -420,7 +690,7 @@ void draw_pause(struct winsize window_size) {
420690
fflush(stdout);421691
}422692
423
-direction read_direction (char* string, direction shift) {693
+direction read_direction (char* string, bool wasd_enabled) {424694
425695
if (strlen(string) >= 3) {426696
if (string[0] == '\033') {@@ -428,20 +698,16 @@ direction read_direction (char* string, direction shift) {
428698
429699
switch (string[2]) {430700
case RIGHT_CODE:431
- if (shift != LEFT)432
- return RIGHT;701
+ return RIGHT;433702
break;434703
case LEFT_CODE:435
- if (shift != RIGHT)436
- return LEFT;704
+ return LEFT;437705
break;438706
case UP_CODE:439
- if (shift != DOWN)440
- return UP;707
+ return UP;441708
break;442709
case DOWN_CODE:443
- if (shift != UP)444
- return DOWN;710
+ return DOWN;445711
break;446712
} 447713
}@@ -449,24 +715,22 @@ direction read_direction (char* string, direction shift) {
449715
}450716
451717
452
- switch (toupper(string[0])) {453
- case 'A':454
- if (shift != RIGHT)718
+ if (wasd_enabled) {719
+ switch (toupper(string[0])) {720
+ case 'A':455721
return LEFT;456
- break;457
- case 'D':458
- if (shift != LEFT)722
+ break;723
+ case 'D':459724
return RIGHT;460
- break;461
- case 'W':462
- if (shift != DOWN)725
+ break;726
+ case 'W':463727
return UP;464
- break;465
- case 'S':466
- if (shift != UP)728
+ break;729
+ case 'S':467730
return DOWN;468
- break;731
+ break;732
+ }469733
}470734
471
- return shift;735
+ return NO_DIRECTION;472736
}