鱼C论坛

 找回密码
 立即注册
查看: 2525|回复: 3

[技术交流] 我写的控制台贪吃蛇

[复制链接]
发表于 2014-8-19 15:14:44 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x

                               
登录/注册后可看大图


                               
登录/注册后可看大图


  1. #ifndef COORDINATE_H
  2. #define COORDINATE_H

  3. #include <cstddef>
  4. #include <unordered_set>

  5. namespace Screen {
  6.     typedef unsigned char pos_type;

  7.     struct Coordinate {
  8.         inline bool operator==(const Coordinate &rhs) const noexcept
  9.         { return x ==  rhs.x && y == rhs.y; }
  10.         pos_type x, y;
  11.     };

  12.     inline Coordinate makeCoordinate(pos_type x, pos_type y) noexcept
  13.     {
  14.         Coordinate ret;
  15.         ret.x = x;
  16.         ret.y = y;
  17.         return ret;
  18.     }
  19. }

  20. namespace std {
  21.     template <>
  22.     struct hash<Screen::Coordinate>  {
  23.         typedef size_t result_type;
  24.         typedef Screen::Coordinate argument_type;

  25.         inline size_t operator()(const Screen::Coordinate &c) const noexcept
  26.         {
  27.             return hash<Screen::pos_type>()(c.x) ^
  28.                     hash<Screen::pos_type>()(c.y);
  29.         }
  30.     };

  31. }

  32. #endif // COORDINATE_H
复制代码

  1. #ifndef KEYBOARDLISTENER_H
  2. #define KEYBOARDLISTENER_H

  3. #include "map.h"
  4. #include <boost/noncopyable.hpp>
  5. #include <boost/scoped_ptr.hpp>
  6. #include <boost/thread.hpp>

  7. namespace Listener {
  8.     class KeyboardListener : boost::noncopyable {
  9.     public:
  10.         KeyboardListener(Map::GameMap &map)
  11.         noexcept : pMap(&map) { startListen(); }
  12.         ~KeyboardListener() noexcept { stopListen(); }
  13.     private:
  14.         void startListen() const noexcept;
  15.         void stopListen() const noexcept;

  16.         Map::GameMap *pMap;
  17.         mutable boost::scoped_ptr<boost::thread> pThread;
  18.     };
  19. }


  20. #endif // KEYBOARDLISTENER_H
复制代码

  1. #ifndef MAP_H
  2. #define MAP_H

  3. #include "snake_basicInformation.h"
  4. #include "screen.h"
  5. #include <boost/noncopyable.hpp>
  6. #include <cstddef>
  7. #include <boost/thread/mutex.hpp>

  8. namespace Map {
  9.     class GameMap : boost::noncopyable {
  10.     public:
  11.         GameMap() noexcept;
  12.         ~GameMap() noexcept
  13.         {Screen::clearScreen(); Screen::hideCursor(true);}

  14.         bool snakeMove(BasicInformation::Control) noexcept;

  15.     private:
  16.         bool move(BasicInformation::Control, Screen::Coordinate &) noexcept;
  17.         void distributionFood(std::size_t = 0) noexcept;

  18.         BasicInformation::wall_t wallCoords;
  19.         BasicInformation::food_t foodCoords;
  20.         BasicInformation::snake_t snakeCoords;
  21.         BasicInformation::Control headDirection =
  22.                 BasicInformation::OTHER;
  23.         bool paused = false;
  24.         bool isOver = false;
  25.     };

  26.     template <class Map_t>
  27.     void draw(const Map_t &, char) noexcept;
  28. }


  29. template <class Map_t>
  30. void Map::draw(const Map_t &map, char ch) noexcept
  31. {
  32.     boost::mutex mu;
  33.     boost::mutex::scoped_lock lock(mu);
  34.     for (auto coord : map)
  35.         Screen::putchar(coord.x, coord.y, ch);
  36. }

  37. #endif // MAP_H
复制代码

  1. #ifndef RANDOM_H
  2. #define RANDOM_H

  3. #include "coordinate.h"

  4. namespace Random {
  5.     Screen::Coordinate randomCoordinate() noexcept; // 随机生成坐标
  6. }

  7. #endif // RANDOM_H
复制代码

  1. #ifndef PUTCHAR_H
  2. #define PUTCHAR_H

  3. #include <cstdint>
  4. #include <cstdlib>

  5. namespace Screen {

  6.     typedef std::uint8_t row_type;
  7.     typedef row_type col_type;

  8.     void putchar(row_type cols, col_type rows,
  9.                  char data) noexcept; // 在rows行(0 - 24), cols列(0 - 79)打印字符
  10.     inline void erase(row_type cols, col_type rows) noexcept // 清除第rows行,cols列的字符
  11.     { putchar(cols, rows, 0); }
  12.     inline void clearScreen() noexcept // 清屏
  13.     { std::system("cls"); }
  14.     void hideCursor(bool = false) noexcept;
  15. }

  16. #endif // PUTCHAR_H
复制代码

  1. #ifndef SNAKE_BASICINFORMATION_H
  2. #define SNAKE_BASICINFORMATION_H

  3. #include "coordinate.h"
  4. #include <deque>
  5. #include <unordered_set>

  6. namespace BasicInformation {
  7.     // 键盘控制枚举
  8.     enum Control { LEFT = 'A',
  9.                    UP = 'W',
  10.                    DOWN = 'S',
  11.                    RIGHT = 'D',
  12.                    PAUSE = ' ',
  13.                    OTHER
  14.                  };
  15.     enum Information { // GROW = 1, // 蛇每次吃到食物生长长度
  16.                        TOTAL_STONE = 20, // 石头的总数
  17.                        TOTAL_FOOD = 5,// 屏幕上最多只能同时有TOTAL_FOOD个食物
  18.                        MINMUM_FOOD = 0, // 当食物降低至MINMUM_FOOD个时补充食物
  19.                        SNAKE = '*',
  20.                        WALL = '#',
  21.                        FOOD = '@',
  22.                        SCREEN_HIGHT = 25, // 屏幕高
  23.                        SCREEN_WIDTH = 80 // 屏幕宽
  24.                      };

  25.     typedef std::deque<Screen::Coordinate> snake_t;
  26.     typedef std::unordered_set<Screen::Coordinate> wall_t;
  27.     typedef wall_t food_t;
  28. }

  29. #endif // SNAKE_BASICINFORMATION_H
复制代码

  1. #include "keyboardListener.h"
  2. #include <conio.h>
  3. #include <cctype>

  4. namespace Helper {
  5.     void listener(Map::GameMap *map) noexcept;
  6. }

  7. void Listener::KeyboardListener::
  8. startListen() const noexcept
  9. {
  10.     pThread.reset(new boost::thread(Helper::listener, pMap));
  11. }

  12. void Listener::KeyboardListener::
  13. stopListen() const noexcept
  14. {
  15.     try {
  16.         pThread->interrupt();
  17.     }
  18.     catch (...) {}
  19. }

  20. void Helper::listener(Map::GameMap *map) noexcept
  21. {
  22.     int ctrl = getch();
  23.     while (map->snakeMove(static_cast<BasicInformation::Control>(
  24.                               std::toupper(ctrl)
  25.                               )))
  26.         ctrl = getch();
  27. }
复制代码

  1. #include "screen.h"
  2. #include "map.h"
  3. #include "snake_basicInformation.h"
  4. #include "keyboardListener.h"
  5. #include <iostream>
  6. #include <conio.h>
  7. #include <windows.h>
  8. #include <cctype>
  9. #include <memory>
  10. #include <cstring>

  11. int main(int argc, char *argv[])
  12. {
  13.     {
  14.         Map::GameMap gameMap;
  15.         Listener::KeyboardListener listener(gameMap);
  16.         while (gameMap.snakeMove(BasicInformation::OTHER)) Sleep(250);
  17.     }


  18.     const char *gameOver = "GameOver";
  19.     Screen::col_type x = (BasicInformation::SCREEN_WIDTH - std::strlen(gameOver))  / 2;
  20.     Screen::row_type y = BasicInformation::SCREEN_HIGHT / 2;

  21.     while (*gameOver)
  22.         Screen::putchar(x++, y, *gameOver++);


  23.     return 0;
  24. }
复制代码

  1. #include "map.h"
  2. #include "random.h"
  3. #include <algorithm>
  4. #include <cassert>

  5. namespace Helper {
  6.     BasicInformation::wall_t distributionWall() noexcept;
  7.     BasicInformation::snake_t distributionSnakePos() noexcept;
  8. }

  9. Map::GameMap::GameMap() noexcept :
  10.     wallCoords(Helper::distributionWall()),
  11.     snakeCoords(Helper::distributionSnakePos())
  12. {
  13.     Screen::hideCursor();
  14.     distributionFood();

  15.     while (wallCoords.find(snakeCoords.front()) != wallCoords.end()) // 如果蛇的初始位置刚好有墙
  16.         snakeCoords = Helper::distributionSnakePos(); // 重新分配蛇的位置

  17.     draw(wallCoords, BasicInformation::WALL); // 画墙
  18.     draw(snakeCoords, BasicInformation::SNAKE); // 画蛇
  19.     draw(foodCoords, BasicInformation::FOOD);
  20. }

  21. bool Map::GameMap::
  22. snakeMove(BasicInformation::Control ctrl) noexcept
  23. {
  24.     boost::mutex mu;
  25.     boost::mutex::scoped_lock lock(mu);

  26.     if (isOver)
  27.         return false;

  28.     if (ctrl == BasicInformation::PAUSE)
  29.         paused = !paused;


  30.     if (!paused) {
  31.         Screen::Coordinate newHead;

  32.         if(!move(ctrl, newHead)) { // 控制值无效
  33.         // 根据蛇头方向移动
  34.         if (!move(headDirection, newHead)) // 蛇头方向无效
  35.             return true; // 返回
  36.         }
  37.         else headDirection = ctrl; // 蛇头方向变为行进方向

  38.         if (wallCoords.find(newHead) != wallCoords.end()) { // 撞墙
  39.             isOver = true;
  40.             return false;
  41.         }

  42.         // 没有吃到食物
  43.         if (foodCoords.find(newHead) == foodCoords.end()) {
  44.             Screen::erase(snakeCoords.back().x, snakeCoords.back().y); // 擦掉尾巴
  45.             snakeCoords.pop_back();
  46.         }
  47.         else { // 吃到食物
  48.             foodCoords.erase(newHead);

  49.             if (foodCoords.size() == BasicInformation::MINMUM_FOOD) {
  50.                 distributionFood(foodCoords.size());
  51.                 draw(foodCoords, BasicInformation::FOOD);
  52.             }
  53.         }

  54.         if (std::find(snakeCoords.begin(), snakeCoords.end(), newHead) // 咬到自己
  55.                 != snakeCoords.end()) {
  56.             isOver = true;
  57.             return false;
  58.         }

  59.         snakeCoords.push_front(newHead);
  60.         draw(snakeCoords, BasicInformation::SNAKE); // 画蛇
  61.     }

  62.     return true;
  63. }

  64. bool Map::GameMap::
  65. move(BasicInformation::Control ctrl, Screen::Coordinate &newHead) noexcept
  66. {
  67.     using namespace BasicInformation;

  68.     switch (ctrl) {
  69.     case LEFT :
  70.         newHead = Screen::makeCoordinate(
  71.                     snakeCoords.front().x - 1,
  72.                     snakeCoords.front().y
  73.                     );
  74.         break;

  75.     case RIGHT :
  76.         newHead = Screen::makeCoordinate(
  77.                     snakeCoords.front().x + 1,
  78.                     snakeCoords.front().y
  79.                     );
  80.         break;

  81.     case UP :
  82.         newHead = Screen::makeCoordinate(
  83.                     snakeCoords.front().x,
  84.                     snakeCoords.front().y - 1
  85.                     );
  86.         break;

  87.     case DOWN :
  88.         newHead = Screen::makeCoordinate(
  89.                     snakeCoords.front().x,
  90.                     snakeCoords.front().y + 1
  91.                     );
  92.         break;

  93.     default: // 控制键无效
  94.         return false;
  95.     }

  96.     return true;
  97. }

  98. void Map::GameMap::
  99. distributionFood(std::size_t currentFoodCount) noexcept
  100. {
  101.     for (std::size_t i = currentFoodCount;
  102.          i != BasicInformation::TOTAL_FOOD; ++i) {
  103.         Screen::Coordinate foodPos = Random::randomCoordinate();

  104.         while (std::find(snakeCoords.begin(),
  105.                          snakeCoords.end(), foodPos) != snakeCoords.end() ||
  106.                wallCoords.find(foodPos) != wallCoords.end()) // 食物的位置与蛇的位置重复或与石头位置重复
  107.             foodPos = Random::randomCoordinate();
  108.         // 食物位置合法
  109.         foodCoords.insert(foodPos);
  110.     }
  111. }

  112. namespace Helper {
  113.     BasicInformation::wall_t make_wall() noexcept;
  114.     void theFirstLine(BasicInformation::wall_t &) noexcept;
  115.     void middle(BasicInformation::wall_t &) noexcept;
  116.     void theLastLine(BasicInformation::wall_t &) noexcept;
  117. }

  118. BasicInformation::wall_t
  119.     Helper::distributionWall() noexcept
  120. {
  121.     static BasicInformation::wall_t w = make_wall();

  122.     return w;
  123. }


  124. BasicInformation::snake_t Helper::distributionSnakePos() noexcept
  125. {
  126.     BasicInformation::snake_t s;

  127.     s.push_back(Random::randomCoordinate());

  128.     return s;
  129. }

  130. BasicInformation::wall_t
  131.     Helper::make_wall() noexcept
  132. {
  133.     BasicInformation::wall_t w;

  134.     theFirstLine(w); // 第一行的墙壁
  135.     middle(w); // 中间的墙壁
  136.     theLastLine(w); // 最后一行的墙壁

  137.     for (std::size_t i = 0;
  138.          i != BasicInformation::TOTAL_STONE; ++i) // 再随机生成TOTAL_STONE个石头
  139.         w.insert(Random::randomCoordinate());

  140.     return w;
  141. }


  142. void Helper::theFirstLine(BasicInformation::wall_t &wmap) noexcept
  143. {
  144.     for (Screen::pos_type x = 0;
  145.          x != BasicInformation::SCREEN_WIDTH; ++x)
  146.         wmap.insert(Screen::makeCoordinate(x, 0));
  147. }

  148. void Helper::middle(BasicInformation::wall_t &wmap) noexcept
  149. {
  150.     for (Screen::pos_type y = 1;
  151.          y != BasicInformation::SCREEN_HIGHT - 1; ++y) {
  152.         wmap.insert(Screen::makeCoordinate(0, y));
  153.         wmap.insert(Screen::makeCoordinate(BasicInformation::SCREEN_WIDTH - 1, y));
  154.     }
  155. }

  156. void Helper::theLastLine(BasicInformation::wall_t &wmap) noexcept
  157. {
  158.     for (Screen::pos_type x = 0;
  159.          x != BasicInformation::SCREEN_WIDTH; ++x)
  160.         wmap.insert(Screen::makeCoordinate(x,
  161.                                             BasicInformation::SCREEN_HIGHT - 1));
  162. }
复制代码

  1. #include "random.h"
  2. #include "snake_basicInformation.h"
  3. #include <random>
  4. #include <ctime>
  5. #include <limits>

  6. Screen::Coordinate Random::randomCoordinate() noexcept
  7. {
  8.     static std::default_random_engine cols_engine(std::time(0));
  9.     static std::default_random_engine rows_engine(std::numeric_limits<unsigned>::max());
  10.     static std::uniform_int_distribution<Screen::pos_type> rand_rows(1,
  11.                                                BasicInformation::SCREEN_HIGHT - 2); // 围墙除外, 行数从1 - SCREEN_HIGHT - 2
  12.     static std::uniform_int_distribution<Screen::pos_type> rand_cols(1,
  13.                                                BasicInformation::SCREEN_WIDTH - 2); // 围墙除外, 列数从1 - SCREEN_WIDTH - 2

  14.     Screen::Coordinate coord;
  15.     coord.y = rand_rows(rows_engine);
  16.     coord.x = rand_cols(cols_engine);

  17.     return coord;
  18. }
复制代码

  1. #include "screen.h"
  2. #include "snake_basicInformation.h"
  3. #include <cassert>
  4. #include <iostream>
  5. #include <windows.h>
  6. #include <boost/thread/mutex.hpp>

  7. void Screen::putchar(row_type cols, col_type rows, char data) noexcept
  8. {
  9.     boost::mutex mu;
  10.     boost::mutex::scoped_lock lock(mu);

  11.     assert(rows < BasicInformation::SCREEN_HIGHT &&
  12.            cols < BasicInformation::SCREEN_WIDTH);

  13.     HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  14.     COORD pos;
  15.     pos.X = cols;
  16.     pos.Y = rows;
  17.     SetConsoleCursorPosition(hOut,pos);
  18.     std::cout.put(data);
  19. }

  20. void Screen::hideCursor(bool isVisible) noexcept
  21. {
  22.     HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  23.     CONSOLE_CURSOR_INFO cci;
  24.     GetConsoleCursorInfo(hOut,&cci);
  25.     cci.bVisible = isVisible;
  26.     SetConsoleCursorInfo(hOut,&cci);
  27. }
复制代码


编译器需要支持C++11, 需要安装boost库
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2014-8-19 23:49:02 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2014-8-20 15:59:23 | 显示全部楼层
谢谢分享
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-8-21 15:19:46 | 显示全部楼层
谢谢分享
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-12 21:31

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表