Repository navigation
Expand file tree
/
Copy pathGame.cpp
More file actions
205 lines (171 loc) · 7.3 KB
/
Copy pathGame.cpp
File metadata and controls
205 lines (171 loc) · 7.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include "Game.h"
#include "InputHandler.h"
#include "CheatHandler.h"
#include <conio.h>
#include "Renderer.h"
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <thread>
// Constructor
Game::Game(const int width, const int height, const char snakeSymbol1, const char snakeSymbol2, const char appleSymbol,
const char emptySymbol, const bool isMultiplayer)
: grid(width, height, emptySymbol), previousGrid(width, height, emptySymbol),
direction1({0, 1}), direction2({0, -1}), isGameOver(false),
isMultiplayer(isMultiplayer), applesEaten(0), snakeSpeed(200), playerWhoLost(0),
snakeSymbol1(snakeSymbol1), snakeSymbol2(snakeSymbol2), appleSymbol(appleSymbol), emptySymbol(emptySymbol), maxApplesOnMap(1), paradiseModeActive(false) {
initializeGame();
// Initialize key states to false
std::fill(std::begin(keys), std::end(keys), false);
}
// Initialize game elements
void Game::initializeGame() {
// Initialize snake 1 in the center
snake1 = {{grid.getWidth() / 2, grid.getHeight() / 2}};
grid.setCell(snake1.front().first, snake1.front().second, snakeSymbol1);
if (isMultiplayer) {
// Initialize snake 2 on the opposite side if multiplayer
snake2 = {{grid.getWidth() / 2, grid.getHeight() / 2 - 2}};
grid.setCell(snake2.front().first, snake2.front().second, snakeSymbol2);
}
previousGrid = grid; // Initialize previousGrid to match current grid
generateFoodPositions(maxApplesOnMap); // Start with one apple
}
// Generate multiple food positions (apples) on the grid
void Game::generateFoodPositions(const int numApples) {
foodPositions.clear(); // Clear any existing food positions
srand(static_cast<unsigned int>(time(nullptr)) + applesEaten); // Ensure better randomness
for (int i = 0; i < numApples; ++i) {
int x, y;
do {
x = rand() % grid.getWidth();
y = rand() % grid.getHeight();
} while (grid.getCell(x, y) == snakeSymbol1 ||
(isMultiplayer && grid.getCell(x, y) == snakeSymbol2) ||
grid.getCell(x, y) == appleSymbol); // Ensure it's not placed on a snake or existing apple
foodPositions.push_back({x, y});
grid.setCell(x, y, appleSymbol); // Set the apple on the grid
}
}
// Move the snake in the current direction
void Game::moveSnake(std::list<std::pair<int, int>>& snake, const std::pair<int, int>& direction,
const char snakeSymbol, const int playerNumber) {
auto newHead = std::make_pair(snake.front().first + direction.first,
snake.front().second + direction.second);
// Wrap around the grid (circular)
newHead.first = (newHead.first + grid.getWidth()) % grid.getWidth();
newHead.second = (newHead.second + grid.getHeight()) % grid.getHeight();
// Check collision with itself
if (checkCollision(newHead, snake)) {
isGameOver = true;
playerWhoLost = playerNumber; // Mark the player who lost
return;
}
// Move the snake: Add new head
snake.push_front(newHead);
grid.setCell(newHead.first, newHead.second, snakeSymbol); // Set the new head as snake
// Check if food is eaten
auto it = std::ranges::find(foodPositions, newHead);
if (it != foodPositions.end()) {
applesEaten++;
foodPositions.erase(it); // Remove the eaten apple
// Check if all apples are eaten, generate new apples if needed
if (foodPositions.empty()) {
if (!paradiseModeActive) {
// Increase the number of apples based on apples eaten
if (applesEaten >= 20) {
maxApplesOnMap = 4; // Max 4 apples
} else if (applesEaten >= 15) {
maxApplesOnMap = 3;
} else if (applesEaten >= 10) {
maxApplesOnMap = 2;
}
// Randomize how many apples to place (between 1 and maxApplesOnMap)
int numApples = (rand() % maxApplesOnMap) + 1;
generateFoodPositions(numApples);
}
}
// Increase speed every 5 apples eaten
if (applesEaten % 5 == 0 && snakeSpeed > 50) { // Ensure speed doesn't become too fast
snakeSpeed -= 20; // Reduce the delay, increasing speed
}
} else {
// Remove the tail
auto tail = snake.back();
snake.pop_back();
grid.setCell(tail.first, tail.second, emptySymbol); // Clear tail (set as empty)
}
}
bool Game::checkCollision(const std::pair<int, int>& newHead, const std::list<std::pair<int, int>>& snake) {
for (const auto& segment : snake) {
if (newHead == segment) {
return true; // Collision detected
}
}
return false;
}
// Update game state (move snakes)
void Game::update() {
if (!isGameOver) {
previousGrid = grid; // Store the previous state of the grid before updating
// Move snake 1
moveSnake(snake1, direction1, snakeSymbol1, 1); // Player 1 is snake 1
// Move snake 2 if multiplayer and game is not over
if (isMultiplayer) {
moveSnake(snake2, direction2, snakeSymbol2, 2); // Player 2 is snake 2
}
// Check for cheat codes
CheatHandler::checkForCheats(*this, snake1, snake2);
}
}
// Set the direction of snake 1
void Game::setDirection1(int dx, int dy) {
// Prevent the snake from reversing into itself
if (dx != -direction1.first || dy != -direction1.second) {
direction1 = {dx, dy};
}
}
// Set the direction of snake 2
void Game::setDirection2(int dx, int dy) {
// Prevent the snake from reversing into itself
if (dx != -direction2.first || dy != -direction2.second) {
direction2 = {dx, dy};
}
}
// Display the grid
void Game::displayGrid(bool& isFirstRender) {
Renderer::printGrid(grid, previousGrid, isFirstRender); // The renderer will display the grid with the chosen symbols
isFirstRender = false;
previousGrid = grid;
}
// Get the game-over state
bool Game::getGameOver() const {
Renderer::moveCursorBelowGrid(grid.getHeight()); // Move cursor two lines below the grid
return isGameOver;
}
void Game::start() {
bool isFirstRender = true; // Track if it's the first render
Renderer::setupSignalHandler(grid.getHeight());
auto lastMoveTime = std::chrono::steady_clock::now();
// Game loop
while (!this->getGameOver()) {
// Handle input (track key presses)
InputHandler::handleInput(*this);
auto currentTime = std::chrono::steady_clock::now();
std::chrono::duration<double, std::milli> timeSinceLastMove = currentTime - lastMoveTime;
// Move the snakes based on the current snakeSpeed
if (timeSinceLastMove.count() >= snakeSpeed) {
this->update(); // Update the snakes' positions
this->displayGrid(isFirstRender); // Only update the specific parts of the grid that changed
lastMoveTime = currentTime; // Update last move time
}
// Small delay to avoid CPU overuse
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
// Display which player lost in multiplayer mode
if (isMultiplayer) {
std::cout << "Game Over! Player " << playerWhoLost << " lost!" << std::endl;
} else {
std::cout << "Game Over!" << std::endl;
}
}