jag

snake_c

public

Small snake game in C

Star0
Watch0
48f97764a77afaa8ee9df7529a2931676ef3f61a
2
ssh://git@git.jaglekon.de:30/jag/snake_c.git
snake.cC982 lines23.5 KB
1
#define _POSIX_C_SOURCE 199309L
2
3
#include <termios.h>
4
#include <unistd.h>
5
6
#include <stdbool.h>
7
#include <stdint.h>
8
#include <stdio.h>
9
#include <stdlib.h>
10
11
#include <sys/ioctl.h>
12
13
#include <time.h>
14
15
#include <ctype.h>
16
#include <string.h>
17
18
#define RIGHT_CODE 'C'
19
#define LEFT_CODE 'D'
20
#define UP_CODE 'A'
21
#define DOWN_CODE 'B'
22
23
#define WIDTH 20
24
#define HEIGHT 20
25
26
#define STRING_LEN 20
27
28
#define SPEED 50 /* in ms */
29
30
#define MAX_LENGTH 1000
31
#define STARTING_LENGTH 2
32
33
typedef enum { NEW_GAME, SETTINGS, EXIT, MAIN_MENU } main_menu;
34
35
typedef enum {HEIGHT_S, WIDTH_S, SPEED_S, BACK_S, SETTINGS_MENU} settings;
36
37
typedef enum {V_FAST, FAST, AVERAGE, SLOW, V_SLOW, SPEED_L} speed;
38
39
typedef enum {
40
  NO_DIRECTION = -1,
41
  UP = 0,
42
  LEFT = 1,
43
  DOWN = 2,
44
  RIGHT = 3,
45
  DIRECTIONS = 4
46
} direction;
47
48
typedef struct selected {
49
  int16_t x;
50
  int16_t y;
51
} selected;
52
53
typedef struct snake_part {
54
  selected coords;
55
  direction directions[2];
56
} snake_part;
57
58
typedef struct snake {
59
  snake_part position[MAX_LENGTH];
60
  uint16_t length;
61
} snake;
62
63
64
uint8_t update_snake (snake *my_snake, direction shift);
65
void update_field(snake my_snake, char board[HEIGHT][WIDTH][STRING_LEN],
66
                  selected fruit);
67
selected new_fruit(char board[HEIGHT][WIDTH][STRING_LEN]);
68
69
bool start_animation();
70
bool draw_menu(main_menu menu, struct winsize window_size);
71
bool draw_board(char board[HEIGHT][WIDTH][STRING_LEN],
72
  struct winsize window_size, bool redraw, bool paused);
73
void draw_pause(struct winsize window_size, 
74
  uint8_t field_width,uint8_t field_height);
75
76
direction read_direction(char *string, bool wasd_enabled);
77
78
int main(int argc, char *args[]) {
79
80
  /* command argument handling */
81
82
  if (argc > 1) {
83
    if (argc > 2) {
84
      printf("Too many parameters\n");
85
      goto help_info;
86
    }
87
88
    if (!strcmp(args[1], "-h") || !strcmp(args[1], "--help")) {
89
90
      printf("This is a small TUI snake game.\n\n");
91
      printf("Controls: Arrow keys/WASD to control the snake.\n");
92
      printf("          P to pause.\n");
93
      printf("          Q to quit.\n");
94
95
      return 0;
96
    }
97
98
    printf("Argument \"%s\" not recognized\n", args[1]);
99
100
  help_info:
101
    printf("Try \"-h\" or \"--help\" for more information\n");
102
    return 1;
103
  }
104
105
  /* Setting up terminal with termios */
106
107
  struct termios old, new;
108
109
  tcgetattr(STDIN_FILENO, &old);
110
111
  new = old;
112
113
  new.c_lflag &= ~(ECHO | ICANON);
114
  new.c_cc[VMIN] = 0;
115
  new.c_cc[VTIME] = 0;
116
117
  tcsetattr(STDIN_FILENO, TCSANOW, &new);
118
119
  srand(time(NULL));
120
121
  printf("\033[?25l");
122
123
  struct winsize init_size, current_size;
124
  ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);
125
  current_size = init_size;
126
  bool too_small = false;
127
128
129
  struct timespec mili100 = {0, 1000000 * 100};
130
131
132
  /* Animation at the start */
133
  if (!start_animation())
134
    goto end_of_program;
135
136
  /* Menu */
137
138
  bool menu = true;
139
  bool setting = false;
140
  bool change_menu = true;
141
142
  main_menu selected_menu = NEW_GAME;
143
  settings selected_setting = HEIGHT_S;
144
  settings current_setting = SETTINGS_MENU;
145
146
147
  speed game_speed = AVERAGE;
148
149
  while (menu) {
150
151
    bool pressed_enter = false;
152
    bool changed_selected = false;
153
154
    direction arrow = NO_DIRECTION;
155
156
    char string[1000] = {};
157
    ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
158
159
160
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
161
162
    if (current_size.ws_col != init_size.ws_col ||
163
        current_size.ws_row != init_size.ws_row) {
164
      init_size = current_size;
165
      changed_selected = true;
166
    }
167
168
169
    if (length_string > 0) {
170
      string[length_string] = '\0';
171
172
      arrow = read_direction(string, false);
173
174
      if (string[0] == '\n')
175
        pressed_enter = true;
176
      
177
      if (toupper(string[0]) == 'Q')
178
        goto end_of_program;
179
    }
180
181
    if (!setting) {
182
      
183
      if (!too_small) {
184
        switch (arrow) {
185
          case UP:
186
            selected_menu = (selected_menu + (MAIN_MENU - 1)) % MAIN_MENU;
187
            changed_selected = true;
188
            break;
189
          case DOWN:
190
            selected_menu = (selected_menu + 1) % MAIN_MENU;
191
            changed_selected = true;
192
            break;
193
          default:
194
            break;
195
        }
196
      }
197
198
      if (changed_selected || change_menu) {
199
        too_small = draw_menu(selected_menu, init_size);
200
        change_menu = false;
201
      }
202
203
      if (pressed_enter && !too_small) {
204
        switch (selected_menu) {
205
        case NEW_GAME:
206
          menu = false;
207
          break;
208
        case SETTINGS:
209
          setting = true;
210
          change_menu = true;
211
          break;
212
        default:
213
          goto end_of_program;
214
          break;
215
        }
216
      }
217
    } else {
218
219
      char *setting_string[] = {
220
        "  HEIGHT  ",
221
        "  WIDTH   ",
222
        "  SPEED   ",
223
        "   BACK   "
224
      };
225
226
      char *speed_char[] = {
227
        "  VERY FAST  ",
228
        "    FAST     ",
229
        "   AVERAGE   ",
230
        "    SLOW     ",
231
        "  VERY SLOW  "
232
      };
233
234
      uint8_t width = (uint8_t) strlen(setting_string[0]);
235
      uint8_t width_setting = (uint8_t) strlen(speed_char[0]);
236
237
238
      if (changed_selected || change_menu) {
239
              
240
        uint8_t i, j;
241
242
        printf("\033[2J\033[H");
243
      
244
         
245
        for (i = 0; i < SETTINGS_MENU; i++) {
246
      
247
          printf("+");
248
          for (j = 0; j < width; j++)
249
            printf("-");
250
          printf("+");
251
252
          if (i != BACK_S) {
253
            printf ("  +");
254
255
            for (j = 0; j < width_setting; j++)
256
              printf("-");
257
            printf("+");
258
          }
259
260
          printf("\n|");
261
262
          if (i == selected_setting)
263
            printf("\033[32m");
264
265
          printf("%s\033[0m", setting_string[i]);
266
267
          printf("|");
268
269
          if (i != BACK_S) {
270
            printf("  |");
271
            if (current_setting == i)
272
              printf("\033[32m");
273
274
            if (i == SPEED_S) {
275
              printf("%s", speed_char[game_speed]);
276
            } else {
277
              for (j = 0; j < width_setting; j++)
278
                printf(" ");
279
            }
280
            printf("\033[0m");
281
            printf("|");
282
          }
283
284
          printf("\n");
285
286
          if (i != HEIGHT_S) {
287
          
288
            printf("+");
289
            for (j = 0; j < width; j++)
290
              printf("-");
291
            printf("+");
292
293
            if (i != BACK_S) {
294
              printf ("  +");
295
296
              for (j = 0; j < width_setting; j++)
297
                printf("-");
298
              printf("+");
299
            }
300
301
            if (i < SETTINGS_MENU - 1)
302
              printf("\n");
303
          }
304
        }
305
        fflush(stdout);
306
307
        change_menu = false;
308
      }
309
310
311
      switch (arrow) {
312
        case UP:
313
          switch (current_setting) {
314
            case SETTINGS_MENU:
315
              selected_setting = (selected_setting + (SETTINGS_MENU - 1)) % SETTINGS_MENU;
316
              break;
317
            case SPEED_S:
318
              if (game_speed > V_FAST)
319
                game_speed--;
320
              break;
321
            default:
322
              break;
323
          }
324
          change_menu = true;
325
          break;
326
        case DOWN:
327
          switch (current_setting) {
328
            case SETTINGS_MENU:
329
              selected_setting = (selected_setting + 1) % SETTINGS_MENU;
330
              break;
331
            case SPEED_S:
332
              if (game_speed < V_SLOW)
333
                game_speed++;
334
              break;
335
            default:
336
              break;
337
          }
338
          change_menu = true;
339
          break;
340
        default:
341
          break;
342
      }
343
      
344
      if (pressed_enter) {
345
        if (current_setting == SETTINGS_MENU) {
346
          switch (selected_setting) {
347
            case BACK_S:
348
              setting = false;
349
              break;
350
            case SPEED_S:
351
              current_setting = SPEED_S;
352
              break;
353
            default:
354
              break;
355
          }
356
        } else {
357
          current_setting = SETTINGS_MENU;
358
        }
359
        change_menu = true;
360
      }
361
        pressed_enter = false;
362
      }
363
364
    nanosleep(&mili100, NULL);
365
  }
366
367
  /* initialisation of the game */
368
369
  snake my_snake = {};
370
  my_snake.length = STARTING_LENGTH;
371
372
  direction shift = RIGHT;
373
  direction tmp_direction = shift;
374
375
  {
376
    uint16_t i;
377
378
    my_snake.position[0].coords.y = HEIGHT >> 1;
379
    my_snake.position[0].coords.x = WIDTH >> 1;
380
    my_snake.position[0].directions[0] = shift;
381
    my_snake.position[0].directions[1] = shift;
382
    for (i = 1; i < my_snake.length; i++) {
383
      my_snake.position[i].coords.y = -1;
384
      my_snake.position[i].coords.x = -1;
385
    }
386
  }
387
388
  selected fruit = {-1, -1};
389
  char board[HEIGHT][WIDTH][STRING_LEN] = {};
390
391
  update_field(my_snake, board, fruit);
392
393
  fruit = new_fruit(board);
394
  update_field(my_snake, board, fruit);
395
396
  bool paused = false;
397
398
  if ((too_small = draw_board(board, current_size, true, false)))
399
    paused = true;
400
401
402
  bool run = true;
403
404
  struct timespec milispeed = {0, 1000000 * (SPEED + game_speed * 25)};
405
406
  /* start of the actual game loop */
407
408
  while (run) {
409
410
    /* input handling  */
411
412
    
413
      char string[1000] = {};
414
      ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
415
416
      if (length_string > 0) {
417
        string[length_string] = '\0';
418
419
        if (!too_small) {
420
          if (toupper(string[0]) == 'P' || string[0] == '\n') {
421
            paused = !paused;
422
            if (paused)
423
              draw_board(board, current_size, true, paused);
424
          }
425
426
          if (!paused)
427
            tmp_direction = read_direction(string, true);
428
        }
429
430
        if (toupper(string[0]) == 'Q')
431
          run = false;
432
      }
433
    
434
435
    /* game logic */
436
437
    /*
438
    struct timeval current;
439
    gettimeofday(&current, NULL);*/
440
441
    if (/*time_diff >= SPEED * 1000*/ true) {
442
443
      ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
444
445
      if (current_size.ws_col != init_size.ws_col ||
446
          current_size.ws_row != init_size.ws_row) {
447
        init_size = current_size;
448
449
        paused = true;
450
        too_small = draw_board(board, current_size, true, paused);
451
      }
452
453
      if (!paused) {
454
455
        if (tmp_direction != NO_DIRECTION &&
456
            ((shift + (DIRECTIONS >> 1)) % DIRECTIONS) != tmp_direction) {
457
          shift = tmp_direction;
458
        }
459
460
        uint8_t update = update_snake(&my_snake, shift);
461
462
        switch (update) {
463
            case 0: 
464
              printf("\nERROR\n");
465
              goto end_of_program;
466
              break;
467
            case 2:
468
              printf("\nHAHA LOST, LOSER");
469
              goto end_of_program;
470
              break;
471
            default:
472
              break;
473
        }
474
475
        update_field(my_snake, board, fruit);
476
477
        
478
        uint8_t head_y = my_snake.position[0].coords.y;
479
        uint8_t head_x = my_snake.position[0].coords.x;
480
481
        if (head_y == fruit.y && head_x == fruit.x) {
482
          my_snake.length++;
483
          fruit = new_fruit(board);
484
        }
485
486
        update_field(my_snake, board, fruit);
487
        draw_board(board, current_size, false, false);
488
      }
489
    }
490
    nanosleep(&milispeed, NULL);
491
  }
492
493
end_of_program:
494
495
  tcsetattr(STDIN_FILENO, TCSANOW, &old);
496
  printf("\033[?25h\n\n");
497
498
499
  printf("\033[32m");
500
  return 0;
501
}
502
503
504
uint8_t update_snake (snake *my_snake, direction shift) {
505
  
506
  my_snake->position[0].directions[1] = my_snake->position[0].directions[0];
507
  my_snake->position[0].directions[0] = shift;
508
509
  uint16_t i;
510
  for (i = my_snake->length; i > 0; i--)
511
    my_snake->position[i] = my_snake->position[i - 1];
512
513
  switch (shift) {
514
  case UP:
515
    my_snake->position[0].coords.y =
516
        ((my_snake->position[0].coords.y + HEIGHT - 1) % HEIGHT);
517
    break;
518
  case DOWN:
519
    my_snake->position[0].coords.y = ((my_snake->position[0].coords.y + 1) % HEIGHT);
520
    break;
521
  case LEFT:
522
    my_snake->position[0].coords.x =((my_snake->position[0].coords.x + WIDTH - 1) % WIDTH);
523
    break;
524
  case RIGHT:
525
    my_snake->position[0].coords.x = ((my_snake->position[0].coords.x + 1) % WIDTH);
526
    break;
527
  default:
528
    return 0;
529
    break;
530
  }
531
532
  uint8_t head_y = my_snake->position[0].coords.y;
533
  uint8_t head_x = my_snake->position[0].coords.x;
534
535
  for (i = 1; i < my_snake->length; i++) {
536
    int16_t x = my_snake->position[i].coords.x;
537
    int16_t y = my_snake->position[i].coords.y;
538
539
    if (x == head_x && y == head_y)
540
      return 2;
541
  }
542
543
544
  return 1;
545
}
546
547
void update_field(snake my_snake, char board[HEIGHT][WIDTH][STRING_LEN],
548
                  selected fruit) {
549
550
  uint8_t i, j;
551
552
  for (i = 0; i < HEIGHT; i++) {
553
    for (j = 0; j < WIDTH; j++) {
554
      strcpy(board[i][j], "  ");
555
    }
556
  }
557
558
  for (i = 0; i < my_snake.length; i++) {
559
    int16_t x = my_snake.position[i].coords.x;
560
    int16_t y = my_snake.position[i].coords.y;
561
    if (x >= 0 && y >= 0) {
562
      if (!i) {
563
        if (my_snake.position[i].directions[0] == RIGHT) {
564
          strcpy(board[y][x], "\033[33m═\033[31mX\033[0m");
565
        } else {
566
          strcpy(board[y][x], "\033[31m X\033[0m");
567
        }
568
      } else {
569
570
        char *snake_bit[DIRECTIONS][DIRECTIONS] = {
571
          [UP][UP] = " ║",
572
          [DOWN][DOWN] = " ║",
573
          
574
          [RIGHT][RIGHT] = "══",
575
          [LEFT][LEFT] = "══",
576
          
577
          [DOWN][RIGHT] = "═╗",
578
          [LEFT][UP] = "═╗",
579
          
580
          [UP][RIGHT] = "═╝",
581
          [LEFT][DOWN] = "═╝",
582
          
583
          [DOWN][LEFT] = " ╔",
584
          [RIGHT][UP] = " ╔",
585
          
586
          [UP][LEFT] = " ╚",
587
          [RIGHT][DOWN] = " ╚"
588
        };
589
590
        direction first = my_snake.position[i].directions[0];
591
        direction second = my_snake.position[i].directions[1];
592
593
        snprintf(board[y][x], sizeof(board[y][x]),"\033[33m%s\033[0m",snake_bit[first][second]);
594
      }
595
    }
596
  }
597
598
  if (fruit.x >= 0 && fruit.y >= 0)
599
    strcpy(board[fruit.y][fruit.x], "\033[32m O\033[0m");
600
}
601
602
selected new_fruit(char board[HEIGHT][WIDTH][STRING_LEN]) {
603
  bool found_space = false;
604
605
  uint8_t x;
606
  uint8_t y;
607
608
  while (!found_space) {
609
    x = rand() % WIDTH;
610
    y = rand() % HEIGHT;
611
612
    if (!strcmp(board[y][x], "  "))
613
      found_space = true;
614
  }
615
616
  selected fruit = {x, y};
617
618
  return fruit;
619
}
620
621
bool start_animation() {  
622
623
  struct winsize current_size;
624
  ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
625
626
  struct timespec mili10 = {0, 1000000 * 10};
627
628
  char *snake[3][7] = {
629
    {
630
      "           ________  ",
631
      "         _/ _      \\_",
632
      "________/  (_)       ",
633
      "\\       \\            ",
634
      "/_______/   _        ",
635
      "        \\_ (_)      _",
636
      "          \\________/ "
637
    },
638
    {
639
      "        ",
640
      "________",
641
      "\\      /",
642
      " \\    / ",
643
      "  \\  /  ",
644
      "___\\/___",
645
      "        ",
646
    },
647
    {
648
      "         ",
649
      "__        ",
650
      "  \\_____ ",
651
      "        \\",
652
      "   _____/",
653
      "__/      ",
654
      "         "
655
    }
656
  };
657
658
  uint8_t width_snake[3] = {};
659
660
  {
661
    uint8_t i;
662
663
    for (i = 0; i < 3; i++)
664
      width_snake[i] = (uint8_t) strlen(snake[i][0]);
665
  }
666
667
  uint8_t middle;
668
669
  uint8_t height = 7;
670
  uint8_t width = width_snake[0] + width_snake[1] + width_snake[2];
671
672
  for (middle = 1; width < current_size.ws_col; middle++)
673
    width += width_snake[1];
674
675
  int32_t end = current_size.ws_col + width;
676
677
678
679
  while (end >= 0) {
680
681
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
682
683
    int32_t start = end - width; 
684
685
    int32_t i, j;
686
 
687
    char string[1000] = {};
688
    ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
689
690
     if (length_string > 0) {
691
    
692
      string[length_string] = '\0';
693
694
695
    if (toupper(string[0]) == 'Q')
696
      return false;
697
698
    if (string[0] == '\n')
699
      break;
700
    }
701
702
    printf("\033[2J\033[H");
703
704
    for (j = 0; j < ((height > current_size.ws_row) ? current_size.ws_row : height) ; j++) {
705
706
    for (i = 0; i < current_size.ws_col; i++) {
707
      int32_t index = i - start;
708
709
      if (index >= 0 && index < width) {
710
        if (index < width_snake[0]) {
711
          printf("%c", snake[0][j][index]);
712
        } else if (index < width_snake[0] + width_snake[1] * middle) {
713
          printf("%c", snake[1][j][(index - width_snake[0]) % width_snake[1]]);
714
        } else {
715
          printf("%c", snake[2][j][index - (width_snake[0] + width_snake[1] * middle)]);
716
        }
717
      } else {
718
        printf(" ");
719
      }
720
    }      
721
    
722
      printf("\n");
723
724
    }
725
    fflush(stdout);
726
    
727
    end--;
728
729
    nanosleep(&mili10, NULL);
730
  }
731
  return true;
732
}
733
734
bool draw_menu(main_menu menu, struct winsize window_size) {
735
736
  char *menu_string[] = {"NEW GAME", "SETTINGS", "EXIT"};
737
738
  const uint8_t width_small = 8;
739
  const uint8_t height_small = 5;
740
741
  char *menu_string_big[][6] = {
742
      {"███╗   ██╗███████╗██╗    ██╗     ██████╗  █████╗ ███╗   ███╗███████╗",
743
       "████╗  ██║██╔════╝██║    ██║    ██╔════╝ ██╔══██╗████╗ ████║██╔════╝",
744
       "██╔██╗ ██║█████╗  ██║ █╗ ██║    ██║  ███╗███████║██╔████╔██║█████╗  ",
745
       "██║╚██╗██║██╔══╝  ██║███╗██║    ██║   ██║██╔══██║██║╚██╔╝██║██╔══╝  ",
746
       "██║ ╚████║███████╗╚███╔███╔╝    ╚██████╔╝██║  ██║██║ ╚═╝ ██║███████╗",
747
       "╚═╝  ╚═══╝╚══════╝ ╚══╝╚══╝      ╚═════╝ ╚═╝  ╚═╝╚═╝     ╚═╝╚══════╝"},
748
      {"███████╗███████╗████████╗████████╗██╗███╗   ██╗ ██████╗ ███████╗",
749
       "██╔════╝██╔════╝╚══██╔══╝╚══██╔══╝██║████╗  ██║██╔════╝ ██╔════╝",
750
       "███████╗█████╗     ██║      ██║   ██║██╔██╗ ██║██║  ███╗███████╗",
751
       "╚════██║██╔══╝     ██║      ██║   ██║██║╚██╗██║██║   ██║╚════██║",
752
       "███████║███████╗   ██║      ██║   ██║██║ ╚████║╚██████╔╝███████║",
753
       "╚══════╝╚══════╝   ╚═╝      ╚═╝   ╚═╝╚═╝  ╚═══╝ ╚═════╝ ╚══════╝"},
754
      {"███████╗██╗  ██╗██╗████████╗", "██╔════╝╚██╗██╔╝██║╚══██╔══╝",
755
       "█████╗   ╚███╔╝ ██║   ██║   ", "██╔══╝   ██╔██╗ ██║   ██║   ",
756
       "███████╗██╔╝ ██╗██║   ██║   ", "╚══════╝╚═╝  ╚═╝╚═╝   ╚═╝   "}};
757
758
  const uint8_t height_line = 6;
759
  const uint8_t height_space = 2;
760
761
  const uint8_t width_text[] = {68, 64, 28};
762
763
  const uint8_t width_big = width_text[0] /*strlen(menu_string_big[0][0])*/;
764
  const uint8_t height_big =
765
      height_line * MAIN_MENU + height_space * (MAIN_MENU - 1);
766
767
  printf("\033[2J\033[H");
768
769
  if (window_size.ws_row < height_small || window_size.ws_col < width_small) {
770
    printf("Too small\n");
771
    return true;
772
  }
773
774
  uint8_t i, j, k;
775
776
  if (window_size.ws_row >= height_big && window_size.ws_col >= width_big) {
777
778
    uint8_t vertical_buffer = (window_size.ws_row - height_big) >> 1;
779
780
    for (i = 0; i < vertical_buffer; i++)
781
      printf("\n");
782
783
    for (i = 0; i < MAIN_MENU; i++) {
784
785
      if (i == menu)
786
        printf("\033[32m");
787
788
      uint8_t horizontal_buffer = (window_size.ws_col - width_text[i]) >> 1;
789
790
      for (j = 0; j < height_line; j++) {
791
792
        for (k = 0; k < horizontal_buffer; k++)
793
          printf(" ");
794
795
        printf("%s", menu_string_big[i][j]);
796
797
        if (j != height_line - 1)
798
          printf("\n");
799
      }
800
801
      printf("\033[0m");
802
803
      if (i != MAIN_MENU - 1)
804
        printf("\n\n");
805
    }
806
807
  } else {
808
809
    uint8_t vertical_buffer = (window_size.ws_row - height_small) >> 1;
810
811
    for (i = 0; i < vertical_buffer; i++)
812
      printf("\n");
813
814
    for (i = 0; i < MAIN_MENU; i++) {
815
      if (i == menu)
816
        printf("\033[32m");
817
818
      uint8_t horizontal_buffer =
819
          (window_size.ws_col - strlen(menu_string[i])) >> 1;
820
821
      for (j = 0; j < horizontal_buffer; j++)
822
        printf(" ");
823
824
      printf("%s\033[0m", menu_string[i]);
825
826
      if (i != MAIN_MENU - 1)
827
        printf("\n\n");
828
    }
829
  }
830
831
  fflush(stdout);
832
  return false;
833
}
834
835
bool draw_board(char board[HEIGHT][WIDTH][STRING_LEN],
836
                struct winsize window_size, bool redraw, bool paused) {
837
838
  uint8_t i, j;
839
840
  uint8_t actual_height = HEIGHT + 2;
841
  uint8_t actual_width = WIDTH * 2 + 2;
842
843
  if (actual_width > window_size.ws_col || actual_height > window_size.ws_row) {
844
845
    printf("\033[2J\033[HToo small\n");
846
    printf("Min width: %i & Min height: %i\n", actual_height, actual_width);
847
848
    return true;
849
  }
850
851
  uint8_t vertical_buffer = (window_size.ws_row - actual_height) >> 1;
852
  uint8_t horizontal_buffer = (window_size.ws_col - actual_width) >> 1;
853
854
  if (redraw) {
855
    printf("\033[2J\033[H");
856
    for (i = 0; i < vertical_buffer; i++)
857
      printf("\n");
858
  } else {
859
    for (i = 0; i < actual_height - 1; i++)
860
      printf("\033[%c", UP_CODE);
861
    printf("\r");
862
  }
863
864
  for (i = 0; i < horizontal_buffer; i++)
865
    printf(" ");
866
  printf("+");
867
  for (i = 0; i < WIDTH; i++)
868
    printf("--");
869
  printf("+\n");
870
871
  for (i = 0; i < HEIGHT; i++) {
872
873
    for (j = 0; j < horizontal_buffer; j++)
874
      printf(" ");
875
876
    printf("|");
877
878
    for (j = 0; j < WIDTH; j++)
879
      printf("%s", board[i][j]);
880
881
    printf("|\n");
882
  }
883
884
  for (i = 0; i < horizontal_buffer; i++)
885
    printf(" ");
886
  printf("+");
887
  for (i = 0; i < WIDTH; i++)
888
    printf("--");
889
  printf("+");
890
891
  if (paused)
892
    draw_pause(window_size, actual_width, actual_height);
893
894
  fflush(stdout);
895
896
  return false;
897
}
898
899
void draw_pause(struct winsize window_size, uint8_t field_width,
900
                uint8_t field_height) {
901
902
  const uint8_t height = 5;
903
  const uint8_t width = 14;
904
905
  const char *pause_menu[] = {
906
    "+------------+", 
907
    "|            |",
908
    "|   PAUSED   |",
909
    "|            |",
910
    "+------------+"
911
  };
912
913
  const uint8_t going_up = (field_height + height) >> 1;
914
  const uint8_t going_right = (field_width - width) >> 1;
915
916
  const uint8_t horizontal_buffer = (window_size.ws_col - field_width) >> 1;
917
918
  uint8_t i, j;
919
920
  for (i = 0; i < going_up; i++)
921
    printf("\033[%c", UP_CODE);
922
  printf("\r");
923
924
  for (i = 0; i < height; i++) {
925
    for (j = 0; j < horizontal_buffer; j++)
926
      printf("\033[%c", RIGHT_CODE);
927
    for (j = 0; j < going_right; j++)
928
      printf("\033[%c", RIGHT_CODE);
929
930
    printf("\033[37m%s\033[0m\033[%c\r", pause_menu[i], DOWN_CODE);
931
  }
932
933
  for (i = 0; i < going_up - height; i++)
934
    printf("\033[%c", DOWN_CODE);
935
  printf("\r");
936
937
  fflush(stdout);
938
}
939
940
direction read_direction(char *string, bool wasd_enabled) {
941
942
  if (strlen(string) >= 3) {
943
    if (string[0] == '\033') {
944
      if (string[1] == '[') {
945
946
        switch (string[2]) {
947
        case RIGHT_CODE:
948
          return RIGHT;
949
          break;
950
        case LEFT_CODE:
951
          return LEFT;
952
          break;
953
        case UP_CODE:
954
          return UP;
955
          break;
956
        case DOWN_CODE:
957
          return DOWN;
958
          break;
959
        }
960
      }
961
    }
962
  }
963
964
  if (wasd_enabled) {
965
    switch (toupper(string[0])) {
966
    case 'A':
967
      return LEFT;
968
      break;
969
    case 'D':
970
      return RIGHT;
971
      break;
972
    case 'W':
973
      return UP;
974
      break;
975
    case 'S':
976
      return DOWN;
977
      break;
978
    }
979
  }
980
981
  return NO_DIRECTION;
982
}