Snake.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <assert.h>
  4. #include "Snake.h"
  5. #include "utils.h"
  6. extern const char snake_vis;
  7. static BoardPiece* snake_get_part_ptr(const Snake* snake, const size_t index) {
  8. if (index >= snake->max_length) {
  9. return NULL;
  10. }
  11. return snake->parts + index;
  12. }
  13. BoardPiece snake_get_part(const Snake* snake, const size_t index) {
  14. assert(index < snake->max_length);
  15. return snake->parts[index];
  16. }
  17. Snake snake_alloc(
  18. const int board_square_count,
  19. const int init_x,
  20. const int init_y,
  21. const char init_dir
  22. )
  23. {
  24. Snake snake = {0};
  25. snake.max_length = board_square_count;
  26. snake.length = 1;
  27. snake.dir = init_dir;
  28. snake.parts = (BoardPiece*) malloc(sizeof(BoardPiece) * snake.max_length);
  29. if (snake.parts == NULL) {
  30. mallocError("snake.parts", __FILE__, "snake_alloc");
  31. }
  32. snake.parts[0] = (BoardPiece) { .x = init_x, .y = init_y, .vis_char = '&' };
  33. return snake;
  34. }
  35. void snake_free(Snake* snake) {
  36. free(snake->parts);
  37. }
  38. static void check_bounds(Snake* snake, const int width, const int height) {
  39. for (size_t i = 0;i < snake->length; i++) {
  40. BoardPiece* part = snake->parts + i;
  41. if (part->x < 0) {
  42. part->x = width - 2;
  43. } else if (part->x >= width) {
  44. part->x = 0;
  45. }
  46. if (part->y < 0) {
  47. part->y = height - 1;
  48. } else if (part->y >= height) {
  49. part->y = 0;
  50. }
  51. }
  52. }
  53. void snake_move(Snake* snake, const int width, const int height) {
  54. BoardPiece* first_part = snake_get_part_ptr(snake, 0);
  55. int first_part_old_x = first_part->x;
  56. int first_part_old_y = first_part->y;
  57. // Move first part
  58. switch (snake->dir) {
  59. case 'w':
  60. first_part->y -= 1;
  61. break;
  62. case 'a':
  63. first_part->x -= 2;
  64. break;
  65. case 's':
  66. first_part->y += 1;
  67. break;
  68. case 'd':
  69. first_part->x += 2;
  70. break;
  71. default:
  72. fprintf(stderr, "ERROR: Invalid direction in snake_move: %c.\n", snake->dir);
  73. exit(EXIT_FAILURE);
  74. }
  75. // Stop here if there is only one part
  76. BoardPiece* last_part = snake_get_part_ptr(snake, snake->length - 1);
  77. if (last_part == first_part) {
  78. check_bounds(snake, width, height);
  79. return;
  80. }
  81. // Move all other parts except for the second one, gets skipped if there are only 2 parts
  82. BoardPiece* second_part = first_part + 1;
  83. BoardPiece* prev_part = 0;
  84. for (BoardPiece* part = last_part; part != second_part; part--) {
  85. prev_part = part - 1;
  86. part->x = prev_part->x;
  87. part->y = prev_part->y;
  88. }
  89. // Move second part
  90. second_part->x = first_part_old_x;
  91. second_part->y = first_part_old_y;
  92. check_bounds(snake, width, height);
  93. }
  94. void snake_change_direction(Snake* snake, const char direction) {
  95. snake->dir = direction;
  96. }
  97. bool snake_collides(const Snake* snake, const BoardPiece* piece) {
  98. for (size_t i = 0; i < snake->length; i++) {
  99. BoardPiece part = snake->parts[i];
  100. if (part.x == piece->x && part.y == piece->y) {
  101. return true;
  102. }
  103. }
  104. return false;
  105. }
  106. bool snake_collides_with_tail(const Snake* snake) {
  107. const BoardPiece* head = snake->parts;
  108. for (size_t i = 1; i < snake->length; i++) {
  109. BoardPiece part = snake->parts[i];
  110. if (part.x == head->x && part.y == head->y) {
  111. return true;
  112. }
  113. }
  114. return false;
  115. }
  116. void snake_add_part(Snake* snake) {
  117. if (snake->length == snake->max_length) {
  118. fprintf(stderr, "ERROR: Cannot add another part to snake. Would exceed max_length.\n");
  119. exit(EXIT_FAILURE);
  120. }
  121. int x_shift;
  122. int y_shift;
  123. BoardPiece last_part;
  124. char prev_part_dir;
  125. if (snake->length == 1) {
  126. last_part = snake_get_part(snake, 0);
  127. prev_part_dir = snake->dir;
  128. } else {
  129. last_part = snake_get_part(snake, snake->length - 1);
  130. const BoardPiece second_to_last_part = snake_get_part(snake, snake->length - 2);
  131. if (second_to_last_part.y < last_part.y && second_to_last_part.x == last_part.x) {
  132. prev_part_dir = 'w';
  133. } else if (second_to_last_part.x < last_part.x && second_to_last_part.y == last_part.y) {
  134. prev_part_dir = 'a';
  135. } else if (second_to_last_part.y > last_part.y && second_to_last_part.x == last_part.x) {
  136. prev_part_dir = 's';
  137. } else if (second_to_last_part.x > last_part.x && second_to_last_part.y == last_part.y) {
  138. prev_part_dir = 'd';
  139. } else {
  140. fprintf(stderr, "%s:%d: ERROR: Invalid direction.\n", __FILE__, __LINE__);
  141. exit(EXIT_FAILURE);
  142. }
  143. }
  144. switch (prev_part_dir) {
  145. case 'w':
  146. x_shift = 0;
  147. y_shift = 1;
  148. break;
  149. case 'a':
  150. x_shift = 1;
  151. y_shift = 0;
  152. break;
  153. case 's':
  154. x_shift = 0;
  155. y_shift = -1;
  156. break;
  157. case 'd':
  158. x_shift = -1;
  159. y_shift = 0;
  160. break;
  161. default:
  162. fprintf(stderr, "%s:%d: ERROR: Invalid direction.\n", __FILE__, __LINE__);
  163. exit(EXIT_FAILURE);
  164. }
  165. snake->parts[snake->length] = (BoardPiece){
  166. .x = last_part.x + x_shift,
  167. .y = last_part.y + y_shift,
  168. .vis_char = snake_vis
  169. };
  170. snake->length++;
  171. }
  172. #ifdef DEBUG
  173. void snake_print_info(const Snake* snake) {
  174. printf("snake: {\n");
  175. printf(" parts: {\n");
  176. for (size_t i = 0; i < snake->length; i++) {
  177. const BoardPiece part = snake->parts[i];
  178. printf(" { x: %d, y: %d }\n", part.x, part.y);
  179. }
  180. printf(" }\n");
  181. printf(" max_length: %zu\n", snake->max_length);
  182. printf(" length: %zu\n", snake->length);
  183. printf(" dir: %c\n", snake->dir);
  184. printf("}\n");
  185. }
  186. #endif // DEBUG