Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "windows-gcc-x86",
"includePath": [
"${workspaceFolder}/**"
],
"compilerPath": "C:/MinGW/bin/gcc.exe",
"cStandard": "${default}",
"cppStandard": "${default}",
"intelliSenseMode": "windows-gcc-x86",
"compilerArgs": [
""
]
}
],
"version": 4
}
24 changes: 24 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C/C++ Runner: Debug Session",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"externalConsole": true,
"cwd": "d:/snake-cli/snake-cli-final",
"program": "d:/snake-cli/snake-cli-final/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
59 changes: 59 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"C_Cpp_Runner.cCompilerPath": "gcc",
"C_Cpp_Runner.cppCompilerPath": "g++",
"C_Cpp_Runner.debuggerPath": "gdb",
"C_Cpp_Runner.cStandard": "",
"C_Cpp_Runner.cppStandard": "",
"C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/VR_NR/Community/VC/Auxiliary/Build/vcvarsall.bat",
"C_Cpp_Runner.useMsvc": false,
"C_Cpp_Runner.warnings": [
"-Wall",
"-Wextra",
"-Wpedantic",
"-Wshadow",
"-Wformat=2",
"-Wcast-align",
"-Wconversion",
"-Wsign-conversion",
"-Wnull-dereference"
],
"C_Cpp_Runner.msvcWarnings": [
"/W4",
"/permissive-",
"/w14242",
"/w14287",
"/w14296",
"/w14311",
"/w14826",
"/w44062",
"/w44242",
"/w14905",
"/w14906",
"/w14263",
"/w44265",
"/w14928"
],
"C_Cpp_Runner.enableWarnings": true,
"C_Cpp_Runner.warningsAsError": false,
"C_Cpp_Runner.compilerArgs": [],
"C_Cpp_Runner.linkerArgs": [],
"C_Cpp_Runner.includePaths": [],
"C_Cpp_Runner.includeSearch": [
"*",
"**/*"
],
"C_Cpp_Runner.excludeSearch": [
"**/build",
"**/build/**",
"**/.*",
"**/.*/**",
"**/.vscode",
"**/.vscode/**"
],
"C_Cpp_Runner.useAddressSanitizer": false,
"C_Cpp_Runner.useUndefinedSanitizer": false,
"C_Cpp_Runner.useLeakSanitizer": false,
"C_Cpp_Runner.showCompilationTime": false,
"C_Cpp_Runner.useLinkTimeOptimization": false,
"C_Cpp_Runner.msvcSecureNoWarnings": false
}
17 changes: 13 additions & 4 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
#include "snake.h"
#include <thread>
using namespace std;

int main(int argc, char *argv[]) {
thread input_thread(input_handler);
thread game_thread(game_play);
system("clear");

HighScores highScores;
Game game(10, highScores);

// start input handler thread and game thread
thread input_thread(inputHandler, ref(game));
thread game_thread(&Game::loop, &game);

input_thread.join();
game_thread.join();
return 0;
}
return 0;
}
220 changes: 147 additions & 73 deletions snake.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,98 +4,172 @@
#include <thread>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h> // for system clear
#include <unistd.h>
#include <map>
#include <deque>
#include <algorithm>
using namespace std;
using std::chrono::system_clock;
using namespace std::this_thread;
char direction='r';

class HighScores {
vector<int> scores;
public:
void update(int score) {
scores.push_back(score);
sort(scores.begin(), scores.end(), greater<int>());
if (scores.size() > 10) scores.resize(10);
}
void show() const {
cout << "\n=== TOP 10 HIGH SCORES ===\n";
if (scores.empty()) {
cout << "No scores yet.\n";
return;
}
for (size_t i = 0; i < scores.size(); i++) {
cout << i + 1 << ". " << scores[i] << endl;
}
}
};

class Game {
int size;
deque<pair<int, int>> snake;
pair<int, int> food;
pair<int, int> poison;
char direction;
bool paused;
int score;
int steps;
int foodsEaten;
int speedMs;
HighScores &highScores; // reference to shared highscore tracker

pair<int, int> randomCellExcluding(const pair<int, int> &other = {-1, -1}) {
pair<int, int> p;
do {
p = make_pair(rand() % size, rand() % size);
} while (find(snake.begin(), snake.end(), p) != snake.end() || p == other);
return p;
}

pair<int, int> getNextHead(pair<int, int> current) {
if (direction == 'r') {
return make_pair(current.first, (current.second + 1) % size);
} else if (direction == 'l') {
return make_pair(current.first, current.second == 0 ? size - 1 : current.second - 1);
} else if (direction == 'd') {
return make_pair((current.first + 1) % size, current.second);
} else if (direction == 'u') {
return make_pair(current.first == 0 ? size - 1 : current.first - 1, current.second);
}
return current;
}

void render() {
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 (i == poison.first && j == poison.second) {
cout << "💀";
} else if (find(snake.begin(), snake.end(), make_pair(int(i), int(j))) != snake.end()) {
cout << "🐍";
} else {
cout << "⬜";
}
}
cout << endl;
}
cout << "Length: " << snake.size()
<< " | Score: " << score
<< (paused ? " | PAUSED (press 'p' to resume)" : "") << endl;
}

void gameOver(const string &reason) {
system("clear");
cout << "Game Over - " << reason << "\nFinal Score: " << score << endl;
highScores.update(score);
highScores.show();
exit(0);
}

public:
Game(int gridSize, HighScores &hs)
: size(gridSize), direction('r'), paused(false), score(0), steps(0),
foodsEaten(0), speedMs(500), highScores(hs) {
snake.push_back(make_pair(0, 0));
food = randomCellExcluding();
poison = randomCellExcluding(food);
}

void togglePause() { paused = !paused; }

void changeDirection(char newDir) { direction = newDir; }

//this is the loop wherein snake goes about doing its job
void loop() {
for (pair<int, int> head = make_pair(0, 1);; head = getNextHead(head, direction)) {
cout << "\033[H";
if (paused) {
render();
sleep_for(200ms);
continue;
}

// self collision - feature
if (find(snake.begin(), snake.end(), head) != snake.end()) {
gameOver("You hit yourself!");
}

// poison collision - feature
if (head == poison) {
gameOver("You ate poison!");
}

// normal food - feature
if (head == food) {
snake.push_back(head);
score += 10;
foodsEaten++;
if (foodsEaten % 10 == 0 && speedMs > 100) speedMs -= 50;
food = randomCellExcluding(poison);
} else {
snake.push_back(head);
snake.pop_front();
}

// move poison - feature
steps++;
if (steps % 10 == 0) poison = randomCellExcluding(food);

render();
sleep_for(std::chrono::milliseconds(speedMs));
}
}
};

void input_handler(){
// change terminal settings
// Separate input handler function but pass Game to it -> this is to pass the game to it and separate input handler, to make it easy
void inputHandler(Game &game) {
struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
// turn off canonical mode and echo
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
map<char, char> keymap = {{'d', 'r'}, {'a', 'l'}, {'w', 'u'}, {'s', 'd'}, {'q', 'q'}};

map<char, char> keymap = {
{'d', 'r'}, {'a', 'l'}, {'w', 'u'}, {'s', 'd'}
};
while (true) {
char input = getchar();
if (keymap.find(input) != keymap.end()) {
// This now correctly modifies the single, shared 'direction' variable
direction = keymap[input];
}else if (input == 'q'){
game.changeDirection(keymap[input]);
} else if (input == 'q') {
exit(0);
} else if (input == 'p') {
game.togglePause();
}
// You could add an exit condition here, e.g., if (input == 'q') break;
}
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}


void render_game(int size, deque<pair<int, int>> &snake, pair<int, int> 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()) {
cout << "🐍";
}else{
cout << "⬜";
}
}
cout << endl;
}
}

pair<int,int> get_next_head(pair<int,int> current, char direction){
pair<int, int> 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);
}
return next;

}



void game_play(){
system("clear");
deque<pair<int, int>> snake;
snake.push_back(make_pair(0,0));

pair<int, int> food = make_pair(rand() % 10, rand() % 10);
for(pair<int, int> 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()) {
system("clear");
cout << "Game Over" << endl;
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.pop_front();
}
render_game(10, snake, food);
cout << "length of snake: " << snake.size() << endl;

sleep_for(500ms);
}
}
Loading