Small snake game in C
Commit 9357e6aedfb4
tarball5 files changed+340-328
@@ -1,2 +1,2 @@
11
snake: snake.c2
- gcc -std=c99 -Wall -Wextra -o snake snake.c drawing.c2
+ gcc -std=c99 -Wall -Wextra -o snake snake.c drawing.c logic.c@@ -214,6 +214,103 @@ bool draw_menu(main_menu menu, struct winsize window_size) {
214214
return false;215215
}216216
217
+218
+void draw_settings(settings selected_setting, speed game_speed, selected game_size) {219
+220
+ char *setting_string[] = {221
+ " HEIGHT ",222
+ " WIDTH ",223
+ " SPEED ",224
+ " BACK "225
+ };226
+227
+ char *speed_char[] = {228
+ " VERY FAST ",229
+ " FAST ",230
+ " AVERAGE ",231
+ " SLOW ",232
+ " VERY SLOW "233
+ };234
+235
+ uint8_t width = (uint8_t) strlen(setting_string[0]);236
+ uint8_t width_setting = (uint8_t) strlen(speed_char[0]);237
+238
+ uint8_t i, j;239
+240
+ printf("\033[2J\033[H");241
+242
+ 243
+ for (i = 0; i < SETTINGS_MENU; i++) {244
+245
+ printf("+");246
+ for (j = 0; j < width; j++)247
+ printf("-");248
+ printf("+");249
+250
+ if (i != BACK_S) {251
+ printf (" +");252
+253
+ for (j = 0; j < width_setting; j++)254
+ printf("-");255
+ printf("+");256
+ }257
+258
+ printf("\n|");259
+260
+ if (i == selected_setting)261
+ printf("\033[32m");262
+263
+ printf("%s\033[0m", setting_string[i]);264
+265
+ printf("|");266
+267
+ if (i != BACK_S) {268
+ printf(" |");269
+ if (selected_setting == i)270
+ printf("\033[32m");271
+272
+ switch (i) {273
+ case SPEED_S:274
+ printf("%s", speed_char[game_speed]);275
+ break;276
+277
+ case WIDTH_S:278
+ case HEIGHT_S:279
+ for (j = 0; j < (width_setting - 3) >> 1; j++)280
+ printf(" ");281
+ printf("%3d", (i == WIDTH_S) ? game_size.x : game_size.y);282
+ for (j = 0; j < width_setting - ((width_setting - 3) >> 1) - 3; j++)283
+ printf(" ");284
+285
+ }286
+ printf("\033[0m");287
+ printf("|");288
+ }289
+290
+ printf("\n");291
+292
+ if (i != HEIGHT_S) {293
+ 294
+ printf("+");295
+ for (j = 0; j < width; j++)296
+ printf("-");297
+ printf("+");298
+299
+ if (i != BACK_S) {300
+ printf (" +");301
+302
+ for (j = 0; j < width_setting; j++)303
+ printf("-");304
+ printf("+");305
+ }306
+307
+ if (i < SETTINGS_MENU - 1)308
+ printf("\n");309
+ }310
+ }311
+ fflush(stdout);312
+}313
+217314
bool draw_board(218315
selected game_size,219316
board_state **board,@@ -0,0 +1,199 @@
1
+#include "snake.h"2
+3
+4
+5
+uint8_t update_snake (selected game_size, snake *my_snake, direction shift) {6
+ 7
+ my_snake->position[0].directions[1] = my_snake->position[0].directions[0];8
+ my_snake->position[0].directions[0] = shift;9
+10
+ uint16_t i;11
+ for (i = my_snake->length; i > 0; i--)12
+ my_snake->position[i] = my_snake->position[i - 1];13
+14
+ switch (shift) {15
+ case UP:16
+ my_snake->position[0].coords.y =17
+ ((my_snake->position[0].coords.y + game_size.y - 1) % game_size.y);18
+ break;19
+ case DOWN:20
+ my_snake->position[0].coords.y = ((my_snake->position[0].coords.y + 1) % game_size.y);21
+ break;22
+ case LEFT:23
+ my_snake->position[0].coords.x =((my_snake->position[0].coords.x + game_size.x - 1) % game_size.x);24
+ break;25
+ case RIGHT:26
+ my_snake->position[0].coords.x = ((my_snake->position[0].coords.x + 1) % game_size.x);27
+ break;28
+ default:29
+ return 0;30
+ break;31
+ }32
+33
+ uint8_t head_y = my_snake->position[0].coords.y;34
+ uint8_t head_x = my_snake->position[0].coords.x;35
+36
+ for (i = 1; i < my_snake->length; i++) {37
+ int16_t x = my_snake->position[i].coords.x;38
+ int16_t y = my_snake->position[i].coords.y;39
+40
+ if (x == head_x && y == head_y)41
+ return 2;42
+ }43
+44
+45
+ return 1;46
+}47
+48
+void update_field(snake my_snake, selected game_size, board_state **board, bool init) {49
+50
+ uint8_t i, j;51
+52
+ board_state snake_bit[DIRECTIONS][DIRECTIONS] = {53
+ [UP][UP] = VERTICAL,54
+ [DOWN][DOWN] = VERTICAL,55
+ 56
+ [RIGHT][RIGHT] = HORIZONTAL,57
+ [LEFT][LEFT] = HORIZONTAL,58
+ 59
+ [DOWN][RIGHT] = BOTTOM_LEFT,60
+ [LEFT][UP] = BOTTOM_LEFT,61
+ 62
+ [UP][RIGHT] = TOP_LEFT,63
+ [LEFT][DOWN] = TOP_LEFT,64
+ 65
+ [DOWN][LEFT] = BOTTOM_RIGHT,66
+ [RIGHT][UP] = BOTTOM_RIGHT,67
+ 68
+ [UP][LEFT] = TOP_RIGHT,69
+ [RIGHT][DOWN] = TOP_RIGHT70
+ };71
+72
+73
+ if (init) {74
+ for (i = 0; i < game_size.y; i++) {75
+ for (j = 0; j < game_size.x; j++) {76
+ board[i][j] = AIR;77
+ }78
+ }79
+80
+ for (i = 1; i < my_snake.length; i++) {81
+ int16_t x = my_snake.position[i].coords.x;82
+ int16_t y = my_snake.position[i].coords.y;83
+ 84
+ if (x >= 0 && y >= 0) {85
+86
+ direction first = my_snake.position[i].directions[0];87
+ direction second = my_snake.position[i].directions[1];88
+89
+ board[y][x] = snake_bit[first][second];90
+ }91
+ }92
+ } else {93
+ 94
+ int16_t x = my_snake.position[my_snake.length].coords.x;95
+ int16_t y = my_snake.position[my_snake.length].coords.y;96
+ 97
+ if (x != -1 && y != -1)98
+ board[y][x] = AIR;99
+100
+ 101
+ x = my_snake.position[1].coords.x;102
+ y = my_snake.position[1].coords.y;103
+ 104
+ direction first = my_snake.position[1].directions[0];105
+ direction second = my_snake.position[1].directions[1];106
+ 107
+ if (x != -1 && y != -1)108
+ board[y][x] = snake_bit[first][second];109
+ }110
+111
+112
+ int16_t x = my_snake.position[0].coords.x;113
+ int16_t y = my_snake.position[0].coords.y;114
+115
+ if (my_snake.position[0].directions[0] == RIGHT) {116
+ board[y][x] = HEAD_1;117
+ } else {118
+ board[y][x] = HEAD_2;119
+ }120
+121
+ for (i = 0; i < game_size.y; i++) {122
+ for (j = 0; j < game_size.x; j++) {123
+ if (board[i][j] == FRUIT)124
+ return;125
+ }126
+ } 127
+ 128
+ while (true) {129
+ x = rand() % game_size.x;130
+ y = rand() % game_size.y;131
+132
+ if (board[y][x] == AIR) {133
+ board[y][x] = FRUIT;134
+ return;135
+ }136
+ } 137
+}138
+139
+direction read_direction(char *string, bool wasd_enabled) {140
+141
+ if (string[0] == '\033') {142
+143
+ if (strlen(string) == 1)144
+ return ESCAPE;145
+146
+ if (strlen(string) >= 3) {147
+148
+ if (string[1] == '[') {149
+150
+ switch (string[2]) {151
+ case RIGHT_CODE:152
+ return RIGHT;153
+ break;154
+ case LEFT_CODE:155
+ return LEFT;156
+ break;157
+ case UP_CODE:158
+ return UP;159
+ break;160
+ case DOWN_CODE:161
+ return DOWN;162
+ break;163
+ }164
+ }165
+ }166
+ }167
+168
+ if (wasd_enabled) {169
+ switch (toupper(string[0])) {170
+ case 'A':171
+ return LEFT;172
+ break;173
+ case 'D':174
+ return RIGHT;175
+ break;176
+ case 'W':177
+ return UP;178
+ break;179
+ case 'S':180
+ return DOWN;181
+ break;182
+ }183
+ }184
+185
+ switch (toupper(string[0])) {186
+ case 'Q':187
+ return Q_KEY;188
+ break;189
+ case 'P':190
+ return P_KEY;191
+ break;192
+ case '\n':193
+ return ENTER;194
+ break;195
+ default:196
+ return DIRECTIONS;197
+ break;198
+ }199
+}@@ -1,5 +1,6 @@
11
#include "snake.h"22
3
+34
int main(int argc, char *args[]) {45
56
/* command argument handling */@@ -14,7 +15,7 @@ int main(int argc, char *args[]) {
1415
1516
printf("This is a small TUI snake game.\n\n");1617
printf("Controls: Arrow keys/WASD to control the snake.\n");17
- printf(" P to pause.\n");18
+ printf(" P/Enter to pause.\n");1819
printf(" Q to quit.\n");1920
2021
return 0;@@ -58,6 +59,11 @@ int main(int argc, char *args[]) {
5859
if (!start_animation())5960
goto end_of_program;6061
62
+ /* game settings */63
+64
+ speed game_speed = AVERAGE;65
+ selected game_size = {WIDTH, HEIGHT};66
+6167
/* Menu */6268
6369
bool menu = true;@@ -67,16 +73,9 @@ int main(int argc, char *args[]) {
6773
main_menu selected_menu = NEW_GAME;6874
settings selected_setting = HEIGHT_S;6975
70
- speed game_speed = AVERAGE;71
-72
- selected game_size = {WIDTH, HEIGHT};73
-7476
while (menu) {7577
76
- bool pressed_enter = false;77
- bool changed_selected = false;78
-79
- direction arrow = NO_DIRECTION;78
+ direction pressed_key = DIRECTIONS;8079
8180
char string[STRING_LEN] = {};8281
ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);@@ -87,45 +86,43 @@ int main(int argc, char *args[]) {
8786
if (current_size.ws_col != init_size.ws_col ||8887
current_size.ws_row != init_size.ws_row) {8988
init_size = current_size;90
- changed_selected = true;89
+ change_menu = true;9190
}9291
9392
9493
if (length_string > 0) {9594
string[length_string] = '\0';9695
97
- arrow = read_direction(string, false);98
-99
- if (string[0] == '\n')100
- pressed_enter = true;101
- 102
- if (toupper(string[0]) == 'Q')103
- goto end_of_program;96
+ pressed_key = read_direction(string, false);10497
}10598
99
+ if (pressed_key == Q_KEY)100
+ goto end_of_program;101
+102
+106103
if (!setting) {107104
108105
if (!too_small) {109
- switch (arrow) {106
+ switch (pressed_key) {110107
case UP:111108
selected_menu = (selected_menu + (MAIN_MENU - 1)) % MAIN_MENU;112
- changed_selected = true;109
+ change_menu = true;113110
break;114111
case DOWN:115112
selected_menu = (selected_menu + 1) % MAIN_MENU;116
- changed_selected = true;113
+ change_menu = true;117114
break;118115
default:119116
break;120117
}121118
}122119
123
- if (changed_selected || change_menu) {120
+ if (change_menu) {124121
too_small = draw_menu(selected_menu, init_size);125122
change_menu = false;126123
}127124
128
- if (pressed_enter && !too_small) {125
+ if (pressed_key == ENTER && !too_small) {129126
switch (selected_menu) {130127
case NEW_GAME:131128
menu = false;@@ -135,120 +132,24 @@ int main(int argc, char *args[]) {
135132
change_menu = true;136133
break;137134
default:138
- tcsetattr(STDIN_FILENO, TCSANOW, &old);139
- printf("\033[?25h\n\n");140
-141
-142
- printf("\033[32m");143
- return 0;135
+ goto end_of_program; 144136
break;145137
}146138
}147
- } else {148
-149
- char *setting_string[] = {150
- " HEIGHT ",151
- " WIDTH ",152
- " SPEED ",153
- " BACK "154
- };155
-156
- char *speed_char[] = {157
- " VERY FAST ",158
- " FAST ",159
- " AVERAGE ",160
- " SLOW ",161
- " VERY SLOW "162
- };163
-164
- uint8_t width = (uint8_t) strlen(setting_string[0]);165
- uint8_t width_setting = (uint8_t) strlen(speed_char[0]);166
-167
-168
- if (changed_selected || change_menu) {169
- 170
- uint8_t i, j;171
-172
- printf("\033[2J\033[H");173
- 174
- 175
- for (i = 0; i < SETTINGS_MENU; i++) {176
- 177
- printf("+");178
- for (j = 0; j < width; j++)179
- printf("-");180
- printf("+");181
-182
- if (i != BACK_S) {183
- printf (" +");184
-185
- for (j = 0; j < width_setting; j++)186
- printf("-");187
- printf("+");188
- }189
-190
- printf("\n|");191
-192
- if (i == selected_setting)193
- printf("\033[32m");194
-195
- printf("%s\033[0m", setting_string[i]);196139
197
- printf("|");198
-199
- if (i != BACK_S) {200
- printf(" |");201
- if (selected_setting == i)202
- printf("\033[32m");203
-204
- switch (i) {205
- case SPEED_S:206
- printf("%s", speed_char[game_speed]);207
- break;208
-209
- case WIDTH_S:210
- case HEIGHT_S:211
- for (j = 0; j < (width_setting - 3) >> 1; j++)212
- printf(" ");213
- printf("%3d", (i == WIDTH_S) ? game_size.x : game_size.y);214
- for (j = 0; j < width_setting - ((width_setting - 3) >> 1) - 3; j++)215
- printf(" ");216
-217
- }218
- printf("\033[0m");219
- printf("|");220
- }221
-222
- printf("\n");223
-224
- if (i != HEIGHT_S) {225
- 226
- printf("+");227
- for (j = 0; j < width; j++)228
- printf("-");229
- printf("+");230
-231
- if (i != BACK_S) {232
- printf (" +");233
-234
- for (j = 0; j < width_setting; j++)235
- printf("-");236
- printf("+");237
- }140
+ } else {238141
239
- if (i < SETTINGS_MENU - 1)240
- printf("\n");241
- }242
- }243
- fflush(stdout);142
+ if (change_menu) {143
+ 144
+ draw_settings(selected_setting, game_speed, game_size);244145
245146
change_menu = false;246147
}247148
248149
249
- if (arrow != NO_DIRECTION) {150
+ if (pressed_key < DIRECTIONS) {250151
251
- switch (arrow) {152
+ switch (pressed_key) {252153
case UP:253154
selected_setting = (selected_setting + (SETTINGS_MENU - 1)) % SETTINGS_MENU;254155
break;@@ -298,7 +199,7 @@ int main(int argc, char *args[]) {
298199
299200
}300201
301
- if (pressed_enter) {202
+ if (pressed_key == ENTER) {302203
switch (selected_setting) {303204
case BACK_S:304205
setting = false;@@ -308,12 +209,12 @@ int main(int argc, char *args[]) {
308209
}309210
change_menu = true;310211
}311
- pressed_enter = false;312212
}313213
314214
nanosleep(&mili100, NULL);315215
}316216
217
+317218
/* initialisation of the game */318219
319220
snake my_snake;@@ -352,18 +253,16 @@ int main(int argc, char *args[]) {
352253
353254
update_field(my_snake, game_size, board, true);354255
355
- selected fruit = new_fruit(game_size, board);356
-357256
bool paused = false;358257
359258
if ((too_small = draw_board(game_size, board, current_size, true, false)))360259
paused = true;361260
362261
363
- bool run = true;364
-365262
struct timespec milispeed = {0, 1000000 * (SPEED + game_speed * 25)};366263
264
+ bool run = true;265
+367266
/* start of the actual game loop */368267
369268
while (run) {@@ -413,7 +312,7 @@ int main(int argc, char *args[]) {
413312
414313
if (!paused) {415314
416
- if (tmp_direction != NO_DIRECTION &&315
+ if (tmp_direction < DIRECTIONS &&417316
((shift + (DIRECTIONS >> 1)) % DIRECTIONS) != tmp_direction) {418317
shift = tmp_direction;419318
}@@ -423,18 +322,19 @@ int main(int argc, char *args[]) {
423322
uint8_t head_y = my_snake.position[0].coords.y;424323
uint8_t head_x = my_snake.position[0].coords.x;425324
426
- 427
- update_field(my_snake, game_size, board, false);428
-429
- if (head_y == fruit.y && head_x == fruit.x) {325
+ if (board[head_y][head_x] == FRUIT) {430326
my_snake.length++;431327
snake_part *tmp = realloc(my_snake.position, (my_snake.length + 1) * sizeof(snake_part));432328
if (tmp == NULL)433329
goto end_free;434330
my_snake.position = tmp;435
- fruit = new_fruit(game_size, board);331
+332
+ my_snake.position[my_snake.length].coords.y = -1;333
+ my_snake.position[my_snake.length].coords.x = -1;436334
}437335
336
+ update_field(my_snake, game_size, board, false);337
+438338
draw_board(game_size, board, current_size, false, false);439339
440340
switch (update) {@@ -470,187 +370,3 @@ end_of_program:
470370
printf("\033[32m");471371
return 0;472372
}473
-474
-475
-uint8_t update_snake (selected game_size, snake *my_snake, direction shift) {476
- 477
- my_snake->position[0].directions[1] = my_snake->position[0].directions[0];478
- my_snake->position[0].directions[0] = shift;479
-480
- uint16_t i;481
- for (i = my_snake->length; i > 0; i--)482
- my_snake->position[i] = my_snake->position[i - 1];483
-484
- switch (shift) {485
- case UP:486
- my_snake->position[0].coords.y =487
- ((my_snake->position[0].coords.y + game_size.y - 1) % game_size.y);488
- break;489
- case DOWN:490
- my_snake->position[0].coords.y = ((my_snake->position[0].coords.y + 1) % game_size.y);491
- break;492
- case LEFT:493
- my_snake->position[0].coords.x =((my_snake->position[0].coords.x + game_size.x - 1) % game_size.x);494
- break;495
- case RIGHT:496
- my_snake->position[0].coords.x = ((my_snake->position[0].coords.x + 1) % game_size.x);497
- break;498
- default:499
- return 0;500
- break;501
- }502
-503
- uint8_t head_y = my_snake->position[0].coords.y;504
- uint8_t head_x = my_snake->position[0].coords.x;505
-506
- for (i = 1; i < my_snake->length; i++) {507
- int16_t x = my_snake->position[i].coords.x;508
- int16_t y = my_snake->position[i].coords.y;509
-510
- if (x == head_x && y == head_y)511
- return 2;512
- }513
-514
-515
- return 1;516
-}517
-518
-void update_field(snake my_snake, selected game_size, board_state **board, bool init) {519
-520
- uint8_t i, j;521
-522
- board_state snake_bit[DIRECTIONS][DIRECTIONS] = {523
- [UP][UP] = VERTICAL,524
- [DOWN][DOWN] = VERTICAL,525
- 526
- [RIGHT][RIGHT] = HORIZONTAL,527
- [LEFT][LEFT] = HORIZONTAL,528
- 529
- [DOWN][RIGHT] = BOTTOM_LEFT,530
- [LEFT][UP] = BOTTOM_LEFT,531
- 532
- [UP][RIGHT] = TOP_LEFT,533
- [LEFT][DOWN] = TOP_LEFT,534
- 535
- [DOWN][LEFT] = BOTTOM_RIGHT,536
- [RIGHT][UP] = BOTTOM_RIGHT,537
- 538
- [UP][LEFT] = TOP_RIGHT,539
- [RIGHT][DOWN] = TOP_RIGHT540
- };541
-542
-543
- if (init) {544
- for (i = 0; i < game_size.y; i++) {545
- for (j = 0; j < game_size.x; j++) {546
- board[i][j] = AIR;547
- }548
- }549
-550
- for (i = 1; i < my_snake.length; i++) {551
- int16_t x = my_snake.position[i].coords.x;552
- int16_t y = my_snake.position[i].coords.y;553
- 554
- if (x >= 0 && y >= 0) {555
-556
- direction first = my_snake.position[i].directions[0];557
- direction second = my_snake.position[i].directions[1];558
-559
- board[y][x] = snake_bit[first][second];560
- }561
- }562
- } else {563
- 564
- int16_t x = my_snake.position[my_snake.length].coords.x;565
- int16_t y = my_snake.position[my_snake.length].coords.y;566
- 567
- if (x != -1 && y != -1)568
- board[y][x] = AIR;569
-570
- 571
- x = my_snake.position[1].coords.x;572
- y = my_snake.position[1].coords.y;573
- 574
- direction first = my_snake.position[1].directions[0];575
- direction second = my_snake.position[1].directions[1];576
- 577
- if (x != -1 && y != -1)578
- board[y][x] = snake_bit[first][second];579
- }580
-581
-582
- int16_t x = my_snake.position[0].coords.x;583
- int16_t y = my_snake.position[0].coords.y;584
-585
- if (my_snake.position[0].directions[0] == RIGHT) {586
- board[y][x] = HEAD_1;587
- } else {588
- board[y][x] = HEAD_2;589
- }590
-}591
-592
-selected new_fruit(selected game_size, board_state **board) {593
- bool found_space = false;594
-595
- uint8_t x;596
- uint8_t y;597
-598
- while (!found_space) {599
- x = rand() % game_size.x;600
- y = rand() % game_size.y;601
-602
- if (board[y][x] == AIR)603
- found_space = true;604
- }605
-606
- selected fruit = {x, y};607
-608
- board[fruit.y][fruit.x] = FRUIT;609
-610
- return fruit;611
-}612
-613
-614
-direction read_direction(char *string, bool wasd_enabled) {615
-616
- if (strlen(string) >= 3) {617
- if (string[0] == '\033') {618
- if (string[1] == '[') {619
-620
- switch (string[2]) {621
- case RIGHT_CODE:622
- return RIGHT;623
- break;624
- case LEFT_CODE:625
- return LEFT;626
- break;627
- case UP_CODE:628
- return UP;629
- break;630
- case DOWN_CODE:631
- return DOWN;632
- break;633
- }634
- }635
- }636
- }637
-638
- if (wasd_enabled) {639
- switch (toupper(string[0])) {640
- case 'A':641
- return LEFT;642
- break;643
- case 'D':644
- return RIGHT;645
- break;646
- case 'W':647
- return UP;648
- break;649
- case 'S':650
- return DOWN;651
- break;652
- }653
- }654
-655
- return NO_DIRECTION;656
-}@@ -44,12 +44,8 @@ typedef enum {HEIGHT_S, WIDTH_S, SPEED_S, BACK_S, SETTINGS_MENU} settings;
4444
typedef enum {V_FAST, FAST, AVERAGE, SLOW, V_SLOW, SPEED_L} speed;4545
4646
typedef enum {47
- NO_DIRECTION = -1,48
- UP = 0,49
- LEFT = 1,50
- DOWN = 2,51
- RIGHT = 3,52
- DIRECTIONS = 447
+ UP, LEFT, DOWN, RIGHT, DIRECTIONS,48
+ ENTER, P_KEY, Q_KEY, ESCAPE5349
} direction;5450
5551
typedef enum {@@ -79,9 +75,13 @@ void update_field(snake my_snake, selected game_size,
7975
selected new_fruit(selected game_size, board_state **board);8076
8177
bool start_animation();78
+8279
bool draw_menu(main_menu menu, struct winsize window_size);80
+void draw_settings(settings selected_setting, speed game_speed, selected game_size);81
+8382
bool draw_board(selected game_size, board_state **board,8483
struct winsize window_size, bool redraw, bool paused);84
+8585
void draw_pause(struct winsize window_size, 8686
uint8_t field_width,uint8_t field_height);