diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 927258f..0000000 --- a/main.cpp +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Ein verbessertes Labyrinth-Spiel - * Autor: Fritz Bökler (fboekler@uos.de) - * Datum: 02.12.2024 - * MIT Lizenz - * - * In diesem Spiel versucht eine SpielerIn (S) das Ziel (Z) zu erreichen. - * Das Labyrinth wird ueber die Konsole (cout) ausgegeben, die Eingabe erfolgt ebenfalls - * zeilengepuffert ueber die Konsole (cin). - * - * Das Labyrinth enthält die folgenden Zeichen: - * . Leeres Feld - * # Wand (nicht begehbar) - * Z Ziel - * S SpielerIn (wird nicht im Labyrint selbst gespeichert) - * K Schluessel - * T Tür - * A Geist - * - * Eine SpielerIn hat eine Anzahl an Schlüsseln. Diese Anzahl wird beim Erreichen eines - * K-Feldes erhöht und beim Erreichen eines T-Feldes reduziert. Eine Tür kann nur durchschritten - * werden, wenn die SpielerIn mindestens einen Schluessel besitzt. Ein aufgenommener Schluessel - * verschwindet (wird zu .), ebenso wie eine durchschrittene Tuer. - * - * Die folgenden Eingaben sind gültig: - * w - nach oben bewegen - * a - nach links bewegen - * s - nach unten bewegen - * d - nach rechts bewegen - * q - Spiel beenden - * - * Das Labyrnith wird zu Beginn eingegeben. - * Syntax: - * : 1 bis 20 - * : 1 bis 20 - * : Eine Folge von * vielen Zeichen aus {., #, Z, K, T, A} - * : 0 bis - 1 - * : 0 bis - 1 - * - * Ein Beispiellabyrinth: 7 7 ...#....#...#T..####Z#....##K###.#......A#.###### 0 4 -*/ - -#include "std_lib_inc.h" - -// Exception fuer nicht erlaubte Bewegungen -class BadMovement {}; - -// Exception fuer unbekannte Eingaben -class UnknownInput {}; - -// Exception fuer eine falsche Labyrinth-Eingabe -class BadMaze {}; - -// Klasse, die eine SpielerIn kapselt -class Player -{ -public: - int no_keys; // Anzahl der Schlüssel der SpielerIn - vector position; // Aktuelle Position der SpielerIn im Labyrinth -}; - -// Klasse, die das Labyrinth kapselt -class Maze -{ -public: - int rows; // Anzahl der Zeilen des Labyrinths - int cols; // Anzahl der Spalten des Labyrinths - vector> data; // Labyrinth-Daten (erst Zeilen dann Spalten) - vector player_start_position; // Startposition der SpielerIn, so wie es in der Übergabe steht -}; - -// Fasst Labyrinth und Spieler zu einem Spiel-Status zusammen -class GameState -{ -public: - Maze maze; // Das Labyrinth - Player player; // Die SpielerIn - bool exit; // Wurde 'q' gerdückt? - bool hit_ghost; // Wurde ein Geist getroffen? -}; - -// Funktion zur Anzeige des Spielfeldes -void display_maze(GameState game_state) -{ - const int player_row = game_state.player.position[0]; - const int player_col = game_state.player.position[1]; - - //cout << "\033[H\033[J"; // ANSI Escape Code zum Loeschen des Bildschirms - for(int i = 0; i < game_state.maze.rows; i++) - { - for(int j = 0; j < game_state.maze.cols; j++) - { - if(i == player_row && j == player_col) - { - cout << 'S'; - } - else - { - cout << game_state.maze.data[i][j]; - } - cout << " "; - } - cout << '\n'; - } -} - -// Funktion zur Umrechnung eines Kommandos zu einer neuen Position -// Vorbedingung: direction muss aus {w, s, a, d} kommen. -vector new_position_by_direction(vector player_position, char direction) -{ - const int row = player_position[0]; - const int col = player_position[1]; - - switch(direction) - { - case 'w': - return {row - 1, col}; - case 's': - return {row + 1, col}; - case 'a': - return {row, col - 1}; - case 'd': - return {row, col + 1}; - default: - assert(false, "new_position_by_direction: invalid direction, assumes direction is one of {w, s, a, d}."); - return {}; - } -} - -// Fuehrt Aktionen des Spieler-Feldes aus -// Vorbedingung: Wenn das Feld eine Tuer ist, muss mindestens ein Schluessel zur Verfuegung stehen -GameState process_tile_action(GameState game_state) -{ - const int row = game_state.player.position[0]; - const int col = game_state.player.position[1]; - - assert(game_state.maze.data[row][col] != 'T' || game_state.player.no_keys > 0, - "process_tile_action(...) assumes enough keys are there when approaching a door."); - - if(game_state.maze.data[row][col] == 'K') - { - ++game_state.player.no_keys; - game_state.maze.data[row][col] = '.'; - } - else if(game_state.maze.data[row][col] == 'T') - { - --game_state.player.no_keys; - game_state.maze.data[row][col] = '.'; - } - else if(game_state.maze.data[row][col] == 'A') - { - game_state.hit_ghost = true; - } - return game_state; -} - -// Gibt true zurueck gdw. die Position begehbar ist -bool position_is_walkable(vector position, GameState game_state) -{ - const int row = position[0]; - const int col = position[1]; - - if(row < 0 || col < 0) - { - return false; - } - if(row >= game_state.maze.rows || col >= game_state.maze.cols) - { - return false; - } - if(game_state.maze.data[row][col] == '#') - { - return false; - } - if(game_state.maze.data[row][col] == 'T' && game_state.player.no_keys == 0) - { - return false; - } - return true; -} - -// Funktion zur Bewegung der SpielerIn -GameState move_player(GameState game_state, char direction) -{ - vector potential_new_position = new_position_by_direction(game_state.player.position, direction); - - if(!position_is_walkable(potential_new_position, game_state)) - { - throw BadMovement {}; - } - - game_state.player.position = potential_new_position; - return process_tile_action(game_state); -} - -// Gibt eine kurze Hilfe aus -void display_help() -{ - cout << "Willkommen zum Labyrinth-Spiel!\n"; - cout << "Ziel des Spiels: Finden Sie den Weg vom Startpunkt (S) zum Ziel (Z).\n"; - cout << "Spielfeld-Erklaerung:\n"; - cout << "S - Startpunkt: Hier beginnt die SpielerIn.\n"; - cout << "Z - Ziel: Erreichen Sie diesen Punkt, um das Spiel zu gewinnen.\n"; - cout << "# - Wand: Diese Felder sind nicht begehbar.\n"; - cout << "K - Schluessel: Hier können Sie einen Schluessel aufsammeln, um eine Tuer zu oeffnen.\n"; - cout << "T - Tuer: Unbegehbares Feld, ausser, Sie haben einen Schluessel. Beim Durchschreiten wird ein Schluessel verbraucht.\n"; - cout << "A - Geist: Ein Geist im Labyrinth. Stehen die SpielerIn auf dem selben Feld, verlieren Sie das Spiel!\n"; - cout << ". - Leeres Feld: Diese Felder koennen betreten werden.\n"; - cout << "\nSteuerung:\n"; - cout << "w - Nach oben bewegen\n"; - cout << "a - Nach links bewegen\n"; - cout << "s - Nach unten bewegen\n"; - cout << "d - Nach rechts bewegen\n"; - cout << "q - Spiel beenden\n"; - cout << "Nach jeder Befehlseingabe muss die Eingabetaste (Enter) gedrueckt werden, um sich zu bewegen.\n"; - cout << "\nViel Erfolg im Labyrinth!\n"; -} - -// Reagiert auf das eingegebene Kommando und gibt an die jeweilige Funktion -// ab, die sich um genau dieses Kommando kuemmert. -GameState process_input(GameState game_state, char input) -{ - switch(input) - { - case 'w': - case 's': - case 'a': - case 'd': - return move_player(game_state, input); - case 'h': - case 'H': - display_help(); - break; - case 'q': - game_state.exit = true; - return game_state; - default: - throw UnknownInput{}; - } - return game_state; -} - -// Gibt true zurueck, wenn das Ziel erreicht wurde -bool reached_goal(GameState game_state) -{ - return game_state.maze.data[game_state.player.position[0]][game_state.player.position[1]] == 'Z'; -} - -// Gibt true zurueck gdw der Geist getroffen wurde -bool hit_ghost(GameState game_state) -{ - return game_state.hit_ghost; -} - -// Gibt true zurueck gdw. das Spiel zuende ist -bool is_end_condition(GameState game_state) -{ - return reached_goal(game_state) || hit_ghost(game_state) || game_state.exit; -} - -// Die Hauptschleife des Spiels -GameState game_loop(GameState game_state) -{ - char input; - while(cin && !is_end_condition(game_state)) - { - assert(game_state.player.no_keys >= 0, - "Player has a negative number of keys."); - - display_maze(game_state); - - cin >> input; - if(cin) - { - try - { - game_state = process_input(game_state, input); - } - catch(BadMovement&) - { - cout << "Bewegung nicht moeglich!\n"; - } - catch(UnknownInput&) - { - cout << "Diese Eingabe kenne ich nicht. Gib 'h' ein, um eine Hilfe zu erhalten.\n"; - } - } - } - - return game_state; -} - -// Liest ein integer von der Eingabe. -// Vorbedingung: cin ist in Ordnung -int read_int() -{ - int integer; - cin >> integer; - if(!cin) {throw BadMaze{};} - return integer; -} - -// Liest die Labyrinth-Daten ein. -// Vorbedingung: cin ist ok. -vector> read_maze_data(int rows, int cols) -{ - vector> maze_data(rows); - char ch; - for(int i = 0; i < rows * cols; ++i) - { - cin >> ch; - if(!cin) {throw BadMaze {};} - if(!(ch == '#' || ch == 'T' || ch == 'A' || ch == '.' || ch == 'K' || ch == 'Z')) - { - throw BadMaze {}; - } - - maze_data[i / cols].push_back(ch); - } - return maze_data; -} - -// Liest das Labyrinth von der Konsole nach der Formatdefinition aus der Aufgabe -Maze read_maze() -{ - int rows = read_int(); - int cols = read_int(); - - if(rows < 1 || cols < 1 || rows > 20 || cols > 20) - { - throw BadMaze {}; - } - - vector> labyrinth_data = read_maze_data(rows, cols); - - int player_row = read_int(); - int player_col = read_int(); - - if(player_row < 0 || player_row >= rows || player_col < 0 || player_col >= cols) - { - throw BadMaze {}; - } - - if(labyrinth_data[player_row][player_col] != '.') - { - throw BadMaze {}; - } - - return {rows, cols, labyrinth_data, {player_row, player_col}}; -} - -// Initialisiert das Labyrinth-Objekt -GameState initialize() -{ - Maze maze = read_maze(); - Player player{0, maze.player_start_position}; - - return GameState {maze, player, false, false}; -} - -int main() -{ - activateAssertions(); - try - { - GameState game_state = initialize(); - - game_state = game_loop(game_state); - - if(reached_goal(game_state)) - { - display_maze(game_state); - cout << "Ziel erreicht! Herzlichen Glueckwunsch!\n"; - } - else if(hit_ghost(game_state)) - { - cout << "Sie haben einen Geist getroffen! Game Over!\n"; - } - else - { - cout << "Schoenen Tag noch!\n"; - } - return 0; - } - catch(BadMaze&) - { - cout << "Fehler beim Einlesen des Labyrinths.\n"; - return 1; - } - catch(...) - { - cout << "Unbekannter Fehler!\n"; - return 1; - } -} diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp new file mode 100644 index 0000000..bc20090 --- /dev/null +++ b/src/Entities/Entity.cpp @@ -0,0 +1,12 @@ +#include "Entity.h" + +namespace game { + Entity::Entity(PositionVector starting_position, char display_character): pos(starting_position), display_character(display_character){} + + bool Entity::is_at_position(const PositionVector& position) const { + return this->pos.eq(position); + } + + + void move(const Maze& maze, const PositionVector& player_position); +} diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h new file mode 100644 index 0000000..d030223 --- /dev/null +++ b/src/Entities/Entity.h @@ -0,0 +1,20 @@ +#include "../Util/PositionVector.h" + +#ifndef ENTITY_H +#define ENTITY_H + +namespace game +{ + class Maze; + class Entity { + private: + PositionVector pos; + char display_character; + public: + Entity(PositionVector starting_position, char display_character); + void move(const Maze& maze, const PositionVector& player_position); + bool is_at_position(const PositionVector& position) const; + }; +} // game + +#endif //ENTITY_H diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp new file mode 100644 index 0000000..9586aa2 --- /dev/null +++ b/src/Entities/Player.cpp @@ -0,0 +1,113 @@ +#include "Player.h" +#include "../Environment/Maze.h" +#include "../Exceptions/MovementNotPossible.h" +#include "../Util/GameState.h" +#include "../Environment/Game.h" + +using game::GameState; +using game::Game; +using game_exceptions::MovementNotPossible; + +namespace game +{ + Player::Player(const int target_x, const int target_y): pos(target_x, target_y), keys_in_inventory(0) + { + } + + Player::Player(const PositionVector pos) : pos(pos), keys_in_inventory(0) + { + } + + PositionVector Player::get_pos() const + { + return this->pos; + } + + void Player::update_position(const PositionVector& target) + { + this->pos = target; + } + + Maze Player::handle_move(Maze& maze, const PositionVector& move_vector) + { + const PositionVector target_position = PositionVector(this->get_pos().x + move_vector.x, + this->get_pos().y + move_vector.y); + + if (maze.is_pos_free(target_position, this->has_key_available())) + { + this->update_position(target_position); + } + else + throw MovementNotPossible("Bewegung nicht moeglich!"); + return maze; + } + + Maze Player::handle_user_input(Maze& maze, const char& input) { + PositionVector move_vector = {0, 0}; + + switch (input) { + case 'w': + move_vector = {-1, 0}; + break; + case 's': + move_vector = {1, 0}; + break; + case 'a': + move_vector = {0, -1}; + break; + case 'd': + move_vector = {0, 1}; + break; + } + + this->handle_move(maze, move_vector); + + return maze; + } + + void Player::handle_keys(Maze& maze) { + switch (maze.get_field(this->pos)) + { + case 'K': + ++this->keys_in_inventory; + maze.update_field(this->get_pos(), '.'); + break; + case 'T': + --this->keys_in_inventory; + maze.update_field(this->get_pos(), '.'); + break; + default: ; + } + } + + GameState Player::handle_collisions(const GameState& prev_game_state, const Maze& maze) { + char field_at_pos = maze.get_field(this->get_pos()); + // Game state sanity check + if (prev_game_state != GameState::RUNNING) + return prev_game_state; + + if (field_at_pos == '.') + return GameState::RUNNING; + switch (field_at_pos) { + case 'A': + case 'B': + case 'C': + return GameState::HIT_BY_GHOST; + break; + default: + break; + } + return prev_game_state; + } + + + void Player::tick(const GameState& prev_game_state, Maze& maze) { + this->handle_keys(maze); + this->handle_collisions(prev_game_state, maze); + } + + bool Player::has_key_available() const + { + return this->keys_in_inventory > 0; + } +} // game diff --git a/src/Entities/Player.h b/src/Entities/Player.h new file mode 100644 index 0000000..97e134a --- /dev/null +++ b/src/Entities/Player.h @@ -0,0 +1,68 @@ +#include "../Util/PositionVector.h" +#include "../Util/GameState.h" + +#ifndef PLAYER_H +#define PLAYER_H + +using game::GameState; + +namespace game +{ + class Maze; + + /// Ein Spieler. + /// Besitzt einen veränderbaren Positionsvektor + class Player + { + private: + /// Die Position des Spielers + PositionVector pos; + int keys_in_inventory; + + /// Bewege den Splieler um den Bewegungsvektor, insofern die Zielposition begehbar ist + /// @param maze Das Maze + /// @param move_vector Die gewollte Bewegung + /// @return Die neue Position des Spielers + Maze handle_move(Maze& maze, const PositionVector& move_vector); + + /// Gebe oder Nehme dem Spieler Schlüssel + /// Updated auch das Maze (Reference!) + /// @param maze Das Maze + void handle_keys(Maze& maze); + + /// Kontrolliere, ob der Spieler gerade in einem Geist steht + /// @param maze Das Maze + GameState handle_collisions(const GameState& prev_game_state, const Maze& maze); + + public: + /// Ein Spieler. + /// Besitzt einen veränderbaren Positionsvektor + /// @param target_x Die Startposition des Spielers (X-Koordinate) + /// @param target_y Die Startposition des Spielers (Y-Koordinate) + Player(int target_x, int target_y); + + /// Ein Spieler + /// @param pos Die Startposition des Spielers + explicit Player(PositionVector pos); + + /// Kriege die Position des Spielers + /// @return Die Position des Spielers + PositionVector get_pos() const; + + /// Aktuallisiere die Position des Spielers ohne weitere Checks + /// @param target Das ziel + void update_position(const PositionVector& target); + + /// + Maze handle_user_input(Maze& maze, const char& input); + + /// + void tick(const GameState& prev_game_state, Maze& maze); + + /// Check, if a player has at least one key + /// @return If the player as an available key + bool has_key_available() const; + }; +} // maze + +#endif //PLAYER_H diff --git a/src/Environment/Game.cpp b/src/Environment/Game.cpp new file mode 100644 index 0000000..e484e4f --- /dev/null +++ b/src/Environment/Game.cpp @@ -0,0 +1,88 @@ +#include "Game.h" +#include "../Entities/Player.h" +#include "Maze.h" +#include "../Exceptions/MovementNotPossible.h" +#include "../Exceptions/UnkownAction.h" +#include "../Util/PositionVector.h" + +using game::Player; +using game::Maze; +using game_exceptions::UnkownAction; +using game_exceptions::MovementNotPossible; + +namespace game +{ + Game::Game(Maze& maze): maze(maze), player(0, 0), infomode_enabled(false), state(GameState::RUNNING) + { + PositionVector player_start_position = this->maze.get_player_start_position(); + + this->player = Player(player_start_position); + } + + GameState Game::get_state() { + return this->state; + } + + void Game::handle_user_input(const char& input) + { + switch (input) + { + case 'w': + case 'a': + case 's': + case 'd': + break; + case 'q': + this->state = GameState::QUITTING; + 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 UnkownAction("Diese Eingabe kenne ich nicht. Gib 'h' ein, um eine Hilfe zu erhalten."); + } + } + + bool Game::should_end_game() { + return this->state != GameState::RUNNING; + } + + void Game::run_game() + { + char game_input; + + // Hauptschleife + while (true) + { + this->maze.render(this->player, this->enemies); + + if (this->maze.is_player_at_goal(this->player)) + { + cout << "Ziel erreicht! Herzlichen Glueckwunsch!\n"; + break; + } + + cin >> game_input; + + try + { + this->handle_user_input(game_input); + this->player.handle_user_input(this->maze, game_input); + } catch (UnkownAction& err) + { + cout << err.what() << "\n"; + } catch (MovementNotPossible& err) + { + cout << err.what() << "\n"; + } + + this->player.tick(this->state, this->maze); + + if (this->should_end_game()) + return; + } + } +} // game diff --git a/src/Environment/Game.h b/src/Environment/Game.h new file mode 100644 index 0000000..e53c965 --- /dev/null +++ b/src/Environment/Game.h @@ -0,0 +1,46 @@ +#include "Maze.h" +#include "../Entities/Player.h" +#include "../Util/GameState.h" + +#ifndef GAME_H +#define GAME_H + +namespace game +{ + /// Eine Instanz des Spiels + class Game + { + private: + /// Das Labyrinth + Maze maze; + /// Der Spieler + Player player; + /// Die Gegner des Spieles + vector enemies; + /// Der Status des Spiels + GameState state; + /// + bool infomode_enabled; + + public: + explicit Game(Maze& maze); + + /// Bearbeite die Eingabe des Spielers + /// @param input Die Eingabe des Nutzers + /// @return Der Bewegungsvektor, um den sich den Spieler bewegen möchte + /// @throws UnkownAction Wenn die Eingabe des Spielers unbekannt ist + void handle_user_input(const char& input); + + /// Kontrolliere, ob das Spiel beendet werden sollte + bool should_end_game(); + + /// Starte das Spiel + void run_game(); + + /// Kriege den aktuellen Status des Spiels + /// @returns Den aktuellen Status + GameState get_state(); + }; +} // game + +#endif //GAME_H diff --git a/src/Environment/Maze.cpp b/src/Environment/Maze.cpp new file mode 100644 index 0000000..175aa15 --- /dev/null +++ b/src/Environment/Maze.cpp @@ -0,0 +1,72 @@ +#include "Maze.h" +#include "../Entities/Player.h" +#include "../Exceptions/MalformedMaze.h" +#include "../Entities/Entity.h" + +using game_exceptions::MalformedMaze; + +namespace game +{ + Maze::Maze(const vector> play_field, const vector player_start_position, const vector enemies): + field(play_field), + player_start_position(PositionVector{player_start_position[1], player_start_position[0]}) + { + if (!this->is_pos_free(this->player_start_position, false)) + throw MalformedMaze("Player oob"); + } + + bool Maze::was_player_killed_by_ghost(const Player& player) const + { + return this->field[player.get_pos().y][player.get_pos().x] == 'A'; + } + + bool Maze::is_player_at_goal(const 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; + if (field[pos.y][pos.x] == '#') + return false; + if (field[pos.y][pos.x] == 'T') + return player_has_key; + return true; + } + + void Maze::render(const Player& player, const vector entities) const + { + for (int y = 0; y < field.size(); ++y) + { + for (int x = 0; x < field[y].size(); ++x) + { + if (y == player.get_pos().y && x == player.get_pos().x) + cout << "S"; + else { + for(Entity e : entities) { + } + cout << field[y][x]; + } + cout << " "; + } + cout << "\n"; + } + } + + char Maze::get_field(const PositionVector& pos) const + { + return this->field[pos.y][pos.x]; + } + + void Maze::update_field(const PositionVector& pos, const char& target) + { + this->field[pos.y][pos.x] = target; + } + + PositionVector Maze::get_player_start_position() const + { + return this->player_start_position; + } +} // game diff --git a/src/Environment/Maze.h b/src/Environment/Maze.h new file mode 100644 index 0000000..0770069 --- /dev/null +++ b/src/Environment/Maze.h @@ -0,0 +1,63 @@ +#include "../std_lib_inc.h" +#include "../Util/PositionVector.h" + +#ifndef MAZE_H +#define MAZE_H + +namespace game +{ + class Player; + class Entity; + + /// Ein Labyrinth. + /// Besitzt ein Feld + class Maze + { + // class -> members private by default + private: + /// Das Spielfeld + vector> field; + /// Die Startposition des Spielers + PositionVector player_start_position; + + public: + /// Das Spielfeld + Maze(const vector> play_field, const vector player_start_position, const vector enemies); + + /// Kontrolliere, ob der Spieler stirbt + /// @param player Der Spieler + /// @return Ob der Spieler tot ist + bool was_player_killed_by_ghost(const Player& player) const; + + /// Kontrolliere, ob der Spieler am Ziel ist + /// @param player Der Spieler + /// @return Ob der Spieler am Ziel ist + bool is_player_at_goal(const 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(const PositionVector& pos, const bool& player_has_key) const; + + /// Zeige das Spielfeld in der Konsole an + /// @param player Der Spieler + void render(const Player& player, const vector entities) const; + + /// Kriege den Wert einer Position + /// @param pos Die gewollte Position + /// @return Der Wert der Position + char get_field(const PositionVector& pos) const; + + /// Ersetze den Wert von einer Position + /// @param pos Die Position die ersetzt werden soll + /// @param target Der Wert, auf den die Position gesetzt werden soll + void update_field(const PositionVector& pos, const char& target); + + /// Kriege die Startposition des Spielers + /// @return Die Startposition des Spielers + PositionVector get_player_start_position() const; + }; +} // game + +#endif //MAZE_H diff --git a/src/Exceptions/MalformedMaze.h b/src/Exceptions/MalformedMaze.h new file mode 100644 index 0000000..c31c453 --- /dev/null +++ b/src/Exceptions/MalformedMaze.h @@ -0,0 +1,23 @@ +#include "../std_lib_inc.h" +#ifndef MALFORMEDMAZE_H +#define MALFORMEDMAZE_H + +namespace game_exceptions +{ + class MalformedMaze + { + string why; + + public: + MalformedMaze(string why): why(why) + { + } + + string what() + { + return this->why; + } + }; +} // game_exceptions + +#endif //MALFORMEDMAZE_H diff --git a/src/Exceptions/MovementNotPossible.h b/src/Exceptions/MovementNotPossible.h new file mode 100644 index 0000000..2eff7ff --- /dev/null +++ b/src/Exceptions/MovementNotPossible.h @@ -0,0 +1,23 @@ +#include "../std_lib_inc.h" +#ifndef MOVEMENTNOTPOSSIBLE_H +#define MOVEMENTNOTPOSSIBLE_H + +namespace game_exceptions +{ + class MovementNotPossible + { + string why; + + public: + MovementNotPossible(string why): why(why) + { + } + + string what() + { + return this->why; + } + }; +} // game_exceptions + +#endif //MOVEMENTNOTPOSSIBLE_H diff --git a/src/Exceptions/UnkownAction.h b/src/Exceptions/UnkownAction.h new file mode 100644 index 0000000..216f9e7 --- /dev/null +++ b/src/Exceptions/UnkownAction.h @@ -0,0 +1,23 @@ +#include "../std_lib_inc.h" +#ifndef UNKOWNACTION_H +#define UNKOWNACTION_H + +namespace game_exceptions +{ + class UnkownAction + { + string why; + + public: + UnkownAction(string why): why(why) + { + } + + string what() + { + return this->why; + } + }; +} // game_exceptions + +#endif //UNKOWNACTION_H diff --git a/src/Util/GameState.h b/src/Util/GameState.h new file mode 100644 index 0000000..496b87a --- /dev/null +++ b/src/Util/GameState.h @@ -0,0 +1,15 @@ +#ifndef GAMESTATE_H +#define GAMESTATE_H + +namespace game +{ + /// Beschreibt den aktuellen GameState + enum class GameState + { + RUNNING, + HIT_BY_GHOST, + QUITTING, + }; +} // game + +#endif //GAMESTATE_H diff --git a/src/Util/MazeParser.cpp b/src/Util/MazeParser.cpp new file mode 100644 index 0000000..5c6785d --- /dev/null +++ b/src/Util/MazeParser.cpp @@ -0,0 +1,69 @@ +#include "MazeParser.h" +#include "../Environment/Maze.h" +#include "../Exceptions/MalformedMaze.h" + +using game_exceptions::MalformedMaze; + +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 MalformedMaze("Cin failed while reading numbers!"); + + if (input > MAX_MAZE_SIZE) + throw MalformedMaze("This maze is too big"); + list.push_back(input); + } + + return list; + } + + bool MazeParser::validate_maze_element(const char& target) + { + for (const char c : valid_maze_elements) + if (c == target) + return true; + return false; + } + + Maze MazeParser::request_maze_from_user() + { + vector maze_size = request_numbers_from_user(2); + + char input; + vector> field; + + for (int y = 0; y < maze_size[0]; ++y) + { + vector row; + for (int x = 0; x < maze_size[1]; ++x) + { + cin >> input; + if (!cin) + throw MalformedMaze("Cin failed while reading chars!"); + + // I don't think that this is needed. I doubt that the test check for this one ~Eric + //if (input == 'q') + // throw ExitGame("Schoenen Tag noch!"); + + if (!validate_maze_element(input)) + throw MalformedMaze("The given input is not a valid element of a maze!"); + + row.push_back(input); + } + + field.push_back(row); + } + + vector player_start_pos = request_numbers_from_user(2); + + return Maze(field, player_start_pos); + } // Beispieleingabe: `4 3 #.# #.K #T# #Z# 0 1` +} // game diff --git a/src/Util/MazeParser.h b/src/Util/MazeParser.h new file mode 100644 index 0000000..b0ccdd3 --- /dev/null +++ b/src/Util/MazeParser.h @@ -0,0 +1,46 @@ +#include "../std_lib_inc.h" + +#ifndef MAZEPARSER_H +#define MAZEPARSER_H + +/* Legende + * S - Spieler + * Z - Ziel + * . - Leerer Raum (begehbar) + * # - Wand + * A - Animaltronic + * K - Schlüssel + * T - Tür + * B - Bowie + * C - Connellys + */ +namespace game +{ + /// Erlaubte Zeichen in einem Labyrinth + /// Ist eine Konstante, darf also in global scope + static const vector valid_maze_elements = {'Z', '.', '#', 'A', 'K', 'T', 'B', 'C'}; + static constexpr int MAX_MAZE_SIZE = 20; + + class Maze; + 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(const int& amount_of_numbers); + + /// Kontrolliere, ob ein Zeichen im Labyrinth vorkommen darf + /// @param target Das Zeichen, welches Kontrolliert werden soll + /// @return Ob das gegebene Zeichen in einem Labyrinth vorkommen darf + static bool validate_maze_element(const char& target); + + 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/src/Util/PositionVector.cpp b/src/Util/PositionVector.cpp new file mode 100644 index 0000000..25cfa9c --- /dev/null +++ b/src/Util/PositionVector.cpp @@ -0,0 +1,20 @@ +#include "PositionVector.h" + +namespace game +{ + PositionVector::PositionVector(const int x, const int y) + { + this->x = x; + this->y = y; + } + + void PositionVector::update(const int& x, const int& y) + { + this->x = x; + this->y = y; + } + + bool PositionVector::eq(const PositionVector& position) const { + return this->x == position.x && this->y == position.y; + } +} // game diff --git a/src/Util/PositionVector.h b/src/Util/PositionVector.h new file mode 100644 index 0000000..5057ff5 --- /dev/null +++ b/src/Util/PositionVector.h @@ -0,0 +1,30 @@ +#ifndef POSITION_H +#define POSITION_H + +namespace game +{ + /// Ein Vector aus zwei zahlen. + /// Kann eine Position oder eine Differenz zwischen zwei Positionen darstellen. + struct PositionVector + { + // struct -> members public by default + // Die beiden Variablen des Vectors + int x; + int y; + + /// Ein Vector aus zwei zahlen. + /// Kann eine Position oder eine Differenz zwischen zwei Positionen darstellen. + /// @param x Die 'X'-Koordinate + /// @param y Die 'Y'-Koordinate + PositionVector(int x, int y); + + /// Aktualisiere die Werte des Vectors + /// @param x Die neue 'X'-Koordinate + /// @param y Die neue 'Y'-Koordinate + void update(const int& x, const int& y); + + bool eq(const PositionVector& position)const ; + }; +} // game + +#endif //POSITION_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..fb584fc --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,55 @@ +#include "Environment/Maze.h" +#include "Util/MazeParser.h" +#include "Environment/Game.h" +#include "Exceptions/MalformedMaze.h" +#include "Exceptions/MovementNotPossible.h" +#include "Exceptions/UnkownAction.h" +#include "Util/GameState.h" + +using game::Maze; +using game::MazeParser; +using game::Game; +using game::GameState; + +using game_exceptions::MalformedMaze; + +// Ein Programm, welches dir erlaubt ein Labyrinth zu erkunden mit 'w', 'a', 's', und 'd'. + +void print_exit_message_based_on_state(const GameState& state); + +int main() +{ + GameState state = GameState::RUNNING; + try + { + Maze maze = MazeParser::request_maze_from_user(); + + Game game = Game(maze); + + game.run_game(); + + state = game.get_state(); + } + catch (MalformedMaze& _) + { + cout << "Fehler beim Einlesen des Labyrinths.\n"; + return 0; + } + + print_exit_message_based_on_state(state); + + return 0; +} + + +void print_exit_message_based_on_state(const GameState& state) { + switch (state){ + case GameState::QUITTING: + cout << "Schoenen Tag noch!" << "\n"; + break; + case GameState::HIT_BY_GHOST: + cout << "Sie haben einen Geist getroffen! Game Over!\n"; + default: + break; + } +} diff --git a/std_lib_inc.h b/src/std_lib_inc.h similarity index 95% rename from std_lib_inc.h rename to src/std_lib_inc.h index f0f1364..6c9ff77 100644 --- a/std_lib_inc.h +++ b/src/std_lib_inc.h @@ -79,6 +79,15 @@ namespace std { using size_type = typename std::vector::size_type; +/* #ifdef _MSC_VER + // microsoft doesn't yet support C++11 inheriting constructors + Vector() { } + explicit Vector(size_type n) :std::vector(n) {} + Vector(size_type n, const T& v) :std::vector(n, v) {} + template + Vector(I first, I last) : std::vector(first, last) {} + Vector(initializer_list list) : std::vector(list) {} +*/ using std::vector::vector; // inheriting constructor T &operator[](unsigned int i) // rather than return at(i);