Sfoglia il codice sorgente

working reworked game

Aaro Saila 1 anno fa
parent
commit
ad5b8beb99
15 ha cambiato i file con 416 aggiunte e 144 eliminazioni
  1. 1 1
      .gitignore
  2. 24 7
      compile.sh
  3. 5 5
      src/Board.c
  4. 15 0
      src/BoardPiece.c
  5. 210 0
      src/Snake.c
  6. 13 0
      src/food.c
  7. 1 1
      src/headers/Board.h
  8. 13 0
      src/headers/BoardPiece.h
  9. 29 0
      src/headers/Snake.h
  10. 10 0
      src/headers/food.h
  11. 0 29
      src/headers/snake.h
  12. 79 17
      src/main.c
  13. 0 83
      src/snake.c
  14. 16 1
      src/utils.c
  15. BIN
      target/sanke

+ 1 - 1
.gitignore

@@ -1,2 +1,2 @@
-build/
 .clangd
+target/

+ 24 - 7
compile.sh

@@ -1,7 +1,26 @@
 #!/bin/sh
 
+arg=-1
+
+if [ -v 1 ]; then
+  arg=$1
+fi
+
+if [ $arg = "clean" ]; then
+  rm -rf ./target/*
+  exit
+fi
+
+oflag="-Og"
+debug_flag="-ggdb"
+
+if [ $arg = "release" ]; then
+  oflag="-O3"
+  debug_flag=""
+fi
+
 src="src/*.c"
-flags="-std=c23 -ggdb -Og -Wall -Wextra -Werror -Wpedantic -pedantic-errors"
+flags="-std=c23 $oflag $debug_flag -Wall -Wextra -Werror -Wpedantic -pedantic-errors"
 includes="-I src/headers"
 
 cmd="gcc $flags $includes $src -o target/sanke"
@@ -9,14 +28,12 @@ cmd="gcc $flags $includes $src -o target/sanke"
 echo $cmd
 $cmd
 
+
 if [ $? -gt 0 ]; then
   exit
 fi
 
-echo 
-
-if [ -v 1 ]; then
-  if [ $1 = "run" ]; then
-    ./target/sanke
-  fi
+if [ $arg = "run" ]; then
+  echo 
+  ./target/sanke
 fi

+ 5 - 5
src/board.c → src/Board.c

@@ -2,8 +2,8 @@
 #include <stdio.h>
 #include <assert.h>
 
-#include "board.h"
-#include "snake.h"
+#include "Board.h"
+#include "Snake.h"
 
 #define MAT_INDEX(mat, w, i, j) (mat)[(j) + (w) * (i)]
 
@@ -73,17 +73,17 @@ void board_set_square(
 void board_clear(Board* board) {
   for (size_t i = 0; i < board->height; i++) {
     for (size_t j = 0; j < board->width; j++) {
-      printf("Clearing board: i: %zu j: %zu\n", i, j);
+      // printf("Clearing board: i: %zu j: %zu\n", i, j);
       board_set_square(board, j, i, ' ');
     }
   }
 }
 
 void board_draw_snake(Board* board, Snake* snake) {
-  SnakePart part = {};
+  BoardPiece part = {};
   for (size_t i = 0; i < snake->length; i++) {
     part = snake_get_part(snake, i);
-    board_set_square(board, part.y, part.x, part.vis_char);
+    board_set_square(board, part.x, part.y, part.vis_char);
   }
 }
 

+ 15 - 0
src/BoardPiece.c

@@ -0,0 +1,15 @@
+#include <stdio.h>
+
+#include "BoardPiece.h"
+
+void board_piece_print_info(BoardPiece* piece, char* name) {
+  printf("%s: {\n", name);
+  printf("  x: %d\n", piece->x);
+  printf("  y: %d\n", piece->y);
+  printf("  vis_char: %c\n", piece->vis_char);
+  printf("}\n");
+}
+
+bool pieces_collide(BoardPiece* piece1, BoardPiece* piece2) {
+  return piece1->x == piece2->x && piece1->y == piece2->y;
+}

+ 210 - 0
src/Snake.c

@@ -0,0 +1,210 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "Snake.h"
+
+extern const char snake_vis;
+
+static BoardPiece* snake_get_part_ptr(const Snake* snake, const size_t index) {
+  if (index >= snake->max_length) {
+    return NULL;
+  }
+
+  return snake->parts + index;
+}
+
+BoardPiece snake_get_part(const Snake* snake, const size_t index) {
+  assert(index < snake->max_length);
+  return snake->parts[index];
+}
+
+Snake snake_alloc(
+    const int board_square_count,
+    const int init_x,
+    const int init_y,
+    const char init_dir
+    )
+{
+  Snake snake = {0};
+  snake.max_length = board_square_count;
+  snake.length = 1;
+  snake.dir = init_dir;
+  snake.parts = (BoardPiece*) malloc(sizeof(BoardPiece) * snake.max_length);
+  snake.parts[0] = (BoardPiece) { .x = init_x, .y = init_y, .vis_char = '&' };
+
+  return snake;
+}
+
+void snake_free(Snake* snake) {
+  free(snake->parts);
+}
+
+static void check_bounds(Snake* snake, const int width, const int height) {
+  for (size_t i = 0;i < snake->length; i++) {
+    BoardPiece* part = snake->parts + i;
+    if (part->x < 0) {
+      part->x = width - 2;
+    } else if (part->x >= width) {
+      part->x = 0;
+    }
+
+    if (part->y < 0) {
+      part->y = height - 1;
+    } else if (part->y >= height) {
+      part->y = 0;
+    }
+  }
+}
+
+void snake_move(Snake* snake, const int width, const int height) {
+  BoardPiece* first_part = snake_get_part_ptr(snake, 0);
+  int first_part_old_x = first_part->x;
+  int first_part_old_y = first_part->y;
+
+  // Move first part
+  switch (snake->dir) {
+    case 'w':
+      first_part->y -= 1;
+      break;
+    case 'a':
+      first_part->x -= 2;
+      break;
+    case 's':
+      first_part->y += 1;
+      break;
+    case 'd':
+      first_part->x += 2;
+      break;
+    default:
+      fprintf(stderr, "ERROR: Invalid direction in snake_move: %c.\n", snake->dir);
+      exit(EXIT_FAILURE);
+  }
+
+  // Stop here if there is only one part
+  BoardPiece* last_part = snake_get_part_ptr(snake, snake->length - 1);
+  if (last_part == first_part) {
+    check_bounds(snake, width, height);
+    return;
+  }
+
+  // Move all other parts except for the second one, gets skipped if there are only 2 parts
+  BoardPiece* second_part = first_part + 1;
+  BoardPiece* prev_part = 0;
+  for (BoardPiece* part = last_part; part != second_part; part--) {
+    prev_part = part - 1;
+    part->x = prev_part->x;
+    part->y = prev_part->y;
+  }
+
+  // Move second part
+  second_part->x = first_part_old_x;
+  second_part->y = first_part_old_y;
+
+  check_bounds(snake, width, height);
+}
+
+void snake_print_info(Snake* snake) {
+  printf("snake: {\n");
+  printf("  parts: {\n");
+  for (size_t i = 0; i < snake->length; i++) {
+    BoardPiece part = snake->parts[i];
+    printf("    x: %d\n", part.x);
+    printf("    y: %d\n", part.y);
+  }
+  printf("  }\n");
+
+  printf("  max_length: %zu\n", snake->max_length);
+  printf("  length: %zu\n", snake->length);
+  printf("  dir: %c\n", snake->dir);
+  printf("}\n");
+}
+
+void snake_change_direction(Snake* snake, const char direction) {
+  snake->dir = direction;
+}
+
+bool snake_collides(const Snake* snake, const BoardPiece* piece) {
+  for (size_t i = 0; i < snake->length; i++) {
+    BoardPiece part = snake->parts[i];
+    if (part.x == piece->x && part.y == piece->y) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool snake_collides_with_tail(const Snake* snake) {
+  const BoardPiece* head = snake->parts;
+  for (size_t i = 1; i < snake->length; i++) {
+    BoardPiece part = snake->parts[i];
+    if (part.x == head->x && part.y == head->y) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void snake_add_part(Snake* snake) {
+  if (snake->length == snake->max_length) {
+    fprintf(stderr, "ERROR: Cannot add another part to snake. Would exceed max_length.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  int x_shift;
+  int y_shift;
+  BoardPiece last_part;
+  char prev_part_dir;
+
+  if (snake->length == 1) {
+    last_part = snake_get_part(snake, 0);
+    prev_part_dir = snake->dir;
+  } else {
+    last_part = snake_get_part(snake, snake->length - 1);
+    const BoardPiece second_to_last_part = snake_get_part(snake, snake->length - 2);
+    
+    if (second_to_last_part.y < last_part.y && second_to_last_part.x == last_part.x) {
+      prev_part_dir = 'w';
+    } else if (second_to_last_part.x < last_part.x && second_to_last_part.y == last_part.y) {
+      prev_part_dir = 'a';
+    } else if (second_to_last_part.y > last_part.y && second_to_last_part.x == last_part.x) {
+      prev_part_dir = 's';
+    } else if (second_to_last_part.x > last_part.x && second_to_last_part.y == last_part.y) {
+      prev_part_dir = 'd';
+    } else {
+      fprintf(stderr, "%s:%d: ERROR: Invalid direction.\n", __FILE__, __LINE__);
+      exit(EXIT_FAILURE);
+    }
+  }
+
+  switch (prev_part_dir) {
+    case 'w':
+      x_shift = 0;
+      y_shift = 1;
+      break;
+    case 'a':
+      x_shift = 1;
+      y_shift = 0;
+      break;
+    case 's':
+      x_shift = 0;
+      y_shift = -1;
+      break;
+    case 'd':
+      x_shift = -1;
+      y_shift = 0;
+      break;
+    default:
+      fprintf(stderr, "%s:%d: ERROR: Invalid direction.\n", __FILE__, __LINE__);
+      exit(EXIT_FAILURE);
+  }
+
+  snake->parts[snake->length] = (BoardPiece){
+    .x = last_part.x + x_shift,
+    .y = last_part.y + y_shift,
+    .vis_char = snake_vis
+  };
+  snake->length++;
+}

+ 13 - 0
src/food.c

@@ -0,0 +1,13 @@
+#include <stdlib.h>
+
+#include "food.h"
+#include "Board.h"
+#include "BoardPiece.h"
+#include "Snake.h"
+
+void food_new_location(Board* board, BoardPiece* food, Snake* snake) {
+  do {
+    food->x = rand() % board->width;
+    food->y = rand() % board->height;
+  } while (food->x % 2 != 0 || snake_collides(snake, food));
+}

+ 1 - 1
src/headers/board.h → src/headers/Board.h

@@ -1,7 +1,7 @@
 #ifndef BOARD_H_
 #define BOARD_H_
 
-#include "snake.h"
+#include "Snake.h"
 
 typedef struct {
   size_t width;

+ 13 - 0
src/headers/BoardPiece.h

@@ -0,0 +1,13 @@
+#ifndef BOARD_PIECE_H_
+#define BOARD_PIECE_H_
+
+typedef struct {
+  int x;
+  int y;
+  char vis_char;
+} BoardPiece;
+
+void board_piece_print_info(BoardPiece* piece, char* name);
+bool pieces_collide(BoardPiece* piece1, BoardPiece* piece2);
+
+#endif // BOARD_PIECE_H_

+ 29 - 0
src/headers/Snake.h

@@ -0,0 +1,29 @@
+#ifndef SNAKE_H_
+#define SNAKE_H_
+
+#include <stddef.h>
+#include "BoardPiece.h"
+
+typedef struct {
+  BoardPiece* parts;
+  size_t max_length;
+  size_t length;
+  char dir;
+} Snake;
+
+BoardPiece snake_get_part(const Snake* snake, const size_t index);
+Snake snake_alloc(
+    const int board_square_count,
+    const int init_x,
+    const int init_y,
+    const char init_dir
+    );
+void snake_free(Snake* snake);
+void snake_move(Snake* snake, const int width, const int height);
+void snake_print_info(Snake* snake);
+void snake_change_direction(Snake* snake, const char direction);
+bool snake_collides(const Snake* snake, const BoardPiece* piece);
+bool snake_collides_with_tail(const Snake* snake);
+void snake_add_part(Snake* snake);
+
+#endif // SNAKE_H_

+ 10 - 0
src/headers/food.h

@@ -0,0 +1,10 @@
+#ifndef FOOD_H_
+#define FOOD_H_
+
+#include "BoardPiece.h"
+#include "Board.h"
+#include "Snake.h"
+
+void food_new_location(Board* board, BoardPiece* food, Snake* snake);
+
+#endif // FOOD_H_

+ 0 - 29
src/headers/snake.h

@@ -1,29 +0,0 @@
-#ifndef SNAKE_H_
-#define SNAKE_H_
-
-#include <stddef.h>
-
-typedef struct {
-  int x;
-  int y;
-  char vis_char;
-} SnakePart;
-
-typedef struct {
-  SnakePart* parts;
-  size_t max_length;
-  size_t length;
-  char dir;
-} Snake;
-
-SnakePart snake_get_part(const Snake* snake, const size_t index);
-Snake snake_alloc(
-    const int board_square_count,
-    const int init_x,
-    const int init_y,
-    const char init_dir
-    );
-void snake_free(Snake* snake);
-void snake_move(Snake* snake);
-
-#endif // SNAKE_H_

+ 79 - 17
src/main.c

@@ -6,55 +6,117 @@
 #include <string.h>
 #include <time.h>
 
-#include "board.h"
-#include "snake.h"
+#include "Board.h"
+#include "Snake.h"
 #include "utils.h"
+#include "food.h"
+#include "BoardPiece.h"
+
+#define TERMIOS 1
+#define DEBUG 0
 
 const char* VERSION = "1.1.9";
 
-const char SNAKE_VIS = '#';
+const char snake_vis = '#';
 
 void cmd_args(int argc, char** argv);
 void empty_stdin_buffer();
 void get_int_or_minus_one(int* dst);
 struct termios set_termios();
 
-int main() {
-  // cmd_args(argc, argv);
-
-  // const clock_t initClock = clock();
-
-  // Set gamespeed
-  // int gameSpeed = 0;
-  // printf("Enter gamespeed (default: 0, max: 180): ");
-  // getIntOrMinusOne(&gameSpeed);
-  // if (gameSpeed == -1 || gameSpeed > 180) {
-  //   gameSpeed = 0;
-  // }
+int main(int argc, char** argv) {
+  cmd_args(argc, argv);
 
+  srand(time(NULL));
 
   // Termios setup
+#if TERMIOS
   const struct termios attr_orig = set_termios();
+#endif // TERMIOS
 
   // Game model init
   const int board_w = 15;
   const int board_h = 15;
   Board board = board_alloc(board_w, board_h);
   Snake snake = snake_alloc(board_w * board_h, 0, 0, 'd');
+  BoardPiece food = { .vis_char = '$' };
+  food_new_location(&board, &food, &snake);
+  unsigned int score = 0;
 
   // Screen init
   system("clear");
 
+  int frame = 0;
+  int fps = 0;
+
+  long second_start = time(NULL);
+  int frame_stamp = frame;
+
   while (true) {
+    long second_check = time(NULL);
+    long elapsed_time = second_check - second_start;
+    if (elapsed_time >= 1) {
+      fps = frame - frame_stamp;
+      if (elapsed_time > 1) {
+        fps /= 2;
+      }
+      frame_stamp = frame;
+      second_start = time(NULL);
+    }
+
     system("clear");
-    snake_move(&snake);   
+
+    // Process input
+    char input = '0';
+    read(STDIN_FILENO, &input, 1);
+
+    if (isalpha(input)) {
+      if (
+          (input == 'w' && snake.dir != 's')
+          || (input == 'a' && snake.dir != 'd')
+          || (input == 's' && snake.dir != 'w')
+          || (input == 'd' && snake.dir != 'a')
+          ) {
+        snake_change_direction(&snake, input);
+      }
+    }
+
+    snake_move(&snake, board.width, board.height);   
     board_clear(&board);
     board_draw_snake(&board, &snake);
+    board_set_square(&board, food.x, food.y, food.vis_char);
+    printf("FPS: %d\n", fps);
     print_board(&board);
-    sleep_ms(1000);
+    printf("Score: %d\n", score);
+
+    BoardPiece snake_head = snake_get_part(&snake, 0);
+
+    if (snake_collides_with_tail(&snake)) {
+      printf("GAME OVER\n");
+      printf("Final score: %d\n", score);
+      exit(EXIT_SUCCESS);
+    }
+
+    if (pieces_collide(&snake_head, &food)) {
+      score++;
+      snake_add_part(&snake);
+      food_new_location(&board, &food, &snake);
+    }
+
+#if DEBUG
+    printf("input: %c\n", input);
+    printf("Frame: %d\n", frame);
+    snake_print_info(&snake);
+    board_piece_print_info(&food, "Food");
+#endif // DEBUG
+
+    frame++;
+    sleep_ms(150);
   }
 
+#if TERMIOS
   tcsetattr(STDIN_FILENO, 0, &attr_orig);
+#endif // TERMIOS
 
   return 0;
 }

+ 0 - 83
src/snake.c

@@ -1,83 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-
-#include "snake.h"
-
-static SnakePart* snake_get_part_ptr(const Snake* snake, const size_t index) {
-  if (index >= snake->max_length) {
-    return NULL;
-  }
-
-  return snake->parts + index;
-}
-
-SnakePart snake_get_part(const Snake* snake, const size_t index) {
-  assert(index < snake->max_length);
-  return snake->parts[index];
-}
-
-Snake snake_alloc(
-    const int board_square_count,
-    const int init_x,
-    const int init_y,
-    const char init_dir
-    )
-{
-  Snake snake = {0};
-  snake.max_length = board_square_count;
-  snake.length = 1;
-  snake.dir = init_dir;
-  snake.parts = (SnakePart*) malloc(sizeof(SnakePart) * snake.max_length);
-  snake.parts[0] = (SnakePart) { .x = init_x, .y = init_y, .vis_char = '&' };
-
-  return snake;
-}
-
-void snake_free(Snake* snake) {
-  free(snake->parts);
-}
-
-void snake_move(Snake* snake) {
-  SnakePart* first_part = snake_get_part_ptr(snake, 0);
-  int first_part_old_x = first_part->x;
-  int first_part_old_y = first_part->y;
-
-  // Move first part
-  switch (snake->dir) {
-    case 'w':
-      first_part->y -= 1;
-      break;
-    case 'a':
-      first_part->x -= 1;
-      break;
-    case 's':
-      first_part->y += 1;
-      break;
-    case 'd':
-      first_part->x += 1;
-      break;
-    default:
-      fprintf(stderr, "ERROR: Invalid direction in snake_move: %c.\n", snake->dir);
-      exit(EXIT_FAILURE);
-  }
-
-  // Stop here if there is only one part
-  SnakePart* last_part = snake_get_part_ptr(snake, snake->length - 1);
-  if (last_part == first_part) {
-    return;
-  }
-
-  // Move all other parts except for the second one, gets skipped if there are only 2 parts
-  SnakePart* second_part = first_part + 1;
-  SnakePart* prev_part = 0;
-  for (SnakePart* part = last_part; part != second_part; part--) {
-    prev_part = part - 1;
-    part->x = prev_part->x;
-    part->y = prev_part->y;
-  }
-
-  // Move second part
-  second_part->x = first_part_old_x;
-  second_part->y = first_part_old_y;
-}

+ 16 - 1
src/utils.c

@@ -3,6 +3,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <time.h>
+#include <errno.h>
 
 #include "utils.h"
 
@@ -23,5 +24,19 @@ void mallocError(const char* varName, const char* fileName, const char* function
 }
 
 void sleep_ms(const unsigned int ms) {
-  nanosleep(&(struct timespec){ .tv_sec = 0, .tv_nsec = ms * 1000000}, NULL);
+  struct timespec ts =  {
+    .tv_sec = ms / 1000
+  };
+  if (ts.tv_sec == 0) {
+    ts.tv_nsec = ms * 1000000;
+  } else {
+    ts.tv_nsec = (ms - ts.tv_sec * 1000) * 1000000;
+  }
+  // printf("timespec: {\n");
+  // printf("  tv_sec : %ld\n", ts.tv_sec);
+  // printf("  tv_nsec: %ld\n", ts.tv_nsec);
+  // printf("}\n");
+  if (nanosleep(&ts, NULL) == -1) {
+    fprintf(stderr, "ERROR: Failed to sleep. ERRNO: %d\n", errno);
+  }
 }

BIN
target/sanke