From 9ce81b8f21dd1ca09f413afd41bb79a2863491fe Mon Sep 17 00:00:00 2001 From: Hardip muliyasiya Date: Sun, 28 Sep 2025 15:46:56 +0530 Subject: [PATCH 1/9] Fixed issue #1: Increase difficulty level as the game proceeds. --- .gitignore | 3 ++ .vscode/settings.json | 6 +++ snake.h | 105 ++++++++++++++++++++++++++++-------------- 3 files changed, 79 insertions(+), 35 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index d4fb281..00bab71 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ # debug information files *.dwo + +# compiled executable file +snake \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..74eb267 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "*.ejs": "html", + "chrono": "cpp" + } +} \ No newline at end of file diff --git a/snake.h b/snake.h index ebe1192..475a0cd 100644 --- a/snake.h +++ b/snake.h @@ -11,10 +11,12 @@ using namespace std; using std::chrono::system_clock; using namespace std::this_thread; -char direction='r'; +char direction = 'r'; +chrono::duration sleep_time = 500ms; -void input_handler(){ +void input_handler() +{ // change terminal settings struct termios oldt, newt; tcgetattr(STDIN_FILENO, &oldt); @@ -23,12 +25,16 @@ void input_handler(){ newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); map keymap = {{'d', 'r'}, {'a', 'l'}, {'w', 'u'}, {'s', 'd'}, {'q', 'q'}}; - while (true) { + while (true) + { char input = getchar(); - if (keymap.find(input) != keymap.end()) { + if (keymap.find(input) != keymap.end()) + { // This now correctly modifies the single, shared 'direction' variable direction = keymap[input]; - }else if (input == 'q'){ + } + else if (input == 'q') + { exit(0); } // You could add an exit condition here, e.g., if (input == 'q') break; @@ -36,66 +42,95 @@ void input_handler(){ tcsetattr(STDIN_FILENO, TCSANOW, &oldt); } - -void render_game(int size, deque> &snake, pair food){ - for(size_t i=0;i> &snake, pair food) +{ + for (size_t i = 0; i < size; i++) + { + for (size_t j = 0; j < size; j++) + { + if (i == food.first && j == food.second) + { cout << "🍎"; - }else if (find(snake.begin(), snake.end(), make_pair(int(i), int(j))) != snake.end()) { + } + else if (find(snake.begin(), snake.end(), make_pair(int(i), int(j))) != snake.end()) + { cout << "🐍"; - }else{ + } + else + { cout << "⬜"; } + } + cout << endl; } - cout << endl; -} } -pair get_next_head(pair current, char direction){ - pair next; - if(direction =='r'){ - next = make_pair(current.first,(current.second+1) % 10); - }else if (direction=='l') +pair get_next_head(pair current, char direction) +{ + pair next; + if (direction == 'r') { - next = make_pair(current.first, current.second==0?9:current.second-1); - }else if(direction =='d'){ - next = make_pair((current.first+1)%10,current.second); - }else if (direction=='u'){ - next = make_pair(current.first==0?9:current.first-1, current.second); - } + next = make_pair(current.first, (current.second + 1) % 10); + } + else if (direction == 'l') + { + next = make_pair(current.first, current.second == 0 ? 9 : current.second - 1); + } + else if (direction == 'd') + { + next = make_pair((current.first + 1) % 10, current.second); + } + else if (direction == 'u') + { + next = make_pair(current.first == 0 ? 9 : current.first - 1, current.second); + } return next; - } +void reduce_sleep_time(deque> &snake) +{ + if (snake.size() % 10 == 0 && sleep_time > 100ms) + { + sleep_time -= 50ms; + } +} + -void game_play(){ +void game_play() +{ system("clear"); deque> snake; - snake.push_back(make_pair(0,0)); + snake.push_back(make_pair(0, 0)); pair food = make_pair(rand() % 10, rand() % 10); - for(pair head=make_pair(0,1);; head = get_next_head(head, direction)){ + for (pair head = make_pair(0, 1);; head = get_next_head(head, direction)) + { // send the cursor to the top cout << "\033[H"; // check self collision - if (find(snake.begin(), snake.end(), head) != snake.end()) { + if (find(snake.begin(), snake.end(), head) != snake.end()) + { system("clear"); cout << "Game Over" << endl; exit(0); - }else if (head.first == food.first && head.second == food.second) { + } + else if (head.first == food.first && head.second == food.second) + { // grow snake food = make_pair(rand() % 10, rand() % 10); - snake.push_back(head); - }else{ + snake.push_back(head); + reduce_sleep_time(snake); + } + else + { // move snake snake.push_back(head); snake.pop_front(); } render_game(10, snake, food); cout << "length of snake: " << snake.size() << endl; - - sleep_for(500ms); + + sleep_for(sleep_time); } } From 5538e26c18e408bdfb4f07d6bba72af5077023e9 Mon Sep 17 00:00:00 2001 From: Hardip muliyasiya Date: Sun, 28 Sep 2025 15:52:35 +0530 Subject: [PATCH 2/9] Fixed issue #2: Food should not spawn inside snakes body --- snake.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/snake.h b/snake.h index 475a0cd..29de34b 100644 --- a/snake.h +++ b/snake.h @@ -96,6 +96,16 @@ void reduce_sleep_time(deque> &snake) } } +pair get_food(deque> &snake) +{ + pair food = make_pair(rand() % 10, rand() % 10); + while (find(snake.begin(), snake.end(), food) != snake.end()) + { + food = make_pair(rand() % 10, rand() % 10); + } + return food; +} + void game_play() { @@ -103,7 +113,7 @@ void game_play() deque> snake; snake.push_back(make_pair(0, 0)); - pair food = make_pair(rand() % 10, rand() % 10); + pair food = get_food(snake); for (pair head = make_pair(0, 1);; head = get_next_head(head, direction)) { // send the cursor to the top @@ -118,7 +128,7 @@ void game_play() else if (head.first == food.first && head.second == food.second) { // grow snake - food = make_pair(rand() % 10, rand() % 10); + food = get_food(snake); snake.push_back(head); reduce_sleep_time(snake); } From 14c47ed9c585ef42999e5b620f2839cd5d90f741 Mon Sep 17 00:00:00 2001 From: Hardip muliyasiya Date: Sun, 28 Sep 2025 15:55:49 +0530 Subject: [PATCH 3/9] Fixed issue #3: Maintain and update score as the game proceeds --- snake.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/snake.h b/snake.h index 29de34b..35cf86f 100644 --- a/snake.h +++ b/snake.h @@ -14,6 +14,7 @@ using namespace std::this_thread; char direction = 'r'; chrono::duration sleep_time = 500ms; +int score = 10; void input_handler() { @@ -106,6 +107,11 @@ pair get_food(deque> &snake) return food; } +void increase_score() +{ + score += 10; +} + void game_play() { @@ -131,6 +137,7 @@ void game_play() food = get_food(snake); snake.push_back(head); reduce_sleep_time(snake); + increase_score(); } else { @@ -139,7 +146,7 @@ void game_play() snake.pop_front(); } render_game(10, snake, food); - cout << "length of snake: " << snake.size() << endl; + cout << "length of snake: " << snake.size() << " - score: " << score << endl; sleep_for(sleep_time); } From 2249b8875d056bbf88523b879e21760714f8aebd Mon Sep 17 00:00:00 2001 From: Hardip muliyasiya Date: Sun, 28 Sep 2025 16:01:24 +0530 Subject: [PATCH 4/9] Fixed issue #4: Spawn poisonous food that snakes needs to avoid. --- snake.h | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/snake.h b/snake.h index 35cf86f..bd6319a 100644 --- a/snake.h +++ b/snake.h @@ -43,7 +43,7 @@ void input_handler() tcsetattr(STDIN_FILENO, TCSANOW, &oldt); } -void render_game(int size, deque> &snake, pair food) +void render_game(int size, deque> &snake, pair food, pair poisonousFood) { for (size_t i = 0; i < size; i++) { @@ -53,6 +53,10 @@ void render_game(int size, deque> &snake, pair food) { cout << "🍎"; } + else if (i == poisonousFood.first && j == poisonousFood.second) + { + cout << "💀"; + } else if (find(snake.begin(), snake.end(), make_pair(int(i), int(j))) != snake.end()) { cout << "🐍"; @@ -112,6 +116,16 @@ void increase_score() score += 10; } +pair get_poisonous_food(deque> &snake) +{ + pair food = make_pair(rand() % 10, rand() % 10); + while (find(snake.begin(), snake.end(), food) != snake.end()) + { + food = make_pair(rand() % 10, rand() % 10); + } + return food; +} + void game_play() { @@ -120,12 +134,13 @@ void game_play() snake.push_back(make_pair(0, 0)); pair food = get_food(snake); + pair poisonousFood = get_poisonous_food(snake); for (pair head = make_pair(0, 1);; head = get_next_head(head, direction)) { // send the cursor to the top cout << "\033[H"; // check self collision - if (find(snake.begin(), snake.end(), head) != snake.end()) + if (find(snake.begin(), snake.end(), head) != snake.end() || (head.first == poisonousFood.first && head.second == poisonousFood.second)) { system("clear"); cout << "Game Over" << endl; @@ -135,6 +150,7 @@ void game_play() { // grow snake food = get_food(snake); + poisonousFood = get_poisonous_food(snake); snake.push_back(head); reduce_sleep_time(snake); increase_score(); @@ -145,7 +161,7 @@ void game_play() snake.push_back(head); snake.pop_front(); } - render_game(10, snake, food); + render_game(10, snake, food, poisonousFood); cout << "length of snake: " << snake.size() << " - score: " << score << endl; sleep_for(sleep_time); From d6a009e95c66287903a77de15696f3d5bfdd4161 Mon Sep 17 00:00:00 2001 From: Hardip muliyasiya Date: Sun, 28 Sep 2025 19:08:10 +0530 Subject: [PATCH 5/9] Fixed issue #5: Add play/pause functinality in the game. --- .vscode/settings.json | 4 +++- snake.h | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 74eb267..89c929c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,8 @@ { "files.associations": { "*.ejs": "html", - "chrono": "cpp" + "chrono": "cpp", + "thread": "cpp", + "ostream": "cpp" } } \ No newline at end of file diff --git a/snake.h b/snake.h index bd6319a..c02d340 100644 --- a/snake.h +++ b/snake.h @@ -15,6 +15,7 @@ char direction = 'r'; chrono::duration sleep_time = 500ms; int score = 10; +bool is_paused = false; void input_handler() { @@ -29,7 +30,10 @@ void input_handler() while (true) { char input = getchar(); - if (keymap.find(input) != keymap.end()) + if (input == 'p') { + is_paused = !is_paused; // Toggle pause state + } + else if (keymap.find(input) != keymap.end()) { // This now correctly modifies the single, shared 'direction' variable direction = keymap[input]; @@ -135,10 +139,15 @@ void game_play() pair food = get_food(snake); pair poisonousFood = get_poisonous_food(snake); - for (pair head = make_pair(0, 1);; head = get_next_head(head, direction)) + for (pair head = make_pair(0, 1);;) { // send the cursor to the top cout << "\033[H"; + if (is_paused) { + cout << "Game Paused. Press 'p' to resume." << endl; + sleep_for(300ms); // Slow refresh while paused + continue; // Skip game logic + } // check self collision if (find(snake.begin(), snake.end(), head) != snake.end() || (head.first == poisonousFood.first && head.second == poisonousFood.second)) { @@ -165,5 +174,6 @@ void game_play() cout << "length of snake: " << snake.size() << " - score: " << score << endl; sleep_for(sleep_time); + head = get_next_head(head, direction); } } From b2d12fbccae99cbd7ef85d6b62d9c8494803d26d Mon Sep 17 00:00:00 2001 From: Hardip muliyasiya Date: Sun, 28 Sep 2025 19:59:21 +0530 Subject: [PATCH 6/9] Fixed issue #6: Keep track of top 10 highest scores --- snake.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/snake.h b/snake.h index c02d340..14a8421 100644 --- a/snake.h +++ b/snake.h @@ -16,6 +16,10 @@ char direction = 'r'; chrono::duration sleep_time = 500ms; int score = 10; bool is_paused = false; +vector top_scores; +bool waiting_for_restart = false; + +void game_play(); void input_handler() { @@ -29,6 +33,7 @@ void input_handler() map keymap = {{'d', 'r'}, {'a', 'l'}, {'w', 'u'}, {'s', 'd'}, {'q', 'q'}}; while (true) { + if (waiting_for_restart) continue; // Ignore input during restart prompt char input = getchar(); if (input == 'p') { is_paused = !is_paused; // Toggle pause state @@ -120,14 +125,43 @@ void increase_score() score += 10; } -pair get_poisonous_food(deque> &snake) +pair get_poisonous_food(deque> &snake, pair food) { - pair food = make_pair(rand() % 10, rand() % 10); - while (find(snake.begin(), snake.end(), food) != snake.end()) + pair pfood = make_pair(rand() % 10, rand() % 10); + while (find(snake.begin(), snake.end(), pfood) != snake.end() || food == pfood) { - food = make_pair(rand() % 10, rand() % 10); + pfood = make_pair(rand() % 10, rand() % 10); + } + return pfood; +} + +void show_top_scores() { + cout << "\n--- Top 10 Scores ---\n"; + for (size_t i = 0; i < top_scores.size(); ++i) { + cout << i + 1 << ". " << top_scores[i] << endl; + } +} + +void update_top_scores(int new_score) { + top_scores.push_back(new_score); + sort(top_scores.rbegin(), top_scores.rend()); + if (top_scores.size() > 10) top_scores.resize(10); +} + +void wait_for_restart() { + cout << "Press 'n' to start again or 'q' to quit." << endl; + while (true) { + char input = getchar(); + if (input == 'n') { + score = 10; + direction = 'r'; + waiting_for_restart = false; + game_play(); + break; + } else if (input == 'q') { + exit(0); + } } - return food; } @@ -138,7 +172,7 @@ void game_play() snake.push_back(make_pair(0, 0)); pair food = get_food(snake); - pair poisonousFood = get_poisonous_food(snake); + pair poisonousFood = get_poisonous_food(snake, food); for (pair head = make_pair(0, 1);;) { // send the cursor to the top @@ -151,15 +185,20 @@ void game_play() // check self collision if (find(snake.begin(), snake.end(), head) != snake.end() || (head.first == poisonousFood.first && head.second == poisonousFood.second)) { + waiting_for_restart = true; system("clear"); cout << "Game Over" << endl; - exit(0); + update_top_scores(score); + show_top_scores(); + wait_for_restart(); + return; // End current game_play loop + // exit(0); } else if (head.first == food.first && head.second == food.second) { // grow snake food = get_food(snake); - poisonousFood = get_poisonous_food(snake); + poisonousFood = get_poisonous_food(snake, food); snake.push_back(head); reduce_sleep_time(snake); increase_score(); From 7c5cdf738e19fc131ed96e67c0bbbbcff6bf3e35 Mon Sep 17 00:00:00 2001 From: Hardip muliyasiya Date: Sun, 28 Sep 2025 20:08:08 +0530 Subject: [PATCH 7/9] Fixed bug: Preventing the snake from reversing direction directly. --- snake.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/snake.h b/snake.h index 14a8421..b86fd8a 100644 --- a/snake.h +++ b/snake.h @@ -21,6 +21,13 @@ bool waiting_for_restart = false; void game_play(); +bool is_opposite_direction(char current, char next) { + return (current == 'r' && next == 'l') || + (current == 'l' && next == 'r') || + (current == 'u' && next == 'd') || + (current == 'd' && next == 'u'); +} + void input_handler() { // change terminal settings @@ -41,7 +48,10 @@ void input_handler() else if (keymap.find(input) != keymap.end()) { // This now correctly modifies the single, shared 'direction' variable - direction = keymap[input]; + char next_dir = keymap[input]; + if (!is_opposite_direction(direction, next_dir)) { + direction = next_dir; + } } else if (input == 'q') { From 1bb5995dc41ce7bc115964fbd70f9e8525851a99 Mon Sep 17 00:00:00 2001 From: Hardip muliyasiya Date: Sun, 28 Sep 2025 20:41:17 +0530 Subject: [PATCH 8/9] Applied refactoring. --- main.cpp | 7 +- snake.h | 215 ++++++++++++++++++++++++++++++++----------------------- 2 files changed, 130 insertions(+), 92 deletions(-) diff --git a/main.cpp b/main.cpp index ef65093..a1e8319 100644 --- a/main.cpp +++ b/main.cpp @@ -1,9 +1,10 @@ #include "snake.h" int main(int argc, char *argv[]) { - thread input_thread(input_handler); - thread game_thread(game_play); + GameState state; + thread input_thread(input_handler, std::ref(state)); + thread game_thread(game_play, std::ref(state)); input_thread.join(); game_thread.join(); -return 0; + return 0; } \ No newline at end of file diff --git a/snake.h b/snake.h index b86fd8a..aa8d9a6 100644 --- a/snake.h +++ b/snake.h @@ -11,15 +11,22 @@ using namespace std; using std::chrono::system_clock; using namespace std::this_thread; -char direction = 'r'; -chrono::duration sleep_time = 500ms; -int score = 10; -bool is_paused = false; -vector top_scores; -bool waiting_for_restart = false; +struct GameState { + char direction = 'r'; + chrono::milliseconds sleep_time = chrono::milliseconds(500); + int score = 10; + bool is_paused = false; + bool waiting_for_restart = false; + deque> snake; + pair food; + pair poisonousFood; +}; + +GameState gameState; +std::vector top_scores; -void game_play(); +void game_play(GameState& state); bool is_opposite_direction(char current, char next) { return (current == 'r' && next == 'l') || @@ -28,38 +35,61 @@ bool is_opposite_direction(char current, char next) { (current == 'd' && next == 'u'); } -void input_handler() -{ - // change terminal settings - struct termios oldt, newt; +void setup_terminal(struct termios& oldt, struct termios& newt) { tcgetattr(STDIN_FILENO, &oldt); newt = oldt; // turn off canonical mode and echo newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); +} + +void restore_terminal(struct termios& oldt) { + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); +} + +void handle_direction_input(char input, map& keymap) { + char next_dir = keymap[input]; + if (!is_opposite_direction(gameState.direction, next_dir)) { + gameState.direction = next_dir; + } +} + +void input_handler(GameState& state) +{ + struct termios oldt, newt; + setup_terminal(oldt, newt); map keymap = {{'d', 'r'}, {'a', 'l'}, {'w', 'u'}, {'s', 'd'}, {'q', 'q'}}; while (true) { - if (waiting_for_restart) continue; // Ignore input during restart prompt + if (state.waiting_for_restart) continue; char input = getchar(); if (input == 'p') { - is_paused = !is_paused; // Toggle pause state + state.is_paused = !state.is_paused; } else if (keymap.find(input) != keymap.end()) { - // This now correctly modifies the single, shared 'direction' variable char next_dir = keymap[input]; - if (!is_opposite_direction(direction, next_dir)) { - direction = next_dir; + if (!is_opposite_direction(state.direction, next_dir)) { + state.direction = next_dir; } } else if (input == 'q') { exit(0); } - // You could add an exit condition here, e.g., if (input == 'q') break; } - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + restore_terminal(oldt); +} + +string render_cell(int i, int j, const deque>& snake, const pair& food, const pair& poisonousFood) { + if (i == food.first && j == food.second) + return "🍎"; + else if (i == poisonousFood.first && j == poisonousFood.second) + return "💀"; + else if (find(snake.begin(), snake.end(), make_pair(i, j)) != snake.end()) + return "🐍"; + else + return "⬜"; } void render_game(int size, deque> &snake, pair food, pair poisonousFood) @@ -68,22 +98,7 @@ void render_game(int size, deque> &snake, pair food, pa { for (size_t j = 0; j < size; j++) { - if (i == food.first && j == food.second) - { - cout << "🍎"; - } - else if (i == poisonousFood.first && j == poisonousFood.second) - { - cout << "💀"; - } - else if (find(snake.begin(), snake.end(), make_pair(int(i), int(j))) != snake.end()) - { - cout << "🐍"; - } - else - { - cout << "⬜"; - } + cout << render_cell(i, j, snake, food, poisonousFood); } cout << endl; } @@ -114,35 +129,36 @@ pair get_next_head(pair current, char direction) void reduce_sleep_time(deque> &snake) { - if (snake.size() % 10 == 0 && sleep_time > 100ms) + if (snake.size() % 10 == 0 && gameState.sleep_time > 100ms) { - sleep_time -= 50ms; + gameState.sleep_time -= 50ms; } } +pair get_random_position(const deque>& snake, const pair* avoid = nullptr) { + pair pos; + do { + pos = make_pair(rand() % 10, rand() % 10); + } while ( + find(snake.begin(), snake.end(), pos) != snake.end() || + (avoid && pos == *avoid) + ); + return pos; +} + pair get_food(deque> &snake) { - pair food = make_pair(rand() % 10, rand() % 10); - while (find(snake.begin(), snake.end(), food) != snake.end()) - { - food = make_pair(rand() % 10, rand() % 10); - } - return food; + return get_random_position(snake); } void increase_score() { - score += 10; + gameState.score += 10; } pair get_poisonous_food(deque> &snake, pair food) { - pair pfood = make_pair(rand() % 10, rand() % 10); - while (find(snake.begin(), snake.end(), pfood) != snake.end() || food == pfood) - { - pfood = make_pair(rand() % 10, rand() % 10); - } - return pfood; + return get_random_position(snake, &food); } void show_top_scores() { @@ -163,10 +179,10 @@ void wait_for_restart() { while (true) { char input = getchar(); if (input == 'n') { - score = 10; - direction = 'r'; - waiting_for_restart = false; - game_play(); + gameState.score = 10; + gameState.direction = 'r'; + gameState.waiting_for_restart = false; + game_play(gameState); break; } else if (input == 'q') { exit(0); @@ -175,54 +191,75 @@ void wait_for_restart() { } -void game_play() +void handle_pause() { + cout << "Game Paused. Press 'p' to resume." << endl; + sleep_for(300ms); +} + +bool is_collision(const deque>& snake, const pair& head, const pair& poisonousFood) { + return find(snake.begin(), snake.end(), head) != snake.end() || + (head.first == poisonousFood.first && head.second == poisonousFood.second); +} + +void handle_game_over(GameState& state) { + gameState.waiting_for_restart = true; + system("clear"); + cout << "Game Over" << endl; + update_top_scores(gameState.score); + show_top_scores(); + wait_for_restart(); +} + +void handle_food_eaten(deque>& snake, pair& food, pair& poisonousFood, const pair& head) { + food = get_food(snake); + poisonousFood = get_poisonous_food(snake, food); + snake.push_back(head); + reduce_sleep_time(snake); + increase_score(); +} + +void move_snake(deque>& snake, const pair& head) { + snake.push_back(head); + snake.pop_front(); +} + +void display_status(const deque>& snake) { + cout << "length of snake: " << snake.size() << " - score: " << gameState.score << endl; +} + +void game_play(GameState& state) { system("clear"); - deque> snake; - snake.push_back(make_pair(0, 0)); + state.snake.clear(); + state.snake.push_back(make_pair(0, 0)); + state.food = get_food(state.snake); + state.poisonousFood = get_poisonous_food(state.snake, state.food); + pair head = make_pair(0, 1); - pair food = get_food(snake); - pair poisonousFood = get_poisonous_food(snake, food); - for (pair head = make_pair(0, 1);;) + while (true) { - // send the cursor to the top cout << "\033[H"; - if (is_paused) { - cout << "Game Paused. Press 'p' to resume." << endl; - sleep_for(300ms); // Slow refresh while paused - continue; // Skip game logic + if (state.is_paused) { + handle_pause(); + continue; } - // check self collision - if (find(snake.begin(), snake.end(), head) != snake.end() || (head.first == poisonousFood.first && head.second == poisonousFood.second)) - { - waiting_for_restart = true; - system("clear"); - cout << "Game Over" << endl; - update_top_scores(score); - show_top_scores(); - wait_for_restart(); - return; // End current game_play loop - // exit(0); + if (is_collision(state.snake, head, state.poisonousFood)) { + handle_game_over(state); + return; } - else if (head.first == food.first && head.second == food.second) + else if (head.first == state.food.first && head.second == state.food.second) { - // grow snake - food = get_food(snake); - poisonousFood = get_poisonous_food(snake, food); - snake.push_back(head); - reduce_sleep_time(snake); - increase_score(); + handle_food_eaten(state.snake, state.food, state.poisonousFood, head); + state.score += 10; } else { - // move snake - snake.push_back(head); - snake.pop_front(); + move_snake(state.snake, head); } - render_game(10, snake, food, poisonousFood); - cout << "length of snake: " << snake.size() << " - score: " << score << endl; + render_game(10, state.snake, state.food, state.poisonousFood); + display_status(state.snake); - sleep_for(sleep_time); - head = get_next_head(head, direction); + sleep_for(state.sleep_time); + head = get_next_head(head, state.direction); } } From 91ad9d15bd51a01cfe53600491bcdbf14ded6066 Mon Sep 17 00:00:00 2001 From: Hardip muliyasiya Date: Sun, 28 Sep 2025 21:01:39 +0530 Subject: [PATCH 9/9] Applied encapsulation. --- main.cpp | 6 +- snake.h | 399 +++++++++++++++++++++++++------------------------------ 2 files changed, 186 insertions(+), 219 deletions(-) diff --git a/main.cpp b/main.cpp index a1e8319..6146b50 100644 --- a/main.cpp +++ b/main.cpp @@ -1,9 +1,9 @@ #include "snake.h" int main(int argc, char *argv[]) { - GameState state; - thread input_thread(input_handler, std::ref(state)); - thread game_thread(game_play, std::ref(state)); + SnakeGame game; + thread input_thread(&SnakeGame::input_handler, &game); + thread game_thread(&SnakeGame::game_play, &game); input_thread.join(); game_thread.join(); return 0; diff --git a/snake.h b/snake.h index aa8d9a6..7a70c0e 100644 --- a/snake.h +++ b/snake.h @@ -1,265 +1,232 @@ +#pragma once #include #include #include #include #include #include -#include // for system clear +#include #include #include #include + using namespace std; -using std::chrono::system_clock; -using namespace std::this_thread; +using namespace std::chrono_literals; -struct GameState { +class SnakeGame { +private: char direction = 'r'; - chrono::milliseconds sleep_time = chrono::milliseconds(500); + std::chrono::milliseconds sleep_time = 500ms; int score = 10; bool is_paused = false; bool waiting_for_restart = false; - deque> snake; - pair food; - pair poisonousFood; -}; - -GameState gameState; -std::vector top_scores; - -void game_play(GameState& state); - -bool is_opposite_direction(char current, char next) { - return (current == 'r' && next == 'l') || - (current == 'l' && next == 'r') || - (current == 'u' && next == 'd') || - (current == 'd' && next == 'u'); -} + std::deque> snake; + std::pair food; + std::pair poisonousFood; + std::vector top_scores; + + bool is_opposite_direction(char current, char next) { + return (current == 'r' && next == 'l') || + (current == 'l' && next == 'r') || + (current == 'u' && next == 'd') || + (current == 'd' && next == 'u'); + } -void setup_terminal(struct termios& oldt, struct termios& newt) { - tcgetattr(STDIN_FILENO, &oldt); - newt = oldt; - // turn off canonical mode and echo - newt.c_lflag &= ~(ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSANOW, &newt); -} + void setup_terminal(struct termios& oldt, struct termios& newt) { + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + } -void restore_terminal(struct termios& oldt) { - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); -} + void restore_terminal(struct termios& oldt) { + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + } -void handle_direction_input(char input, map& keymap) { - char next_dir = keymap[input]; - if (!is_opposite_direction(gameState.direction, next_dir)) { - gameState.direction = next_dir; + std::string render_cell(int i, int j) { + if (i == food.first && j == food.second) + return "🍎"; + else if (i == poisonousFood.first && j == poisonousFood.second) + return "💀"; + else if (find(snake.begin(), snake.end(), make_pair(i, j)) != snake.end()) + return "🐍"; + else + return "⬜"; } -} -void input_handler(GameState& state) -{ - struct termios oldt, newt; - setup_terminal(oldt, newt); - map keymap = {{'d', 'r'}, {'a', 'l'}, {'w', 'u'}, {'s', 'd'}, {'q', 'q'}}; - while (true) - { - if (state.waiting_for_restart) continue; - char input = getchar(); - if (input == 'p') { - state.is_paused = !state.is_paused; - } - else if (keymap.find(input) != keymap.end()) - { - char next_dir = keymap[input]; - if (!is_opposite_direction(state.direction, next_dir)) { - state.direction = next_dir; + void render_game(int size) { + for (size_t i = 0; i < size; i++) { + for (size_t j = 0; j < size; j++) { + cout << render_cell(i, j); } - } - else if (input == 'q') - { - exit(0); + cout << endl; } } - restore_terminal(oldt); -} -string render_cell(int i, int j, const deque>& snake, const pair& food, const pair& poisonousFood) { - if (i == food.first && j == food.second) - return "🍎"; - else if (i == poisonousFood.first && j == poisonousFood.second) - return "💀"; - else if (find(snake.begin(), snake.end(), make_pair(i, j)) != snake.end()) - return "🐍"; - else - return "⬜"; -} - -void render_game(int size, deque> &snake, pair food, pair poisonousFood) -{ - for (size_t i = 0; i < size; i++) - { - for (size_t j = 0; j < size; j++) - { - cout << render_cell(i, j, snake, food, poisonousFood); - } - cout << endl; + std::pair get_next_head(std::pair current, char dir) { + if (dir == 'r') + return {current.first, (current.second + 1) % 10}; + else if (dir == 'l') + return {current.first, current.second == 0 ? 9 : current.second - 1}; + else if (dir == 'd') + return {(current.first + 1) % 10, current.second}; + else // 'u' + return {current.first == 0 ? 9 : current.first - 1, current.second}; } -} -pair get_next_head(pair current, char direction) -{ - pair next; - if (direction == 'r') - { - next = make_pair(current.first, (current.second + 1) % 10); - } - else if (direction == 'l') - { - next = make_pair(current.first, current.second == 0 ? 9 : current.second - 1); - } - else if (direction == 'd') - { - next = make_pair((current.first + 1) % 10, current.second); - } - else if (direction == 'u') - { - next = make_pair(current.first == 0 ? 9 : current.first - 1, current.second); + void reduce_sleep_time() { + if (snake.size() % 10 == 0 && sleep_time > 100ms) + sleep_time -= 50ms; } - return next; -} + std::pair get_random_position(const std::pair* avoid = nullptr) { + std::pair pos; + do { + pos = {rand() % 10, rand() % 10}; + } while (find(snake.begin(), snake.end(), pos) != snake.end() || + (avoid && pos == *avoid)); + return pos; + } -void reduce_sleep_time(deque> &snake) -{ - if (snake.size() % 10 == 0 && gameState.sleep_time > 100ms) - { - gameState.sleep_time -= 50ms; + std::pair get_food() { + return get_random_position(); } -} -pair get_random_position(const deque>& snake, const pair* avoid = nullptr) { - pair pos; - do { - pos = make_pair(rand() % 10, rand() % 10); - } while ( - find(snake.begin(), snake.end(), pos) != snake.end() || - (avoid && pos == *avoid) - ); - return pos; -} + void increase_score() { + score += 10; + } -pair get_food(deque> &snake) -{ - return get_random_position(snake); -} + std::pair get_poisonous_food(std::pair food_pos) { + return get_random_position(&food_pos); + } -void increase_score() -{ - gameState.score += 10; -} + void show_top_scores() { + cout << "\n--- Top 10 Scores ---\n"; + for (size_t i = 0; i < top_scores.size(); ++i) + cout << i + 1 << ". " << top_scores[i] << endl; + } -pair get_poisonous_food(deque> &snake, pair food) -{ - return get_random_position(snake, &food); -} + void update_top_scores(int new_score) { + top_scores.push_back(new_score); + sort(top_scores.rbegin(), top_scores.rend()); + if (top_scores.size() > 10) top_scores.resize(10); + } -void show_top_scores() { - cout << "\n--- Top 10 Scores ---\n"; - for (size_t i = 0; i < top_scores.size(); ++i) { - cout << i + 1 << ". " << top_scores[i] << endl; + void handle_pause() { + cout << "Game Paused. Press 'p' to resume." << endl; + std::this_thread::sleep_for(300ms); } -} -void update_top_scores(int new_score) { - top_scores.push_back(new_score); - sort(top_scores.rbegin(), top_scores.rend()); - if (top_scores.size() > 10) top_scores.resize(10); -} + bool is_collision(const std::pair& head) { + return find(snake.begin(), snake.end(), head) != snake.end() || + (head.first == poisonousFood.first && head.second == poisonousFood.second); + } -void wait_for_restart() { - cout << "Press 'n' to start again or 'q' to quit." << endl; - while (true) { - char input = getchar(); - if (input == 'n') { - gameState.score = 10; - gameState.direction = 'r'; - gameState.waiting_for_restart = false; - game_play(gameState); - break; - } else if (input == 'q') { - exit(0); + void handle_game_over() { + waiting_for_restart = true; + system("clear"); + cout << "Game Over" << endl; + update_top_scores(score); + show_top_scores(); + cout << "Press 'n' to start again or 'q' to quit." << endl; + while (true) { + char input = getchar(); + if (input == 'n') { + score = 10; + direction = 'r'; + waiting_for_restart = false; + break; + } else if (input == 'q') { + restore_terminal(saved_oldt); + exit(0); + } } } -} - - -void handle_pause() { - cout << "Game Paused. Press 'p' to resume." << endl; - sleep_for(300ms); -} -bool is_collision(const deque>& snake, const pair& head, const pair& poisonousFood) { - return find(snake.begin(), snake.end(), head) != snake.end() || - (head.first == poisonousFood.first && head.second == poisonousFood.second); -} - -void handle_game_over(GameState& state) { - gameState.waiting_for_restart = true; - system("clear"); - cout << "Game Over" << endl; - update_top_scores(gameState.score); - show_top_scores(); - wait_for_restart(); -} + void handle_food_eaten(const std::pair& head) { + food = get_food(); + poisonousFood = get_poisonous_food(food); + snake.push_back(head); + reduce_sleep_time(); + increase_score(); + } -void handle_food_eaten(deque>& snake, pair& food, pair& poisonousFood, const pair& head) { - food = get_food(snake); - poisonousFood = get_poisonous_food(snake, food); - snake.push_back(head); - reduce_sleep_time(snake); - increase_score(); -} + void move_snake(const std::pair& head) { + snake.push_back(head); + snake.pop_front(); + } -void move_snake(deque>& snake, const pair& head) { - snake.push_back(head); - snake.pop_front(); -} + void display_status() { + cout << "length of snake: " << snake.size() << " - score: " << score << endl; + } -void display_status(const deque>& snake) { - cout << "length of snake: " << snake.size() << " - score: " << gameState.score << endl; -} + struct termios saved_oldt; // For restoring terminal on exit + +public: + SnakeGame() {} + + void input_handler() { + struct termios oldt, newt; + setup_terminal(oldt, newt); + saved_oldt = oldt; + map keymap = {{'d', 'r'}, {'a', 'l'}, {'w', 'u'}, {'s', 'd'}, {'q', 'q'}}; + while (true) { + if (waiting_for_restart) continue; + char input = getchar(); + if (input == 'p') { + is_paused = !is_paused; + } + else if (keymap.find(input) != keymap.end()) { + char next_dir = keymap[input]; + if (!is_opposite_direction(direction, next_dir)) { + direction = next_dir; + } + } + else if (input == 'q') { + restore_terminal(oldt); + exit(0); + } + } + } -void game_play(GameState& state) -{ - system("clear"); - state.snake.clear(); - state.snake.push_back(make_pair(0, 0)); - state.food = get_food(state.snake); - state.poisonousFood = get_poisonous_food(state.snake, state.food); - pair head = make_pair(0, 1); + void game_play() { + system("clear"); + snake.clear(); + snake.push_back({0, 0}); + food = get_food(); + poisonousFood = get_poisonous_food(food); + std::pair head = {0, 1}; - while (true) - { - cout << "\033[H"; - if (state.is_paused) { - handle_pause(); - continue; - } - if (is_collision(state.snake, head, state.poisonousFood)) { - handle_game_over(state); - return; - } - else if (head.first == state.food.first && head.second == state.food.second) - { - handle_food_eaten(state.snake, state.food, state.poisonousFood, head); - state.score += 10; - } - else + while (true) { - move_snake(state.snake, head); - } - render_game(10, state.snake, state.food, state.poisonousFood); - display_status(state.snake); + cout << "\033[H"; + if (is_paused) { + handle_pause(); + continue; + } + if (is_collision(head)) { + handle_game_over(); + // Restart game after 'n' + snake.clear(); + snake.push_back({0, 0}); + food = get_food(); + poisonousFood = get_poisonous_food(food); + head = {0, 1}; + score = 10; + sleep_time = 500ms; + continue; + } + else if (head.first == food.first && head.second == food.second) { + handle_food_eaten(head); + } + else { + move_snake(head); + } + render_game(10); + display_status(); - sleep_for(state.sleep_time); - head = get_next_head(head, state.direction); + std::this_thread::sleep_for(sleep_time); + head = get_next_head(head, direction); + } } -} +};