diff --git a/Entities/Entity.cpp b/Entities/Entity.cpp deleted file mode 100644 index f9cb67a..0000000 --- a/Entities/Entity.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "Entity.h" -#include "../Environment/Maze.h" - -namespace game { - Entity::Entity(const Vector2d starting_position, const char display_character): pos(starting_position), display_character(display_character), move_left(true){} - - bool Entity::is_at_position(const Vector2d& position) const { - return this->pos.eq(position); - } - - void Entity::tick(const Maze& maze, const Vector2d& player_position){ - switch (this->display_character) { - case 'A': - // No thoughts, head empty :P - return; - case 'B': - this->handle_bowie(maze); - return; - case 'C': - this->handle_connelly(maze, player_position); - return; - default: - cout << "ERR: THIS IS NOT A GHOST!"; - /// This case will never happen - return; - } - } - - void Entity::handle_bowie(const Maze& maze) { - Vector2d target_position = this->pos; - if (this->move_left) - target_position.change_x(-1); - else - target_position.change_x(1); - if (maze.is_pos_free(target_position, false)) - this->pos = target_position; - else - { - this->move_left = !this->move_left; - if (this->move_left) - target_position.change_x(-1); - else - target_position.change_x(1); - if (maze.is_pos_free(target_position, false)) - this->pos = target_position; - } - } - - bool Entity::connelly_move_up(const Maze& maze) { - Vector2d top = this->pos.get_new_updated(0, 1); - if (maze.is_pos_free(top, false)) { - this->pos = top; - return true; - } - return false; - } - - bool Entity::connelly_move_down(const Maze& maze) { - Vector2d bottom = this->pos.get_new_updated(0, -1); - if (maze.is_pos_free(bottom, false)) { - this-> pos = bottom; - return true; - } - return false; - } - - bool Entity::connelly_move_left(const Maze& maze) { - Vector2d left = this->pos.get_new_updated(-1, 0); - if (maze.is_pos_free(left, false)) { - this->pos = left; - return true; - } - return false; - } - - bool Entity::connelly_move_right(const Maze& maze) { - Vector2d right = this->pos.get_new_updated(1, 0); - if (maze.is_pos_free(right, false)) { - this->pos = right; - return true; - } - return false; - } - - void Entity::handle_connelly(const Maze& maze, const Vector2d& player_position) { - Vector2d diff = maze.get_delta_vector(player_position, this->pos); - Vector2d normalized = diff.normalize(); - - if ((normalized.y == normalized.x || normalized.y > normalized.x) && normalized.y != 0) { - if (diff.y > 0 ) { - if (this->connelly_move_up(maze)) - return; - } else { - if (this->connelly_move_down(maze)) - return; - } - } - if (normalized.x != 0) { - if (diff.x > 0){ - if (this->connelly_move_right(maze)) - return; - } - else { - bool _ = this->connelly_move_left(maze); - } - } - } - - char Entity::get_display_character() const { - return this->display_character; - } -} diff --git a/Entities/Entity.h b/Entities/Entity.h deleted file mode 100644 index 157fdcd..0000000 --- a/Entities/Entity.h +++ /dev/null @@ -1,66 +0,0 @@ -#include "../Util/Vector2d.h" - -#ifndef ENTITY_H -#define ENTITY_H - -namespace game -{ - class Maze; - /// Eine Entität, z.B. ein Geist - class Entity { - private: - /// Die aktuelle Position des Geistes - Vector2d pos; - /// Wie der Geist dargestellt werden sollte - char display_character; - /// Ist wahr, wenn der Bowie nach Links läuft, falsch, wenn er nach Rechts läuft. - bool move_left; - - /// Behandle Bowie Bewegungen - /// @param maze Das Labyrinth - void handle_bowie(const Maze& maze); - - /// Behandle Connelly Bewegungen - /// @param maze Das Labyrinth - /// @param player_position Die Position des Spielers - void handle_connelly(const Maze& maze, const Vector2d& player_position); - - /// Versuche den Connelly nach oben zu bewegen - /// @param maze Das Labyrinth - /// @returns Ob die Bewegung geglückt hat - bool connelly_move_up(const Maze& maze); - - /// Versuche den Connelly nach unten zu bewegen - /// @param maze Das Labyrinth - /// @returns Ob die Bewegung geglückt hat - bool connelly_move_down(const Maze& maze); - - /// Versuche den Connelly nach links zu bewegen - /// @param maze Das Labyrinth - /// @returns Ob die Bewegung geglückt hat - bool connelly_move_left(const Maze& maze); - - /// Versuche den Connelly nach Rechts zu bewegen - /// @param maze Das Labyrinth - /// @returns Ob die Bewegung geglückt hat - bool connelly_move_right(const Maze& maze); - public: - Entity(Vector2d starting_position, char display_character); - - /// Halte den Entität bzw. den Geist up to date, bewege ihn - /// @param maze Das Labyrinth - /// @param player_position Die Position des Spielers - void tick(const Maze& maze, const Vector2d& player_position); - - /// Kontrolliere, ob sich das Entity auf einer Position befindet - /// @param position Die Position - /// @returns Ob das sich das Entity auf der Position befindet - bool is_at_position(const Vector2d& position) const; - - /// Besorge dir das Zeichen, was auf dem Labyrinth angezeigt werden soll - /// @returns Das Zeichen - char get_display_character() const; - }; -} // game - -#endif //ENTITY_H diff --git a/Entities/Player.cpp b/Entities/Player.cpp deleted file mode 100644 index c03b22a..0000000 --- a/Entities/Player.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#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 Vector2d pos) : pos(pos), keys_in_inventory(0) - { - } - - Vector2d Player::get_pos() const - { - return this->pos; - } - - void Player::update_position(const Vector2d& target) - { - this->pos = target; - } - - Maze Player::handle_move(Maze& maze, const Vector2d& move_vector) - { - const Vector2d target_position = Vector2d(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) { - Vector2d move_vector = {0, 0}; - - switch (input) { - case 'w': - move_vector = {0, -1}; - break; - case 's': - move_vector = {0, 1}; - break; - case 'a': - move_vector = {-1, 0}; - break; - case 'd': - move_vector = {1, 0}; - 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 Game& game, const Maze& maze) const { - char field_at_pos = maze.get_field(this->get_pos()); - // Game state sanity check - if (game.get_state() != GameState::RUNNING) - return game.get_state(); - - if (field_at_pos == '.'){ - if (game.is_enemy_at_pos(this->get_pos())) - return GameState::HIT_BY_GHOST; - return GameState::RUNNING; - } - // You are not supposed to be here! - return game.get_state(); - } - - - GameState Player::tick(const Game& game, Maze& maze) { - this->handle_keys(maze); - return this->handle_collisions(game, maze); - } - - bool Player::has_key_available() const - { - return this->keys_in_inventory > 0; - } -} // game diff --git a/Entities/Player.h b/Entities/Player.h deleted file mode 100644 index 78f1fb9..0000000 --- a/Entities/Player.h +++ /dev/null @@ -1,74 +0,0 @@ -#include "../Util/Vector2d.h" -#include "../Util/GameState.h" - -#ifndef PLAYER_H -#define PLAYER_H - -using game::GameState; - -namespace game -{ - class Maze; - class Game; - - /// Ein Spieler. - /// Besitzt einen veränderbaren Positionsvektor - class Player - { - private: - /// Die Position des Spielers - Vector2d 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 Vector2d& 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 Game& game, const Maze& maze) const; - - 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(Vector2d pos); - - /// Kriege die Position des Spielers - /// @return Die Position des Spielers - Vector2d get_pos() const; - - /// Aktuallisiere die Position des Spielers ohne weitere Checks - /// @param target Das ziel - void update_position(const Vector2d& target); - - /// Behandle die eingabe des Nutzers für den Spieler - /// @param maze Das Labyrinth - /// @param input Die Eingabe des Nutzers - Maze handle_user_input(Maze& maze, const char& input); - - /// Halte den Spieler aktuell - /// @param game Das Spiel - /// @param maze Das Labyrinth - /// @returns Der Status des Spiels - GameState tick(const Game& game, 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/Environment/Game.cpp b/Environment/Game.cpp deleted file mode 100644 index bb61e71..0000000 --- a/Environment/Game.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "Game.h" -#include "Maze.h" -#include "../Exceptions/MovementNotPossible.h" -#include "../Exceptions/UnkownAction.h" -#include "../Util/Vector2d.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), enemies({}), state(GameState::RUNNING), infomode_enabled(false) - { - Vector2d player_start_position = this->maze.get_player_start_position(); - - this->player = Player(player_start_position); - this->enemies = maze.get_entities(); - } - - GameState Game::get_state() const { - return this->state; - } - - void Game::handle_user_input(const char& input) - { - switch (input) - { - case 'w': - case 'a': - case 's': - case 'd': - break; - case 'i': - this->infomode_enabled = !this->infomode_enabled; - break; - case 'q': - this->state = GameState::QUITTING; - break; - 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::is_enemy_at_pos(const Vector2d& position) const { - for (Entity e : this->enemies) - if (e.is_at_position(position)) - return true; - return false; - } - - bool Game::should_end_game() const - { - return this->state != GameState::RUNNING; - } - - void Game::run_game() - { - char game_input; - - // Hauptschleife - while (true) - { - this->maze.render(this->player, this->enemies, this->infomode_enabled); - - if (this->maze.is_player_at_goal(this->player)) - { - cout << "Ziel erreicht! Herzlichen Glueckwunsch!\n"; - break; - } - - cin >> game_input; - - if (!cin) - this->state = GameState::QUITTING; - - 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->state = this->player.tick(*this, this->maze); - - for (Entity& e : this->enemies) - e.tick(this->maze, this->player.get_pos()); - - this->state = this->player.tick(*this, this->maze); - - if (this->should_end_game()) - return; - } - } -} // game diff --git a/Environment/Game.h b/Environment/Game.h deleted file mode 100644 index b4cc407..0000000 --- a/Environment/Game.h +++ /dev/null @@ -1,52 +0,0 @@ -#include "Maze.h" -#include "../Entities/Player.h" -#include "../Util/GameState.h" -#include "../Entities/Entity.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() const; - - /// Starte das Spiel - void run_game(); - - /// Kriege den aktuellen Status des Spiels - /// @returns Den aktuellen Status - GameState get_state() const; - - /// Krontroliere ob ein Geist sich an einer Position befindet - /// @param position Die Position - /// @returns Ob sich dort ein Geist aufhält - bool is_enemy_at_pos(const Vector2d& position) const ; - }; -} // game - -#endif //GAME_H diff --git a/Environment/Maze.cpp b/Environment/Maze.cpp deleted file mode 100644 index eac97e9..0000000 --- a/Environment/Maze.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "Maze.h" -#include "../Entities/Player.h" -#include "../Exceptions/MalformedMaze.h" -#include "../Entities/Entity.h" -#include "../Util/MathUtil.h" - -using game_exceptions::MalformedMaze; - -namespace game -{ - class MathUtil; - - Maze::Maze(const vector>& play_field, const vector& player_start_position, const vector& enemies): - field(play_field), - player_start_position(Vector2d{player_start_position[1], player_start_position[0]}), - enemies(enemies) - { - 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 Vector2d& 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 bool& infomode_enabled) - { - 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 { - bool an_enemy_is_at_this_position = false; - for (Entity e : entities) - if (e.is_at_position({x, y})) { - an_enemy_is_at_this_position = true; - cout << e.get_display_character(); - } - if (!an_enemy_is_at_this_position) - cout << field[y][x]; - } - cout << " "; - } - if (y == 0 && infomode_enabled) - { - const int steps = this->calculate_steps_until_win(player.get_pos(), 5); - if (steps < 999) - cout << steps << " Schritte bis zum Ziel"; - } - cout << "\n"; - } - } - - char Maze::get_field(const Vector2d& pos) const - { - return this->field[pos.y][pos.x]; - } - - void Maze::update_field(const Vector2d& pos, const char& target) - { - this->field[pos.y][pos.x] = target; - } - - Vector2d Maze::get_player_start_position() const - { - return this->player_start_position; - } - - Vector2d Maze::get_delta_vector(const Vector2d& pos1, const Vector2d& pos2) const { - int x_diff = pos1.x - pos2.x; - int y_diff = pos1.y - pos2.y; - - return {x_diff, y_diff}; - } - - int Maze::calculate_steps_until_win(Vector2d position, const int& steps) { - if (!this->is_pos_free(position, false)) - return 999; - if (this->get_field(position) == 'Z') - return 0; - if (steps <= 0) - return 999; - - vector i; - i.push_back(this->calculate_steps_until_win(position.get_new_updated(0, -1), steps - 1)); - i.push_back(this->calculate_steps_until_win(position.get_new_updated(0, 1), steps - 1)); - i.push_back(this->calculate_steps_until_win(position.get_new_updated(1, 0), steps - 1)); - i.push_back(this->calculate_steps_until_win(position.get_new_updated(-1, 0), steps - 1)); - - return MathUtil::get_min(i) + 1; - } - - vector Maze::get_entities() { - return this->enemies; - } -} // game diff --git a/Environment/Maze.h b/Environment/Maze.h deleted file mode 100644 index 81e551d..0000000 --- a/Environment/Maze.h +++ /dev/null @@ -1,80 +0,0 @@ -#include "../std_lib_inc.h" -#include "../Util/Vector2d.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 - Vector2d player_start_position; - /// Eine Liste an Gegnern - vector enemies; - - 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 Vector2d& pos, const bool& player_has_key) const; - - /// Zeige das Spielfeld in der Konsole an - /// @param player Der Spieler - /// @param entities Die Entities auf dem Spielfeld - /// @param infomode_enabled Ob der Infomode aktiv ist - void render(const Player& player, const vector& entities, const bool& infomode_enabled); - - /// Kriege den Wert einer Position - /// @param pos Die gewollte Position - /// @return Der Wert der Position - char get_field(const Vector2d& 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 Vector2d& pos, const char& target); - - /// Kriege die Startposition des Spielers - /// @return Die Startposition des Spielers - Vector2d get_player_start_position() const; - - /// Berrechne den Abstand zwischen zwei Vektoren - /// @return Der Abstand als Differenzvektor - Vector2d get_delta_vector(const Vector2d& pos1, const Vector2d& pos2) const; - - /// Berechne wie viele Schritte benötigt werden, um das Labyrinth zu schaffen - /// @warning Steps nicht zu groß setzen! Diese Funktion ist 4-fach rekursiv! - /// @param position Die Startposition - /// @param steps Wie viele Schritte maximal gegangen werden sollten - /// @returns Wie viele Schritte benötigt werden - int calculate_steps_until_win(Vector2d position, const int& steps); - - /// Kriege alle eingelesenen Entities - vector get_entities(); - }; -} // game - -#endif //MAZE_H diff --git a/Exceptions/MalformedMaze.h b/Exceptions/MalformedMaze.h deleted file mode 100644 index c31c453..0000000 --- a/Exceptions/MalformedMaze.h +++ /dev/null @@ -1,23 +0,0 @@ -#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/Exceptions/MovementNotPossible.h b/Exceptions/MovementNotPossible.h deleted file mode 100644 index 2eff7ff..0000000 --- a/Exceptions/MovementNotPossible.h +++ /dev/null @@ -1,23 +0,0 @@ -#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/Exceptions/UnkownAction.h b/Exceptions/UnkownAction.h deleted file mode 100644 index 216f9e7..0000000 --- a/Exceptions/UnkownAction.h +++ /dev/null @@ -1,23 +0,0 @@ -#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/Util/GameState.h b/Util/GameState.h deleted file mode 100644 index 496b87a..0000000 --- a/Util/GameState.h +++ /dev/null @@ -1,15 +0,0 @@ -#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/Util/MathUtil.cpp b/Util/MathUtil.cpp deleted file mode 100644 index b9c6241..0000000 --- a/Util/MathUtil.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "MathUtil.h" - -namespace game { - int MathUtil::get_min(const vector& numbers) { - int i = numbers[0]; - for (const int j : numbers) - if (j < i) - i = j; - return i; - } -} // game diff --git a/Util/MathUtil.h b/Util/MathUtil.h deleted file mode 100644 index 340a660..0000000 --- a/Util/MathUtil.h +++ /dev/null @@ -1,16 +0,0 @@ -#include "../std_lib_inc.h" -#ifndef UTIL_H -#define UTIL_H - -namespace game -{ - class MathUtil{ - public: - /// Gebe die minimale Nummer aus einer Liste zurück - /// @param numbers Eine liste an nummern - /// @returns Die kleinste Nummer - static int get_min(const vector& numbers); - }; -} // game - -#endif //UTIL_H diff --git a/Util/MazeParser.cpp b/Util/MazeParser.cpp deleted file mode 100644 index 9fb1876..0000000 --- a/Util/MazeParser.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "MazeParser.h" -#include "../Environment/Maze.h" -#include "../Exceptions/MalformedMaze.h" -#include "../Entities/Entity.h" - -using game_exceptions::MalformedMaze; -using game::Entity; - -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; - } - - bool MazeParser::is_valid_enemy(const char& target) { - for (const char c : valid_enemies) - 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; - vector enemies; - - 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 they test the 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!"); - if (is_valid_enemy(input)) - { - enemies.push_back(Entity({x, y}, input)); - row.push_back('.'); - } - else { - row.push_back(input); - } - } - - field.push_back(row); - } - - vector player_start_pos = request_numbers_from_user(2); - - return {field, player_start_pos, enemies}; - } // Beispieleingabe: `4 3 #.# #.K #T# #Z# 0 1` -} // game diff --git a/Util/MazeParser.h b/Util/MazeParser.h deleted file mode 100644 index 3de7fe0..0000000 --- a/Util/MazeParser.h +++ /dev/null @@ -1,54 +0,0 @@ -#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'}; - /// Welche Geistertypen es gibt - static const vector valid_enemies = {'A', 'B', 'C'}; - /// Die Maximale Labyrinthgröße - 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); - - /// Ob der angegebene char in valider Geist ist - /// @param target Der zu kontrollierende Wert - /// @returns Ob der Wert ein Valider Geist ist - static bool is_valid_enemy(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/Util/Vector2d.cpp b/Util/Vector2d.cpp deleted file mode 100644 index 51db2d9..0000000 --- a/Util/Vector2d.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "Vector2d.h" - -namespace game -{ - Vector2d::Vector2d(const int x, const int y) - { - this->x = x; - this->y = y; - } - - void Vector2d::update(const int& x, const int& y) - { - this->x = x; - this->y = y; - } - - void Vector2d::change_x(const int& amount) { - this->x += amount; - } - - void Vector2d::change_y(const int& amount) { - this->y += amount; - } - - Vector2d Vector2d::normalize() { - Vector2d v = *this; - if (v.x < 0) - v.x *= -1; - if (v.y < 0) - v.y *= -1; - return v; - } - - bool Vector2d::eq(const Vector2d& position) const { - return this->x == position.x && this->y == position.y; - } - - - Vector2d Vector2d::get_new_updated(const int& diff_x, const int& diff_y) const { - Vector2d new_position = *this; - new_position.change_x(diff_x); - new_position.change_y(diff_y); - - return new_position; - } - -} // game diff --git a/Util/Vector2d.h b/Util/Vector2d.h deleted file mode 100644 index b506f4e..0000000 --- a/Util/Vector2d.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef POSITION_H -#define POSITION_H - -namespace game -{ - /// Ein Vector aus zwei zahlen. - /// Kann eine Position oder eine Differenz zwischen zwei Positionen darstellen. - struct Vector2d - { - // 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 - Vector2d(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); - - /// Verschiebe den X Wert des Vektors um eine Anzahl - /// @param amount Die zu verschiebene Anzahl - void change_x(const int& amount); - - /// Verschiebe den Y Wert des Vektors um eine Anzhal - /// @param amount Die zu verschiebene Anzahl - void change_y(const int& amount); - - /// Kriege einen normalisierten Vektor zurück - /// @returns Den aktuellen Vektor als normalisierter Vektor - Vector2d normalize(); - - /// Kontrolliere, ob ein Vektor einem anderen Enspricht - /// @param position Die Position mit der verglichen werden soll - /// @returns Ob die Position die gleiche ist - bool eq(const Vector2d& position) const; - - /// Kriege einen Vektor, der mit den gegebenen Werten verschoben worden ist - /// @param diff_x Die Verschiebung auf der X-Achse - /// @param diff_y Die Verschiebung auf der Y-Achse - /// @returns Den berrechneten Vektor - Vector2d get_new_updated(const int& diff_x, const int& diff_y) const; - }; -} // game - -#endif //POSITION_H diff --git a/main.cpp b/main.cpp index 0c2457a..927258f 100644 --- a/main.cpp +++ b/main.cpp @@ -1,53 +1,395 @@ -#include "Environment/Maze.h" -#include "Environment/Game.h" -#include "Exceptions/MalformedMaze.h" -#include "Util/GameState.h" +/* + * 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 +*/ -using game::Maze; -using game::Game; -using game::GameState; +#include "std_lib_inc.h" -using game_exceptions::MalformedMaze; +// Exception fuer nicht erlaubte Bewegungen +class BadMovement {}; -// Ein Programm, welches dir erlaubt ein Labyrinth zu erkunden mit 'w', 'a', 's', und 'd'. +// Exception fuer unbekannte Eingaben +class UnknownInput {}; -void print_exit_message_based_on_state(const GameState& state); +// 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() { - GameState state = GameState::RUNNING; + activateAssertions(); try { - Maze maze = MazeParser::request_maze_from_user(); + GameState game_state = initialize(); - Game game = Game(maze); + game_state = game_loop(game_state); - game.run_game(); - - state = game.get_state(); - } - catch (MalformedMaze& _) - { - cout << "Fehler beim Einlesen des Labyrinths.\n"; + 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; } - - print_exit_message_based_on_state(state); - - return 0; -} - - -/// Schriebe eine Nachricht in die Konsole wenn das Programm beendet wird -/// @param state Der Spielzustand als das Programm beendet wurde -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; + catch(BadMaze&) + { + cout << "Fehler beim Einlesen des Labyrinths.\n"; + return 1; + } + catch(...) + { + cout << "Unbekannter Fehler!\n"; + return 1; } } diff --git a/std_lib_inc.h b/std_lib_inc.h index 6c9ff77..f0f1364 100644 --- a/std_lib_inc.h +++ b/std_lib_inc.h @@ -79,15 +79,6 @@ 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);