马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
#ifndef COORDINATE_H
#define COORDINATE_H
#include <cstddef>
#include <unordered_set>
namespace Screen {
typedef unsigned char pos_type;
struct Coordinate {
inline bool operator==(const Coordinate &rhs) const noexcept
{ return x == rhs.x && y == rhs.y; }
pos_type x, y;
};
inline Coordinate makeCoordinate(pos_type x, pos_type y) noexcept
{
Coordinate ret;
ret.x = x;
ret.y = y;
return ret;
}
}
namespace std {
template <>
struct hash<Screen::Coordinate> {
typedef size_t result_type;
typedef Screen::Coordinate argument_type;
inline size_t operator()(const Screen::Coordinate &c) const noexcept
{
return hash<Screen::pos_type>()(c.x) ^
hash<Screen::pos_type>()(c.y);
}
};
}
#endif // COORDINATE_H
#ifndef KEYBOARDLISTENER_H
#define KEYBOARDLISTENER_H
#include "map.h"
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/thread.hpp>
namespace Listener {
class KeyboardListener : boost::noncopyable {
public:
KeyboardListener(Map::GameMap &map)
noexcept : pMap(&map) { startListen(); }
~KeyboardListener() noexcept { stopListen(); }
private:
void startListen() const noexcept;
void stopListen() const noexcept;
Map::GameMap *pMap;
mutable boost::scoped_ptr<boost::thread> pThread;
};
}
#endif // KEYBOARDLISTENER_H
#ifndef MAP_H
#define MAP_H
#include "snake_basicInformation.h"
#include "screen.h"
#include <boost/noncopyable.hpp>
#include <cstddef>
#include <boost/thread/mutex.hpp>
namespace Map {
class GameMap : boost::noncopyable {
public:
GameMap() noexcept;
~GameMap() noexcept
{Screen::clearScreen(); Screen::hideCursor(true);}
bool snakeMove(BasicInformation::Control) noexcept;
private:
bool move(BasicInformation::Control, Screen::Coordinate &) noexcept;
void distributionFood(std::size_t = 0) noexcept;
BasicInformation::wall_t wallCoords;
BasicInformation::food_t foodCoords;
BasicInformation::snake_t snakeCoords;
BasicInformation::Control headDirection =
BasicInformation::OTHER;
bool paused = false;
bool isOver = false;
};
template <class Map_t>
void draw(const Map_t &, char) noexcept;
}
template <class Map_t>
void Map::draw(const Map_t &map, char ch) noexcept
{
boost::mutex mu;
boost::mutex::scoped_lock lock(mu);
for (auto coord : map)
Screen::putchar(coord.x, coord.y, ch);
}
#endif // MAP_H
#ifndef RANDOM_H
#define RANDOM_H
#include "coordinate.h"
namespace Random {
Screen::Coordinate randomCoordinate() noexcept; // 随机生成坐标
}
#endif // RANDOM_H
#ifndef PUTCHAR_H
#define PUTCHAR_H
#include <cstdint>
#include <cstdlib>
namespace Screen {
typedef std::uint8_t row_type;
typedef row_type col_type;
void putchar(row_type cols, col_type rows,
char data) noexcept; // 在rows行(0 - 24), cols列(0 - 79)打印字符
inline void erase(row_type cols, col_type rows) noexcept // 清除第rows行,cols列的字符
{ putchar(cols, rows, 0); }
inline void clearScreen() noexcept // 清屏
{ std::system("cls"); }
void hideCursor(bool = false) noexcept;
}
#endif // PUTCHAR_H
#ifndef SNAKE_BASICINFORMATION_H
#define SNAKE_BASICINFORMATION_H
#include "coordinate.h"
#include <deque>
#include <unordered_set>
namespace BasicInformation {
// 键盘控制枚举
enum Control { LEFT = 'A',
UP = 'W',
DOWN = 'S',
RIGHT = 'D',
PAUSE = ' ',
OTHER
};
enum Information { // GROW = 1, // 蛇每次吃到食物生长长度
TOTAL_STONE = 20, // 石头的总数
TOTAL_FOOD = 5,// 屏幕上最多只能同时有TOTAL_FOOD个食物
MINMUM_FOOD = 0, // 当食物降低至MINMUM_FOOD个时补充食物
SNAKE = '*',
WALL = '#',
FOOD = '@',
SCREEN_HIGHT = 25, // 屏幕高
SCREEN_WIDTH = 80 // 屏幕宽
};
typedef std::deque<Screen::Coordinate> snake_t;
typedef std::unordered_set<Screen::Coordinate> wall_t;
typedef wall_t food_t;
}
#endif // SNAKE_BASICINFORMATION_H
#include "keyboardListener.h"
#include <conio.h>
#include <cctype>
namespace Helper {
void listener(Map::GameMap *map) noexcept;
}
void Listener::KeyboardListener::
startListen() const noexcept
{
pThread.reset(new boost::thread(Helper::listener, pMap));
}
void Listener::KeyboardListener::
stopListen() const noexcept
{
try {
pThread->interrupt();
}
catch (...) {}
}
void Helper::listener(Map::GameMap *map) noexcept
{
int ctrl = getch();
while (map->snakeMove(static_cast<BasicInformation::Control>(
std::toupper(ctrl)
)))
ctrl = getch();
}
#include "screen.h"
#include "map.h"
#include "snake_basicInformation.h"
#include "keyboardListener.h"
#include <iostream>
#include <conio.h>
#include <windows.h>
#include <cctype>
#include <memory>
#include <cstring>
int main(int argc, char *argv[])
{
{
Map::GameMap gameMap;
Listener::KeyboardListener listener(gameMap);
while (gameMap.snakeMove(BasicInformation::OTHER)) Sleep(250);
}
const char *gameOver = "GameOver";
Screen::col_type x = (BasicInformation::SCREEN_WIDTH - std::strlen(gameOver)) / 2;
Screen::row_type y = BasicInformation::SCREEN_HIGHT / 2;
while (*gameOver)
Screen::putchar(x++, y, *gameOver++);
return 0;
}
#include "map.h"
#include "random.h"
#include <algorithm>
#include <cassert>
namespace Helper {
BasicInformation::wall_t distributionWall() noexcept;
BasicInformation::snake_t distributionSnakePos() noexcept;
}
Map::GameMap::GameMap() noexcept :
wallCoords(Helper::distributionWall()),
snakeCoords(Helper::distributionSnakePos())
{
Screen::hideCursor();
distributionFood();
while (wallCoords.find(snakeCoords.front()) != wallCoords.end()) // 如果蛇的初始位置刚好有墙
snakeCoords = Helper::distributionSnakePos(); // 重新分配蛇的位置
draw(wallCoords, BasicInformation::WALL); // 画墙
draw(snakeCoords, BasicInformation::SNAKE); // 画蛇
draw(foodCoords, BasicInformation::FOOD);
}
bool Map::GameMap::
snakeMove(BasicInformation::Control ctrl) noexcept
{
boost::mutex mu;
boost::mutex::scoped_lock lock(mu);
if (isOver)
return false;
if (ctrl == BasicInformation::PAUSE)
paused = !paused;
if (!paused) {
Screen::Coordinate newHead;
if(!move(ctrl, newHead)) { // 控制值无效
// 根据蛇头方向移动
if (!move(headDirection, newHead)) // 蛇头方向无效
return true; // 返回
}
else headDirection = ctrl; // 蛇头方向变为行进方向
if (wallCoords.find(newHead) != wallCoords.end()) { // 撞墙
isOver = true;
return false;
}
// 没有吃到食物
if (foodCoords.find(newHead) == foodCoords.end()) {
Screen::erase(snakeCoords.back().x, snakeCoords.back().y); // 擦掉尾巴
snakeCoords.pop_back();
}
else { // 吃到食物
foodCoords.erase(newHead);
if (foodCoords.size() == BasicInformation::MINMUM_FOOD) {
distributionFood(foodCoords.size());
draw(foodCoords, BasicInformation::FOOD);
}
}
if (std::find(snakeCoords.begin(), snakeCoords.end(), newHead) // 咬到自己
!= snakeCoords.end()) {
isOver = true;
return false;
}
snakeCoords.push_front(newHead);
draw(snakeCoords, BasicInformation::SNAKE); // 画蛇
}
return true;
}
bool Map::GameMap::
move(BasicInformation::Control ctrl, Screen::Coordinate &newHead) noexcept
{
using namespace BasicInformation;
switch (ctrl) {
case LEFT :
newHead = Screen::makeCoordinate(
snakeCoords.front().x - 1,
snakeCoords.front().y
);
break;
case RIGHT :
newHead = Screen::makeCoordinate(
snakeCoords.front().x + 1,
snakeCoords.front().y
);
break;
case UP :
newHead = Screen::makeCoordinate(
snakeCoords.front().x,
snakeCoords.front().y - 1
);
break;
case DOWN :
newHead = Screen::makeCoordinate(
snakeCoords.front().x,
snakeCoords.front().y + 1
);
break;
default: // 控制键无效
return false;
}
return true;
}
void Map::GameMap::
distributionFood(std::size_t currentFoodCount) noexcept
{
for (std::size_t i = currentFoodCount;
i != BasicInformation::TOTAL_FOOD; ++i) {
Screen::Coordinate foodPos = Random::randomCoordinate();
while (std::find(snakeCoords.begin(),
snakeCoords.end(), foodPos) != snakeCoords.end() ||
wallCoords.find(foodPos) != wallCoords.end()) // 食物的位置与蛇的位置重复或与石头位置重复
foodPos = Random::randomCoordinate();
// 食物位置合法
foodCoords.insert(foodPos);
}
}
namespace Helper {
BasicInformation::wall_t make_wall() noexcept;
void theFirstLine(BasicInformation::wall_t &) noexcept;
void middle(BasicInformation::wall_t &) noexcept;
void theLastLine(BasicInformation::wall_t &) noexcept;
}
BasicInformation::wall_t
Helper::distributionWall() noexcept
{
static BasicInformation::wall_t w = make_wall();
return w;
}
BasicInformation::snake_t Helper::distributionSnakePos() noexcept
{
BasicInformation::snake_t s;
s.push_back(Random::randomCoordinate());
return s;
}
BasicInformation::wall_t
Helper::make_wall() noexcept
{
BasicInformation::wall_t w;
theFirstLine(w); // 第一行的墙壁
middle(w); // 中间的墙壁
theLastLine(w); // 最后一行的墙壁
for (std::size_t i = 0;
i != BasicInformation::TOTAL_STONE; ++i) // 再随机生成TOTAL_STONE个石头
w.insert(Random::randomCoordinate());
return w;
}
void Helper::theFirstLine(BasicInformation::wall_t &wmap) noexcept
{
for (Screen::pos_type x = 0;
x != BasicInformation::SCREEN_WIDTH; ++x)
wmap.insert(Screen::makeCoordinate(x, 0));
}
void Helper::middle(BasicInformation::wall_t &wmap) noexcept
{
for (Screen::pos_type y = 1;
y != BasicInformation::SCREEN_HIGHT - 1; ++y) {
wmap.insert(Screen::makeCoordinate(0, y));
wmap.insert(Screen::makeCoordinate(BasicInformation::SCREEN_WIDTH - 1, y));
}
}
void Helper::theLastLine(BasicInformation::wall_t &wmap) noexcept
{
for (Screen::pos_type x = 0;
x != BasicInformation::SCREEN_WIDTH; ++x)
wmap.insert(Screen::makeCoordinate(x,
BasicInformation::SCREEN_HIGHT - 1));
}
#include "random.h"
#include "snake_basicInformation.h"
#include <random>
#include <ctime>
#include <limits>
Screen::Coordinate Random::randomCoordinate() noexcept
{
static std::default_random_engine cols_engine(std::time(0));
static std::default_random_engine rows_engine(std::numeric_limits<unsigned>::max());
static std::uniform_int_distribution<Screen::pos_type> rand_rows(1,
BasicInformation::SCREEN_HIGHT - 2); // 围墙除外, 行数从1 - SCREEN_HIGHT - 2
static std::uniform_int_distribution<Screen::pos_type> rand_cols(1,
BasicInformation::SCREEN_WIDTH - 2); // 围墙除外, 列数从1 - SCREEN_WIDTH - 2
Screen::Coordinate coord;
coord.y = rand_rows(rows_engine);
coord.x = rand_cols(cols_engine);
return coord;
}
#include "screen.h"
#include "snake_basicInformation.h"
#include <cassert>
#include <iostream>
#include <windows.h>
#include <boost/thread/mutex.hpp>
void Screen::putchar(row_type cols, col_type rows, char data) noexcept
{
boost::mutex mu;
boost::mutex::scoped_lock lock(mu);
assert(rows < BasicInformation::SCREEN_HIGHT &&
cols < BasicInformation::SCREEN_WIDTH);
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = cols;
pos.Y = rows;
SetConsoleCursorPosition(hOut,pos);
std::cout.put(data);
}
void Screen::hideCursor(bool isVisible) noexcept
{
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cci;
GetConsoleCursorInfo(hOut,&cci);
cci.bVisible = isVisible;
SetConsoleCursorInfo(hOut,&cci);
}
编译器需要支持C++11, 需要安装boost库 |