贪吃蛇和俄罗斯方块差不多,都是另起一个线程监听键盘输入,然后就是不断刷新。(控制台跳屏的感觉真不爽,感觉要瞎了)
很简单的就一个snake类和一个game类外加辅助的random_food可调用对象类。用枚举变量代表snake当前的行进方向
point_and_dir.h
#ifndef _POINT_AND_DIR_H #define _POINT_AND_DIR_H enum _direction{ _up, _left, _down, _right }; class _pt { public: _pt(int x = 0, int y = 0) :_x(x), _y(y){} inline int x()const{ return _x; } inline int y()const{ return _y; } inline void set(int x, int y){ _x = x, _y = y; } bool operator== (const _pt & rhs) { if (x() == rhs.x() && y() == rhs.y()) return true; return false; } private: int _x; int _y; }; #endif
random_food.h
#ifndef _RANDOM_FOOD_H #define _RANDOM_FOOD_H #include<random> #include"point_and_dir.h" struct Random_food{ _pt operator()(int h,int w){ static std::default_random_engine e; static std::uniform_int_distribution<unsigned> u(0, ~(1<<31)); return _pt(u(e) % h, u(e) % w); } }; #endif
#ifndef _SNAKE_H #define _SNAKE_H #include<list> #include"point_and_dir.h" class Snake { public: Snake(int x = 0, int y = 0) : dir(_right), body(std::list<_pt>(1,_pt(x,y))) {} void move(); void grow(); bool collided(); bool setdir(_direction); int length()const{ return body.size(); } _pt bodies(int i){ if (i >= length() || i < 0) return _pt(-1,-1); auto it = body.begin(); while (i--)++it; return *it; } private: _direction dir; std::list<_pt> body; }; #endif
#ifndef _GAME_H #define _GAME_H #include<mutex> #include"snake.h" class game { public: game(int w = 60, int h = 25) : mtx(), score(0), width(w), height(h), growing(false), food(0, 0), snake(height / 2, width / 2) { makefood(); } void start(); private: void print(); void keylistener(); void keydown(char); void move(); bool setdir(_direction); void makefood(); bool crashed(); void checkfood(); std::mutex mtx; int score; int width; int height; bool growing; _pt food; Snake snake; }; #endif
snake.cpp
#include"snake.h" void Snake::move() { grow(); body.pop_back(); } void Snake::grow() { int x = body.front().x(); int y = body.front().y(); _pt offset; switch (dir){ case _up: offset.set(x - 1, y); break; case _left: offset.set(x, y - 1); break; case _down: offset.set(x + 1, y); break; case _right: offset.set(x, y + 1); break; default : break; } body.push_front(offset); } bool Snake::collided() { int headx = body.front().x(); int heady = body.front().y(); auto it = body.begin(); for (++it; it != body.end(); ++it) if (headx == it->x() && heady == it->y()) return true; return false; } bool Snake::setdir(_direction d) { if (dir == d || dir == (d + 2) % 4) return false; dir = d; return true; }
game.cpp
#include<conio.h> #include<vector> #include<string> #include<thread> #include<iostream> #include<Windows.h> #include"game.h" #include"random_food.h" void game::print() { system("cls"); std::vector<std::string> win ( std::vector<std::string>( height, std::string(width * 2, ' ') )); std::string s = "●"; win[food.x()][food.y() * 2] = s[0]; win[food.x()][food.y() * 2 + 1] = s[1]; s = "■"; for (int i = 1; i != snake.length(); ++i) win[snake.bodies(i).x()][2 * snake.bodies(i).y()] = s[0], win[snake.bodies(i).x()][2 * snake.bodies(i).y() + 1] = s[1]; s = "○"; win[snake.bodies(0).x()][2 * snake.bodies(0).y()] = s[0]; win[snake.bodies(0).x()][2 * snake.bodies(0).y() + 1] = s[1]; std::cout << "||"; for (int i = 0; i < width * 2; ++i)std::cout << "="; std::cout << "||" <<std::endl; for (int i = 0; i != height; ++i) std::cout << "||" << win[i] << "||" << std::endl; std::cout << "||"; for (int i = 0; i < width * 2; ++i)std::cout << "="; std::cout << "||" << std::endl; std::cout << "\t\t\t\t\t\t\tSCORE:" << score << std::endl; } void game::keydown(char c) { switch (c){ case 'w': if(setdir(_up)) move(); break; case 'a': if(setdir(_left)) move(); break; case 's': if(setdir(_down)) move(); break; case 'd': if(setdir(_right)) move(); break; default: break; } } void game::checkfood() { if (snake.bodies(0) == food){ ++score; growing = true; makefood(); } } void game::move() { if (growing){ snake.grow(); growing = false; } else snake.move(); checkfood(); } bool game::setdir(_direction dir) { return snake.setdir(dir); } void game::makefood() { food = Random_food()(height,width); } bool game::crashed() { if (snake.collided() || snake.bodies(0).x() >= height || snake.bodies(0).x() < 0 || snake.bodies(0).y() >= width || snake.bodies(0).y() < 0) return true; return false; } void game::keylistener() { while (true){ char c; c = _getch(); mtx.lock(); keydown(c); if (crashed()) break; print(); mtx.unlock(); } mtx.unlock(); } void game::start() { std::thread t(&game::keylistener, this); t.detach(); while (true){ if (mtx.try_lock()){ move(); if (crashed()) break; print(); mtx.unlock(); } Sleep(200); } mtx.unlock(); std::cout << "GAME OVER!" << std::endl; }
食用方法:
main.cpp
#include"game.h" int main() { game g(30,20); g.start(); return 0; }