321 lines
9.4 KiB
C++
321 lines
9.4 KiB
C++
#include "std_lib_inc.h"
|
|
|
|
// Ein Programm, welches dir erlaubt ein Labyrinth zu erkunden mit 'w', 'a', 's', und 'd'.
|
|
|
|
// ** KONSTANTEN ** //
|
|
const vector<char> allowed_symbols_in_maze = {'Z', '.', '#', 'A', 'K', 'T'};
|
|
|
|
class UnknownAction{};
|
|
class MalformedMaze{};
|
|
class ExitGame{};
|
|
class MovementNotPossible{};
|
|
|
|
class PositionVector
|
|
{
|
|
public:
|
|
int vec_x;
|
|
int vec_y;
|
|
|
|
PositionVector(const int x, const int y)
|
|
{
|
|
vec_x = x;
|
|
vec_y = y;
|
|
}
|
|
};
|
|
|
|
class Maze
|
|
{
|
|
public:
|
|
vector<vector<char>> the_maze_vector = {{}};
|
|
PositionVector the_player_start_position = {0, 0};
|
|
|
|
Maze(vector<vector<char>> field, PositionVector player_start_position)
|
|
{
|
|
the_maze_vector = field;
|
|
the_player_start_position = player_start_position;
|
|
}
|
|
};
|
|
|
|
class Player
|
|
{
|
|
public:
|
|
|
|
PositionVector the_pos = {0, 1};
|
|
int keys = 0;
|
|
|
|
Player(PositionVector pos)
|
|
{
|
|
the_pos = pos;
|
|
keys = 0;
|
|
}
|
|
};
|
|
|
|
class GameState
|
|
{
|
|
public:
|
|
Maze the_maze = {{},{{},{}}};
|
|
Player the_player = {{{}, {}}};
|
|
|
|
GameState(Maze maze, Player player)
|
|
{
|
|
the_maze = maze;
|
|
the_player = player;
|
|
}
|
|
};
|
|
|
|
/// Kontrolliere, ob der Spieler an einem Geist sterben sollte
|
|
/// @param gs Der Zustand des Spiels
|
|
/// @return Ob der Spieler sich auf einem Feld mit einem Geist befindet
|
|
bool was_player_killed_by_ghost(const GameState& gs)
|
|
{
|
|
return gs.the_maze.the_maze_vector[gs.the_player.the_pos.vec_y][gs.the_player.the_pos.vec_x] == 'A';
|
|
}
|
|
|
|
/// Kontrolliere, ob der Spieler das Ziel erreicht hat
|
|
/// @param gs Der Zustand des Spiels
|
|
/// @return Ob der Spieler das Ziel erreicht hat
|
|
bool has_player_reached_goal(const GameState& gs)
|
|
{
|
|
return gs.the_maze.the_maze_vector[gs.the_player.the_pos.vec_y][gs.the_player.the_pos.vec_x] == 'Z';
|
|
}
|
|
|
|
/// Zeige dem Nutzer das Spielfeld
|
|
/// @param gs Der Zustand des Spiels
|
|
void render_maze(const GameState& gs)
|
|
{
|
|
for (int y = 0; y < gs.the_maze.the_maze_vector.size(); ++y)
|
|
{
|
|
for (int x = 0; x < gs.the_maze.the_maze_vector[y].size(); ++x)
|
|
{
|
|
if (y == gs.the_player.the_pos.vec_y && x == gs.the_player.the_pos.vec_x)
|
|
cout << "S";
|
|
else
|
|
cout << gs.the_maze.the_maze_vector[y][x];
|
|
cout << " ";
|
|
}
|
|
cout << "\n";
|
|
}
|
|
}
|
|
|
|
/// Kontrolliere, ob der Spieler eine Position gehen kann
|
|
/// @param mz Das Labyrinth
|
|
/// @param pv Die zu überprüfende Position
|
|
/// @param does_player_have_key Ob der Spieler einen Schlüssel besitzt
|
|
/// @return Ob der Spieler die Position begehen kann
|
|
bool is_position_free(const Maze& mz, const PositionVector& pv, const bool& does_player_have_key)
|
|
{
|
|
if (pv.vec_x < 0 || pv.vec_y < 0 || pv.vec_y > mz.the_maze_vector.size() - 1 || pv.vec_x > mz.the_maze_vector[pv.vec_y].size() - 1)
|
|
return false;
|
|
if (mz.the_maze_vector[pv.vec_y][pv.vec_x] == '#')
|
|
return false;
|
|
if (mz.the_maze_vector[pv.vec_y][pv.vec_x] == 'T')
|
|
return does_player_have_key;
|
|
return true;
|
|
|
|
}
|
|
|
|
/// Kontrolliere, ob ein gegebenes Symbol im Labyrinth vorkommen darf
|
|
/// @param symbol Das zu überprüfende Zeichen
|
|
/// @return Ob das gegebene Symbol in einem Labyrinth vorkommen darf
|
|
bool is_symbol_valid_maze_element(const char& symbol)
|
|
{
|
|
for (char c : allowed_symbols_in_maze)
|
|
if (c == symbol)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// Lese beliebig viele Nummern aus der Konsolenausgabe aus
|
|
/// @param amount_of_numbers_to_read Wie viele Nummern eingelesen werden sollen
|
|
/// @return Die eingelesenen Nummern
|
|
/// @throws MalformedMaze Wenn der Nutzer etwas anderes als Zahlen eingibt
|
|
vector<int> get_numbers_from_user(const int& amount_of_numbers_to_read)
|
|
{
|
|
int input;
|
|
vector<int> numbers_provided_by_user;
|
|
for (int i = 0; i < amount_of_numbers_to_read; ++i)
|
|
{
|
|
cin >> input;
|
|
if (!cin)
|
|
throw MalformedMaze();
|
|
numbers_provided_by_user.push_back(input);
|
|
}
|
|
|
|
return numbers_provided_by_user;
|
|
}
|
|
|
|
/// Lese ein Labyrinth aus
|
|
/// @param rows Wie viele Reihen das Labyrinth haben soll
|
|
/// @param columns Wie viele Zeilen das Labyrinth haben soll
|
|
/// @return Das fertige Labyrinth
|
|
/// @throw MalformedMaze Wenn ein eingegebenes Symbol kein char ist, oder ein Symbol eingegeben wird, welches nicht in einem Labyrinth erlaubt ist, oder die Dimension des Mazes falsch ist.
|
|
/// @throw ExitGame Wenn 'q' eingegeben wird
|
|
vector<vector<char>> get_field_from_user(const int& rows, const int& columns)
|
|
{
|
|
if ((rows <= 0 || rows > 20) || (columns <= 0 || columns > 20))
|
|
throw MalformedMaze();
|
|
|
|
char input;
|
|
vector<vector<char>> maze;
|
|
|
|
for (int current_row = 0; current_row < rows; ++current_row)
|
|
{
|
|
vector<char> current_column;
|
|
for (int element_of_column = 0; element_of_column < columns; ++element_of_column)
|
|
{
|
|
cin >> input;
|
|
if (!cin)
|
|
throw MalformedMaze();
|
|
if (input == 'q')
|
|
throw ExitGame();
|
|
if (!is_symbol_valid_maze_element(input))
|
|
throw MalformedMaze();
|
|
current_column.push_back(input);
|
|
}
|
|
maze.push_back(current_column);
|
|
}
|
|
return maze;
|
|
}
|
|
|
|
/// Lasse den Spieler ein Labyrinth eingeben
|
|
/// @return Ein vom Spieler eingegebenes Labyrinth
|
|
Maze get_complete_maze_from_user()
|
|
{
|
|
vector<int> maze_dimensions = get_numbers_from_user(2);
|
|
vector<vector<char>> field = get_field_from_user(maze_dimensions[0], maze_dimensions[1]);
|
|
vector<int> player_start_position = get_numbers_from_user(2);
|
|
|
|
return Maze(field, PositionVector(player_start_position[1], player_start_position[0]));
|
|
}
|
|
|
|
/// Lese aus der Eingabe des Spielers, was er machen möchte
|
|
/// @param input Die Eingabe des Nutzers
|
|
/// @return Die gewollte Bewegung des Spielers
|
|
/// @throw ExitGame Wenn der Nutzer 'q' eingegeben hat und das Spiel verlassen möchte
|
|
/// @throw UnknownAction Wenn der Nutzer einen Befehl eingibt, den das Programm nicht kennt
|
|
PositionVector handle_user_input(const char& input)
|
|
{
|
|
PositionVector movement_vector = PositionVector(0, 0);
|
|
|
|
switch (input)
|
|
{
|
|
case 'w':
|
|
movement_vector.vec_y = -1;
|
|
break;
|
|
case 'a':
|
|
movement_vector.vec_x = -1;
|
|
break;
|
|
case 's':
|
|
movement_vector.vec_y = 1;
|
|
break;
|
|
case 'd':
|
|
movement_vector.vec_x = 1;
|
|
break;
|
|
case 'q':
|
|
throw ExitGame();
|
|
case 'h':
|
|
cout <<
|
|
"Du wurdest von einem Zauberer in ein Labyrinth gesperrt, nachdem du seine Künste beleidigt hast.\n"
|
|
<< "Er laesst dich leben, wenn du es schaffst den Ausgang (Z) zu finden. Solltest du keinen Erfolg haben, laesst er dich verhungern.\n"
|
|
<< "Auf deinem Abenteuer wirst du dabei boesen Geistern (A) begegnen und mit Schluesseln (K) Tueren (T) aufschliessen.\n"
|
|
<< "Bewege dich mit 'w', 'a', 's' und 'd'.\n";
|
|
break;
|
|
default:
|
|
throw UnknownAction();
|
|
}
|
|
|
|
return movement_vector;
|
|
}
|
|
|
|
/// Bewege den Spieler um den gegebenen Vektor
|
|
/// @param gs Der Spielzustand
|
|
/// @param requested_movement Die gewollte Bewegung
|
|
/// @returns The new GameState
|
|
/// @throw MovementNotPossible Wenn die Bewegung aus irgendeinem Grund nicht möglich ist
|
|
GameState move_player(GameState gs, PositionVector requested_movement)
|
|
{
|
|
const PositionVector target_position = PositionVector(gs.the_player.the_pos.vec_x + requested_movement.vec_x,
|
|
gs.the_player.the_pos.vec_y + requested_movement.vec_y);
|
|
|
|
if (is_position_free(gs.the_maze, target_position, gs.the_player.keys > 0))
|
|
{
|
|
gs.the_player.the_pos = target_position;
|
|
|
|
switch (gs.the_maze.the_maze_vector[target_position.vec_y][target_position.vec_x])
|
|
{
|
|
case 'K':
|
|
++gs.the_player.keys;
|
|
gs.the_maze.the_maze_vector[target_position.vec_y][target_position.vec_x] = '.';
|
|
break;
|
|
case 'T':
|
|
--gs.the_player.keys;
|
|
gs.the_maze.the_maze_vector[target_position.vec_y][target_position.vec_x] = '.';
|
|
break;
|
|
default: ;
|
|
}
|
|
}
|
|
else
|
|
throw MovementNotPossible();
|
|
return gs;
|
|
}
|
|
|
|
/// Die Hauptschleife des Spiels. Hier findet die Logik statt
|
|
/// @param gs Der Spielzustand
|
|
void game_loop(GameState& gs)
|
|
{
|
|
char game_input;
|
|
|
|
while (true)
|
|
{
|
|
if (was_player_killed_by_ghost(gs))
|
|
{
|
|
cout << "Sie haben einen Geist getroffen! Game Over!\n";
|
|
break;
|
|
}
|
|
|
|
render_maze(gs);
|
|
|
|
if (has_player_reached_goal(gs))
|
|
{
|
|
cout << "Ziel erreicht! Herzlichen Glueckwunsch!\n";
|
|
break;
|
|
}
|
|
|
|
cin >> game_input;
|
|
|
|
PositionVector movement_vector = PositionVector(0, 0);
|
|
|
|
try
|
|
{
|
|
movement_vector = handle_user_input(game_input);
|
|
gs = move_player(gs, movement_vector);
|
|
} catch (UnknownAction& _)
|
|
{
|
|
cout << "Diese Eingabe kenne ich nicht. Gib 'h' ein, um eine Hilfe zu erhalten." << "\n";
|
|
} catch (MovementNotPossible& _)
|
|
{
|
|
cout << "Bewegung nicht moeglich!" << "\n";
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int main()
|
|
{
|
|
try
|
|
{
|
|
Maze mz = get_complete_maze_from_user();
|
|
Player pl = Player(mz.the_player_start_position);
|
|
GameState gs = GameState(mz, pl);
|
|
game_loop(gs);
|
|
} catch (MalformedMaze& _)
|
|
{
|
|
cout << "Fehler beim Einlesen des Labyrinths.\n";
|
|
} catch (ExitGame& _)
|
|
{
|
|
cout << "Schoenen Tag noch!" << "\n";
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|