贪吃蛇和俄罗斯方块差不多,都是另起一个线程监听键盘输入,然后就是不断刷新。(控制台跳屏的感觉真不爽,感觉要瞎了)
很简单的就一个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
game.h
#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;
}