Last active
June 19, 2016 13:34
-
-
Save viennadd/a67047dd21cf74fe2d034cd44a43fa31 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
#include <deque> | |
#include <iostream> | |
#include <thread> | |
#include <chrono> | |
#include <random> | |
using namespace std; | |
class Snake; | |
class SnakeGame; | |
class Board; | |
class GameSprite; | |
typedef struct | |
{ | |
int x; | |
int y; | |
} Point; | |
enum class Direction { Up = 'w', Down = 's', Left = 'a', Right = 'd' }; | |
class GameSprite { | |
public: | |
virtual void drawOn(Board &board) = 0; | |
}; | |
class Snake : public GameSprite { | |
deque<Point> points; | |
Direction currentDirection; | |
public: | |
Snake(); | |
void move(Board &board); | |
void drawOn(Board &board); | |
Point getNextPoint(); | |
Direction& getCurrentDirection(); | |
}; | |
Snake::Snake() | |
{ | |
for (int l = 8; l >= 2; l--) | |
points.push_back(Point{l, 1}); | |
currentDirection = Direction::Right; | |
} | |
Direction& Snake::getCurrentDirection() | |
{ | |
return currentDirection; | |
} | |
Point Snake::getNextPoint() | |
{ | |
const Point &head = points.front(); | |
Point p = head; | |
switch (currentDirection) { | |
case Direction::Up: | |
p.y--; | |
break; | |
case Direction::Down: | |
p.y++; | |
break; | |
case Direction::Left: | |
p.x--; | |
break; | |
case Direction::Right: | |
p.x++; | |
break; | |
} | |
return p; | |
} | |
class Board | |
{ | |
unsigned int boardWidth; | |
unsigned int boardHeight; | |
char buffer[25][80]; | |
public: | |
Board(); | |
Board(int width, int height); | |
void setPixel(Point &p, char c); | |
char getPixel(Point &p); | |
unsigned int getWidth() { return boardWidth; } | |
unsigned int getHeight() { return boardHeight; } | |
friend ostream& operator<<(ostream &out, const Board &board); | |
private: | |
void InitBoard(); | |
}; | |
char Board::getPixel(Point &p) | |
{ | |
return buffer[p.y][p.x]; | |
} | |
void Board::InitBoard() | |
{ | |
for (int r = 0; r < boardHeight; ++r) { | |
for (int c = 0; c < boardWidth; ++c) { | |
buffer[r][c] = '.'; | |
} | |
buffer[r][boardWidth] = '\0'; | |
} | |
} | |
Board::Board() : boardWidth(70), boardHeight(22) | |
{ | |
InitBoard(); | |
} | |
Board::Board(int width, int height) : boardWidth(width), boardHeight(height) | |
{ | |
InitBoard(); | |
} | |
void Board::setPixel(Point &p, char c) | |
{ | |
buffer[p.y][p.x] = c; | |
} | |
void Snake::drawOn(Board &board) | |
{ | |
for (auto &p : points) { | |
board.setPixel(p, '#'); | |
} | |
} | |
ostream& operator<<(ostream &out, const Board &board) | |
{ | |
for (int r = 0; r < board.boardHeight; ++r) { | |
out << board.buffer[r] << endl; | |
} | |
return out; | |
} | |
/* | |
* =========================== | |
* | |
*/ | |
class SnakeGame | |
{ | |
enum class GameState { Running, Stop }; | |
GameState gameState; | |
Board board; | |
Snake snake; | |
public: | |
SnakeGame(); | |
void start(); | |
static void gameLoop(Board &board, Snake &snake); | |
static bool isGameEnd(Board &board, Snake &snake); | |
static void newFood(Board &board); | |
static void KeyboardHandler(SnakeGame &game, Snake &snake); | |
}; | |
SnakeGame::SnakeGame() : gameState(GameState::Stop) | |
{ | |
} | |
void SnakeGame::KeyboardHandler(SnakeGame &game, Snake &snake) | |
{ | |
char c; | |
Direction &direction = snake.getCurrentDirection(); | |
while ((c = getchar()) != EOF && game.gameState != GameState::Stop) { | |
// current up or down, then left or right will accept | |
// current left or right, then up or down will accept | |
if ( | |
((direction == Direction::Left || direction == Direction::Right) && (c == 'w' || c == 's')) || | |
((direction == Direction::Up || direction == Direction::Down) && (c == 'a' || c == 'd'))) | |
{ | |
direction = (Direction)c; | |
cout << "current dir is " << c << endl; | |
} | |
} | |
} | |
bool SnakeGame::isGameEnd(Board &board, Snake &snake) | |
{ | |
Point head = snake.getNextPoint(); | |
if (head.x < 0 | |
|| head.y < 0 | |
|| head.x >= board.getWidth() | |
|| head.y >= board.getHeight() | |
|| board.getPixel(head) == '#') | |
return true; | |
else | |
return false; | |
}; | |
void SnakeGame::gameLoop(Board &board, Snake &snake) | |
{ | |
auto Sleep = [](int milliseconds) { | |
this_thread::sleep_for(chrono::milliseconds(milliseconds)); | |
}; | |
while (!isGameEnd(board, snake)) { | |
snake.move(board); | |
system("clear"); | |
cout << board; | |
Sleep(100); | |
} | |
} | |
void SnakeGame::newFood(Board &board) | |
{ | |
random_device rd; | |
Point p; | |
do { | |
p.x = rd() % board.getWidth(); | |
p.y = rd() % board.getHeight(); | |
} while (board.getPixel(p) != '.'); | |
board.setPixel(p, '*'); | |
} | |
void Snake::move(Board &board) | |
{ | |
Point nextPoint = getNextPoint(); | |
if (board.getPixel(nextPoint) == '*') { | |
SnakeGame::newFood(board); | |
} else { | |
Point lastPoint = points.back(); | |
board.setPixel(lastPoint, '.'); | |
points.pop_back(); | |
} | |
points.push_front(nextPoint); | |
board.setPixel(nextPoint, '#'); | |
} | |
void SnakeGame::start() | |
{ | |
gameState = GameState::Running; | |
snake.drawOn(board); | |
SnakeGame::newFood(board); | |
thread thGameLoop(gameLoop, ref(board), ref(snake)); | |
thread thKeyboardHandler(KeyboardHandler, ref(*this), ref(snake)); | |
thGameLoop.join(); | |
gameState = GameState::Stop; | |
cout << "Game Over." << endl; | |
thKeyboardHandler.join(); | |
} | |
int main() | |
{ | |
SnakeGame game; | |
game.start(); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment