[C++] 推箱子

#include <vector>
#include <string>
#include <iostream>
#include <cassert>

using namespace std;

struct State
{
	const static int Empty = 0;
	const static int Player = 1;
	const static int Wall = 2;
	const static int Box = 4;
	const static int Target = 8;
};

struct Tile
{
	enum
	{
		Empty = ' ',
		Player = 'p',
		PlayerOnTarget = 'P',
		Wall = '#',
		Box = 'o',
		BoxOnTarget = 'O',
		Target = '.'
	};
};

class Game
{
	class Player;
	unique_ptr<Player> player;
	vector<vector<int>> map = vector<vector<int>>();
	int remainedBox = 0;
	Game() { }
	bool isLegal(int x, int y) const
	{
		return x >= 0 && y >= 0 && y < map.size() && x < map[y].size();
	}
public:
	struct Player
	{
		friend class Game;
		friend class unique_ptr<Player>;
		int x, y;
		Player(int x, int y) :x(x), y(y) { }
		void set(int x, int y)
		{
			this->x = x;
			this->y = y;
		}
		enum MoveMode
		{
			Up, Down, Left, Right
		};
	};
	static Game loadByString(const string& str)
	{
		Game ret;
		bool nextLine = true;
		int x = 0, y = -1;
		for (auto&& ch : str)
		{
			if (ch == '\n')
			{
				nextLine = true;
				continue;
			}
			if (nextLine)
			{
				nextLine = false;
				++y;
				x = 0;
				ret.map.emplace_back();
			}
			auto& line = ret.map[y];
			switch (ch)
			{
			case Tile::Empty:
				line.emplace_back(State::Empty);
				break;
			case Tile::Player:
				assert(!ret.player);
				ret.player = make_unique<Player>(x, y);
				line.emplace_back(State::Player);
				break;
			case Tile::PlayerOnTarget:
				assert(!ret.player);
				ret.player = make_unique<Player>(x, y);
				line.emplace_back(State::Player | State::Target);
				break;
			case Tile::Wall:
				line.emplace_back(State::Wall);
				break;
			case Tile::Box:
				++ret.remainedBox;
				line.emplace_back(State::Box);
				break;
			case Tile::BoxOnTarget:
				line.emplace_back(State::Box | State::Target);
				break;
			case Tile::Target:
				line.emplace_back(State::Target);
				break;
			default:
				break;
			}
			++x;
		}
		assert(ret.player);
		return ret;
	}
	void draw() const
	{
		system("cls");
		for (auto&& line : map)
		{
			for (auto&& state : line)
			{
				auto ch = '\0';
				switch (state)
				{
				case State::Empty:
					ch = Tile::Empty;
					break;
				case State::Player:
					ch = Tile::Player;
					break;
				case State::Wall:
					ch = Tile::Wall;
					break;
				case State::Box:
					ch = Tile::Box;
					break;
				case State::Target:
					ch = Tile::Target;
					break;
				case State::Player | State::Target:
					ch = Tile::PlayerOnTarget;
					break;
				case State::Box | State::Target:
					ch = Tile::BoxOnTarget;
					break;
				default:
					assert(false);
					break;
				}
				cout << ch;
			}
			cout << endl;
		}
	}
	bool isGameOver()
	{
		return remainedBox == 0;
	}

	void playerMove(Player::MoveMode mode)
	{
		int nextX, nextY;
		switch (mode)
		{
		case Player::MoveMode::Up:
			nextX = player->x;
			nextY = player->y - 1;
			break;
		case Player::MoveMode::Down:
			nextX = player->x;
			nextY = player->y + 1;
			break;
		case Player::MoveMode::Left:
			nextX = player->x - 1;
			nextY = player->y;
			break;
		case Player::MoveMode::Right:
			nextX = player->x + 1;
			nextY = player->y;
			break;
		default:
			assert(false);
			break;
		}
		if (!isLegal(nextX, nextY))
			return;
		switch (map[nextY][nextX])
		{
		case State::Box:
		case State::Box | State::Target:
			int boxNextX, boxNextY;
			switch (mode)
			{
			case Game::Player::Up:
				boxNextX = nextX;
				boxNextY = nextY - 1;
				break;
			case Game::Player::Down:
				boxNextX = nextX;
				boxNextY = nextY + 1;
				break;
			case Game::Player::Left:
				boxNextX = nextX - 1;
				boxNextY = nextY;
				break;
			case Game::Player::Right:
				boxNextX = nextX + 1;
				boxNextY = nextY;
				break;
			default:
				assert(false);
				break;
			}
			if ((isLegal(boxNextX, boxNextY) && !(map[boxNextY][boxNextX] & State::Box) && !(map[boxNextY][boxNextX] & State::Wall)))
			{
				map[nextY][nextX] &= ~State::Box;
				map[boxNextY][boxNextX] |= State::Box;
				if (map[nextY][nextX] & State::Target)
					remainedBox++;
				if (map[boxNextY][boxNextX] & State::Target)
					remainedBox--;
			}
			else
			{
				break;
			}

		case State::Target:
		case State::Empty:
			map[player->y][player->x] &= ~State::Player;
			player->set(nextX, nextY);
			map[nextY][nextX] |= State::Player;
			break;
		case State::Wall:
			break;
		default:
			assert(false);
			break;
		}
	}
};

struct Key
{
	enum Enum
	{
		w = 'w',
		W = 'W',
		a = 'a',
		A = 'A',
		s = 's',
		S = 'S',
		d = 'd',
		D = 'D',
		q = 'q',
		Q = 'Q',
	};
};

class Input
{
	Input() { }
public:
	Input(const Input&) = delete;
	Input(Input&&) = delete;
	Input& operator=(const Input&) = delete;
	Input& operator=(Input&&) = delete;

	static Input& instance() { static Input in; return in; }

	char get()
	{
		char key;
		cin >> key;
		cin.ignore(numeric_limits<streamsize>::max(), '\n');
		return key;
	}

};

int main()
{
	auto map =
		"  ####  \n"
		"  #..#  \n"
		" ## .## \n"
		" #  o.# \n"
		"## o  ##\n"
		"#  #oo #\n"
		"#  p   #\n"
		"########\n";
	auto&& g = Game::loadByString(map);
	auto&& in = Input::instance();

	while (!g.isGameOver())
	{
		g.draw();
		bool end = false;
		Game::Player::MoveMode mode;
		switch (in.get())
		{
		case Key::Q:
		case Key::q:
			end = true;
			break;
		case Key::W:
		case Key::w:
			mode = Game::Player::MoveMode::Up;
			break;
		case Key::A:
		case Key::a:
			mode = Game::Player::MoveMode::Left;
			break;
		case Key::S:
		case Key::s:
			mode = Game::Player::MoveMode::Down;
			break;
		case Key::D:
		case Key::d:
			mode = Game::Player::MoveMode::Right;
			break;
		default:
			break;
		}
		if (end)
		{
			break;
		}
		g.playerMove(mode);
	}

}

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据