jag

snake_c

public

Small snake game in C

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