From 519fd86a349c2a49a1872f8355f8d151a2d90ee4 Mon Sep 17 00:00:00 2001 From: Dipen Padhiyar <37871638+dipenpadhiyar@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:29:22 +0530 Subject: [PATCH 1/2] Add difficulty levels, score tracking, and game over display to Snake game --- .vscode/settings.json | 11 ++++++++ README.md | 17 ++++++++++-- src/main.cpp | 48 +++++++++++++++++++++++++++++----- src/snake.cpp | 38 +++++++++++++++++++++++++-- src/snake.h | 61 ++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..20643be --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.associations": { + "*.json.liquid": "json", + "*.yaml.liquid": "yaml", + "*.md.liquid": "markdown", + "*.js.liquid": "liquid-javascript", + "*.css.liquid": "liquid-css", + "*.scss.liquid": "liquid-scss", + "utility": "cpp" + } +} \ No newline at end of file diff --git a/README.md b/README.md index 5c53689..98324c7 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,12 @@ A classic snake game implemented in C++. * Threaded input and game logic * Unit tests using Google Test framework +- **Three difficulty levels**: Easy, Medium, and Hard (affects speed and score multiplier) +- **Score tracking**: Earn points based on difficulty (Easy: 10pts, Medium: 20pts, Hard: 30pts per food) +- **Pause functionality**: Press 'P' to pause/resume the game +- **Clean game over screen**: Shows final score, snake length, and difficulty +- **Real-time stats display**: Score, length, and difficulty shown during gameplay + ## Getting Started ### Prerequisites @@ -43,7 +49,13 @@ This will create two executables in the `build` directory: `snake_game` and `sna To play the game, run the following command from the `build` directory: ```bash -./snake_game +g++ -o snake main.cpp snake.cpp -pthread +./snake +``` + +You can also specify difficulty via command line (1=Easy, 2=Medium, 3=Hard): +```bash +./snake 2 # Start with Medium difficulty ``` ### Running Tests @@ -51,7 +63,8 @@ To play the game, run the following command from the `build` directory: To run the tests, execute the following command from the `build` directory: ```bash -./snake_tests +g++ -o snake_tests snake_test.cpp snake_input_test.cpp snake.cpp -lgtest -lgtest_main -pthread +./my_tests ``` ## How to Play diff --git a/src/main.cpp b/src/main.cpp index 17811f1..3b013df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,12 +1,48 @@ #include "snake.h" +void display_menu(){ + system("clear"); + cout << "╔════════════════════════════════╗" << endl; + cout << "║ SNAKE GAME CLI ║" << endl; + cout << "╚════════════════════════════════╝" << endl; + cout << endl; + cout << "Select Difficulty:" << endl; + cout << "1. Easy (Slow)" << endl; + cout << "2. Medium (Normal)" << endl; + cout << "3. Hard (Fast)" << endl; + cout << endl; + cout << "Enter your choice (1-3): "; +} + int main(int argc, char *argv[]) { - shared_ptr input_manager = make_shared(); - Game game = Game(input_manager); + int difficulty = 1; + bool play_again = true; + + while(play_again){ + // Show menu and get difficulty selection + if(argc > 1){ + difficulty = atoi(argv[1]); + if(difficulty < 1 || difficulty > 3) difficulty = 1; + } else { + display_menu(); + char choice; + cin >> choice; + difficulty = choice - '0'; + if(difficulty < 1 || difficulty > 3) difficulty = 1; + } + + shared_ptr input_manager = make_shared(); + Game game = Game(input_manager, difficulty); - thread input_thread(input_handler, std::ref(game)); - thread game_thread(game_play, std::ref(game)); - input_thread.join(); - game_thread.join(); + thread input_thread(input_handler, std::ref(game)); + thread game_thread(game_play, std::ref(game)); + input_thread.join(); + game_thread.join(); + + // After threads join, check if we want to play again + // The game_play function handles the exit if user chooses not to play + // If we reach here after game_play exits normally, user chose to play again + play_again = true; + } return 0; } diff --git a/src/snake.cpp b/src/snake.cpp index d71a2ee..c691916 100644 --- a/src/snake.cpp +++ b/src/snake.cpp @@ -7,6 +7,8 @@ void handle_single_input(Game& game){ game.set_direction(keymap[input]); }else if (input == 'q'){ exit(0); + }else if (input == 'p' || input == 'P'){ + game.togglePause(); } } @@ -25,14 +27,46 @@ void move_snake(deque &snake, Cell new_head){ snake.pop_front(); } +bool display_game_over(Game& game){ + system("clear"); + cout << endl; + cout << "╔════════════════════════════════╗" << endl; + cout << "║ GAME OVER! ║" << endl; + cout << "╚════════════════════════════════╝" << endl; + cout << endl; + cout << "Final Score: " << game.getScore() << endl; + cout << "Snake Length: " << game.getSize() << endl; + cout << "Difficulty: "; + if(game.getDifficulty() == 1) cout << "Easy"; + else if(game.getDifficulty() == 2) cout << "Medium"; + else cout << "Hard"; + cout << endl << endl; + cout << "Thanks for playing! 🐍" << endl; + cout << endl; + cout << "Would you like to play again?" << endl; + cout << "1. Play Again" << endl; + cout << "2. Exit" << endl; + cout << endl; + cout << "Enter your choice (1-2): "; + + char choice; + cin >> choice; + return choice == '1'; +} + void game_play(Game& game){ system("clear"); game.set_direction('r'); - while(true){ + while(!game.isGameOver()){ reset_cursor(); game.update(); game.render(); - + } + + // Display game over and return whether to play again + bool play_again = display_game_over(game); + if(!play_again){ + exit(0); } } diff --git a/src/snake.h b/src/snake.h index e21591a..90383f2 100644 --- a/src/snake.h +++ b/src/snake.h @@ -149,6 +149,10 @@ class Game{ // speed std::chrono::milliseconds speed_timer = 200ms; shared_ptr input_manager; + int score = 0; + int difficulty = 1; // 1=easy, 2=medium, 3=hard + bool paused = false; + bool game_over = false; public: @@ -156,6 +160,12 @@ class Game{ this->snake = Snake(); this->food = this->generate_random_cell(); } + + Game(shared_ptr input_manager, int difficulty) : input_manager(input_manager), difficulty(difficulty) { + this->snake = Snake(); + this->food = this->generate_random_cell(); + setDifficulty(difficulty); + } std::chrono::milliseconds getSpeed(){ return this->speed_timer; @@ -163,6 +173,37 @@ class Game{ int getSize(){ return this->size; } + + int getScore(){ + return this->score; + } + + void setDifficulty(int diff){ + this->difficulty = diff; + if(diff == 1) this->speed_timer = 200ms; // Easy + else if(diff == 2) this->speed_timer = 100ms; // Medium + else if(diff == 3) this->speed_timer = 50ms; // Hard + } + + int getDifficulty(){ + return this->difficulty; + } + + void togglePause(){ + this->paused = !this->paused; + } + + bool isPaused(){ + return this->paused; + } + + bool isGameOver(){ + return this->game_over; + } + + void setGameOver(){ + this->game_over = true; + } Cell generate_random_cell(){ Cell new_pos = make_pair(rand() % this->getSize(), rand() % this->getSize()); @@ -184,14 +225,16 @@ class Game{ return this->snake.get_direction(); } void update(){ + if(this->paused) return; + // check self collision if (this->checkCollission(this->snake.get_next_position())) { - system("clear"); - cout << "Game Over" << endl; - exit(0); + this->game_over = true; + return; }else if (this->snake.contains(food)) { this->food = this->generate_random_cell(); this->snake.grow(); + this->score += (10 * this->difficulty); // Higher difficulty = more points }else{ this->snake.move(); @@ -199,6 +242,17 @@ class Game{ } void render(){ + // Display score and info + cout << "Score: " << this->score << " | Length: " << this->snake.getSize() << " | Difficulty: "; + if(this->difficulty == 1) cout << "Easy"; + else if(this->difficulty == 2) cout << "Medium"; + else cout << "Hard"; + + if(this->paused) cout << " | PAUSED"; + cout << endl; + cout << "Controls: WASD=Move, P=Pause, Q=Quit" << endl; + cout << "───────────────────────" << endl; + for(size_t i=0;igetSize();i++){ for(size_t j=0;jgetSize();j++){ if (snake.contains(make_pair(int(i), int(j)))) { @@ -225,4 +279,5 @@ void input_handler(Game& game); void reset_cursor(); void move_snake(deque &snake, Cell new_head); void game_play(Game& game); +bool display_game_over(Game& game); From 0cda354f90670bedee7c63f0ba84c0f1b552e523 Mon Sep 17 00:00:00 2001 From: Dipen Padhiyar <37871638+dipenpadhiyar@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:41:53 +0530 Subject: [PATCH 2/2] Delete .vscode directory --- .vscode/settings.json | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 20643be..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "files.associations": { - "*.json.liquid": "json", - "*.yaml.liquid": "yaml", - "*.md.liquid": "markdown", - "*.js.liquid": "liquid-javascript", - "*.css.liquid": "liquid-css", - "*.scss.liquid": "liquid-scss", - "utility": "cpp" - } -} \ No newline at end of file