|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
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库 |
|