控制台小游戏之贪吃蛇

贪吃蛇和俄罗斯方块差不多,都是另起一个线程监听键盘输入,然后就是不断刷新。(控制台跳屏的感觉真不爽,感觉要瞎了)


很简单的就一个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
#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 u(0, ~(1<<31));
		return _pt(u(e) % h, u(e) % w);
	}
};

#endif



snake.h


#ifndef _SNAKE_H
#define _SNAKE_H

#include
#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
#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
#include
#include
#include
#include
#include
#include"game.h"
#include"random_food.h"

void game::print()
{
	system("cls");
	std::vector win
		(
		std::vector(
		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 << "||" <= 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;
}


你可能感兴趣的:(控制台游戏)