diff --git a/CMakeLists.txt b/CMakeLists.txt index 0af7b9f..ce52906 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,4 +14,6 @@ add_executable(epr24pr3_ojanssen2 std_lib_inc.h Maze.h Maze.cpp + MazeParser.h + MazeParser.cpp ) diff --git a/Maze.cpp b/Maze.cpp index 741c131..2f6ad43 100644 --- a/Maze.cpp +++ b/Maze.cpp @@ -3,29 +3,38 @@ // #include "Maze.h" +#include "Player.h" namespace game { - Maze::Maze() + Maze::Maze(const vector> play_field, const vector player_start_position): + field(play_field), + player_start_position(player_start_position) { - } - bool Maze::is_player_at_goal(const PositionVector pos) const + bool Maze::was_player_killed_by_ghost(Player player) const { - return this->field[pos.y][pos.x] == 'Z'; + return this->field[player.get_pos().y][player.get_pos().x] == 'A'; } - bool Maze::is_pos_free(const PositionVector pos) const + bool Maze::is_player_at_goal(Player player) const + { + return this->field[player.get_pos().y][player.get_pos().x] == 'Z'; + } + + bool Maze::is_pos_free(const PositionVector pos, const bool player_has_key) const { if (pos.x < 0 || pos.y < 0 || pos.y > field.size() - 1 || pos.x > field[pos.y].size() - 1) return false; // Zielposition ist außerhalb des Spielfelds if (field[pos.y][pos.x] == '#') return false; // Zielposition ist eine Wand + if (field[pos.y][pos.x] == 'T') + return player_has_key; // Zielposition ist eine Tür return true; // Zielposition ist betretbar (ist keine Wand und auch nicht außerhalb des Spielfeldes) } - void Maze::render(const PositionVector pos) const + void Maze::render(Player player) const { for (int y = 0; y < field.size(); ++y) { @@ -33,7 +42,7 @@ namespace game for (int x = 0; x < field[y].size(); ++x) { // ... schreibe für jedes Element ... - if (y == pos.y && x == pos.x) + if (y == player.get_pos().y && x == player.get_pos().x) cout << "S"; // ... 'S' wenn der aktuelle Eintrag die Position des Spielers ist else // sonst cout << field[y][x]; // ... den tatsächlichen Eintrag @@ -42,4 +51,20 @@ namespace game cout << "\n"; // Beende die Reihe mit einem Zeilenumbruch } } + + char Maze::get_field(PositionVector pos) const + { + return this->field[pos.y][pos.x]; + } + + void Maze::update_field(PositionVector pos, char target) + { + this->field[pos.y][pos.x] = target; + } + + vector Maze::get_player_start_position() + { + return this->player_start_position; + } + } // game diff --git a/Maze.h b/Maze.h index a1076a4..73b7adc 100644 --- a/Maze.h +++ b/Maze.h @@ -7,43 +7,70 @@ #ifndef MAZE_H #define MAZE_H +/* Legende + * S - Spieler + * Z - Ziel + * . - Leerer Raum (begehbar) + * # - Wand + * A - Geist + * K - Schlüssel + * T - Tür + */ namespace game { + class Player; // Forward declaration + /// Ein Labyrinth. + /// Besitzt ein Feld class Maze - { // class -> members private by default + { + // class -> members private by default + private: /// Das Spielfeld - vector> field = { - {'#', '.', '.', '.', '.'}, - {'#', '.', '#', '.', '.'}, - {'.', 'Z', '#', '.', '.'}, - {'.', '#', '#', '#', '.'}, - {'.', '.', '.', '.', '.'} - // ^ Spieler startet hier (4,0) - }; - /* Legende - * S - Spieler - * Z - Ziel - * . - Leerer Raum (begehbar) - * # - Wand - */ + vector> field; + /// Die Startposition des Spielers + vector player_start_position; public: /// Das Spielfeld - Maze(); + Maze(vector> play_field, vector player_start_position); + + /// Ein Standard Konstruktor + /// Variablen innerhalb der Klasse bleiben uninitialisiert + Maze() = default; + + /// Kontrolliere, ob der Spieler stirbt + /// @param player Der Spieler + /// @return Ob der Spieler tot ist + bool was_player_killed_by_ghost(Player player) const; /// Kontrolliere, ob der Spieler am Ziel ist /// @param pos Die Position des Spielers /// @return Ob der Spieler am Ziel ist - bool is_player_at_goal(PositionVector pos) const; + bool is_player_at_goal(Player player) const; /// Kontrolliere, ob eine bestimmte Position begehbar ist /// @param pos Die Position, die überprüft werden soll + /// @param player_has_key If the player has at least one key /// @return Ob die Position begehbar ist - bool is_pos_free(PositionVector pos) const; + bool is_pos_free(PositionVector pos, bool player_has_key) const; /// Zeige das Spielfeld in der Konsole an /// @param pos Die Position des Spielers - void render(PositionVector pos) const; + void render(Player player) const; + + /// Kriege den Wert einer Position + /// @param pos Die gewollte Position + /// @return Der Wert der Position + char get_field(PositionVector pos) const; + + /// Ersetze den Wert von einer Position + /// @param target_pos Die Position die ersetzt werden soll + /// @param target Der Wert, auf den die Position gesetzt werden soll + void update_field(PositionVector pos, char target); + + /// Kriege die Startposition des Spielers + /// @return Die Startposition des Spielers + vector get_player_start_position(); }; } // game diff --git a/MazeParser.cpp b/MazeParser.cpp new file mode 100644 index 0000000..f989899 --- /dev/null +++ b/MazeParser.cpp @@ -0,0 +1,43 @@ +// +// Created by moonleay on 12/16/24. +// + +#include "MazeParser.h" + +namespace game { + vector MazeParser::request_numbers_from_user(const int amount_of_numbers) + { + int input; + vector list; + for (int i = 0; i < amount_of_numbers; ++i) + { + cin >> input; + if (!cin) + throw runtime_error("Cin failed while reading numbers!"); + list.push_back(input); + } + return list; + } + + + Maze MazeParser::request_maze_from_user() + { + vector maze_size = request_numbers_from_user(2); + char input; + vector> field; + for (int i = 0; i < maze_size[0]; ++i) + { + vector row; + for (int j = 0; j < maze_size[1]; ++j) + { + cin >> input; + if (!cin) + throw runtime_error("Cin failed while reading chars!"); + row.push_back(input); + } + field.push_back(row); + } + vector player_start_pos = request_numbers_from_user(2); + return Maze(field, player_start_pos); + } // `4 3 #.# #.K #T# #Z# 0 1` +} // game diff --git a/MazeParser.h b/MazeParser.h new file mode 100644 index 0000000..6b629c2 --- /dev/null +++ b/MazeParser.h @@ -0,0 +1,29 @@ +// +// Created by moonleay on 12/16/24. +// + +#include "Maze.h" + +#ifndef MAZEPARSER_H +#define MAZEPARSER_H + +namespace game { + +class MazeParser { +private: + /// Lese eine bestimmte Anzahl an Nummern aus der Konsole + /// @param amount_of_numbers Wie viele Nummern eingelesen werden sollen + /// @return Die eingelesenen Nummern + static vector request_numbers_from_user(int amount_of_numbers); + +public: + /// Lese ein Labyrinth aus der Konsole + /// @return Das Labyrinth + /// @throws runtime_exception Falls die Eingabe nicht korrekt verlaufen ist. + static Maze request_maze_from_user(); +}; + + +} // game + +#endif //MAZEPARSER_H diff --git a/Player.cpp b/Player.cpp index 57e7b17..56283a7 100644 --- a/Player.cpp +++ b/Player.cpp @@ -8,7 +8,7 @@ namespace game { - Player::Player(const int target_x, const int target_y): pos(target_x, target_y) + Player::Player(const int target_x, const int target_y): pos(target_x, target_y), keys_in_inventory(0) { // Wir brauchen keinen Inhalt in diesem Konstruktor, da wir nur die position speichern müssen } @@ -24,16 +24,42 @@ namespace game } - void Player::move(Maze maze, const PositionVector move_vector) + Maze Player::move(Maze maze, const PositionVector move_vector) { // Berechne die Position, zu der der Spieler sich bewegen möchte const PositionVector target_position = PositionVector(this->get_pos().x + move_vector.x, - this->get_pos().y + move_vector.y); + this->get_pos().y + move_vector.y); // Bewege den Spieler zu der gewollten Position, wenn diese frei ist - if (maze.is_pos_free(target_position)) + if (maze.is_pos_free(target_position, this->has_key_available())) + { + // Bewege Spieler zur neuen Position this->update_position(target_position); + + // Kontrolliere das Feld nach speziellen Attributen + switch (maze.get_field(target_position)) + { + case 'K': // Spieler steht auf einem Schlüssel. + // Gebe dem Spieler einen Schlüssel und ersetze das Feld mit einem leeren + ++this->keys_in_inventory; + maze.update_field(target_position, '.'); + break; + case 'T': // Spieler steht auf einer Tür + // Nehme dem Spieler einen Schlüssel weg und ersetze das Feld mit einem leeren + --this->keys_in_inventory; + maze.update_field(target_position, '.'); + break; + default: ; + } + } else - cout << "Bewegung nicht moeglich!\n"; + throw runtime_error("Bewegung nicht moeglich!"); + return maze; } + + bool Player::has_key_available() const + { + return this->keys_in_inventory > 0; + } + } // game diff --git a/Player.h b/Player.h index b780ec3..8a85de3 100644 --- a/Player.h +++ b/Player.h @@ -2,19 +2,20 @@ // Created by moonleay on 12/12/24. // #include "PositionVector.h" -#include "Maze.h" #ifndef PLAYER_H #define PLAYER_H namespace game { + class Maze; // use instead of include to speed up the compiler :) /// Ein Spieler. /// Besitzt einen veränderbaren Positionsvektor class Player { /// Die Position des Spielers PositionVector pos; + int keys_in_inventory; public: /// Ein Spieler. @@ -35,7 +36,11 @@ namespace game /// @param maze Das Maze /// @param move_vector Die gewollte Bewegung /// @return Die neue Position des Spielers - void move(Maze maze, PositionVector move_vector); + Maze move(Maze maze, PositionVector move_vector); + + /// Check, if a player has at least one key + /// @return If the player as an available key + bool has_key_available() const; }; } // maze diff --git a/PositionVector.h b/PositionVector.h index fede846..9ae0efc 100644 --- a/PositionVector.h +++ b/PositionVector.h @@ -10,7 +10,8 @@ namespace game /// Ein Vector aus zwei zahlen. /// Kann eine Position oder eine Differenz zwischen zwei Positionen darstellen. struct PositionVector - { // struct -> members public by default + { + // struct -> members public by default // Die beiden Variablen des Vectors int x; int y; diff --git a/main.cpp b/main.cpp index c589498..00e664e 100644 --- a/main.cpp +++ b/main.cpp @@ -1,34 +1,51 @@ +#include "Maze.h" #include "std_lib_inc.h" #include "Player.h" #include "PositionVector.h" -// #include "Maze.h" -// Muss nicht inkludiert werden, da es schon in Player.h inkludiert wird +#include "MazeParser.h" using game::Player; using game::PositionVector; using game::Maze; +using game::MazeParser; // Ein Programm, welches dir erlaubt ein Labyrinth zu erkunden mit 'w', 'a', 's', und 'd'. const vector player_start_position = {4, 0}; - int main() { - const Maze maze = Maze(); + Maze maze; + try + { + maze = MazeParser::request_maze_from_user(); + } catch (...) + { + cout << "Fehler beim Einlesen des Labyrinths.\n"; + return 0; + } + + vector player_start_position = maze.get_player_start_position(); + // Erstelle einen Spieler mit der gegebenen start position Player player = Player(player_start_position[1], player_start_position[0]); // Erstelle eine Variable für den Input - char input; + char game_input; // Durchlaufe die Hauptschleife des Spiels while (true) { // Zeige dem Spieler das Spielfeld - maze.render(player.get_pos()); + maze.render(player); + + if (maze.was_player_killed_by_ghost(player)) + { + cout << "Sie haben einen Geist getroffen! Game Over!\n"; + break; + } // Kontrolliere, ob der Spieler schon das Ziel erreicht hat - if (maze.is_player_at_goal(player.get_pos())) + if (maze.is_player_at_goal(player)) { // Ziel erreicht! Herzlichen Glückwunsch! cout << "Ziel erreicht! Herzlichen Glueckwunsch!\n"; @@ -36,15 +53,15 @@ int main() } // Lese Eingabe des Spielers - cin >> input; + cin >> game_input; // Erstelle einen Vector mit einer Bewegung von 0 und 0 - PositionVector movement_vector = PositionVector(0,0); + PositionVector movement_vector = PositionVector(0, 0); // Kontrolliere, was der Spieler machen möchte. Speichere die erforderte Bewegung im Bewegungsvektor. // Schreibe nachrichten in die Konsole, // wenn nach Hilfe gefragt wird oder eine unbekannte Eingabe eingegeben wurde - switch (input) + switch (game_input) { case 'w': movement_vector.update(0, -1); @@ -60,7 +77,10 @@ int main() break; case 'h': // Schreibe hilfsreiche Tipps in die Konsole - cout << "Gebe 'w', 'a', 's', oder 'd' ein zum bewegen. Gebe 'h' ein um diesen Text anzuzeigen.\n"; + cout << "Du wurdest von einem Zauberer in eine 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: // Die gewollte Aktion kenne ich nicht. Schreibe eine Fehlernachricht in die Konsole @@ -69,7 +89,15 @@ int main() } // Kontrolliere gewollte Bewegung und setze sie um. - player.move(maze, movement_vector); + try + { + maze = player.move(maze, movement_vector); + } catch (runtime_error& err) + { + // Diese Bewegung ist nicht möglich! + // Gebe aus, warum. + cout << err.what() << "\n"; + } } // Beende das Programm return 0;