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
Binary file added .DS_Store
Binary file not shown.
8 changes: 3 additions & 5 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
#include "snake.h"

int main(int argc, char *argv[]) {
thread input_thread(input_handler);
thread game_thread(game_play);
input_thread.join();
game_thread.join();
return 0;
Game game(10);
game.run();
return 0;
}
Binary file added my_tests
Binary file not shown.
Binary file added snake
Binary file not shown.
174 changes: 174 additions & 0 deletions snake.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#include "snake.h"

// Snake Class Implementation
Snake::Snake(int board_size) : board_size(board_size), direction('r') {
body.push_front(Point{0, 0});
}

void Snake::move() {
Point next_head = get_next_head();
body.push_front(next_head);
body.pop_back();
}

void Snake::grow() {
Point next_head = get_next_head();
body.push_front(next_head);
}

bool Snake::check_collision() {
Point head = get_head();
for (deque<Point>::const_iterator it = body.begin() + 1; it != body.end(); ++it) {
if (head == *it) {
return true;
}
}
return false;
}

Point Snake::get_head() {
return body.front();
}

const deque<Point>& Snake::get_body() const {
return body;
}

void Snake::set_direction(char new_direction) {
// FIX: Only prevent reversal if the snake is longer than 1 segment
if (body.size() > 1 &&
((direction == 'r' && new_direction == 'l') ||
(direction == 'l' && new_direction == 'r') ||
(direction == 'u' && new_direction == 'd') ||
(direction == 'd' && new_direction == 'u'))) {
return;
}
direction = new_direction;
}

char Snake::get_direction() {
return direction;
}

Point Snake::get_next_head() {
Point current_head = get_head();
Point next_head = current_head;
if (direction == 'r') {
next_head.y = (current_head.y + 1) % board_size;
} else if (direction == 'l') {
next_head.y = (current_head.y == 0) ? board_size - 1 : current_head.y - 1;
} else if (direction == 'd') {
next_head.x = (current_head.x + 1) % board_size;
} else if (direction == 'u') {
next_head.x = (current_head.x == 0) ? board_size - 1 : current_head.x - 1;
}
return next_head;
}

// Food Class Implementation
Food::Food(int board_size, const deque<Point>& snake_body) : board_size(board_size) {
generate_new_food(snake_body);
}

void Food::generate_new_food(const deque<Point>& snake_body) {
while (true) {
position = Point{rand() % board_size, rand() % board_size};
bool on_snake = false;
for (deque<Point>::const_iterator it = snake_body.begin(); it != snake_body.end(); ++it) {
if (position == *it) {
on_snake = true;
break;
}
}
if (!on_snake) {
break;
}
}
}

Point Food::get_position() {
return position;
}

// Game Class Implementation
Game::Game(int size) : board_size(size), snake(size), food(size, snake.get_body()), game_over(false), input_thread(&Game::process_input, this) {}

void Game::run() {
while (!game_over) {
cout << "\033[H";
update();
render();
cout << "Length of snake: " << snake.get_body().size() << endl;
sleep_for(chrono::milliseconds(500));
}

input_thread.join();
system("clear");
cout << "Game Over" << endl;
}

void Game::render() {
system("clear");
for (int i = 0; i < board_size; ++i) {
for (int j = 0; j < board_size; ++j) {
Point p = {i, j};
if (p == food.get_position()) {
cout << "🍎";
} else {
bool is_snake_part = false;
const deque<Point>& snake_body = snake.get_body();
for (deque<Point>::const_iterator it = snake_body.begin(); it != snake_body.end(); ++it) {
if (p == *it) {
is_snake_part = true;
break;
}
}
if (is_snake_part) {
cout << "🐍";
} else {
cout << "⬜";
}
}
}
cout << endl;
}
}

void Game::update() {
if (snake.check_collision()) {
game_over = true;
return;
}

if (snake.get_head() == food.get_position()) {
snake.grow();
food.generate_new_food(snake.get_body());
} else {
snake.move();
}
}

void Game::process_input() {
struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);

map<char, char> keymap;
keymap['d'] = 'r';
keymap['a'] = 'l';
keymap['w'] = 'u';
keymap['s'] = 'd';

while (!game_over) {
char input = getchar();
if (keymap.count(input)) {
snake.set_direction(keymap[input]);
} else if (input == 'q') {
game_over = true;
}
}

tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}
126 changes: 46 additions & 80 deletions snake.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,98 +4,64 @@
#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';


void input_handler(){
// change terminal settings
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) {
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'){
exit(0);
}
// You could add an exit condition here, e.g., if (input == 'q') break;
struct Point {
int x, y;
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}
};

class Snake {
public:
Snake(int board_size);
void move();
void grow();
bool check_collision();
Point get_head();
const deque<Point>& get_body() const;
void set_direction(char new_direction);
char get_direction();

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;
}
}
private:
deque<Point> body;
char direction;
int board_size;
Point get_next_head();
};

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;

}
class Food {
public:
Food(int board_size, const deque<Point>& snake_body);
void generate_new_food(const deque<Point>& snake_body);
Point get_position();

private:
Point position;
int board_size;
};

class Game {
public:
Game(int size);
void run();

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

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);
}
}
int board_size;
Snake snake;
Food food;
bool game_over;
thread input_thread;
};
Loading