commit 3d79b8c199783143563149b6a2e7e1382ade0d60 Author: Darkheim Date: Tue Jul 8 21:50:28 2025 -0500 Initial commit: Console Snake game diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fdf9154 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Build directories +build/ +cmake-build-*/ + +# CMake files +CMakeCache.txt +CMakeFiles/ +Makefile +cmake_install.cmake +build.ninja +rules.ninja + +# Executables +Snake +*.exe + +# IDE files +.idea/ +.vscode/ +*.user + +# Testing +Testing/ + +# OS files +.DS_Store \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..73cf1f3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.10) +project(Snake) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Find SFML +find_package(SFML 2.5 COMPONENTS graphics window system REQUIRED) + +# Add executable +add_executable(Snake + main.cpp + Game.cpp + Snake.cpp + Food.cpp +) + +# Include headers +target_include_directories(Snake PRIVATE .) + +# Link SFML libraries +target_link_libraries(Snake sfml-graphics sfml-window sfml-system) diff --git a/Direction.h b/Direction.h new file mode 100644 index 0000000..2708bf6 --- /dev/null +++ b/Direction.h @@ -0,0 +1,6 @@ +#ifndef DIRECTION_H +#define DIRECTION_H + +enum class Direction { UP, DOWN, LEFT, RIGHT }; + +#endif //DIRECTION_H diff --git a/Food.cpp b/Food.cpp new file mode 100644 index 0000000..defce49 --- /dev/null +++ b/Food.cpp @@ -0,0 +1,20 @@ +#include "Food.h" +#include +#include + +void Food::generateNew(int maxX, int maxY, const std::vector& occupiedPositions) +{ + std::random_device rd; + std::mt19937 gen(rd()); + + std::uniform_int_distribution<> disX(0, maxX - 1); + std::uniform_int_distribution<> disY(0, maxY - 1); + + Point newPos; + do + { + newPos = Point(disX(gen), disY(gen)); + } while (std::find(occupiedPositions.begin(), occupiedPositions.end(), newPos) != occupiedPositions.end()); + + position = newPos; +} diff --git a/Food.h b/Food.h new file mode 100644 index 0000000..6fa632a --- /dev/null +++ b/Food.h @@ -0,0 +1,17 @@ +#ifndef FOOD_H +#define FOOD_H + +#include "Point.h" +#include + +class Food +{ + Point position; + +public: + Food() : position(0, 0) {} + + void generateNew(int maxX, int maxY, const std::vector& occupiedPositions); + Point getPosition() const { return position; } +}; +#endif //FOOD_H diff --git a/Game.cpp b/Game.cpp new file mode 100644 index 0000000..47f56e7 --- /dev/null +++ b/Game.cpp @@ -0,0 +1,492 @@ +#include "Game.h" +#include +#include +#include +#include +#include + +Game::Game() + : snake(Point(BOARD_WIDTH / 2, BOARD_HEIGHT / 2)), + score(0), + bestScore(0), + gameOver(false), + paused(false), + inMenu(true) +{ + loadBestScore(); + generateFood(); +} + +void Game::run() +{ + sf::RenderWindow window(sf::VideoMode(BOARD_WIDTH * CELL_SIZE, BOARD_HEIGHT * CELL_SIZE + 60), "Snake Game"); + sf::Clock clock; + float timer = 0.0f, delay = 0.15f; + + // Menu buttons initialization + MenuButton startBtn, exitBtn, restartBtn, menuBtn; + startBtn.rect = sf::FloatRect(CELL_SIZE * BOARD_WIDTH / 2 - 110, 180, 220, 48); + startBtn.label = "Start"; + startBtn.color = sf::Color(80, 180, 80); + startBtn.hoverColor = sf::Color(120, 220, 120); + + exitBtn.rect = sf::FloatRect(BOARD_WIDTH * CELL_SIZE / 2 - 110, 250, 220, 40); + exitBtn.label = "Exit"; + exitBtn.color = sf::Color(180, 80, 80); + exitBtn.hoverColor = sf::Color(220, 120, 120); + + restartBtn.rect = sf::FloatRect(BOARD_WIDTH * CELL_SIZE / 2 - 110, 180, 220, 48); + restartBtn.label = "Restart"; + restartBtn.color = sf::Color(80, 180, 80); + restartBtn.hoverColor = sf::Color(120, 220, 120); + + menuBtn.rect = sf::FloatRect(BOARD_WIDTH * CELL_SIZE / 2 - 110, 250, 220, 40); + menuBtn.label = "Menu"; + menuBtn.color = sf::Color(180, 80, 80); + menuBtn.hoverColor = sf::Color(220, 120, 120); + + while (window.isOpen()) + { + sf::Event event; + while (window.pollEvent(event)) + { + if (event.type == sf::Event::Closed) + window.close(); + processEvent(event, &window, &startBtn, &exitBtn, &restartBtn, &menuBtn); + } + + if (!paused && !gameOver && !inMenu) + { + timer += clock.restart().asSeconds(); + if (timer > delay) + { + update(); + timer = 0; + } + } + else + { + clock.restart(); + } + + render(window, &startBtn, &exitBtn, &restartBtn, &menuBtn); + } +} + +// Handles all events, including mouse support for menu buttons +void Game::processEvent(const sf::Event& event, sf::RenderWindow* window, + MenuButton* startBtn, MenuButton* exitBtn, + MenuButton* restartBtn, MenuButton* menuBtn) +{ + sf::Vector2f mousePos; + if (window) + mousePos = window->mapPixelToCoords(sf::Mouse::getPosition(*window)); + + // Reset hover state for all buttons + if (startBtn) startBtn->hovered = false; + if (exitBtn) exitBtn->hovered = false; + if (restartBtn) restartBtn->hovered = false; + if (menuBtn) menuBtn->hovered = false; + + if (inMenu) + { + if (startBtn && startBtn->rect.contains(mousePos)) + startBtn->hovered = true; + if (exitBtn && exitBtn->rect.contains(mousePos)) + exitBtn->hovered = true; + + if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) + { + if (startBtn && startBtn->rect.contains(mousePos)) + { + inMenu = false; + gameOver = false; + paused = false; + reset(); + return; + } + if (exitBtn && exitBtn->rect.contains(mousePos)) + { + if (window) window->close(); + return; + } + } + if (event.type == sf::Event::KeyPressed) + { + if (event.key.code == sf::Keyboard::Enter) + { + inMenu = false; + gameOver = false; + paused = false; + reset(); + } + else if (event.key.code == sf::Keyboard::Escape) + { + if (window) window->close(); + } + } + return; + } + + if (gameOver) + { + if (restartBtn && restartBtn->rect.contains(mousePos)) + restartBtn->hovered = true; + if (menuBtn && menuBtn->rect.contains(mousePos)) + menuBtn->hovered = true; + + if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) + { + if (restartBtn && restartBtn->rect.contains(mousePos)) + { + reset(); + gameOver = false; + paused = false; + inMenu = false; + return; + } + if (menuBtn && menuBtn->rect.contains(mousePos)) + { + inMenu = true; + return; + } + } + if (event.type == sf::Event::KeyPressed) + { + if (event.key.code == sf::Keyboard::Enter) + { + reset(); + gameOver = false; + paused = false; + inMenu = false; + } + else if (event.key.code == sf::Keyboard::Escape) + { + inMenu = true; + } + } + return; + } + + // Snake controls + if (event.type == sf::Event::KeyPressed) + { + switch (event.key.code) + { + case sf::Keyboard::W: + case sf::Keyboard::Up: + if (snake.getDirection() != Direction::DOWN) + snake.setDirection(Direction::UP); + paused = false; + break; + case sf::Keyboard::S: + case sf::Keyboard::Down: + if (snake.getDirection() != Direction::UP) + snake.setDirection(Direction::DOWN); + paused = false; + break; + case sf::Keyboard::A: + case sf::Keyboard::Left: + if (snake.getDirection() != Direction::RIGHT) + snake.setDirection(Direction::LEFT); + paused = false; + break; + case sf::Keyboard::D: + case sf::Keyboard::Right: + if (snake.getDirection() != Direction::LEFT) + snake.setDirection(Direction::RIGHT); + paused = false; + break; + case sf::Keyboard::Space: + paused = !paused; + break; + case sf::Keyboard::Q: + case sf::Keyboard::Escape: + inMenu = true; + break; + default: + break; + } + } +} + +// Rendering with interactive menu buttons and centered text +void Game::render(sf::RenderWindow& window, + MenuButton* startBtn, MenuButton* exitBtn, + MenuButton* restartBtn, MenuButton* menuBtn) +{ + // Draw gradient background + sf::VertexArray background(sf::Quads, 4); + background[0].position = sf::Vector2f(0, 0); + background[1].position = sf::Vector2f(window.getSize().x, 0); + background[2].position = sf::Vector2f(window.getSize().x, window.getSize().y); + background[3].position = sf::Vector2f(0, window.getSize().y); + background[0].color = sf::Color(40, 40, 60); + background[1].color = sf::Color(40, 40, 60); + background[2].color = sf::Color(20, 20, 30); + background[3].color = sf::Color(20, 20, 30); + window.draw(background); + + // Draw game border + sf::RectangleShape border(sf::Vector2f(BOARD_WIDTH * CELL_SIZE, BOARD_HEIGHT * CELL_SIZE)); + border.setPosition(0, 0); + border.setFillColor(sf::Color::Transparent); + border.setOutlineThickness(3); + border.setOutlineColor(sf::Color(180, 180, 220)); + window.draw(border); + + // Draw food with highlight + sf::CircleShape foodCircle(CELL_SIZE / 2 - 3); + foodCircle.setFillColor(sf::Color(255, 80, 80)); + foodCircle.setOutlineThickness(2); + foodCircle.setOutlineColor(sf::Color(255, 180, 180)); + foodCircle.setOrigin(foodCircle.getRadius(), foodCircle.getRadius()); + foodCircle.setPosition(food.getPosition().x * CELL_SIZE + CELL_SIZE / 2, food.getPosition().y * CELL_SIZE + CELL_SIZE / 2); + window.draw(foodCircle); + + // Draw snake + for (const auto& segment : snake.getBody()) + { + sf::RectangleShape cell(sf::Vector2f(CELL_SIZE - 4, CELL_SIZE - 4)); + cell.setPosition(segment.x * CELL_SIZE + 2, segment.y * CELL_SIZE + 2); + if (segment == snake.getHead()) { + cell.setFillColor(sf::Color(80, 255, 80)); + cell.setOutlineThickness(2); + cell.setOutlineColor(sf::Color(40, 180, 40)); + } else { + cell.setFillColor(sf::Color(100, 200, 100)); + cell.setOutlineThickness(1); + cell.setOutlineColor(sf::Color(60, 120, 60)); + } + window.draw(cell); + } + + // Helper for drawing shadowed text + auto drawShadowedText = [&](sf::RenderTarget& target, const sf::Text& text, sf::Color shadowColor = sf::Color(0,0,0,120), float offset = 2.f) { + sf::Text shadow = text; + shadow.setFillColor(shadowColor); + shadow.move(offset, offset); + target.draw(shadow); + target.draw(text); + }; + + static sf::Font font; + static bool fontLoaded = false; + if (!fontLoaded) { + fontLoaded = font.loadFromFile("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"); + } + + if (fontLoaded) + { + sf::Text text; + text.setFont(font); + text.setCharacterSize(22); + text.setFillColor(sf::Color::White); + + if (inMenu) + { + // Title + text.setString("SNAKE GAME"); + text.setCharacterSize(38); + text.setStyle(sf::Text::Bold); + sf::FloatRect titleRect = text.getLocalBounds(); + text.setOrigin(titleRect.left + titleRect.width / 2.0f, titleRect.top + titleRect.height / 2.0f); + text.setPosition(BOARD_WIDTH * CELL_SIZE / 2.0f, 80); + drawShadowedText(window, text); + + // Best score + text.setString("Best score: " + std::to_string(bestScore)); + text.setCharacterSize(22); + text.setStyle(sf::Text::Regular); + sf::FloatRect bestRect = text.getLocalBounds(); + text.setOrigin(bestRect.left + bestRect.width / 2.0f, bestRect.top + bestRect.height / 2.0f); + text.setPosition(BOARD_WIDTH * CELL_SIZE / 2.0f, 130); + drawShadowedText(window, text); + + // Start button + sf::RectangleShape startShape(sf::Vector2f(startBtn->rect.width, startBtn->rect.height)); + startShape.setPosition(startBtn->rect.left, startBtn->rect.top); + startShape.setFillColor(startBtn->hovered ? startBtn->hoverColor : startBtn->color); + startShape.setOutlineThickness(3); + startShape.setOutlineColor(sf::Color(40, 120, 40)); + window.draw(startShape); + + text.setString("Start"); + text.setCharacterSize(24); + text.setFillColor(sf::Color::White); + sf::FloatRect startTextRect = text.getLocalBounds(); + text.setOrigin(startTextRect.left + startTextRect.width / 2.0f, startTextRect.top + startTextRect.height / 2.0f); + text.setPosition( + startBtn->rect.left + startBtn->rect.width / 2.0f, + startBtn->rect.top + startBtn->rect.height / 2.0f + ); + drawShadowedText(window, text); + + // Exit button + sf::RectangleShape exitShape(sf::Vector2f(exitBtn->rect.width, exitBtn->rect.height)); + exitShape.setPosition(exitBtn->rect.left, exitBtn->rect.top); + exitShape.setFillColor(exitBtn->hovered ? exitBtn->hoverColor : exitBtn->color); + exitShape.setOutlineThickness(3); + exitShape.setOutlineColor(sf::Color(120, 40, 40)); + window.draw(exitShape); + + text.setString("Exit"); + text.setCharacterSize(22); + text.setFillColor(sf::Color::White); + sf::FloatRect exitTextRect = text.getLocalBounds(); + text.setOrigin(exitTextRect.left + exitTextRect.width / 2.0f, exitTextRect.top + exitTextRect.height / 2.0f); + text.setPosition( + exitBtn->rect.left + exitBtn->rect.width / 2.0f, + exitBtn->rect.top + exitBtn->rect.height / 2.0f + ); + drawShadowedText(window, text); + } + else if (gameOver) + { + // Game Over title + text.setString("GAME OVER"); + text.setCharacterSize(38); + text.setStyle(sf::Text::Bold); + text.setFillColor(sf::Color(255, 80, 80)); + sf::FloatRect overRect = text.getLocalBounds(); + text.setOrigin(overRect.left + overRect.width / 2.0f, overRect.top + overRect.height / 2.0f); + text.setPosition(BOARD_WIDTH * CELL_SIZE / 2.0f, 80); + drawShadowedText(window, text, sf::Color(0,0,0,180), 3.f); + + // Score info + text.setString("Score: " + std::to_string(score) + " Best: " + std::to_string(bestScore)); + text.setCharacterSize(22); + text.setStyle(sf::Text::Regular); + text.setFillColor(sf::Color::White); + sf::FloatRect scoreRect = text.getLocalBounds(); + text.setOrigin(scoreRect.left + scoreRect.width / 2.0f, scoreRect.top + scoreRect.height / 2.0f); + text.setPosition(BOARD_WIDTH * CELL_SIZE / 2.0f, 130); + drawShadowedText(window, text); + + // Restart button + sf::RectangleShape restartShape(sf::Vector2f(restartBtn->rect.width, restartBtn->rect.height)); + restartShape.setPosition(restartBtn->rect.left, restartBtn->rect.top); + restartShape.setFillColor(restartBtn->hovered ? restartBtn->hoverColor : restartBtn->color); + restartShape.setOutlineThickness(3); + restartShape.setOutlineColor(sf::Color(40, 120, 40)); + window.draw(restartShape); + + text.setString("Restart"); + text.setCharacterSize(24); + text.setFillColor(sf::Color::White); + sf::FloatRect restartTextRect = text.getLocalBounds(); + text.setOrigin(restartTextRect.left + restartTextRect.width / 2.0f, restartTextRect.top + restartTextRect.height / 2.0f); + text.setPosition( + restartBtn->rect.left + restartBtn->rect.width / 2.0f, + restartBtn->rect.top + restartBtn->rect.height / 2.0f + ); + drawShadowedText(window, text); + + // Menu button + sf::RectangleShape menuShape(sf::Vector2f(menuBtn->rect.width, menuBtn->rect.height)); + menuShape.setPosition(menuBtn->rect.left, menuBtn->rect.top); + menuShape.setFillColor(menuBtn->hovered ? menuBtn->hoverColor : menuBtn->color); + menuShape.setOutlineThickness(3); + menuShape.setOutlineColor(sf::Color(120, 40, 40)); + window.draw(menuShape); + + text.setString("Menu"); + text.setCharacterSize(22); + text.setFillColor(sf::Color::White); + sf::FloatRect menuTextRect = text.getLocalBounds(); + text.setOrigin(menuTextRect.left + menuTextRect.width / 2.0f, menuTextRect.top + menuTextRect.height / 2.0f); + text.setPosition( + menuBtn->rect.left + menuBtn->rect.width / 2.0f, + menuBtn->rect.top + menuBtn->rect.height / 2.0f + ); + drawShadowedText(window, text); + } + else + { + // Score bar + text.setString("Score: " + std::to_string(score) + " Best: " + std::to_string(bestScore) + + (paused ? " [PAUSED]" : "")); + text.setCharacterSize(22); + text.setFillColor(sf::Color::White); + text.setStyle(sf::Text::Regular); + text.setPosition(5, BOARD_HEIGHT * CELL_SIZE + 8); + drawShadowedText(window, text); + + // Controls + text.setString("WASD / Arrows - Move SPACE - Pause ESC - Menu"); + text.setCharacterSize(16); + text.setFillColor(sf::Color(180, 180, 220)); + text.setPosition(5, BOARD_HEIGHT * CELL_SIZE + 36); + drawShadowedText(window, text, sf::Color(0,0,0,100), 1.5f); + } + } + + window.display(); +} + +void Game::update() +{ + snake.updateDirection(); + Point newHead = getTeleportedHead(); + + if (snake.checkSelfCollision(newHead)) + { + gameOver = true; + if (score > bestScore) + { + bestScore = score; + saveBestScore(); + } + return; + } + + if (newHead == food.getPosition()) + { + snake.grow(newHead); + score += 10; + generateFood(); + } + else + { + snake.move(newHead); + } +} + +Point Game::getTeleportedHead() const +{ + Point newHead = snake.getNextHeadPosition(); + if (newHead.x < 0) newHead.x = BOARD_WIDTH - 1; + else if (newHead.x >= BOARD_WIDTH) newHead.x = 0; + if (newHead.y < 0) newHead.y = BOARD_HEIGHT - 1; + else if (newHead.y >= BOARD_HEIGHT) newHead.y = 0; + return newHead; +} + +void Game::generateFood() +{ + std::vector occupiedPositions(snake.getBody().begin(), snake.getBody().end()); + food.generateNew(BOARD_WIDTH, BOARD_HEIGHT, occupiedPositions); +} + +void Game::loadBestScore() +{ + std::ifstream in(BEST_SCORE_FILE); + if (in) + in >> bestScore; + else + bestScore = 0; +} + +void Game::saveBestScore() +{ + std::ofstream out(BEST_SCORE_FILE); + if (out) + out << bestScore; +} + +void Game::reset() +{ + snake = Snake(Point(BOARD_WIDTH / 2, BOARD_HEIGHT / 2)); + score = 0; + generateFood(); +} diff --git a/Game.h b/Game.h new file mode 100644 index 0000000..43cf2d3 --- /dev/null +++ b/Game.h @@ -0,0 +1,63 @@ +#ifndef GAME_H +#define GAME_H + +#include "Snake.h" +#include "Food.h" +#include "Direction.h" +#include +#include +#include + +class Game +{ + Snake snake; + Food food; + int score; + int bestScore; + bool gameOver; + bool paused; + bool inMenu; + + // Menu button struct + struct MenuButton { + sf::FloatRect rect; + std::string label; + sf::Color color; + sf::Color hoverColor; + bool hovered = false; + }; + + void generateFood(); + Point getTeleportedHead() const; + void loadBestScore(); + void saveBestScore(); + void reset(); + +public: + static const int BOARD_WIDTH = 20; + static const int BOARD_HEIGHT = 20; + static const int CELL_SIZE = 24; + static constexpr const char* BEST_SCORE_FILE = "best_score.txt"; + + Game(); + + void run(); + void processEvent(const sf::Event& event, + sf::RenderWindow* window = nullptr, + MenuButton* startBtn = nullptr, + MenuButton* exitBtn = nullptr, + MenuButton* restartBtn = nullptr, + MenuButton* menuBtn = nullptr); + void update(); + void render(sf::RenderWindow& window, + MenuButton* startBtn = nullptr, + MenuButton* exitBtn = nullptr, + MenuButton* restartBtn = nullptr, + MenuButton* menuBtn = nullptr); + + bool isGameOver() const { return gameOver; } + int getScore() const { return score; } + int getBestScore() const { return bestScore; } +}; + +#endif //GAME_H diff --git a/Point.h b/Point.h new file mode 100644 index 0000000..4c1a285 --- /dev/null +++ b/Point.h @@ -0,0 +1,17 @@ +#ifndef POINT_H +#define POINT_H + +class Point +{ +public: + int x, y; + + explicit Point(int x = 0, int y = 0) : x(x), y(y){} + + bool operator==(const Point& other) const + { + return x == other.x && y == other.y; + } +}; + +#endif //POINT_H diff --git a/README.md b/README.md new file mode 100644 index 0000000..4a160e9 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# 🐍 Snake Game + +Classic Snake game implemented in C++ with a console interface. + +## 📁 Project Structure +- `main.cpp` — Entry point +- `Game.*` — Game logic and rendering +- `Snake.*` — Snake class implementation +- `Food.*` — Food generation system +- `Point.h` — Coordinate structure +- `Direction.h` — Direction enumeration +- `best_score.txt` — High score storage +- `CMakeLists.txt` — Build configuration + +## 🛠️ Requirements +- C++11 or higher +- CMake 3.10+ +- Linux/Unix terminal + +## 🎮 Features +- Console-based graphics +- WASD controls +- Food generation +- Collision detection +- Growing snake mechanics +- Score tracking +- Best score persistence + +## 🎯 Controls +- W — Move up +- S — Move down +- A — Move left +- D — Move right +- Q — Quit game + +## 📦 Build and Run +```bash +mkdir build && cd build +cmake .. +make +./Snake +``` diff --git a/Snake.cpp b/Snake.cpp new file mode 100644 index 0000000..0c8e2e7 --- /dev/null +++ b/Snake.cpp @@ -0,0 +1,70 @@ +#include "Snake.h" + +// Constructor +Snake::Snake(Point startPos) + : currentDirection(Direction::RIGHT), nextDirection(Direction::RIGHT) +{ + body.push_back(startPos); +} + +// Set the movement direction +void Snake::setDirection(Direction dir) +{ + if ((currentDirection == Direction::UP && dir == Direction::DOWN) || + (currentDirection == Direction::DOWN && dir == Direction::UP) || + (currentDirection == Direction::LEFT && dir == Direction::RIGHT) || + (currentDirection == Direction::RIGHT && dir == Direction::LEFT)) + return; + nextDirection = dir; +} + +// Check if the snake's body contains the point pos +bool Snake::contains(Point pos) const +{ + for (const Point& segment : body) + if (segment == pos) + return true; + return false; +} + +// Move the snake (without growing) +void Snake::move(const Point& newHead) +{ + body.push_front(newHead); + body.pop_back(); +} + +// Grow the snake (add a head) +void Snake::grow(const Point& newHead) +{ + body.push_front(newHead); +} + +// Update the current direction +void Snake::updateDirection() +{ + currentDirection = nextDirection; +} + +// Get the next head position +Point Snake::getNextHeadPosition() const +{ + Point head = body.front(); + switch (currentDirection) + { + case Direction::UP: return Point(head.x, head.y - 1); + case Direction::DOWN: return Point(head.x, head.y + 1); + case Direction::LEFT: return Point(head.x - 1, head.y); + case Direction::RIGHT: return Point(head.x + 1, head.y); + default: return head; + } +} + +// Check for self-collision +bool Snake::checkSelfCollision(const Point& newHead) const +{ + for (const Point& segment : body) + if (newHead == segment) + return true; + return false; +} diff --git a/Snake.h b/Snake.h new file mode 100644 index 0000000..22d19f5 --- /dev/null +++ b/Snake.h @@ -0,0 +1,50 @@ +#ifndef SNAKE_H +#define SNAKE_H + +#include +#include +#include "Point.h" +#include "Direction.h" + +class Snake +{ + std::deque body; // The segments of the snake's body + Direction currentDirection; // The current direction of the snake + Direction nextDirection; // The next direction the snake will move towards + +public: + // Constructor to initialize the snake's position + Snake(Point startPos); + + // Set the snake's direction + void setDirection(Direction dir); + + // Move the snake to a new head position + void move(const Point& newHead); + + // Grow the snake by adding a new head segment + void grow(const Point& newHead); + + // Update the snake's direction based on the next direction + void updateDirection(); + + // Get the position of the snake's next head + Point getNextHeadPosition() const; + + // Check if moving to the new head position will cause the snake to collide with itself + bool checkSelfCollision(const Point& newHead) const; + + // Get the current direction of the snake + Direction getDirection() const { return currentDirection; } + + // Get the segments of the snake's body + const std::deque& getBody() const { return body; } + + // Get the position of the snake's head + Point getHead() const { return body.front(); } + + // Check if the snake's body contains a specific point + bool contains(Point pos) const; +}; + +#endif // SNAKE_H diff --git a/best_score.txt b/best_score.txt new file mode 100644 index 0000000..77ac542 --- /dev/null +++ b/best_score.txt @@ -0,0 +1,2 @@ +0 + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..760b792 --- /dev/null +++ b/main.cpp @@ -0,0 +1,9 @@ +#include "Game.h" + +int main() +{ + + Game game; + game.run(); + return 0; +} \ No newline at end of file