├── .gitignore ├── README.md └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.out -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🐍 Classic Snake Game in C++ | Console-Based Open-Source Project 2 | 3 | --- 4 | 5 | ## 📌 Table of Contents 6 | - [📚 Introduction](#-introduction) 7 | - [✨ Features](#-features) 8 | - [🎮 How to Play](#-how-to-play) 9 | - [🎯 Game Controls](#-game-controls) 10 | - [🕹️ Game Mechanics](#-game-mechanics) 11 | - [💡 Code Structure & OOP Concepts](#-code-structure--oop-concepts) 12 | - [📊 Data Structures Used](#-data-structures-used) 13 | - [📝 Code Explanation](#-code-explanation) 14 | - [🚀 Future Enhancements](#-future-enhancements) 15 | - [👥 Contributors](#-contributors) 16 | - [⭐ Star & Contribute](#-star--contribute) 17 | 18 | --- 19 | 20 | ## 📚 Introduction 21 | This **Classic Snake Game** is a **console-based** game built using **C++ and Object-Oriented Programming (OOP)** principles. It runs seamlessly on **Windows & Linux**, requiring no external libraries. This project is **open-source**, making it a valuable resource for beginners interested in **C++ game development**. 22 | 23 | 🔥 **Why Choose This Game?** 24 | - Learn **C++ game development** from scratch. 25 | - Explore **OOP principles (classes, inheritance, encapsulation, abstraction)**. 26 | - Develop problem-solving skills with **data structures & algorithms**. 27 | - Experience **real-time user input handling** and game logic implementation. 28 | 29 | --- 30 | 31 | ## ✨ Features 32 | - 🎯 **Classic grid-based gameplay**. 33 | - 🎮 **Smooth snake movement** with real-time input handling. 34 | - 🍏 **Normal & Special Fruits** for different score values. 35 | - 🛠 **Configurable difficulty levels**. 36 | - 🌟 **Score tracking system**. 37 | - 🔴 **Dynamic obstacles** appear as the game progresses. 38 | - 🛡 **Wall collision mode** (toggleable). 39 | - 👀 **User-friendly UI with game stats**. 40 | 41 | --- 42 | 43 | ## 🎮 How to Play 44 | - Control the snake using `W, A, S, D` or **arrow keys**. 45 | - 🍏 **Eat normal fruits (`@`)** to gain **5 points**. 46 | - 💰 **Eat special fruits (`$`)** to gain **20 points**. 47 | - 🚫 **Avoid walls, obstacles (`#`), and self-collision**. 48 | - ☠️ **Game Over** if the snake collides with itself or an obstacle. 49 | - ↺ Restart or exit after game over. 50 | 51 | --- 52 | 53 | ## 🎯 Game Controls 54 | | 🎮 Key | 🛠 Action | 55 | |--------|-----------| 56 | | `W / ⬆️` | Move Up | 57 | | `S / ⬇️` | Move Down | 58 | | `A / ⬅️` | Move Left | 59 | | `D / ➡️` | Move Right | 60 | | `P` | Pause | 61 | | `R` | Resume | 62 | | `X` | Reset Game | 63 | | `ESC` | Exit Game | 64 | 65 | --- 66 | 67 | ## 🕹️ Game Mechanics 68 | - 🐍 The snake moves continuously in the last chosen direction. 69 | - 🍏 Eating a **normal fruit (`@`)** increases score by **5 points**. 70 | - 💰 Eating a **special fruit (`$`)** increases score by **20 points**. 71 | - 🛠 **Obstacles appear** once the score reaches **50**. 72 | - 🌟 The difficulty **dynamically increases** as score progresses. 73 | 74 | --- 75 | 76 | ## 💡 Code Structure & OOP Concepts 77 | This project follows **Object-Oriented Programming (OOP)** principles: 78 | 79 | - 🛠 **Game (Base Class)** → Defines core game logic. 80 | - 🐍 **Snake (Inherits from Game)** → Handles movement & tail growth. 81 | - 🍏 **Fruit (Inherits from Snake)** → Manages fruit generation. 82 | - 🎮 **Main (Inherits from Fruit)** → Controls the **game loop**, rendering, and input handling. 83 | 84 | --- 85 | 86 | ## 📊 Data Structures Used 87 | ### **🔹 Data Structures Implemented** 88 | | Data Structure | Purpose | 89 | |---------------|---------| 90 | | **Arrays** (`int Tail_X[], Tail_Y[]`) | Stores the snake’s tail positions. | 91 | | **Vector** (`vector> obstacles`) | Efficiently stores obstacle positions. | 92 | | **Enum** (`enum direction`) | Represents movement directions (`LEFT, RIGHT, UP, DOWN, STOP`). | 93 | 94 | --- 95 | 96 | ## 📝 Code Explanation 97 | ### **🔹 Important Variables** 98 | | Variable | Data Type | Purpose | 99 | |----------|----------|---------| 100 | | `width, height` | `const int` | Defines board size. | 101 | | `SnakeX, SnakeY` | `int` | Snake’s head position. | 102 | | `fruitX, fruitY` | `int` | Fruit’s position. | 103 | | `score` | `int` | Player’s current score. | 104 | | `Tail_Length` | `int` | Stores snake’s length. | 105 | | `Tail_X[], Tail_Y[]` | `int arrays` | Stores tail coordinates. | 106 | | `obstacles` | `vector>` | Stores obstacle positions. | 107 | | `Dir` | `enum direction` | Tracks movement direction. | 108 | | `isGameOver` | `bool` | Checks if game is over. | 109 | 110 | --- 111 | 112 | ## 🚀 Future Enhancements 113 | Planned features for future updates: 114 | - 🌟 **Multiple Levels** with increasing difficulty. 115 | - 🎉 **Multiplayer Mode** for competitive gameplay. 116 | - 🏆 **High Score System** for tracking performance. 117 | - 🤖 **AI-controlled Snakes** as opponents. 118 | - 🎨 **Graphical UI Version** using a graphics library. 119 | 120 | **Have ideas? Open an issue or contribute!** 🚀 121 | 122 | --- 123 | 124 | ## 👥 Contributors 125 | - 🏅 **Tirth Patel (202401157)** 126 | - 🏅 **Raj Patel (202401152)** 127 | - 🏅 **Shlok Patel (202401156)** 128 | - 🏅 **Prakriti Pandey (202401164)** 129 | 130 | 👏 **Special thanks to all contributors!** 131 | 132 | --- 133 | 134 | ## ⭐ Star & Contribute 135 | If you find this project useful, please **star ⭐ the repo** and share it! 136 | 137 | 👉 **GitHub Repository:** [Snake Game in C++](https://github.com/Tirth9978/Snake_Game) 138 | 139 | 📈 **Want to contribute?** Fork the repo, make improvements, and submit a pull request! 🚀 140 | 141 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | 5 | IT206 Project 1 : Snake Game 6 | 7 | This game is made by Bitwise Wizards . 8 | 9 | Makers : 10 | 11 | Tirth Patel (202401157) 12 | Raj Patel (202401152) 13 | Shlok Patel (202401156) 14 | Prakriti Pandey (202401164) 15 | 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | using namespace std ; 24 | 25 | 26 | #if defined(_WIN32) || defined(_WIN64) 27 | #include 28 | #include 29 | // #include 30 | // #pragma comment(lib, "winmm.lib") 31 | #define CLEAR "cls" 32 | 33 | #else 34 | #include 35 | #include 36 | #include 37 | #define CLEAR "clear" 38 | 39 | int _kbhit() { 40 | struct termios oldt, newt; 41 | int ch; 42 | int oldf; 43 | 44 | tcgetattr(STDIN_FILENO, &oldt); 45 | newt = oldt; 46 | newt.c_lflag &= ~(ICANON | ECHO); 47 | tcsetattr(STDIN_FILENO, TCSANOW, &newt); 48 | oldf = fcntl(STDIN_FILENO, F_GETFL, 0); 49 | fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); 50 | 51 | ch = getchar(); 52 | 53 | tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 54 | fcntl(STDIN_FILENO, F_SETFL, oldf); 55 | 56 | if (ch != EOF) { 57 | ungetc(ch, stdin); 58 | return 1; 59 | } 60 | return 0; 61 | } 62 | 63 | int _getch() { 64 | struct termios oldt, newt; 65 | int ch; 66 | tcgetattr(STDIN_FILENO, &oldt); 67 | newt = oldt; 68 | newt.c_lflag &= ~(ICANON | ECHO); 69 | tcsetattr(STDIN_FILENO, TCSANOW, &newt); 70 | ch = getchar(); 71 | 72 | if (ch == 27) { 73 | if (getchar() == '[') { 74 | switch (getchar()) { 75 | case 'A': ch = 'w'; break; // Up Arrow 76 | case 'B': ch = 's'; break; // Down Arrow 77 | case 'C': ch = 'd'; break; // Right Arrow 78 | case 'D': ch = 'a'; break; // Left Arrow 79 | } 80 | } 81 | } 82 | 83 | tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 84 | return ch; 85 | } 86 | 87 | #endif 88 | 89 | // using namespace std; 90 | 91 | // ENUM Part 92 | enum direction { 93 | STOP = 0, LEFT, RIGHT, UP, DOWN 94 | }; 95 | 96 | /* 97 | We have used encapsulation to make our variables private and ensure data security. Additionally, we have implemented inheritance to establish relationships between classes. 98 | 99 | At the starting point, all constructors will be called, initializing and allocating the necessary resources for the game. 100 | */ 101 | 102 | // Main game base 103 | class Game { 104 | // Here in Encapsulation, we Protected variable members . 105 | protected : 106 | int width; 107 | int height; 108 | int score; 109 | // int max_score ; 110 | bool isGameOver; 111 | direction Dir; 112 | vector> obstacles; 113 | 114 | public : 115 | /* 116 | First of all this will be called After creation of class 117 | */ 118 | 119 | Game() { 120 | this->width = 70; 121 | this->height = 20; 122 | this->score = 0; 123 | // this->max_score = 0; 124 | this->isGameOver = false; 125 | this->Dir = STOP; 126 | } 127 | }; 128 | 129 | // This is the Snake class inherited from Game class 130 | class Snake :protected Game { 131 | protected : 132 | int snakeX, snakeY; 133 | int *Tail_X; 134 | int *Tail_Y; 135 | int Tail_Length; 136 | public : 137 | /* 138 | Second time this will be called 139 | */ 140 | Snake() { 141 | this->snakeX = this->width / 2; 142 | this->snakeY = this->height / 2; 143 | // Dynamic allocation of tail of shake 144 | Tail_X = new int[100]; 145 | Tail_Y = new int[100]; 146 | Tail_Length = 2; 147 | Tail_X[0] = (this->width / 2) - 1; 148 | Tail_Y[0] = this->height / 2; 149 | Tail_Y[1] = this->height / 2; 150 | Tail_X[1] = (this->width / 2) - 2; 151 | } 152 | 153 | ~Snake() { 154 | delete[] Tail_X; 155 | delete[] Tail_Y; 156 | } 157 | 158 | }; 159 | 160 | // From Snake Class 161 | class Fruit : protected Snake { 162 | protected : 163 | int fruitX, fruitY; 164 | bool specialFruit; 165 | public : 166 | //Third time, this will be called 167 | Fruit() { 168 | generateFruit(); 169 | } 170 | 171 | void generateFruit() { 172 | this->fruitX = 1 + rand() % (this->width - 2); 173 | this->fruitY = 1 + rand() % (this->height - 2); 174 | this->specialFruit = (bool)(rand() % 7 == 0); 175 | } 176 | }; 177 | 178 | /* 179 | It is the main body of the Game. 180 | In this we have Main_Board, Update_Game, Input mathods . 181 | */ 182 | class Main : protected Fruit { 183 | private : 184 | // Data Structer to Store Obstacle's X and Y coordinates. 185 | vector> obstacles; 186 | int frameCount = 0; 187 | bool obstaclesEnable = false; 188 | 189 | public : 190 | 191 | // This Method Generates Obstacles.. 192 | void GenerateObstacles() { 193 | if(!obstaclesEnable) { 194 | return; 195 | } 196 | 197 | obstacles.clear(); // Clear Obstacles. 198 | // initial Number of Obstacles is 5 (when score reaches at 50) then obstacles increase by 3 per 20 points. 199 | int numObstacles = 5 + ((score - 50) / 20) * 3; 200 | 201 | for (int i = 0; i < numObstacles; i++) { 202 | int obsX, obsY; 203 | bool valid; 204 | do { 205 | valid = true; 206 | // It Gives Random Coordinates to Obstacles. 207 | obsX = 2 + rand() % (width - 2); 208 | obsY = 2 + rand() % (height - 2); 209 | 210 | //Check Obstacles and Snake don't overlap. 211 | if (obsX == snakeX && obsY == snakeY) { 212 | valid = false; 213 | } 214 | if (obsX == fruitX && obsY == fruitY) { 215 | valid = false; 216 | } 217 | 218 | for (auto t : obstacles) { 219 | if (t.first == obsX && t.second == obsY) valid = false; 220 | } 221 | 222 | } while (!valid); 223 | 224 | obstacles.push_back({obsX, obsY}); 225 | } 226 | } 227 | 228 | void Main_Board(string name,int& max_score) { 229 | system(CLEAR); 230 | 231 | cout << "\n\n"; 232 | 233 | for(int i=0; iwidth+2; i++) { 234 | cout << "-"; 235 | } cout << "\n"; 236 | 237 | for(int i=1; iscore; 281 | if (i == 19) cout << " " << name << "'s High Score : " << max_score ; 282 | 283 | cout << endl; 284 | } 285 | 286 | for(int i=0; i<=width+1; i++) { 287 | cout << "-"; 288 | } 289 | cout << "\n\n"; 290 | 291 | } 292 | 293 | void Update_Game(int diff, int wallsEnable,int& max_score){ 294 | 295 | frameCount++; 296 | 297 | if(Dir == STOP) { 298 | return; 299 | } 300 | 301 | int preX = Tail_X[0]; 302 | int preY = Tail_Y[0]; 303 | 304 | int preXX, preYY; 305 | 306 | Tail_X[0] = snakeX; 307 | Tail_Y[0] = snakeY; 308 | 309 | // Moving Snake.. 310 | for(int i=1; i= width || snakeX <= 0 || snakeY >= height || snakeY <= 0) { 334 | isGameOver = true; 335 | } 336 | } else { // Wall Are not Enable, so snake can pass through it. 337 | if(snakeX >= width) { 338 | snakeX = 1; 339 | } 340 | else if(snakeX <= 0) { 341 | snakeX = width - 1; 342 | } 343 | else if(snakeY >= height) { 344 | snakeY = 1; 345 | } 346 | else if(snakeY <= 0) { 347 | snakeY = height - 1; 348 | } 349 | } 350 | 351 | // Score >= 50 then generate obstacles 352 | if (score >= 50 && !obstaclesEnable) { 353 | obstaclesEnable = true; 354 | GenerateObstacles(); 355 | } 356 | 357 | // Randomize Oblstacle's Coordinates every 10 second. 358 | if (obstaclesEnable && frameCount % 100 == 0) { 359 | GenerateObstacles(); 360 | } 361 | 362 | for (auto obs : obstacles) { 363 | if (snakeX == obs.first && snakeY == obs.second) { 364 | isGameOver = true; // Snake dies if it hits an obstacle 365 | } 366 | } 367 | 368 | for(int i=0; i max_score){ 381 | max_score = score; 382 | } 383 | Tail_Length++; 384 | 385 | bool find; 386 | 387 | do { 388 | find = true; 389 | 390 | generateFruit(); 391 | 392 | if (snakeX == fruitX && snakeY == fruitY) { 393 | find = false; 394 | } 395 | 396 | // Check Generated Fruit and Snake don't Overlap; 397 | for(int i=0; iReset(); 434 | return; 435 | } 436 | 437 | if(ch == 'a' || ch == 75) { 438 | if(Dir != RIGHT) Dir = LEFT; 439 | } else if(ch == 's' || ch == 80) { 440 | if(Dir != UP) Dir = DOWN; 441 | } else if(ch == 'w' || ch == 72) { 442 | if(Dir != DOWN) Dir = UP; 443 | } else if(ch == 'd' || ch == 77) { 444 | if(Dir != LEFT) Dir = RIGHT; 445 | } 446 | } 447 | else { 448 | if(ch == 's' || ch == 80) { 449 | if(Dir != UP) Dir = DOWN; 450 | } else if(ch == 'w' || ch == 72) { 451 | if(Dir != DOWN) Dir = UP; 452 | } else if(ch == 'd' || ch == 77) { 453 | if(Dir != LEFT) Dir = RIGHT; 454 | } 455 | } 456 | } 457 | } 458 | 459 | // Reset The Game; 460 | void Reset() { 461 | this->score = 0; 462 | this->isGameOver = false; 463 | this->Dir = STOP; 464 | this->snakeX = width / 2; 465 | this->snakeY = height / 2; 466 | Tail_Length = 0; 467 | 468 | // Generate new fruit 469 | fruitX = rand() % (width-1); 470 | fruitY = rand() % (height-1); 471 | specialFruit = (rand() % 10 == 0); 472 | } 473 | 474 | 475 | bool isOver() {return this->isGameOver;} 476 | }; 477 | 478 | // Loading Animation part 479 | void animation(string name) { 480 | system(CLEAR); 481 | 482 | cout << "\n\n\n"; 483 | cout << " *************************************************************************\n\n"; 484 | cout << " S N A K E G A M E\n\n"; 485 | cout << " Get Ready, " << name << "..!!\n\n"; 486 | cout << " *************************************************************************\n\n\n\n\n\n\n"; 487 | 488 | cout << " L O A D I N G "; 489 | 490 | for (int i = 0; i < 5; i++) { 491 | cout << "." << flush; 492 | #if defined(_WIN32) || defined(_WIN64) 493 | Sleep(700); 494 | #else 495 | usleep(750 * 1000); 496 | #endif 497 | } 498 | 499 | for (int i = 3; i >= 0; i--) { 500 | system(CLEAR); 501 | 502 | cout << "\n\n\n\n\n"; 503 | cout << "\r Starting in:\n\n\n"; 504 | if(i == 3) { 505 | cout << R"( 506 | ____ 507 | |___ \ 508 | __) | 509 | |__ < 510 | ___) | 511 | |____/ 512 | )" << flush; 513 | } else if(i == 2) { 514 | cout << R"( 515 | ______ 516 | / ____ \ 517 | \/ / / 518 | / / 519 | / /___ 520 | /______| 521 | )" << flush; 522 | } else if(i == 1) { 523 | cout << R"( 524 | _ 525 | / | 526 | | | 527 | | | 528 | | | 529 | |_| 530 | )" << flush; 531 | } else { 532 | system(CLEAR); 533 | cout << "\n\n\n\n\n\n" R"( 534 | _____ _______ __ _____ _______ 535 | / ____| |_______| / \ | __ \ |_______| 536 | | (___ | | / _ \ | |__) | | | 537 | \___ \ | | / / \ \ | _ / | | 538 | ____) | | | / ____ \ | | \ \ | | 539 | |_____/ |_| /__/ \__\ |_| \_\ |_| 540 | )" << flush; 541 | } 542 | #if defined(_WIN32) || defined(_WIN64) 543 | Sleep(900); 544 | #else 545 | usleep(950 * 1000); 546 | #endif 547 | } 548 | } 549 | 550 | /* 551 | main function of this cpp file. 552 | */ 553 | int main() { 554 | 555 | system(CLEAR); 556 | 557 | cout << "\n\n\n"; 558 | cout << " **************************************************************************************\n\n"; 559 | cout << " W E L C O M E T O S N A K E G A M E ! !\n\n"; 560 | cout << " **************************************************************************************\n\n\n\n"; 561 | 562 | string name; 563 | cout << "\n\nEnter your Name : "; 564 | getline(cin, name); 565 | 566 | 567 | 568 | 569 | 570 | srand(time(NULL)); // Like Seed For rand() Function; 571 | int max_score = 0; 572 | int play = 0; 573 | // this is the do-while loop for if you want to play again the game 574 | do { 575 | 576 | // system(CLEAR); 577 | // Set Difficuly of the game. 578 | int diff = 0; 579 | cout << "\n\n"; 580 | cout << "Difficulty Levels : " << endl; 581 | cout << "1. Easy\n2. Medium\n3. Hard\n\n"; 582 | cout << "NOTE : If You Press Key Other than 1, 2 or 3 then Difficulty Will Set Automatically to Medium..\n\n"; 583 | cout << "Set Difficulty Level : "; 584 | cin >> diff; 585 | if (diff == 3) { 586 | diff = 90; 587 | } else if(diff == 2) { 588 | diff = 140; 589 | } else if(diff == 1) { 590 | diff = 190; 591 | } else { 592 | diff = 140; 593 | } 594 | cout <<"\n\n"; 595 | 596 | int wallsEnable = 1; 597 | cout << "Do You Want To Enable The Walls..?\n\n"; 598 | cout << "NOTE : if Walls are Enable then if you touch the walls, The game is Over..\n"; 599 | cout << "If You Press other than 1 or 0 then it will take 1 and walls are enabled automatically..\n\n"; 600 | cout << "Press 1 for Walls Enable and 0 for not enable : "; 601 | cin >> wallsEnable; 602 | 603 | animation(name); 604 | 605 | Main game; 606 | // main part of methods calling 607 | while(!game.isOver()){ 608 | // Frontend of the game board 609 | game.Main_Board(name,max_score); 610 | // Keys press 611 | game.Input(); 612 | // Backend Part of the game 613 | game.Update_Game(diff, wallsEnable,max_score); 614 | 615 | #if defined(_WIN32) || defined(_WIN64) 616 | Sleep(max(50, diff)); 617 | #else 618 | usleep(max(50, diff) * 900); 619 | #endif 620 | 621 | } 622 | 623 | system(CLEAR); 624 | // #if defined(_WIN32) || defined(_WIN64) 625 | // PlaySound(TEXT("game-over-31-179699.wav"), NULL, SND_FILENAME | SND_ASYNC); 626 | // #endif 627 | // Sleep() 628 | cout << "\n\n\n"; 629 | cout << R"( 630 | _____ ____ 631 | / ____| / __ \ 632 | | | __ __ _ _ __ ___ ___ | | | |_ _____ _ __ 633 | | | |_ |/ _` | '_ ` _ \ / _ \ | | | \ \ / / _ \ '__| 634 | | |__| | (_| | | | | | | __/ | |__| |\ V / __/ | 635 | \_____|\__,_|_| |_| |_|\___| \____/ \_/ \___|_| 636 | )" << "\n\n\n\n\n"; 637 | // #if defined(_WIN32) || defined(_WIN64) 638 | // Sleep(3000); 639 | // #endif 640 | 641 | cout << " "; 642 | cout << "G A M E O V E R ! ! ! \n\n\n\n\n"; 643 | cout << "Your Highest Score : " << max_score << "\n\n\n"; 644 | cout << "\n\nDo You Want To Play Again..?\n\n"; 645 | cout << "NOTE : If You Press Key Other Than 0 or 1 Then Computer will treat it as 0..\n"; 646 | cout << "Enter 1 For \"YES\" and 0 For \"NO\" : "; 647 | 648 | cin >> play; 649 | 650 | } while(play == 1 || play == 49); 651 | 652 | cout << "\n\n\n\n\nOkay, Byee... "; 653 | cout << "Thank You " << name << " For Playing Our Game..!!\n\n\n\n"; 654 | 655 | return 0; 656 | } 657 | --------------------------------------------------------------------------------