From 44e556f70b098af8a50888a6193ea0fe2cd81088 Mon Sep 17 00:00:00 2001 From: Om Somani Date: Thu, 18 Sep 2025 13:27:57 +0530 Subject: [PATCH 1/4] All issues solved --- snake.h | 282 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 230 insertions(+), 52 deletions(-) diff --git a/snake.h b/snake.h index ebe1192..529bcb4 100644 --- a/snake.h +++ b/snake.h @@ -4,98 +4,276 @@ #include #include #include -#include // for system clear +#include #include #include #include +#include +#include using namespace std; using std::chrono::system_clock; -using namespace std::this_thread; -char direction='r'; +constexpr int BOARD_SIZE = 10; -void input_handler(){ - // change terminal settings +char direction = 'r'; + +void input_handler() +{ struct termios oldt, newt; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; - // turn off canonical mode and echo - newt.c_lflag &= ~(ICANON | ECHO); + 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()) { - // This now correctly modifies the single, shared 'direction' variable + if (keymap.find(input) != keymap.end()) + { direction = keymap[input]; - }else if (input == 'q'){ + } + else if (input == 'q') + { + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); exit(0); } - // You could add an exit condition here, e.g., if (input == 'q') break; + else if (input == 'p') + { + if (direction != 'P') + direction = 'P'; + else + direction = 'r'; + } } tcsetattr(STDIN_FILENO, TCSANOW, &oldt); } - -void render_game(int size, deque> &snake, pair food){ - for(size_t i=0;i> &snake, pair food, pair poison, int score) +{ + for (int i = 0; i < size; ++i) + { + for (int 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 (i == poison.first && j == poison.second) + { + cout << "☠️"; + } + else if (find(snake.begin(), snake.end(), make_pair(i, j)) != snake.end()) + { cout << "🐍"; - }else{ + } + else + { cout << "⬜"; } + } + cout << '\n'; } - cout << endl; + cout << "Length: " << snake.size() << " Score: " << score << "\n"; } + +pair get_next_head(pair current, char direction) +{ + pair next; + if (direction == 'r') + { + next = make_pair(current.first, (current.second + 1) % BOARD_SIZE); + } + else if (direction == 'l') + { + next = make_pair(current.first, current.second == 0 ? BOARD_SIZE - 1 : current.second - 1); + } + else if (direction == 'd') + { + next = make_pair((current.first + 1) % BOARD_SIZE, current.second); + } + else + { + next = make_pair(current.first == 0 ? BOARD_SIZE - 1 : current.first - 1, current.second); + } + return next; } -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 spawn_food(const deque> &snake) +{ + vector> freeCells; + for (int i = 0; i < BOARD_SIZE; ++i) { - 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); + for (int j = 0; j < BOARD_SIZE; ++j) + { + pair pos = make_pair(i, j); + if (find(snake.begin(), snake.end(), pos) == snake.end()) + { + freeCells.push_back(pos); + } } - return next; - + } + + if (freeCells.empty()) + return make_pair(-1, -1); + + int idx = rand() % freeCells.size(); + return freeCells[idx]; } +void save_score(int score) +{ + ofstream file("scores.txt", ios::app); + if (file.is_open()) + { + file << score << "\n"; + file.close(); + } +} +vector load_top_scores() +{ + ifstream file("scores.txt"); + vector scores; + int s; -void game_play(){ + while (file >> s) + { + scores.push_back(s); + } + file.close(); + + sort(scores.begin(), scores.end(), greater()); + + if (scores.size() > 10) + { + scores.resize(10); + } + + return scores; +} + +void game_play() +{ system("clear"); deque> snake; - snake.push_back(make_pair(0,0)); + snake.push_back(make_pair(0, 0)); + + pair food = spawn_food(snake); + pair poison = spawn_food(snake); + + int score = 0; + int level = 1; + int baseDelay = 500; + + while (true) + { + pair currentHead = snake.back(); + pair nextHead = get_next_head(currentHead, direction); - pair food = make_pair(rand() % 10, rand() % 10); - 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()) { + + bool willGrow = (nextHead == food); + bool collision = false; + + if (direction == 'P') + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + if (willGrow) + { + if (find(snake.begin(), snake.end(), nextHead) != snake.end()) + collision = true; + } + else + { + auto itStart = snake.begin(); + if (!snake.empty()) + ++itStart; + if (find(itStart, snake.end(), nextHead) != snake.end()) + collision = true; + } + + if (collision) + { system("clear"); - cout << "Game Over" << endl; + cout << "Game Over\n"; + cout << "Final Length: " << snake.size() << " Final Score: " << score << "\n"; + + save_score(score); + + cout << "\n=== Top 10 Scores ===\n"; + vector topScores = load_top_scores(); + for (int i = 0; i < topScores.size(); i++) + { + cout << (i + 1) << ". " << topScores[i] << "\n"; + } + + exit(0); + } + + if (nextHead == poison) + { + system("clear"); + cout << "Snake ate poison ☠️ Game Over!\n"; + cout << "Final Length: " << snake.size() << " Final Score: " << score << "\n"; + + save_score(score); + + cout << "\n=== Top 10 Scores ===\n"; + vector topScores = load_top_scores(); + for (int i = 0; i < topScores.size(); i++) + { + cout << (i + 1) << ". " << topScores[i] << "\n"; + } + exit(0); - }else if (head.first == food.first && head.second == food.second) { - // grow snake - food = make_pair(rand() % 10, rand() % 10); - snake.push_back(head); - }else{ - // move snake - snake.push_back(head); + } + + snake.push_back(nextHead); + + if (willGrow) + { + score += 1; + food = spawn_food(snake); + + if (score % 3 == 0) + { + poison = spawn_food(snake); + } + + if (food.first == -1) + { + system("clear"); + cout << "You Win! Final Length: " << snake.size() << " Final Score: " << score << "\n"; + + save_score(score); + + cout << "\n=== Top 10 Scores ===\n"; + vector topScores = load_top_scores(); + for (int i = 0; i < topScores.size(); i++) + { + cout << (i + 1) << ". " << topScores[i] << "\n"; + } + + exit(0); + } + } + else + { snake.pop_front(); } - render_game(10, snake, food); - cout << "length of snake: " << snake.size() << endl; - - sleep_for(500ms); + + level = (score / 5) + 1; + + render_game(BOARD_SIZE, snake, food, poison, score); + + cout << "Level: " << level << "\n"; + + int delay_ms = max(50, baseDelay - (level - 1) * 50 - (int)snake.size() * 5); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); } -} +} \ No newline at end of file From d3efbf846586dcb0c1791e147b9b2f790179b87c Mon Sep 17 00:00:00 2001 From: Om Somani Date: Fri, 26 Sep 2025 18:31:15 +0530 Subject: [PATCH 2/4] Refactored after all 6 issues solved --- snake.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/snake.h b/snake.h index 529bcb4..1630fae 100644 --- a/snake.h +++ b/snake.h @@ -10,8 +10,10 @@ #include #include #include + using namespace std; using std::chrono::system_clock; +using namespace std::this_thread; constexpr int BOARD_SIZE = 10; @@ -99,7 +101,6 @@ pair get_next_head(pair current, char direction) return next; } - pair spawn_food(const deque> &snake) { vector> freeCells; @@ -167,6 +168,9 @@ void game_play() int level = 1; int baseDelay = 500; + auto sleep_duration = 500ms; + int food_eaten = 0; + while (true) { pair currentHead = snake.back(); @@ -179,7 +183,7 @@ void game_play() if (direction == 'P') { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + sleep_for(100ms); continue; } @@ -238,6 +242,7 @@ void game_play() if (willGrow) { score += 1; + food_eaten++; food = spawn_food(snake); if (score % 3 == 0) @@ -245,6 +250,11 @@ void game_play() poison = spawn_food(snake); } + if (food_eaten % 10 == 0 && sleep_duration > 100ms) + { + sleep_duration -= 50ms; + } + if (food.first == -1) { system("clear"); @@ -272,8 +282,9 @@ void game_play() render_game(BOARD_SIZE, snake, food, poison, score); cout << "Level: " << level << "\n"; + cout << "Speed: " << sleep_duration.count() << " ms per frame\n"; int delay_ms = max(50, baseDelay - (level - 1) * 50 - (int)snake.size() * 5); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); + sleep_for(min(sleep_duration, chrono::milliseconds(delay_ms))); } -} \ No newline at end of file +} From ddf50373985d066cb1e7561ecc2e05cf343a2505 Mon Sep 17 00:00:00 2001 From: Om Somani Date: Fri, 26 Sep 2025 18:54:18 +0530 Subject: [PATCH 3/4] Add comprehensive snake tests using Google Test --- main.cpp | 15 +++++--- snake_test.cpp | 102 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 78 insertions(+), 39 deletions(-) diff --git a/main.cpp b/main.cpp index ef65093..a6af84b 100644 --- a/main.cpp +++ b/main.cpp @@ -1,9 +1,12 @@ #include "snake.h" +#include -int main(int argc, char *argv[]) { - thread input_thread(input_handler); - thread game_thread(game_play); - input_thread.join(); - game_thread.join(); -return 0; +char direction = 'r'; // initialize global direction + +int main() +{ + thread inputThread(input_handler); + game_play(); + inputThread.join(); + return 0; } \ No newline at end of file diff --git a/snake_test.cpp b/snake_test.cpp index 42f8561..0d59078 100644 --- a/snake_test.cpp +++ b/snake_test.cpp @@ -1,49 +1,85 @@ -#include #include "snake.h" +#include +char direction = 'r'; -TEST(SnakeBehaviour, NextHeadRight) { - pair current = make_pair(rand() % 10, rand() % 10); - EXPECT_EQ(get_next_head(current, 'r'),make_pair(current.first,current.second+1)); - +TEST(SnakeTest, MoveRight) +{ + pair pos = {2, 3}; + auto next = get_next_head(pos, 'r'); + EXPECT_EQ(next, make_pair(2, 4)); } +TEST(SnakeTest, MoveLeft) +{ + pair pos = {2, 3}; + auto next = get_next_head(pos, 'l'); + EXPECT_EQ(next, make_pair(2, 2)); +} + +TEST(SnakeTest, MoveDown) +{ + pair pos = {2, 3}; + auto next = get_next_head(pos, 'd'); + EXPECT_EQ(next, make_pair(3, 3)); +} -TEST(SnakeBehaviour, NextHeadLeft) { - pair current = make_pair(rand() % 10, rand() % 10); - EXPECT_EQ(get_next_head(current, 'l'),make_pair(current.first,current.second-1)); - +TEST(SnakeTest, MoveUp) +{ + pair pos = {2, 3}; + auto next = get_next_head(pos, 'u'); + EXPECT_EQ(next, make_pair(1, 3)); } -TEST(SnakeBehaviour, NextHeadUp) { - pair current = make_pair(rand() % 10, rand() % 10); - EXPECT_EQ(get_next_head(current, 'u'),make_pair(current.first-1,current.second)); +TEST(SnakeTest, WrapRightEdge) +{ + pair pos = {1, BOARD_SIZE - 1}; + auto next = get_next_head(pos, 'r'); + EXPECT_EQ(next, make_pair(1, 0)); } -TEST(SnakeBehaviour, NextHeadDown) { - pair current = make_pair(rand() % 10, rand() % 10); - EXPECT_EQ(get_next_head(current, 'd'),make_pair(current.first+1,current.second)); - +TEST(SnakeTest, WrapLeftEdge) +{ + pair pos = {1, 0}; + auto next = get_next_head(pos, 'l'); + EXPECT_EQ(next, make_pair(1, BOARD_SIZE - 1)); } +TEST(SnakeTest, WrapBottomEdge) +{ + pair pos = {BOARD_SIZE - 1, 2}; + auto next = get_next_head(pos, 'd'); + EXPECT_EQ(next, make_pair(0, 2)); +} -/** - * g++ -o my_tests snake_test.cpp -lgtest -lgtest_main -pthread; - * This command is a two-part shell command. Let's break it down. +TEST(SnakeTest, WrapTopEdge) +{ + pair pos = {0, 2}; + auto next = get_next_head(pos, 'u'); + EXPECT_EQ(next, make_pair(BOARD_SIZE - 1, 2)); +} - The first part is the compilation: - g++ -o my_tests hello_gtest.cpp -lgtest -lgtest_main -pthread +TEST(SnakeTest, WrapTopLeft) +{ + pair pos = {0, 0}; + auto next = get_next_head(pos, 'u'); + EXPECT_EQ(next, make_pair(BOARD_SIZE - 1, 0)); + next = get_next_head(pos, 'l'); + EXPECT_EQ(next, make_pair(0, BOARD_SIZE - 1)); +} +TEST(SnakeTest, WrapBottomRight) +{ + pair pos = {BOARD_SIZE - 1, BOARD_SIZE - 1}; + auto next = get_next_head(pos, 'd'); + EXPECT_EQ(next, make_pair(0, BOARD_SIZE - 1)); + next = get_next_head(pos, 'r'); + EXPECT_EQ(next, make_pair(BOARD_SIZE - 1, 0)); +} - * g++: This invokes the GNU C++ compiler. - * -o my_tests: This tells the compiler to create an executable file named - my_tests. - * hello_gtest.cpp: This is the C++ source file containing your tests. - * -lgtest: This links the Google Test library, which provides the core testing - framework. - * -lgtest_main: This links a pre-compiled main function provided by Google - Test, which saves you from writing your own main() to run the tests. - * -pthread: This links the POSIX threads library, which is required by Google - Test for its operation. - * -*/ \ No newline at end of file +TEST(SnakeTest, InvalidDirection) +{ + pair pos = {3, 3}; + auto next = get_next_head(pos, 'x'); + EXPECT_EQ(next, pos); +} From a7e04a223607c2b3dc65e45e63b285eeb7085ee9 Mon Sep 17 00:00:00 2001 From: Om Somani Date: Fri, 26 Sep 2025 18:55:25 +0530 Subject: [PATCH 4/4] Add comprehensive snake tests using Google Test --- snake.h | 146 +++++++++++++++++++------------------------------------- 1 file changed, 49 insertions(+), 97 deletions(-) diff --git a/snake.h b/snake.h index 1630fae..dba3dac 100644 --- a/snake.h +++ b/snake.h @@ -1,3 +1,6 @@ +#ifndef SNAKE_H +#define SNAKE_H + #include #include #include @@ -10,21 +13,20 @@ #include #include #include - using namespace std; -using std::chrono::system_clock; -using namespace std::this_thread; constexpr int BOARD_SIZE = 10; -char direction = 'r'; +// Global direction variable +extern char direction; -void input_handler() +// ✅ Input handler (runs in thread) +inline void input_handler() { struct termios oldt, newt; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; - newt.c_lflag &= ~(ICANON | ECHO); + newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); map keymap = {{'d', 'r'}, {'a', 'l'}, {'w', 'u'}, {'s', 'd'}, {'q', 'q'}}; @@ -43,87 +45,66 @@ void input_handler() else if (input == 'p') { if (direction != 'P') - direction = 'P'; + direction = 'P'; else - direction = 'r'; + direction = 'r'; } } tcsetattr(STDIN_FILENO, TCSANOW, &oldt); } -void render_game(int size, deque> &snake, pair food, pair poison, int score) +// ✅ Render board +inline void render_game(int size, deque> &snake, pair food, + pair poison, int score) { for (int i = 0; i < size; ++i) { for (int j = 0; j < size; ++j) { if (i == food.first && j == food.second) - { cout << "🍎"; - } else if (i == poison.first && j == poison.second) - { cout << "☠️"; - } else if (find(snake.begin(), snake.end(), make_pair(i, j)) != snake.end()) - { cout << "🐍"; - } else - { cout << "⬜"; - } } cout << '\n'; } cout << "Length: " << snake.size() << " Score: " << score << "\n"; } -pair get_next_head(pair current, char direction) +// ✅ Next head +inline pair get_next_head(pair current, char direction) { - pair next; if (direction == 'r') - { - next = make_pair(current.first, (current.second + 1) % BOARD_SIZE); - } + return {current.first, (current.second + 1) % BOARD_SIZE}; else if (direction == 'l') - { - next = make_pair(current.first, current.second == 0 ? BOARD_SIZE - 1 : current.second - 1); - } + return {current.first, current.second == 0 ? BOARD_SIZE - 1 : current.second - 1}; else if (direction == 'd') - { - next = make_pair((current.first + 1) % BOARD_SIZE, current.second); - } - else - { - next = make_pair(current.first == 0 ? BOARD_SIZE - 1 : current.first - 1, current.second); - } - return next; + return {(current.first + 1) % BOARD_SIZE, current.second}; + else // 'u' + return {current.first == 0 ? BOARD_SIZE - 1 : current.first - 1, current.second}; } -pair spawn_food(const deque> &snake) +// ✅ Spawn food/poison +inline pair spawn_food(const deque> &snake) { vector> freeCells; for (int i = 0; i < BOARD_SIZE; ++i) - { for (int j = 0; j < BOARD_SIZE; ++j) - { - pair pos = make_pair(i, j); - if (find(snake.begin(), snake.end(), pos) == snake.end()) - { - freeCells.push_back(pos); - } - } - } + if (find(snake.begin(), snake.end(), make_pair(i, j)) == snake.end()) + freeCells.push_back({i, j}); if (freeCells.empty()) - return make_pair(-1, -1); + return {-1, -1}; - int idx = rand() % freeCells.size(); - return freeCells[idx]; + return freeCells[rand() % freeCells.size()]; } -void save_score(int score) +// ✅ Save score +inline void save_score(int score) { ofstream file("scores.txt", ios::app); if (file.is_open()) @@ -133,60 +114,51 @@ void save_score(int score) } } -vector load_top_scores() +// ✅ Load top 10 scores +inline vector load_top_scores() { ifstream file("scores.txt"); vector scores; int s; - while (file >> s) - { scores.push_back(s); - } file.close(); sort(scores.begin(), scores.end(), greater()); - if (scores.size() > 10) - { scores.resize(10); - } return scores; } -void game_play() +// ✅ Game loop +inline void game_play() { system("clear"); deque> snake; - snake.push_back(make_pair(0, 0)); + snake.push_back({0, 0}); pair food = spawn_food(snake); - pair poison = spawn_food(snake); + pair poison = spawn_food(snake); - int score = 0; - int level = 1; - int baseDelay = 500; - - auto sleep_duration = 500ms; - int food_eaten = 0; + int score = 0, level = 1, baseDelay = 500; while (true) { - pair currentHead = snake.back(); - pair nextHead = get_next_head(currentHead, direction); + auto currentHead = snake.back(); + auto nextHead = get_next_head(currentHead, direction); cout << "\033[H"; - bool willGrow = (nextHead == food); - bool collision = false; - if (direction == 'P') { - sleep_for(100ms); + this_thread::sleep_for(chrono::milliseconds(100)); continue; } + bool willGrow = (nextHead == food); + bool collision = false; + if (willGrow) { if (find(snake.begin(), snake.end(), nextHead) != snake.end()) @@ -196,7 +168,7 @@ void game_play() { auto itStart = snake.begin(); if (!snake.empty()) - ++itStart; + ++itStart; if (find(itStart, snake.end(), nextHead) != snake.end()) collision = true; } @@ -206,15 +178,12 @@ void game_play() system("clear"); cout << "Game Over\n"; cout << "Final Length: " << snake.size() << " Final Score: " << score << "\n"; - save_score(score); cout << "\n=== Top 10 Scores ===\n"; - vector topScores = load_top_scores(); + auto topScores = load_top_scores(); for (int i = 0; i < topScores.size(); i++) - { cout << (i + 1) << ". " << topScores[i] << "\n"; - } exit(0); } @@ -224,15 +193,12 @@ void game_play() system("clear"); cout << "Snake ate poison ☠️ Game Over!\n"; cout << "Final Length: " << snake.size() << " Final Score: " << score << "\n"; - save_score(score); cout << "\n=== Top 10 Scores ===\n"; - vector topScores = load_top_scores(); + auto topScores = load_top_scores(); for (int i = 0; i < topScores.size(); i++) - { cout << (i + 1) << ". " << topScores[i] << "\n"; - } exit(0); } @@ -241,50 +207,36 @@ void game_play() if (willGrow) { - score += 1; - food_eaten++; + score++; food = spawn_food(snake); if (score % 3 == 0) - { poison = spawn_food(snake); - } - - if (food_eaten % 10 == 0 && sleep_duration > 100ms) - { - sleep_duration -= 50ms; - } if (food.first == -1) { system("clear"); cout << "You Win! Final Length: " << snake.size() << " Final Score: " << score << "\n"; - save_score(score); cout << "\n=== Top 10 Scores ===\n"; - vector topScores = load_top_scores(); + auto topScores = load_top_scores(); for (int i = 0; i < topScores.size(); i++) - { cout << (i + 1) << ". " << topScores[i] << "\n"; - } exit(0); } } else - { snake.pop_front(); - } level = (score / 5) + 1; - render_game(BOARD_SIZE, snake, food, poison, score); - cout << "Level: " << level << "\n"; - cout << "Speed: " << sleep_duration.count() << " ms per frame\n"; int delay_ms = max(50, baseDelay - (level - 1) * 50 - (int)snake.size() * 5); - sleep_for(min(sleep_duration, chrono::milliseconds(delay_ms))); + this_thread::sleep_for(chrono::milliseconds(delay_ms)); } } + +#endif \ No newline at end of file