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
15 changes: 9 additions & 6 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#include "snake.h"
#include <thread>

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;
}
249 changes: 195 additions & 54 deletions snake.h
Original file line number Diff line number Diff line change
@@ -1,101 +1,242 @@
#ifndef SNAKE_H
#define SNAKE_H

#include <iostream>
#include <vector>
#include <chrono>
#include <thread>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h> // for system clear
#include <unistd.h>
#include <map>
#include <deque>
#include <algorithm>
#include <utility>
#include <fstream>
using namespace std;
using std::chrono::system_clock;
using namespace std::this_thread;
char direction='r';

constexpr int BOARD_SIZE = 10;

// Global direction variable
extern char direction;

void input_handler(){
// change terminal settings
// ✅ Input handler (runs in thread)
inline void input_handler()
{
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'}};
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<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){
// ✅ Render board
inline void render_game(int size, deque<pair<int, int>> &snake, pair<int, int> food,
pair<int, int> 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";
}

// ✅ Next head
inline pair<int, int> get_next_head(pair<int, int> current, char direction)
{
if (direction == 'r')
return {current.first, (current.second + 1) % BOARD_SIZE};
else if (direction == 'l')
return {current.first, current.second == 0 ? BOARD_SIZE - 1 : current.second - 1};
else if (direction == 'd')
return {(current.first + 1) % BOARD_SIZE, current.second};
else // 'u'
return {current.first == 0 ? BOARD_SIZE - 1 : current.first - 1, current.second};
}

// ✅ Spawn food/poison
inline pair<int, int> spawn_food(const deque<pair<int, int>> &snake)
{
vector<pair<int, int>> freeCells;
for (int i = 0; i < BOARD_SIZE; ++i)
for (int j = 0; j < BOARD_SIZE; ++j)
if (find(snake.begin(), snake.end(), make_pair(i, j)) == snake.end())
freeCells.push_back({i, j});

if (freeCells.empty())
return {-1, -1};

return freeCells[rand() % freeCells.size()];
}

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')
// ✅ Save score
inline void save_score(int score)
{
ofstream file("scores.txt", ios::app);
if (file.is_open())
{
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;

file << score << "\n";
file.close();
}
}

// ✅ Load top 10 scores
inline vector<int> load_top_scores()
{
ifstream file("scores.txt");
vector<int> scores;
int s;
while (file >> s)
scores.push_back(s);
file.close();

sort(scores.begin(), scores.end(), greater<int>());
if (scores.size() > 10)
scores.resize(10);

void game_play(){
return scores;
}

// ✅ Game loop
inline void game_play()
{
system("clear");
deque<pair<int, int>> snake;
snake.push_back(make_pair(0,0));
snake.push_back({0, 0});

pair<int, int> food = spawn_food(snake);
pair<int, int> poison = spawn_food(snake);

int score = 0, level = 1, baseDelay = 500;

while (true)
{
auto currentHead = snake.back();
auto nextHead = get_next_head(currentHead, direction);

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()) {

if (direction == 'P')
{
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())
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";
auto 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";
auto 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.pop_front();
}
render_game(10, snake, food);
cout << "length of snake: " << snake.size() << endl;

sleep_for(500ms);

snake.push_back(nextHead);

if (willGrow)
{
score++;
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";
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";

int delay_ms = max(50, baseDelay - (level - 1) * 50 - (int)snake.size() * 5);
this_thread::sleep_for(chrono::milliseconds(delay_ms));
}
}

#endif
Loading