jag

snake_c

public

Small snake game in C

Star0
Watch0
f8327e555c6d67ab879285f1129a1532efcb5df2
2
ssh://git@git.jaglekon.de:30/jag/snake_c.git
drawing.cC714 lines19.9 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", "HIGH SCORE", "EXIT"};
120
121
  const uint8_t width_small = 10;
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, j;
540
541
  char *header[] = {
542
    "   _  _ ___ ___ _  _   ",
543
    "  | || |_ _/ __| || |  ",
544
    "  | __ || | (_ | __ |  ",
545
    " _|_||_|___\\___|_||_|_ ",
546
    "/ __|/ __/ _ \\| _ \\ __|",
547
    "\\__ \\ (_| (_) |   / _| ",
548
    "|___/\\___\\___/|_|_\\___|",
549
    "                       "
550
  };
551
552
  uint8_t header_height = 8;
553
  uint8_t header_width = strlen(header[0]);
554
555
  uint8_t full_height = header_height + SCORE_NUMBER + 5 + 1;
556
  uint8_t full_width = header_width + 4;
557
558
  uint8_t gap_name = 9;
559
560
  printf("\033[2J\033[H");
561
562
  if (window_size.ws_col < full_width || window_size.ws_row < full_height) {
563
    printf("Too small\n");
564
    printf("Min size: x: %d y: %d", full_width, full_height);
565
    fflush(stdout);
566
    return;
567
  }
568
569
  for (i = 0; i < (window_size.ws_row - full_height) >> 1; i++)
570
    printf("\n");
571
572
  for (i = 0; i < full_height - 1; i++) {
573
    
574
    for (j = 0; j < (window_size.ws_col - full_width) >> 1; j++)
575
      printf(" ");
576
577
    if (!i) {
578
      printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
579
    } else if (i < header_height + 1) {
580
      printf("┃ \033[31;1m%s\033[0m ┃\n", header[i - 1]);
581
    } else if(i < header_height + 2) {
582
      printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
583
    } else if (i < header_height + 3) {
584
      printf("┃ RANK     NAME    SCORE  ┃\n");
585
    } else if (i < header_height + 4) {
586
      printf("┠─────────────────────────┨\n");
587
    } else if (i < header_height + 4 + SCORE_NUMBER) { 
588
      uint8_t pos = i - header_height - 4;
589
      char *ordinals[] = {
590
        "st", "nd", "rd", "th"
591
      };
592
593
      printf("┃ \033[%dm%2d%-4s %-9s %06d\033[0m ┃\n", (pos == new_score) ? 32 : 0 ,pos + 1, (pos >= 3) ? ordinals[3] : ordinals[pos] ,leader_board[pos].name, leader_board[pos].points);
594
    } else {
595
      printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");
596
    }
597
  }
598
599
  fflush(stdout);
600
601
  if (new_score != -1) {
602
 
603
    char *input = "Put your Name in";
604
605
    for (i = 0; i < (window_size.ws_col - strlen(input)) >> 1; i++)
606
      printf(" ");
607
    printf("%s", input);
608
609
610
    for (i = (SCORE_NUMBER - new_score) + 1; i > 0; i--)
611
      printf("\033[%c", UP_CODE);
612
    printf("\r");
613
    for (i = 0; i < (window_size.ws_col - full_width) >> 1; i++)
614
      printf(" ");
615
    for (i = 0; i < gap_name; i++)
616
      printf("\033[%c", RIGHT_CODE);
617
    printf("_");
618
    fflush(stdout);
619
620
    uint8_t number_characters = 0;
621
622
    while(true) {
623
      char string[STRING_LEN] = {};
624
      ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);
625
626
      if (length_string > 0) {
627
        
628
        direction pressed_key = read_direction(string, false);
629
630
        if (pressed_key == ENTER) {
631
          leader_board[new_score].name[number_characters] = '\0';
632
          break;        
633
        }
634
         
635
        if (pressed_key == DEL && number_characters > 0) {
636
          printf("\033[%c \033[%c\033[%c_", LEFT_CODE, LEFT_CODE, LEFT_CODE);
637
          leader_board[new_score].name[--number_characters] = '\0';
638
        }
639
640
        if (isalnum(string[0]) && number_characters < SCORE_LEN - 2) {
641
          printf("\033[%c%c_", LEFT_CODE, string[0]);
642
          leader_board[new_score].name[number_characters++] = string[0];
643
        }
644
      }
645
      fflush(stdout);
646
      struct timespec mili100 = {0, 1000000 * 100};
647
      nanosleep(&mili100, NULL);
648
    }
649
  } else {
650
651
    char *back = "BACK";
652
653
    for (i = 0; i < (window_size.ws_col - strlen(back)) >> 1; i++)
654
      printf(" ");
655
    printf("\033[32m%s\033[0m", back);
656
    fflush(stdout);
657
  }
658
}
659
660
661
char *give_state(board_state square) {
662
663
  switch (square) {
664
    case FRUIT:
665
      return "\033[32m O\033[0m";
666
      break;
667
    case HEAD_1:
668
      return "\033[33m═\033[31mX\033[0m";
669
      break;
670
    case HEAD_2:
671
      return "\033[31m\33[CX\033[0m";
672
      break;    
673
    case VERTICAL:
674
      return "\033[33m ║\033[0m";
675
      break;
676
    case HORIZONTAL:
677
      return "\033[33m══\033[0m";
678
      break;
679
    case TOP_RIGHT:
680
      return "\033[33m ╚\033[0m";
681
      break;
682
    case TOP_LEFT:
683
      return "\033[33m═╝\033[0m";
684
      break;
685
    case BOTTOM_RIGHT:
686
      return "\033[33m ╔\033[0m";
687
      break;
688
    case BOTTOM_LEFT:
689
      return "\033[33m═╗\033[0m";
690
      break;
691
    default:
692
      return " ·";
693
      break;
694
  }
695
}
696
697
bool print_buffer(uint16_t width, char *string) {
698
  uint16_t len = strlen(string);
699
  uint16_t i;
700
701
  if (width < len)
702
    return false;
703
704
  uint16_t prebuffer = (width - len) >> 1; 
705
  uint16_t postbuffer = width - len - prebuffer; 
706
707
  for (i = 0; i < prebuffer; i++)
708
    printf(" ");
709
  printf("%s", string);
710
  for (i = 0; i < postbuffer; i++)
711
    printf(" ");
712
713
  return true;
714
}