jag

snake_c

public

Small snake game in C

Star0
Watch0
c28baaaf800e7154b7be80296404158ec5978630
2
ssh://git@git.jaglekon.de:30/jag/snake_c.git
drawing.cC659 lines18.2 KB
1
#include "snake.h"
2
3
bool start_animation() {  
4
5
  struct winsize current_size;
6
  ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
7
8
  struct timespec mili10 = {0, 1000000 * 10};
9
10
  char *snake[3][7] = {
11
    {
12
      "           ________  ",
13
      "         _/ _      \\_",
14
      "________/  (_)       ",
15
      "\\       \\            ",
16
      "/_______/   _        ",
17
      "        \\_ (_)      _",
18
      "          \\________/ "
19
    },
20
    {
21
      "        ",
22
      "________",
23
      "\\      /",
24
      " \\    / ",
25
      "  \\  /  ",
26
      "___\\/___",
27
      "        ",
28
    },
29
    {
30
      "         ",
31
      "__        ",
32
      "  \\_____ ",
33
      "        \\",
34
      "   _____/",
35
      "__/      ",
36
      "         "
37
    }
38
  };
39
40
  uint8_t width_snake[3] = {};
41
42
  {
43
    uint8_t i;
44
45
    for (i = 0; i < 3; i++)
46
      width_snake[i] = (uint8_t) strlen(snake[i][0]);
47
  }
48
49
  uint8_t middle;
50
51
  uint8_t height = 7;
52
  uint8_t width = width_snake[0] + width_snake[1] + width_snake[2];
53
54
  for (middle = 1; width < current_size.ws_col; middle++)
55
    width += width_snake[1];
56
57
  int32_t end = current_size.ws_col + width;
58
59
60
61
  while (end >= 0) {
62
63
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &current_size);
64
65
    int32_t start = end - width; 
66
67
    int32_t i, j;
68
 
69
    char string[1000] = {};
70
    ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
71
72
     if (length_string > 0) {
73
    
74
      string[length_string] = '\0';
75
76
      direction pressed_key = read_direction(string, true);
77
78
      if (pressed_key == Q_KEY || pressed_key == ESCAPE)
79
        return false;
80
81
      if (pressed_key == ENTER)
82
        break;
83
    }
84
85
    printf("\033[2J\033[H");
86
87
    for (j = 0; j < ((height > current_size.ws_row) ? current_size.ws_row : height) ; j++) {
88
89
    for (i = 0; i < current_size.ws_col; i++) {
90
      int32_t index = i - start;
91
92
      if (index >= 0 && index < width) {
93
        if (index < width_snake[0]) {
94
          printf("%c", snake[0][j][index]);
95
        } else if (index < width_snake[0] + width_snake[1] * middle) {
96
          printf("%c", snake[1][j][(index - width_snake[0]) % width_snake[1]]);
97
        } else {
98
          printf("%c", snake[2][j][index - (width_snake[0] + width_snake[1] * middle)]);
99
        }
100
      } else {
101
        printf(" ");
102
      }
103
    }      
104
    
105
      printf("\n");
106
107
    }
108
    fflush(stdout);
109
    
110
    end--;
111
112
    nanosleep(&mili10, NULL);
113
  }
114
  return true;
115
}
116
117
bool draw_menu(main_menu menu, struct winsize window_size) {
118
119
  char *menu_string[] = {"NEW GAME", "SETTINGS", "HIGHSCORE", "EXIT"};
120
121
  const uint8_t width_small = 9;
122
  const uint8_t height_small = 5;
123
124
  char *menu_string_big[][6] = {
125
      {"███╗   ██╗███████╗██╗    ██╗     ██████╗  █████╗ ███╗   ███╗███████╗",
126
       "████╗  ██║██╔════╝██║    ██║    ██╔════╝ ██╔══██╗████╗ ████║██╔════╝",
127
       "██╔██╗ ██║█████╗  ██║ █╗ ██║    ██║  ███╗███████║██╔████╔██║█████╗  ",
128
       "██║╚██╗██║██╔══╝  ██║███╗██║    ██║   ██║██╔══██║██║╚██╔╝██║██╔══╝  ",
129
       "██║ ╚████║███████╗╚███╔███╔╝    ╚██████╔╝██║  ██║██║ ╚═╝ ██║███████╗",
130
       "╚═╝  ╚═══╝╚══════╝ ╚══╝╚══╝      ╚═════╝ ╚═╝  ╚═╝╚═╝     ╚═╝╚══════╝"},
131
      {"███████╗███████╗████████╗████████╗██╗███╗   ██╗ ██████╗ ███████╗",
132
       "██╔════╝██╔════╝╚══██╔══╝╚══██╔══╝██║████╗  ██║██╔════╝ ██╔════╝",
133
       "███████╗█████╗     ██║      ██║   ██║██╔██╗ ██║██║  ███╗███████╗",
134
       "╚════██║██╔══╝     ██║      ██║   ██║██║╚██╗██║██║   ██║╚════██║",
135
       "███████║███████╗   ██║      ██║   ██║██║ ╚████║╚██████╔╝███████║",
136
       "╚══════╝╚══════╝   ╚═╝      ╚═╝   ╚═╝╚═╝  ╚═══╝ ╚═════╝ ╚══════╝"},
137
      {"██╗  ██╗██╗ ██████╗ ██╗  ██╗███████╗ ██████╗ ██████╗ ██████╗ ███████╗",
138
       "██║  ██║██║██╔════╝ ██║  ██║██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝",
139
       "███████║██║██║  ███╗███████║███████╗██║     ██║   ██║██████╔╝█████╗  ",
140
       "██╔══██║██║██║   ██║██╔══██║╚════██║██║     ██║   ██║██╔══██╗██╔══╝  ",
141
       "██║  ██║██║╚██████╔╝██║  ██║███████║╚██████╗╚██████╔╝██║  ██║███████╗",
142
       "╚═╝  ╚═╝╚═╝ ╚═════╝ ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝  ╚═╝╚══════╝"},
143
      {"███████╗██╗  ██╗██╗████████╗", 
144
       "██╔════╝╚██╗██╔╝██║╚══██╔══╝",
145
       "█████╗   ╚███╔╝ ██║   ██║   ", 
146
       "██╔══╝   ██╔██╗ ██║   ██║   ",
147
       "███████╗██╔╝ ██╗██║   ██║   ", 
148
       "╚══════╝╚═╝  ╚═╝╚═╝   ╚═╝   "}
149
  };
150
151
  const uint8_t height_line = 6;
152
  const uint8_t height_space = 2;
153
154
  const uint8_t width_text[] = {68, 64, 69, 28};
155
156
  const uint8_t width_big = width_text[2] /*strlen(menu_string_big[0][0])*/;
157
  const uint8_t height_big =
158
      height_line * MAIN_MENU + height_space * (MAIN_MENU - 1);
159
160
  printf("\033[2J\033[H");
161
162
  if (window_size.ws_row < height_small || window_size.ws_col < width_small) {
163
    printf("Too small\n");
164
    return true;
165
  }
166
167
  uint8_t i, j, k;
168
169
  if (window_size.ws_row >= height_big && window_size.ws_col >= width_big) {
170
171
    uint8_t vertical_buffer = (window_size.ws_row - height_big) >> 1;
172
173
    for (i = 0; i < vertical_buffer; i++)
174
      printf("\n");
175
176
    for (i = 0; i < MAIN_MENU; i++) {
177
178
      if (i == menu)
179
        printf("\033[32m");
180
181
      uint8_t horizontal_buffer = (window_size.ws_col - width_text[i]) >> 1;
182
183
      for (j = 0; j < height_line; j++) {
184
185
        for (k = 0; k < horizontal_buffer; k++)
186
          printf(" ");
187
188
        printf("%s", menu_string_big[i][j]);
189
190
        if (j != height_line - 1)
191
          printf("\n");
192
      }
193
194
      printf("\033[0m");
195
196
      if (i != MAIN_MENU - 1)
197
        printf("\n\n");
198
    }
199
200
  } else {
201
202
    uint8_t vertical_buffer = (window_size.ws_row - height_small) >> 1;
203
204
    for (i = 0; i < vertical_buffer; i++)
205
      printf("\n");
206
207
    for (i = 0; i < MAIN_MENU; i++) {
208
      if (i == menu)
209
        printf("\033[32m");
210
211
      uint8_t horizontal_buffer =
212
          (window_size.ws_col - strlen(menu_string[i])) >> 1;
213
214
      for (j = 0; j < horizontal_buffer; j++)
215
        printf(" ");
216
217
      printf("%s\033[0m", menu_string[i]);
218
219
      if (i != MAIN_MENU - 1)
220
        printf("\n\n");
221
    }
222
  }
223
224
  fflush(stdout);
225
  return false;
226
}
227
228
229
void draw_settings(settings selected_setting, speed game_speed, selected game_size, struct winsize window_size) {
230
231
  char *title[] = {
232
    " ___ ___ _____ _____ ___ _  _  ___ ___ ",
233
    "/ __| __|_   _|_   _|_ _| \\| |/ __/ __|",
234
    "\\__ \\ _|  | |   | |  | || .` | (_ \\__ \\",
235
    "|___/___| |_|   |_| |___|_|\\_|\\___|___/",
236
                                        
237
  };
238
239
  char *setting_string[] = {
240
    "HEIGHT",
241
    "WIDTH",
242
    "SPEED",
243
    "BACK"
244
  };
245
246
  char *speed_char[] = {
247
    "VERY FAST",
248
    "FAST",
249
    "AVERAGE",
250
    "SLOW",
251
    "VERY SLOW"
252
  };
253
254
255
  uint8_t title_height = 4;
256
  uint8_t title_width = strlen(title[0]);
257
258
  
259
  uint8_t setting_width;
260
  if (title_width % 2 == 0) {
261
    setting_width = (title_width + 2) >> 1;
262
  } else {
263
    setting_width = (title_width + 1) >> 1;
264
  }
265
266
  uint8_t settings_height = SETTINGS_MENU * 2 + 1;
267
268
  uint16_t full_height = title_height + settings_height + 2;
269
  uint16_t full_width = setting_width * 2 + 3;
270
271
  uint8_t i, j;
272
273
  printf("\033[2J\033[H");
274
275
  if (window_size.ws_col < full_width || window_size.ws_row < full_height) {
276
    printf("Too small!\n");
277
    printf("Min: x: %d y: %d", full_width, full_height);
278
    fflush(stdout);
279
    return;
280
  }
281
282
  uint8_t vertical_buffer = (window_size.ws_row - full_height) >> 1;
283
  uint8_t horizontal_buffer = (window_size.ws_col - full_width) >> 1;
284
285
  for(i = 0; i < vertical_buffer; i++)
286
    printf("\n");
287
288
289
  for(i = 0; i < title_height + 2; i++) {
290
    for (j = 0; j < horizontal_buffer; j++)
291
      printf(" ");
292
    if (!i) {
293
      printf("┏");
294
      for(j = 0; j < full_width - 2; j++)
295
        printf("━");
296
      printf("┓\n");
297
    } else {
298
      printf("┃ ");
299
      if (i == title_height + 1) {
300
        for(j = 0; j < title_width; j++)
301
          printf(" ");
302
      } else {
303
        printf("\033[31;1m%s\033[0m", title[i - 1]);
304
      }
305
      if (title_width % 2 == 0)
306
        printf(" ");
307
      printf(" ┃\n");
308
    }
309
  }
310
   
311
  for (i = 0; i < settings_height; i++) {
312
313
    for (j = 0; j < horizontal_buffer; j++)
314
      printf(" ");
315
316
    if (i % 2 == 0) {
317
318
      if (i == 0) {
319
        printf("┣");
320
        for(j = 0; j < setting_width; j++)
321
          printf("━");
322
        printf("┯");
323
        for(j = 0; j < setting_width; j++)
324
          printf("━");
325
        printf("┫\n");
326
327
      } else if (i == settings_height - 1) {
328
        printf("┗");
329
        for(j = 0; j < setting_width * 2 + 1; j++)
330
          printf("━");
331
        printf("┛");
332
333
      } else {
334
      
335
        printf("┠");
336
        for(j = 0; j < setting_width; j++)
337
          printf("─");
338
        printf("%s", i == settings_height - 3 ? "┴" : "┼");
339
        for(j = 0; j < setting_width; j++)
340
          printf("─");
341
        printf("┨\n");
342
      }
343
344
    } else {
345
346
      settings current_setting = (i - 1) >> 1;
347
348
      printf("┃");
349
350
      if (current_setting == selected_setting)
351
        printf("\033[32m");
352
353
      print_buffer(current_setting != BACK_S ? setting_width : 2 * setting_width + 1, 
354
        setting_string[current_setting]);
355
      
356
      printf("\033[0m");
357
358
      if (current_setting != BACK_S) {
359
360
        printf("│");
361
362
        if (current_setting == selected_setting)
363
          printf("\033[32m");
364
365
        char *pointer;
366
367
        char number[5];
368
369
        switch (current_setting) {
370
          case SPEED_S:
371
            pointer = speed_char[game_speed];
372
            break;
373
          case WIDTH_S:
374
            sprintf(number, "%d", game_size.x);
375
            pointer = number;
376
            break;
377
          case HEIGHT_S:
378
            sprintf(number, "%d", game_size.y);
379
            pointer = number;
380
            break;
381
          default:
382
            break;
383
        }
384
385
        print_buffer(setting_width, pointer);
386
      }
387
388
      printf("\033[0m┃\n");
389
    }
390
  }
391
392
  fflush(stdout);
393
}
394
395
bool draw_board(
396
  selected game_size,
397
  board_state **board,
398
  struct winsize window_size, 
399
  bool redraw, uint8_t paused,
400
  uint16_t score
401
) {
402
403
  uint8_t i, j;
404
405
  uint8_t actual_height = game_size.y + 2 + 2 + 1;
406
  uint8_t actual_width = game_size.x * 2 + 1 + 2;
407
408
409
  if (actual_width > window_size.ws_col || actual_height > window_size.ws_row) {
410
411
    printf("\033[2J\033[HToo small\n");
412
    printf("Min width: %i & Min height: %i\n", actual_height, actual_width);
413
414
    return true;
415
  }
416
417
  uint8_t vertical_buffer = (window_size.ws_row - actual_height) >> 1;
418
  uint8_t horizontal_buffer = (window_size.ws_col - actual_width) >> 1;
419
420
  if (redraw) {
421
    printf("\033[2J\033[H");
422
    for (i = 0; i < vertical_buffer; i++)
423
      printf("\n");
424
  } else {
425
    for (i = 0; i < actual_height - 1; i++)
426
      printf("\033[%c", UP_CODE);
427
    printf("\r");
428
  }
429
  
430
  for (i = 0; i < ((actual_width - 9) >> 1) + horizontal_buffer; i++)
431
    printf(" ");
432
  printf("╭───────╮\n");
433
    for (i = 0; i < ((actual_width - 9) >> 1) + horizontal_buffer; i++)
434
    printf(" ");
435
  printf("│ \033[32m%05d\033[0m │\n", score);
436
437
  for (i = 0; i < horizontal_buffer; i++)
438
    printf(" ");
439
  printf("╭");
440
  for (i = 0; i < game_size.x; i++)
441
    printf("──");
442
  printf("─╮");
443
  printf("\r");
444
  for (i = 0; i < ((actual_width - 9) >> 1) + horizontal_buffer; i++)
445
    printf("\033[%c", RIGHT_CODE);
446
  printf("┴───────┴\n");
447
448
449
450
  for (i = 0; i < game_size.y; i++) {
451
452
    for (j = 0; j < horizontal_buffer; j++)
453
      printf(" ");
454
455
    printf("│");
456
457
    for (j = 0; j < game_size.x; j++)
458
      printf("%s", give_state(board[i][j]));
459
460
    printf("%s", give_state(board[i][0]));
461
462
    printf("\033[%c│\n", LEFT_CODE);
463
  }
464
465
  for (i = 0; i < horizontal_buffer; i++)
466
    printf(" ");
467
  printf("╰");
468
  for (i = 0; i < game_size.x; i++)
469
    printf("──");
470
  printf("─╯\n");
471
472
  if (paused) {
473
    if (paused == 2) {
474
      char *game_over = "Press ENTER to continue";
475
    
476
      for (i = 0; i < (window_size.ws_col - strlen(game_over)) >> 1; i++)
477
        printf(" ");
478
      printf("%s", game_over);
479
    }
480
481
    draw_pause(window_size, actual_width, actual_height, paused);
482
483
  }
484
485
  fflush(stdout);
486
487
  return false;
488
}
489
490
void draw_pause(struct winsize window_size, uint8_t field_width,
491
  uint8_t field_height, uint8_t game_over) {
492
493
  const uint8_t height = 5;
494
  const uint8_t width = 14;
495
496
  const char *pause_menu[] = {
497
    "┌────────────┐", 
498
    "│            │",
499
    "│   PAUSED   │",
500
    "│            │",
501
    "└────────────┘"
502
  };
503
504
  const char *game_over_menu = "│ GAME OVER  │";
505
506
  const uint8_t going_up = (field_height + height) >> 1;
507
  const uint8_t going_right = (field_width - width) >> 1;
508
509
  const uint8_t horizontal_buffer = (window_size.ws_col - field_width) >> 1;
510
511
  uint8_t i, j;
512
513
  for (i = 0; i < going_up; i++)
514
    printf("\033[%c", UP_CODE);
515
  printf("\r");
516
517
  for (i = 0; i < height; i++) {
518
    for (j = 0; j < horizontal_buffer; j++)
519
      printf("\033[%c", RIGHT_CODE);
520
    for (j = 0; j < going_right; j++)
521
      printf("\033[%c", RIGHT_CODE);
522
523
    if (i == 2 && game_over == 2) {
524
      printf("\033[37m%s\033[0m\033[%c\r", game_over_menu, DOWN_CODE);
525
    } else {
526
      printf("\033[37m%s\033[0m\033[%c\r", pause_menu[i], DOWN_CODE);
527
    }
528
  }
529
530
  for (i = 0; i < going_up - height; i++)
531
    printf("\033[%c", DOWN_CODE);
532
  printf("\r");
533
534
  fflush(stdout);
535
}
536
537
void draw_highscore (int16_t new_score, score leader_board[SCORE_NUMBER], struct winsize window_size) {
538
  
539
  uint8_t i;
540
541
  printf("\033[2J\033[H");
542
543
  printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
544
545
  printf("┃ RANK      NAME     SCORE ┃\n");
546
  printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
547
 
548
549
  for (i = 0; i < SCORE_NUMBER; i++) {
550
551
    char *ordinals[] = {
552
      "st", "nd", "rd", "th"
553
    };
554
555
    printf("┃ %2d%-4s  %-9s %06d ┃\n",i + 1, (i >= 3) ? ordinals[3] : ordinals[i] ,leader_board[i].name, leader_board[i].points);
556
    printf("┃                          ┃\n");
557
 
558
  }
559
  
560
  printf("\033[%c┗━━━━━━━━━━━━━━━━━━━━━━━━━━┛", UP_CODE);
561
  
562
  fflush(stdout);
563
564
  if (new_score != -1) {
565
    for (i = (SCORE_NUMBER - new_score) * 2 -  1; i > 0; i--)
566
      printf("\033[%c", UP_CODE);
567
    printf("\r");
568
    for (i = 0; i < 8 + 1; i++)
569
      printf("\033[%c", RIGHT_CODE);
570
    printf("_");
571
    fflush(stdout);
572
573
    uint8_t number_characters = 0;
574
575
    while(true) {
576
      char string[STRING_LEN] = {};
577
      ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
578
579
      if (length_string > 0) {
580
        
581
        direction pressed_key = read_direction(string, false);
582
583
        if (pressed_key == ENTER) {
584
          leader_board[new_score].name[number_characters] = '\0';
585
          break;        
586
        }
587
         
588
        if (pressed_key == DEL && number_characters > 0) {
589
          printf("\033[%c \033[%c\033[%c_", LEFT_CODE, LEFT_CODE, LEFT_CODE);
590
          leader_board[new_score].name[--number_characters] = '\0';
591
        }
592
593
        if (isalnum(string[0]) && number_characters < SCORE_LEN - 2) {
594
          printf("\033[%c%c_", LEFT_CODE, string[0]);
595
          leader_board[new_score].name[number_characters++] = string[0];
596
        }
597
      }
598
      fflush(stdout);
599
      struct timespec mili100 = {0, 1000000 * 100};
600
      nanosleep(&mili100, NULL);
601
    }
602
  }
603
}
604
605
606
char *give_state(board_state square) {
607
608
  switch (square) {
609
    case FRUIT:
610
      return "\033[32m O\033[0m";
611
      break;
612
    case HEAD_1:
613
      return "\033[33m═\033[31mX\033[0m";
614
      break;
615
    case HEAD_2:
616
      return "\033[31m\33[CX\033[0m";
617
      break;    
618
    case VERTICAL:
619
      return "\033[33m ║\033[0m";
620
      break;
621
    case HORIZONTAL:
622
      return "\033[33m══\033[0m";
623
      break;
624
    case TOP_RIGHT:
625
      return "\033[33m ╚\033[0m";
626
      break;
627
    case TOP_LEFT:
628
      return "\033[33m═╝\033[0m";
629
      break;
630
    case BOTTOM_RIGHT:
631
      return "\033[33m ╔\033[0m";
632
      break;
633
    case BOTTOM_LEFT:
634
      return "\033[33m═╗\033[0m";
635
      break;
636
    default:
637
      return " ·";
638
      break;
639
  }
640
}
641
642
bool print_buffer(uint16_t width, char *string) {
643
  uint16_t len = strlen(string);
644
  uint16_t i;
645
646
  if (width < len)
647
    return false;
648
649
  uint16_t prebuffer = (width - len) >> 1; 
650
  uint16_t postbuffer = width - len - prebuffer; 
651
652
  for (i = 0; i < prebuffer; i++)
653
    printf(" ");
654
  printf("%s", string);
655
  for (i = 0; i < postbuffer; i++)
656
    printf(" ");
657
658
  return true;
659
}