Last active
September 8, 2019 18:01
-
-
Save mgiuffrida/97a227ec43bf2c865210efe37e52ff57 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// libcurses: sudo apt-get install libncurses5-dev | |
// g++ -o snake snake.cpp --std=c++11 -lncurses -Wall && ./snake | |
#include <curses.h> | |
#include <unistd.h> | |
#include <cassert> | |
#include <cstdlib> | |
#include <string> | |
#include <list> | |
#include <vector> | |
struct Coord { | |
int x; | |
int y; | |
}; | |
class Snake { | |
public: | |
Snake(Coord head); | |
const Coord& head() const { return coords_.front(); } | |
const Coord& tail_end() const { return coords_.back(); } | |
void MoveTo(Coord new_head); | |
void MoveToAndGrow(Coord new_head); | |
void Grow(Coord new_tail_end); | |
size_t size() const { return coords_.size(); } | |
private: | |
std::list<Coord> coords_; | |
}; | |
Snake::Snake(Coord head) { | |
coords_.push_back(head); | |
} | |
void Snake::MoveTo(Coord new_head) { | |
coords_.push_front(new_head); | |
coords_.pop_back(); | |
} | |
void Snake::MoveToAndGrow(Coord new_head) { | |
coords_.push_front(new_head); | |
} | |
void Snake::Grow(Coord new_tail_end) { | |
coords_.push_back(new_tail_end); | |
} | |
class Board { | |
public: | |
enum class Cell { | |
kEmpty, | |
kSnakeHead, | |
kSnakeTail, | |
kApple, | |
}; | |
enum class Direction { | |
kUp, | |
kDown, | |
kLeft, | |
kRight, | |
}; | |
enum class Status { | |
kPlaying, | |
kHitWall, | |
kHitSnake, | |
}; | |
Board(int width, int height); | |
~Board(); | |
void MoveSnake(Direction direction); | |
void Print() const; | |
Status status() const { return status_; } | |
size_t GetScore() const; | |
static Coord GetNewCoord(Coord coord, Direction direction); | |
private: | |
void AddApple(); | |
Status status_ = Status::kPlaying; | |
int width_; | |
int height_; | |
Snake snake_; | |
std::vector<std::vector<Cell>> cells_; | |
}; | |
Board::Board(int width, int height) : | |
width_(width), height_(height), | |
snake_({width/2, height/2}), | |
cells_(height, std::vector<Cell>(width, Cell::kEmpty)) { | |
srand(time(nullptr)); | |
cells_[height/2][width/2] = Cell::kSnakeHead; | |
for (int i = 0; i < 2; i++) { | |
snake_.Grow(GetNewCoord(snake_.tail_end(), Direction::kRight)); | |
cells_[snake_.tail_end().y][snake_.tail_end().x] = Cell::kSnakeTail; | |
} | |
AddApple(); | |
initscr(); | |
curs_set(0); | |
cbreak(); | |
noecho(); | |
nodelay(stdscr, true); | |
keypad(stdscr, true); | |
} | |
Board::~Board() { | |
endwin(); | |
} | |
void Board::MoveSnake(Direction direction) { | |
assert(status_ == Status::kPlaying); | |
Coord old_head = snake_.head(); | |
Coord new_head = GetNewCoord(old_head, direction); | |
if (new_head.y < 0 || new_head.y >= height_ || | |
new_head.x < 0 || new_head.x >= width_) { | |
status_ = Status::kHitWall; | |
return; | |
} | |
assert(cells_[new_head.y][new_head.x] != Cell::kSnakeHead); | |
if (cells_[new_head.y][new_head.x] == Cell::kSnakeTail) { | |
status_ = Status::kHitSnake; | |
return; | |
} | |
// Move the snake. | |
bool was_apple = cells_[new_head.y][new_head.x] == Cell::kApple; | |
cells_[new_head.y][new_head.x] = Cell::kSnakeHead; | |
cells_[old_head.y][old_head.x] = Cell::kSnakeTail; | |
if (was_apple) { | |
snake_.MoveToAndGrow(new_head); | |
AddApple(); | |
} else { | |
cells_[snake_.tail_end().y][snake_.tail_end().x] = Cell::kEmpty; | |
snake_.MoveTo(new_head); | |
} | |
} | |
void Board::Print() const { | |
erase(); | |
move(0, 0); | |
addstr(std::string(2*width_ + 2, '-').c_str()); | |
addch('\n'); | |
for (const auto& row : cells_) { | |
std::string row_str; | |
for (Cell cell : row) { | |
switch (cell) { | |
case Cell::kEmpty: | |
row_str += " "; | |
break; | |
case Cell::kSnakeHead: | |
row_str += "S"; | |
break; | |
case Cell::kSnakeTail: | |
row_str += "s"; | |
break; | |
case Cell::kApple: | |
row_str += "a"; | |
break; | |
} | |
row_str += " "; | |
} | |
addch('|'); | |
addstr(row_str.c_str()); | |
addch('|'); | |
addch('\n'); | |
} | |
addstr(std::string(2*width_ + 2, '-').c_str()); | |
addch('\n'); | |
switch (status_) { | |
case Status::kPlaying: | |
addch('\n'); | |
break; | |
case Status::kHitWall: | |
addstr("You hit a wall!\n"); | |
printw("Score: %d", GetScore()); | |
break; | |
case Status::kHitSnake: | |
addstr("You hit your tail!\n"); | |
printw("Score: %d", GetScore()); | |
break; | |
} | |
refresh(); | |
} | |
size_t Board::GetScore() const { | |
return snake_.size(); | |
} | |
Coord Board::GetNewCoord(Coord coord, Direction direction) { | |
Coord new_coord = coord; | |
switch (direction) { | |
case Direction::kUp: | |
new_coord.y -= 1; | |
break; | |
case Direction::kDown: | |
new_coord.y += 1; | |
break; | |
case Direction::kLeft: | |
new_coord.x -= 1; | |
break; | |
case Direction::kRight: | |
new_coord.x += 1; | |
break; | |
} | |
return new_coord; | |
} | |
void Board::AddApple() { | |
int x, y; | |
do { | |
x = rand() % width_; | |
y = rand() % height_; | |
} while (cells_[x][y] != Cell::kEmpty); | |
cells_[x][y] = Cell::kApple; | |
} | |
int main() { | |
Board board(20, 20); | |
board.Print(); | |
Board::Direction direction = Board::Direction::kLeft; | |
while (board.status() == Board::Status::kPlaying) { | |
usleep(100000); | |
int ch = getch(); | |
/* | |
int next_ch = ch; | |
while (next_ch != ERR) { | |
ch = next_ch; | |
next_ch = getch(); | |
} | |
*/ | |
if (ch == KEY_LEFT && direction != Board::Direction::kRight) | |
direction = Board::Direction::kLeft; | |
else if (ch == KEY_RIGHT && direction != Board::Direction::kLeft) | |
direction = Board::Direction::kRight; | |
else if (ch == KEY_UP && direction != Board::Direction::kDown) | |
direction = Board::Direction::kUp; | |
else if (ch == KEY_DOWN && direction != Board::Direction::kUp) | |
direction = Board::Direction::kDown; | |
board.MoveSnake(direction); | |
board.Print(); | |
} | |
nodelay(stdscr, false); | |
getch(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment