1 Commits

Author SHA1 Message Date
AaroSaila
855f646dea Clearer language 2024-12-15 12:58:35 +02:00
24 changed files with 528 additions and 709 deletions

2
.gitignore vendored
View File

@@ -1,2 +0,0 @@
.clangd
target

View File

@@ -9,8 +9,21 @@ Click image for video
## How it works ## How it works
The program makes the terminal use non-canonical input and output by using termios. Then it updates and prints a 2D character array containing the board borders, snake, and food. Before terminating, the program restores canonical mode. The program makes the terminal use non-canonical input and output by using termios. Then it updates and prints a 2D character array containing the board borders, snake, and food. Before terminating, the program restores canonical mode.
Each part of the snake is a node of a dynamically allocated linked list that contains:
- The snake's x and y coordinates
- The character that represents it on the board
- The part's direction
- A pointer to a order node (more on orders below)
- A pointer to the next snake part node
An order keeps information on when the direction of the snake part has to change and to which direction it should change. An order is also a node in a dynamically allocated linked list that contains:
- The direction that this order eventually tells the snake part to go to
- The delay i.e. how many ticks until the snake part has to change direction.
- A pointer to the next order
## Info ## Info
Only tested with the combination of Only tested and designed to work with the combination of
- GCC - GCC
- Linux - Linux
- x86 CPU - x86 CPU

View File

@@ -1,42 +1,3 @@
#!/bin/sh #!/bin/bash
arg=-1 gcc -Wall -Werror -pedantic -o sanke src/main.c src/snake/**.c src/utils/**.c
if [ -v 1 ]; then
arg=$1
fi
if [ $arg = "clean" ]; then
rm -rf ./target/*
exit
fi
version=$(git rev-parse HEAD)
oflag="-Og"
debug_flag="-ggdb"
if [ $arg = "release" ]; then
oflag="-O3"
debug_flag=""
fi
src="src/*.c"
macros="-DVERSION=\"$version\""
flags="-std=c23 $oflag $debug_flag $macros -Wall -Wextra -Werror -Wpedantic -pedantic-errors"
includes="-I src/headers"
cmd="gcc $flags $includes $src -o target/sanke"
echo $cmd
$cmd
if [ $? -gt 0 ]; then
exit
fi
if [ $arg = "run" ]; then
echo
./target/sanke
fi

View File

@@ -1,98 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "config.h"
#include "Board.h"
#include "Snake.h"
#define MAT_INDEX(mat, w, i, j) (mat)[(j) + (w) * (i)]
Board board_alloc(const int width, const int height) {
Board board = {};
board.width = width * 2;
board.height = height;
board.width_with_borders = board.width + 2;
board.height_with_borders = board.height + 2;
board.squares = (char*) malloc(sizeof(char) * board.width_with_borders * board.height_with_borders);
for (size_t i = 1; i < board.height_with_borders; i++) {
for (size_t j = 1; j < board.width_with_borders; j++) {
MAT_INDEX(board.squares, board.width_with_borders, i, j) = ' ';
}
}
// Create border pattern
const int width_with_borders_last_i = board.width_with_borders - 1;
const int height_with_borders_last_i = board.height_with_borders - 1;
// Vertical bars
for (int i = 1; i < height_with_borders_last_i; i++) {
MAT_INDEX(board.squares, board.width_with_borders, i, 0) = CHAR_BORDER_VER;
MAT_INDEX(board.squares, board.width_with_borders, i, width_with_borders_last_i) = CHAR_BORDER_VER;
}
// Horizontal lines
for (int j = 1; j < width_with_borders_last_i; j++) {
MAT_INDEX(board.squares, board.width_with_borders, 0, j) = CHAR_BORDER_HOR;
MAT_INDEX(board.squares, board.width_with_borders, height_with_borders_last_i, j) = CHAR_BORDER_HOR;
}
// Corners
MAT_INDEX(board.squares, board.width_with_borders, 0, 0) = CHAR_BORDER_CORNER_TL;
MAT_INDEX(board.squares, board.width_with_borders, height_with_borders_last_i, 0) = CHAR_BORDER_CORNER_BL;
MAT_INDEX(board.squares, board.width_with_borders, 0, width_with_borders_last_i) = CHAR_BORDER_CORNER_TR;
MAT_INDEX(board.squares, board.width_with_borders, height_with_borders_last_i, width_with_borders_last_i) = CHAR_BORDER_CORNER_BR;
return board;
}
void board_free(Board* board) {
free(board->squares);
}
bool board_coords_out_of_bounds(Board* board, const size_t x, const size_t y) {
return x >= board->width_with_borders || y >= board->height_with_borders;
}
void board_set_square(
Board* board,
int x,
int y,
const char ch
)
{
x++;
y++;
if (board_coords_out_of_bounds(board, x, y)) {
fprintf(stderr, "ERROR: Board coords out of bounds: x: %d y: %d\n", x, y);
exit(EXIT_FAILURE);
}
// assert(board_coords_out_of_bounds(board, x, y));
MAT_INDEX(board->squares, board->width_with_borders, y, x) = ch;
}
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);
board_set_square(board, j, i, ' ');
}
}
}
void board_draw_snake(Board* board, Snake* snake) {
BoardPiece part = {};
for (size_t i = 0; i < snake->length; i++) {
part = snake_get_part(snake, i);
board_set_square(board, part.x, part.y, part.vis_char);
}
}
void print_board(Board* board) {
for (size_t i = 0; i < board->height_with_borders; i++) {
for (size_t j = 0; j < board->width_with_borders; j++) {
printf("%c", MAT_INDEX(board->squares, board->width_with_borders, i, j));
}
printf("\n");
}
}

View File

@@ -1,15 +0,0 @@
#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;
}

View File

@@ -1,210 +0,0 @@
#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++;
}

View File

@@ -1,82 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "args.h"
static void handle_version() {
printf("Sanke version %s\n", VERSION);
exit(0);
}
static void set_width(Arguments* args, char* width_str) {
printf("width_str: %s\n", width_str);
int width = atoi(width_str);
if (width < 10) {
printf("Width must be between 10 and 50. Was %d.\n", width);
exit(0);
}
args->width = width;
}
static void set_height(Arguments* args, char* height_str) {
int height = atoi(height_str);
if (height < 10) {
printf("Width must be between 10 and 50. Was %d.\n", height);
exit(0);
}
args->height = height;
}
static void set_speed(Arguments* args, char* speed_str) {
int squares_per_second = atoi(speed_str);
if (squares_per_second < 1 || squares_per_second > 1000) {
printf("Speed must be between 1 and 1000. Was %d.\n", squares_per_second);
exit(0);
}
args->sleep_ms = 1000 / squares_per_second;
}
Arguments cmd_args(int argc, char** argv) {
Arguments args = {
.width = 15,
.height = 15,
.sleep_ms = 150
};
for (int i = 1; i < argc; i++) {
if (
strcmp(argv[i], "--version") == 0
|| strcmp(argv[i], "-v") == 0
)
{
handle_version();
} else if (
strcmp(argv[i], "--width") == 0
|| strcmp(argv[i], "-w") == 0
)
{
set_width(&args, argv[i + 1]);
} else if (
strcmp(argv[i], "--height") == 0
|| strcmp(argv[i], "-h") == 0
)
{
set_height(&args, argv[i + 1]);
} else if (
strcmp(argv[i], "--speed") == 0
|| strcmp(argv[i], "-s") == 0
)
{
set_speed(&args, argv[i + 1]);
}
}
return args;
}

31
src/board/board.c Normal file
View File

@@ -0,0 +1,31 @@
#include <stdio.h>
#include "../globals.h"
extern boardInfo brdInfo;
void setBoardBorders(char board[][brdInfo.x]) {
for (int i = 0; i < brdInfo.y; i++) {
for (int j = 0; j < brdInfo.x; j++) {
if (i == 0 || i == brdInfo.y - 1)
board[i][j] = '-';
else if (j == 0 || j == brdInfo.x - 1)
board[i][j] = '|';
else
board[i][j] = ' ';
}
}
board[0][0] = '+';
board[brdInfo.y - 1][0] = '+';
board[0][brdInfo.x - 1] = '+';
board[brdInfo.y - 1][brdInfo.x - 1] = '+';
}
void printBoard(char board[][brdInfo.x]) {
for (int i = 0; i < brdInfo.y; i++) {
for (int j = 0; j < brdInfo.x; j++) {
printf("%c", board[i][j]);
}
printf("\n");
}
}

11
src/board/board.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef BOARD_H_
#define BOARD_H_
#include "../globals.h"
extern boardInfo brdInfo;
void setBoardBorders(char board[][brdInfo.x]);
void printBoard(char board[][brdInfo.x]);
#endif

View File

@@ -1,13 +0,0 @@
#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));
}

16
src/globals.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef GLOBALS_H_
#define GLOBALS_H_
typedef struct {
int x;
int y;
} boardInfo;
typedef struct {
int xs;
int xe;
int ys;
int ye;
} playableBoardInfo;
#endif // GLOBALS_H_

View File

@@ -1,30 +0,0 @@
#ifndef BOARD_H_
#define BOARD_H_
#include "Snake.h"
typedef struct {
size_t width;
size_t height;
size_t width_with_borders;
size_t height_with_borders;
char* squares;
} Board;
Board board_alloc(
const int width,
const int height
);
void board_free(Board* board);
bool board_coords_out_of_bounds(Board* board, const size_t x, const size_t y);
void board_set_square(
Board* board,
const int x,
const int y,
const char ch
);
void board_clear(Board* board);
void board_draw_snake(Board* board, Snake* snake);
void print_board(Board* board);
#endif

View File

@@ -1,13 +0,0 @@
#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_

View File

@@ -1,29 +0,0 @@
#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_

View File

@@ -1,12 +0,0 @@
#ifndef ARGS_H_
#define ARGS_H_
typedef struct {
int width;
int height;
int sleep_ms;
} Arguments;
Arguments cmd_args(int argc, char** argv);
#endif // ARGS_H_

View File

@@ -1,11 +0,0 @@
#ifndef CONFIG_H_
#define CONFIG_H_
#define CHAR_BORDER_VER '|'
#define CHAR_BORDER_HOR '-'
#define CHAR_BORDER_CORNER_TL '+'
#define CHAR_BORDER_CORNER_TR '+'
#define CHAR_BORDER_CORNER_BL '+'
#define CHAR_BORDER_CORNER_BR '+'
#endif // CONFIG_H_

View File

@@ -1,10 +0,0 @@
#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_

View File

@@ -1,8 +0,0 @@
#ifndef UTILS_H_
#define UTILS_H_
int randomInt(const int start, const int end, const unsigned int seed);
void mallocError(const char* varName, const char* fileName, const char* functionName);
void sleep_ms(const unsigned int ms);
#endif // UTILS_H_

View File

@@ -3,110 +3,46 @@
#include <termios.h> #include <termios.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <string.h>
#include <time.h> #include <time.h>
#include <stdbool.h>
#include "./utils/utils.h"
#include "./snake/snake.h"
#include "./board/board.h"
#include "globals.h"
#include "Board.h" const char SNAKE_VIS = '#';
#include "Snake.h"
#include "utils.h"
#include "food.h"
#include "BoardPiece.h"
#include "args.h"
#define TERMIOS 1 boardInfo brdInfo;
#define DEBUG 0 playableBoardInfo plBrdInfo;
const char snake_vis = '#'; int main() {
const clock_t initClock = clock();
struct termios set_termios(); // Board Constraints
printf("Set board size (15 - 60): ");
scanf("%d", &brdInfo.y);
if (!(brdInfo.y >= 15 && brdInfo.y <= 60)) {
printf("Invalid input. Board size must be greater than 0.\n");
exit(0);
}
brdInfo.x = brdInfo.y * 2;
printf("brdInfo.x: %d\n", brdInfo.x);
printf("brdInfo.y: %d\n", brdInfo.y);
char board[brdInfo.y][brdInfo.x];
int main(int argc, char** argv) { plBrdInfo.xs = 1;
Arguments args = cmd_args(argc, argv); plBrdInfo.xe = brdInfo.x - 2;
plBrdInfo.ys = 1;
plBrdInfo.ye = brdInfo.y - 2;
srand(time(NULL));
// Termios setup // Termios setup
#if TERMIOS
const struct termios attr_orig = set_termios();
#endif // TERMIOS
// Game model init
const int board_w = args.width;
const int board_h = args.height;
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");
#if DEBUG
long long frame = 0;
#endif // DEBUG
while (true) {
system("clear");
// 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);
print_board(&board);
printf("Score: %d\n", score);
BoardPiece snake_head = snake_get_part(&snake, 0);
if (snake_collides_with_tail(&snake)) {
printf("GAME OVER\n");
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");
frame++;
#endif // DEBUG
sleep_ms(args.sleep_ms);
}
#if TERMIOS
tcsetattr(STDIN_FILENO, 0, &attr_orig);
#endif // TERMIOS
return 0;
}
struct termios set_termios() {
struct termios attr; struct termios attr;
tcgetattr(STDIN_FILENO, &attr); tcgetattr(STDIN_FILENO, &attr);
const struct termios attr_orig = attr; const struct termios ATTR_ORIG = attr;
attr.c_lflag &= ~(ECHO | ICANON); attr.c_lflag &= ~(ECHO | ICANON);
attr.c_cc[VMIN] = 0; attr.c_cc[VMIN] = 0;
@@ -114,5 +50,113 @@ struct termios set_termios() {
tcsetattr(STDIN_FILENO, 0, &attr); tcsetattr(STDIN_FILENO, 0, &attr);
return attr_orig; // Game board setup
setBoardBorders(board);
int points = 0;
int gameSpeed = 0;
const int sleepInterval = 200;
// Snake head setup
snakePart* snakeHead = (snakePart*) malloc(sizeof(snakePart));
snakeHead->x = randomX(initClock);
snakeHead->y = randomY(initClock);
snakeHead->visChar = '&';
snakeHead->dir = 'w';
snakeHead->order = (order*) malloc(sizeof(order));
snakeHead->order->dir = snakeHead->dir;
snakeHead->order->delay = -1;
snakeHead->order->next = NULL;
snakeHead->next = NULL;
board[snakeHead->y][snakeHead->x] = snakeHead->visChar;
// Food setup
struct {
int x;
int y;
char visChar;
} food;
food.x = randomX(initClock);
food.y = randomY(initClock);
food.visChar = '$';
board[food.y][food.x] = food.visChar;
// Screen init
system("clear");
// Game loop
while (1) {
fflush(stdout);
char buf[1] = {0};
// Food collision
if (snakeHead->x == food.x
&& snakeHead->y == food.y) {
points++;
if (points % 5 == 0)
gameSpeed += 15;
do {
food.x = randomX(initClock);
food.y = randomY(initClock);
} while (checkCollision(snakeHead, food.x, food.y));
addSnakePart(board, snakeHead);
}
// Input handling
if (read(STDIN_FILENO, buf, 1) != 0 && isalpha(buf[0])) {
char input = buf[0];
input = tolower(input);
if (
(input == 'w' && snakeHead->dir != 's')
|| (input == 's' && snakeHead->dir != 'w')
|| (input == 'a' && snakeHead->dir != 'd')
|| (input == 'd' && snakeHead->dir != 'a')
) {
snakeHead->dir = input;
addOrders(snakeHead, input);
}
}
// Update board
board[food.y][food.x] = food.visChar;
mvSnakeParts(board, snakeHead);
// Snake collision
if (snakeHead->next != NULL) {
snakePart* current = snakeHead->next;
while (1) {
if (current->x == snakeHead->x
&& current->y == snakeHead->y)
goto game_over;
if (current->next == NULL)
break;
current = current->next;
}
}
system("clear");
printBoard(board);
printf("Points: %d\n", points);
printf("Game Speed: %d\n", gameSpeed);
sleep_ms(sleepInterval - gameSpeed);
}
game_over:
// Game over
system("clear");
printBoard(board);
printf("Final Points: %d\n", points);
printf("Final Game Speed: %d\n", gameSpeed);
tcsetattr(STDIN_FILENO, 0, &ATTR_ORIG);
return 0;
} }

203
src/snake/snake.c Normal file
View File

@@ -0,0 +1,203 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "snake.h"
#include "../board/board.h"
extern char SNAKE_VIS;
extern boardInfo brdInfo;
extern playableBoardInfo plBrdInfo;
void mvSnakeParts(char board[][brdInfo.x], snakePart* head) {
snakePart* part = head;
while (1) {
order* orderHead = part->order;
if (orderHead->next != NULL) {
if (orderHead->next->delay == 0) {
part->dir = orderHead->next->dir;
removeOrder(orderHead);
}
}
if (orderHead->next != NULL) {
order* current = orderHead->next;
while (1) {
if (current->delay > 0)
current->delay--;
if (current->next == NULL)
break;
current = current->next;
}
}
int x = part->x;
int y = part->y;
board[y][x] = ' ';
switch (part->dir) {
case 'w':
y = y - 1 < plBrdInfo.ys ? plBrdInfo.ye : y - 1;
break;
case 's':
y = y + 1 > plBrdInfo.ye ? plBrdInfo.ys : y + 1;
break;
case 'a':
x = x - 2 < plBrdInfo.xs ? plBrdInfo.xe - 1 : x - 2;
break;
case 'd':
x = x + 2 > plBrdInfo.xe ? plBrdInfo.xs : x + 2;
break;
default:
printf("ERROR in func mvSnakeParts\n");
printf("dir: %c\n", part->dir);
exit(1);
}
part->x = x;
part->y = y;
board[y][x] = part->visChar;
if (part->next == NULL)
return;
part = part->next;
}
}
void addSnakePart(char board[][brdInfo.x], snakePart* head) {
snakePart* tail = head;
while (tail->next != NULL)
tail = tail->next;
snakePart* newTail = (snakePart*) malloc(sizeof(snakePart));
newTail->visChar = SNAKE_VIS;
newTail->dir = tail->dir;
// Order head
newTail->order = (order*) malloc(sizeof(order));
newTail->order->dir = newTail->dir;
newTail->order->delay = -1;
// First order if exists
if (tail->order->next != NULL)
copyOrders(tail->order, newTail->order);
switch (newTail->dir) {
case 'w':
newTail->x = tail->x;
newTail->y = tail->y + 1;
break;
case 's':
newTail->x = tail->x;
newTail->y = tail->y - 1;
break;
case 'a':
newTail->x = tail->x + 2;
newTail->y = tail->y;
break;
case 'd':
newTail->x = tail->x - 2;
newTail->y = tail->y;
break;
default:
printf("Invalid direction in func addSnakePart\n");
exit(1);
}
/*
Untested fix to a bug where the new part
would replace a part of the board border.
*/
if (newTail->x > plBrdInfo.xe) {
newTail->x = plBrdInfo.xs;
} else if (newTail->x < plBrdInfo.xs) {
newTail->x = plBrdInfo.xe;
}
if (newTail->y > plBrdInfo.ye) {
newTail->y = plBrdInfo.ys;
} else if (newTail->y < plBrdInfo.ys) {
newTail->y = plBrdInfo.ye;
}
// bugfix end
newTail->next = NULL;
tail->next = newTail;
board[newTail->y][newTail->x] = newTail->visChar;
}
void pushOrder(order* head, char dir, int delay) {
order* current = head;
while (current->next != NULL)
current = current->next;
order* newOrder = (order*) malloc(sizeof(order));
newOrder->dir = dir;
newOrder->delay = delay;
newOrder->next = NULL;
current->next = newOrder;
}
void removeOrder(order* head) {
order* newFirstOrder = head->next->next;
free(head->next);
head->next = newFirstOrder;
}
void addOrders(snakePart* head, char dir) {
snakePart* current = head;
int i = 1;
while (1) {
if (current->next == NULL)
break;
current = current->next;
pushOrder(current->order, dir, i);
i++;
}
}
void copyOrders(order* srcHead, order* destHead) {
if (srcHead->next == NULL) {
printf("ERROR in copyOrders: no orders to copy\n");
exit(1);
}
order* srcCurrent = srcHead;
order* destCurrent = destHead;
order* destPrev = destHead;
while (srcCurrent->next != NULL) {
srcCurrent = srcCurrent->next;
destPrev = destCurrent;
destCurrent = (order*) malloc(sizeof(order));
destPrev->next = destCurrent;
destCurrent->dir = srcCurrent->dir;
destCurrent->delay = srcCurrent->delay + 1;
destCurrent->next = NULL;
}
}
bool checkCollision(snakePart* head, int x, int y) {
snakePart* current = head;
while (1) {
if (current->x == x && current->y == y)
return true;
if (current->next == NULL)
break;
current = current->next;
}
return false;
}

32
src/snake/snake.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef SNAKE_H_
#define SNAKE_H_
#include "../globals.h"
extern boardInfo brdInfo;
typedef struct orderNode {
char dir;
int delay;
struct orderNode* next;
} order;
typedef struct snakeNode {
int x;
int y;
char visChar;
char dir;
order* order;
struct snakeNode* next;
} snakePart;
void mvSnakeParts(char board[][brdInfo.x], snakePart* head);
void addSnakePart(char board[][brdInfo.x], snakePart* head);
void pushOrder(order* head, char dir, int delay);
void removeOrder(order* head);
void addOrders(snakePart* head, char dir);
void copyOrders(order* srcHead, order* destHead);
bool checkCollision(snakePart* head, int x, int y);
#endif // SNAKE_H_

View File

@@ -1,42 +0,0 @@
#define _POSIX_C_SOURCE 199309L
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include "utils.h"
int randomInt(const int start, const int end, const unsigned int seed) {
/*
* Gets random int from range
* [start, end[
*/
srand(seed);
int result = rand() % end;
result = result >= start ? result : result + start;
return result;
}
void mallocError(const char* varName, const char* fileName, const char* functionName) {
printf("Ran out of memory to allocate to %s in %s/%s\n", varName, fileName, functionName);
exit(1);
}
void sleep_ms(const unsigned int ms) {
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);
}
}

67
src/utils/utils.c Normal file
View File

@@ -0,0 +1,67 @@
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include "utils.h"
extern boardInfo brdInfo;
extern playableBoardInfo plBrdInfo;
int randomInt(const int start, const int end, const unsigned int seed) {
/*
* Gets random int from range
* [start, end[
*/
srand(seed);
int result = rand() % end;
result = result >= start ? result : result + start;
return result;
}
int randomX(const clock_t initClock) {
int x;
const unsigned int seed = clock() - initClock;
for (int i = randomInt(0, 100, seed); ; i++) {
x = randomInt(plBrdInfo.xs, plBrdInfo.xe, i);
if (x % 2 != 0)
break;
}
return x;
}
int randomY(const clock_t initClock) {
const unsigned int seed = clock() - initClock;
return randomInt(plBrdInfo.ys, plBrdInfo.ye, seed);
}
void sleep_ms(const int ms) {
usleep(ms * 1000);
}
void setBoardBorders(char board[][brdInfo.x]) {
for (int i = 0; i < brdInfo.y; i++) {
for (int j = 0; j < brdInfo.x; j++) {
if (i == 0 || i == brdInfo.y - 1)
board[i][j] = '-';
else if (j == 0 || j == brdInfo.x - 1)
board[i][j] = '|';
else
board[i][j] = ' ';
}
}
board[0][0] = '+';
board[brdInfo.y - 1][0] = '+';
board[0][brdInfo.x - 1] = '+';
board[brdInfo.y - 1][brdInfo.x - 1] = '+';
}
void printBoard(char board[][brdInfo.x]) {
for (int i = 0; i < brdInfo.y; i++) {
for (int j = 0; j < brdInfo.x; j++) {
printf("%c", board[i][j]);
}
printf("\n");
}
}

16
src/utils/utils.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef UTILS_H_
#define UTILS_H_
#include <time.h>
#include "../globals.h"
extern boardInfo brdInfo;
int randomInt(const int start, const int end, const unsigned int seed);
int randomX(const clock_t initClock);
int randomY(const clock_t initClock);
void sleep_ms(const int ms);
void setBoardBorders(char board[][brdInfo.x]);
void printBoard(char board[][brdInfo.x]);
#endif // UTILS_H_