196 lines
5.4 KiB
C++
196 lines
5.4 KiB
C++
#include <SFML/Graphics.hpp>
|
|
#include <vector>
|
|
#include <random>
|
|
|
|
using namespace sf;
|
|
using namespace std;
|
|
|
|
const int WIDTH = 800;
|
|
const int HEIGHT = 600;
|
|
const int GRID_SIZE = 20;
|
|
const int GRID_WIDTH = WIDTH / GRID_SIZE;
|
|
const int GRID_HEIGHT = HEIGHT / GRID_SIZE;
|
|
|
|
struct SnakeSegment {
|
|
int x, y;
|
|
};
|
|
|
|
class Game {
|
|
private:
|
|
RenderWindow window;
|
|
vector<SnakeSegment> snake;
|
|
Vector2f fruit;
|
|
Vector2f direction;
|
|
bool isMoving;
|
|
Clock clock;
|
|
float speed;
|
|
int score;
|
|
Font font;
|
|
Text scoreText;
|
|
|
|
public:
|
|
Game() : window(VideoMode(WIDTH, HEIGHT), "SFML Snake Game"), direction(1, 0), isMoving(false), speed(0.1f), score(0) {
|
|
window.setFramerateLimit(60);
|
|
|
|
// Initialize snake with 3 segments
|
|
snake.push_back({GRID_WIDTH / 2, GRID_HEIGHT / 2});
|
|
snake.push_back({GRID_WIDTH / 2 - 1, GRID_HEIGHT / 2});
|
|
snake.push_back({GRID_WIDTH / 2 - 2, GRID_HEIGHT / 2});
|
|
|
|
spawnFruit();
|
|
|
|
// Load font and setup score text
|
|
if (!font.loadFromFile("/usr/share/fonts/gnu-free/FreeSans.ttf")) {
|
|
// Fallback if the exact font isn't found
|
|
font.loadFromFile("/usr/share/fonts/liberation/LiberationSans-Regular.ttf");
|
|
}
|
|
scoreText.setFont(font);
|
|
scoreText.setCharacterSize(24);
|
|
scoreText.setFillColor(Color::White);
|
|
scoreText.setPosition(10, 10);
|
|
updateScoreText();
|
|
}
|
|
|
|
void spawnFruit() {
|
|
static random_device rd;
|
|
static mt19937 gen(rd());
|
|
uniform_int_distribution<> distX(0, GRID_WIDTH - 1);
|
|
uniform_int_distribution<> distY(0, GRID_HEIGHT - 1);
|
|
|
|
fruit.x = distX(gen);
|
|
fruit.y = distY(gen);
|
|
|
|
// Make sure fruit doesn't spawn on snake
|
|
for (const auto& segment : snake) {
|
|
if (segment.x == fruit.x && segment.y == fruit.y) {
|
|
spawnFruit();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void updateScoreText() {
|
|
scoreText.setString("Score: " + to_string(score));
|
|
}
|
|
|
|
void handleInput() {
|
|
if (Keyboard::isKeyPressed(Keyboard::Left) && direction.x == 0) {
|
|
direction = Vector2f(-1, 0);
|
|
isMoving = true;
|
|
}
|
|
else if (Keyboard::isKeyPressed(Keyboard::Right) && direction.x == 0) {
|
|
direction = Vector2f(1, 0);
|
|
isMoving = true;
|
|
}
|
|
else if (Keyboard::isKeyPressed(Keyboard::Up) && direction.y == 0) {
|
|
direction = Vector2f(0, -1);
|
|
isMoving = true;
|
|
}
|
|
else if (Keyboard::isKeyPressed(Keyboard::Down) && direction.y == 0) {
|
|
direction = Vector2f(0, 1);
|
|
isMoving = true;
|
|
}
|
|
}
|
|
|
|
void update() {
|
|
if (clock.getElapsedTime().asSeconds() < speed || !isMoving)
|
|
return;
|
|
|
|
clock.restart();
|
|
|
|
// Move snake
|
|
SnakeSegment newHead = {snake[0].x + direction.x, snake[0].y + direction.y};
|
|
|
|
// Check wall collision
|
|
if (newHead.x < 0 || newHead.x >= GRID_WIDTH || newHead.y < 0 || newHead.y >= GRID_HEIGHT) {
|
|
reset();
|
|
return;
|
|
}
|
|
|
|
// Check self collision
|
|
for (size_t i = 1; i < snake.size(); i++) {
|
|
if (newHead.x == snake[i].x && newHead.y == snake[i].y) {
|
|
reset();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check fruit collision
|
|
if (newHead.x == fruit.x && newHead.y == fruit.y) {
|
|
snake.insert(snake.begin(), newHead);
|
|
spawnFruit();
|
|
score += 10;
|
|
updateScoreText();
|
|
|
|
// Increase speed slightly every 50 points
|
|
if (score % 50 == 0 && speed > 0.05f) {
|
|
speed -= 0.01f;
|
|
}
|
|
} else {
|
|
snake.insert(snake.begin(), newHead);
|
|
snake.pop_back();
|
|
}
|
|
}
|
|
|
|
void reset() {
|
|
snake.clear();
|
|
snake.push_back({GRID_WIDTH / 2, GRID_HEIGHT / 2});
|
|
snake.push_back({GRID_WIDTH / 2 - 1, GRID_HEIGHT / 2});
|
|
snake.push_back({GRID_WIDTH / 2 - 2, GRID_HEIGHT / 2});
|
|
direction = Vector2f(1, 0);
|
|
isMoving = false;
|
|
speed = 0.1f;
|
|
score = 0;
|
|
updateScoreText();
|
|
spawnFruit();
|
|
}
|
|
|
|
void render() {
|
|
window.clear(Color::Black);
|
|
|
|
// Draw snake
|
|
RectangleShape segment(Vector2f(GRID_SIZE - 1, GRID_SIZE - 1));
|
|
segment.setFillColor(Color::Green);
|
|
|
|
for (size_t i = 0; i < snake.size(); i++) {
|
|
segment.setPosition(snake[i].x * GRID_SIZE, snake[i].y * GRID_SIZE);
|
|
if (i == 0) {
|
|
segment.setFillColor(Color::Yellow); // Head is different color
|
|
} else {
|
|
segment.setFillColor(Color::Green);
|
|
}
|
|
window.draw(segment);
|
|
}
|
|
|
|
// Draw fruit
|
|
RectangleShape fruitShape(Vector2f(GRID_SIZE - 1, GRID_SIZE - 1));
|
|
fruitShape.setFillColor(Color::Red);
|
|
fruitShape.setPosition(fruit.x * GRID_SIZE, fruit.y * GRID_SIZE);
|
|
window.draw(fruitShape);
|
|
|
|
// Draw score
|
|
window.draw(scoreText);
|
|
|
|
window.display();
|
|
}
|
|
|
|
void run() {
|
|
while (window.isOpen()) {
|
|
Event event;
|
|
while (window.pollEvent(event)) {
|
|
if (event.type == Event::Closed)
|
|
window.close();
|
|
}
|
|
|
|
handleInput();
|
|
update();
|
|
render();
|
|
}
|
|
}
|
|
};
|
|
|
|
int main() {
|
|
Game game;
|
|
game.run();
|
|
return 0;
|
|
} |