jag

snake_c

public

Small snake game in C

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