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