控制檯小遊戲之貪喫蛇

貪喫蛇和俄羅斯方塊差不多,都是另起一個線程監聽鍵盤輸入,然後就是不斷刷新。(控制檯跳屏的感覺真不爽,感覺要瞎了)


很簡單的就一個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



snake.h


#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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章