diff --git a/CMakeLists.txt b/CMakeLists.txt index 413095b..93bdeb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(snake) # Specify the source files set(SOURCES main.c - #tetris.c + snake.c #util.c #file3.c ) @@ -24,4 +24,4 @@ target_compile_options(snake PUBLIC -std=c99 -Wall) # Used libraries -#target_link_libraries(tetris PUBLIC ncurses SDL2 SDL2_mixer) +target_link_libraries(snake PUBLIC ncurses) diff --git a/main.c b/main.c index e69de29..9379241 100644 --- a/main.c +++ b/main.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + + + +int main() { + srand(time(NULL)); // Seed for random number generation + initscr(); // Initialize ncurses + noecho(); // Don't echo pressed keys + curs_set(0); // Hide the cursor + setup(); + + while (!gameOver()) { + draw(); + input(); + logic(); + usleep(100000); // Sleep for a short duration + } + + endwin(); // End ncurses mode + printf("Game Over!\n"); + return 0; +} \ No newline at end of file diff --git a/snake.c b/snake.c new file mode 100644 index 0000000..f027983 --- /dev/null +++ b/snake.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include + +#define WIDTH 20 +#define HEIGHT 20 + +typedef struct { + int x; + int y; +} Point; + +typedef struct { + Point body[100]; // Maximum length of the snake + int length; +} Snake; + +Point food; +Snake snake; +int direction; // 0: UP, 1: DOWN, 2: LEFT, 3: RIGHT + +void setup() { + snake.length = 1; + snake.body[0].x = WIDTH / 2; + snake.body[0].y = HEIGHT / 2; + direction = 3; // Start moving to the right + generateFood(); +} + + +void draw() { + clear(); // Clear the screen + + for (int i = 0; i < WIDTH + 2; i++) + printw("#"); + printw("\n"); + + for (int i = 0; i < HEIGHT; i++) { + for (int j = 0; j < WIDTH; j++) { + if (j == 0) + printw("#"); // Left wall + if (i == food.y && j == food.x) + printw("F"); // Food + else { + int isSnakePart = 0; + for (int k = 0; k < snake.length; k++) { + if (snake.body[k].x == j && snake.body[k].y == i) { + printw("O"); // Snake body + isSnakePart = 1; + } + } + if (!isSnakePart) + printw(" "); + } + if (j == WIDTH - 1) + printw("#"); // Right wall + } + printw("\n"); + } + + for (int i = 0; i < WIDTH + 2; i++) + printw("#"); + printw("\n"); + refresh(); // Refresh the screen to show changes +} + + +void input() { + keypad(stdscr, TRUE); // Enable special keys + halfdelay(1); // Wait for input for a short time + + int ch = getch(); + switch (ch) { + case KEY_UP: + if (direction != 1) direction = 0; // Up + break; + case KEY_DOWN: + if (direction != 0) direction = 1; // Down + break; + case KEY_LEFT: + if (direction != 3) direction = 2; // Left + break; + case KEY_RIGHT: + if (direction != 2) direction = 3; // Right + break; + } +} + + +void logic() { + Point prevSegment = snake.body[0]; + Point prev2Segment; + + // Update the head of the snake based on the direction + switch (direction) { + case 0: // UP + snake.body[0].y--; + break; + case 1: // DOWN + snake.body[0].y++; + break; + case 2: // LEFT + snake.body[0].x--; + break; + case 3: // RIGHT + snake.body[0].x++; + break; + } + + // Update the body segments + for (int i = 1; i < snake.length; i++) { + prev2Segment = snake.body[i]; + snake.body[i] = prevSegment; + prevSegment = prev2Segment; + } + + // Check if the snake has eaten the food + if (snake.body[0].x == food.x && snake.body[0].y == food.y) { + snake.length++; + generateFood(); // Generate new food + } +} + +void generateFood() { + int validPosition = 0; + while (!validPosition) { + food.x = rand() % WIDTH; + food.y = rand() % HEIGHT; + + // Check if the food position overlaps with the snake + validPosition = 1; // Assume valid until proven otherwise + for (int i = 0; i < snake.length; i++) { + if (snake.body[i].x == food.x && snake.body[i].y == food.y) { + validPosition = 0; // Found overlap, need to generate again + break; + } + } + } +} + +int gameOver() { + // Check for wall collisions + if (snake.body[0].x < 0 || snake.body[0].x >= WIDTH || + snake.body[0].y < 0 || snake.body[0].y >= HEIGHT) { + return 1; // Game over due to wall collision + } + + // Check for self-collision + for (int i = 1; i < snake.length; i++) { + if (snake.body[i].x == snake.body[0].x && + snake.body[i].y == snake.body[0].y) { + return 1; // Game over due to self-collision + } + } + + return 0; // Game is still running +} diff --git a/snake.h b/snake.h new file mode 100644 index 0000000..a812a89 --- /dev/null +++ b/snake.h @@ -0,0 +1,29 @@ +/***************************************************************************//** + + @file snake.h + + @author Jiri Stach + + @date Created $date + + @brief Snake game declarations. + + @copyright Copyright (c), Jiri Stach. Released under the Revised + BSD License. See LICENSE.txt for details. + +*******************************************************************************/ + +#ifndef SNAKE_H +#define SNAKE_H + + +void setup(); +void draw(); +void input(); +void logic(); +void generateFood(); +int gameOver(); + + + +#endif // SNAKE_H \ No newline at end of file