From 7c116c8cfc93b5021f51ceeef2c5f301cb26fd86 Mon Sep 17 00:00:00 2001 From: moonleay Date: Sat, 14 Dec 2024 17:32:06 +0100 Subject: [PATCH] feat!: reworked former project to use classes, structs and multiple files --- .gitignore | 2 + CMakeLists.txt | 17 +++ Maze.cpp | 45 ++++++++ Maze.h | 50 +++++++++ Player.cpp | 39 +++++++ Player.h | 42 ++++++++ PositionVector.cpp | 20 ++++ PositionVector.h | 31 ++++++ main.cpp | 257 ++++++++++++--------------------------------- 9 files changed, 314 insertions(+), 189 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 Maze.cpp create mode 100644 Maze.h create mode 100644 Player.cpp create mode 100644 Player.h create mode 100644 PositionVector.cpp create mode 100644 PositionVector.h diff --git a/.gitignore b/.gitignore index d1f18bf..441b2c9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ !.gitattributes !*/ + +cmake-build-debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0af7b9f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.30) +project(epr24pr3_ojanssen2) + +set(CMAKE_CXX_STANDARD 14) + +include_directories(.) + +add_executable(epr24pr3_ojanssen2 + main.cpp + Player.cpp + Player.h + PositionVector.cpp + PositionVector.h + std_lib_inc.h + Maze.h + Maze.cpp +) diff --git a/Maze.cpp b/Maze.cpp new file mode 100644 index 0000000..741c131 --- /dev/null +++ b/Maze.cpp @@ -0,0 +1,45 @@ +// +// Created by moonleay on 12/13/24. +// + +#include "Maze.h" + +namespace game +{ + Maze::Maze() + { + + } + + bool Maze::is_player_at_goal(const PositionVector pos) const + { + return this->field[pos.y][pos.x] == 'Z'; + } + + bool Maze::is_pos_free(const PositionVector pos) 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 + return true; // Zielposition ist betretbar (ist keine Wand und auch nicht außerhalb des Spielfeldes) + } + + void Maze::render(const PositionVector pos) const + { + for (int y = 0; y < field.size(); ++y) + { + // Für jede Reihe ... + for (int x = 0; x < field[y].size(); ++x) + { + // ... schreibe für jedes Element ... + if (y == pos.y && x == pos.x) + cout << "S"; // ... 'S' wenn der aktuelle Eintrag die Position des Spielers ist + else // sonst + cout << field[y][x]; // ... den tatsächlichen Eintrag + cout << " "; // Füge ein Leerzeichen zwischen den Einträgen hinzu + } + cout << "\n"; // Beende die Reihe mit einem Zeilenumbruch + } + } +} // game diff --git a/Maze.h b/Maze.h new file mode 100644 index 0000000..a1076a4 --- /dev/null +++ b/Maze.h @@ -0,0 +1,50 @@ +// +// Created by moonleay on 12/13/24. +// +#include "std_lib_inc.h" +#include "PositionVector.h" + +#ifndef MAZE_H +#define MAZE_H + +namespace game +{ + class Maze + { // class -> members private by default + /// Das Spielfeld + vector> field = { + {'#', '.', '.', '.', '.'}, + {'#', '.', '#', '.', '.'}, + {'.', 'Z', '#', '.', '.'}, + {'.', '#', '#', '#', '.'}, + {'.', '.', '.', '.', '.'} + // ^ Spieler startet hier (4,0) + }; + /* Legende + * S - Spieler + * Z - Ziel + * . - Leerer Raum (begehbar) + * # - Wand + */ + + public: + /// Das Spielfeld + Maze(); + + /// 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; + + /// Kontrolliere, ob eine bestimmte Position begehbar ist + /// @param pos Die Position, die überprüft werden soll + /// @return Ob die Position begehbar ist + bool is_pos_free(PositionVector pos) const; + + /// Zeige das Spielfeld in der Konsole an + /// @param pos Die Position des Spielers + void render(PositionVector pos) const; + }; +} // game + +#endif //MAZE_H diff --git a/Player.cpp b/Player.cpp new file mode 100644 index 0000000..57e7b17 --- /dev/null +++ b/Player.cpp @@ -0,0 +1,39 @@ +// +// Created by moonleay on 12/12/24. +// + +#include "std_lib_inc.h" +#include "Player.h" +#include "Maze.h" + +namespace game +{ + Player::Player(const int target_x, const int target_y): pos(target_x, target_y) + { + // Wir brauchen keinen Inhalt in diesem Konstruktor, da wir nur die position speichern müssen + } + + PositionVector Player::get_pos() const + { + return this->pos; + } + + void Player::update_position(const PositionVector target) + { + this->pos = target; + } + + + void 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); + + // Bewege den Spieler zu der gewollten Position, wenn diese frei ist + if (maze.is_pos_free(target_position)) + this->update_position(target_position); + else + cout << "Bewegung nicht moeglich!\n"; + } +} // game diff --git a/Player.h b/Player.h new file mode 100644 index 0000000..b780ec3 --- /dev/null +++ b/Player.h @@ -0,0 +1,42 @@ +// +// Created by moonleay on 12/12/24. +// +#include "PositionVector.h" +#include "Maze.h" + +#ifndef PLAYER_H +#define PLAYER_H + +namespace game +{ + /// Ein Spieler. + /// Besitzt einen veränderbaren Positionsvektor + class Player + { + /// Die Position des Spielers + PositionVector pos; + + 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); + + /// 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(PositionVector target); + + /// 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 + void move(Maze maze, PositionVector move_vector); + }; +} // maze + +#endif //PLAYER_H diff --git a/PositionVector.cpp b/PositionVector.cpp new file mode 100644 index 0000000..bb5bb24 --- /dev/null +++ b/PositionVector.cpp @@ -0,0 +1,20 @@ +// +// Created by moonleay on 12/12/24. +// + +#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; + } +} // game diff --git a/PositionVector.h b/PositionVector.h new file mode 100644 index 0000000..fede846 --- /dev/null +++ b/PositionVector.h @@ -0,0 +1,31 @@ +// +// Created by moonleay on 12/12/24. +// + +#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(int x, int y); + }; +} // game + +#endif //POSITION_H diff --git a/main.cpp b/main.cpp index 8fb2ac0..c589498 100644 --- a/main.cpp +++ b/main.cpp @@ -1,197 +1,76 @@ -// Ein Labyrinth-Spiel -// Autor: Fritz Bökler -// Datum 20.11.2024 -// MIT Lizenz -// -// Labyrinth wird in der Konsole ausgegeben. -// . Leeres Feld -// # Wand (nicht betretbar) -// Z Ziel -// S SpielerIn (wird nicht im Labyrinth gespeichert) -// -// Ziel des Spiels ist, das Ziel-Feld zu erreichen. -// Eingabe erfolgt zeilengepuffert über cin. - #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 -// Globale Labyrinth-Definition als konstanter Ausdruck -constexpr int kRows = 5; -constexpr int kCols = 5; +using game::Player; +using game::PositionVector; +using game::Maze; -// Labyrinth-Daten -// Interpretiere als Zeilen, dann Spalten -const vector> kMaze = { - {'#', '.', '.', '.', '.'}, - {'#', '.', '#', '.', '.'}, - {'.', 'Z', '#', '.', '.'}, - {'.', '#', '#', '#', '.'}, - {'.', '.', '.', '.', '.'}, -}; +// Ein Programm, welches dir erlaubt ein Labyrinth zu erkunden mit 'w', 'a', 's', und 'd'. +const vector player_start_position = {4, 0}; -const vector kPlayerStartPosition = {4, 0}; - -// Funktion zur Anzeige des Labyrinths -// player_position: Position der SpielerIn im Labyrinth -void display_maze(vector player_position) -{ - int player_row = player_position[0]; - int player_col = player_position[1]; - - for(int i = 0; i < kRows; i++) - { - for(int j = 0; j < kCols; j++) - { - if(i == player_row && j == player_col) - { - cout << 'S'; - } - else - { - cout << kMaze[i][j]; - } - cout << " "; - } - cout << '\n'; - } -} - -// Funktion zur Umrechnung eines Kommandos zu einer neuen Position -// player_position: Position der SpielerIn im Labyrinth -// direction: Richtungskommando -// Rückgabe: Die neue SpielerInnenposition -// Vorbedingung: direction muss aus {w, s, a, d} kommen. -vector new_position_by_direction(vector player_position, char direction) -{ - int row = player_position[0]; - int col = player_position[1]; - - assert(direction == 'w' || direction == 's' || direction == 'a' || direction == 'd', - "new_position_by_direction: invalid direction. Must be from {w, s, a, d}."); - - 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}; - } -} - -// Gibt true zurueck gdw. die Position position begehbar ist -// position: Zu testende Position -// Rückgabe: true gdw. die Position begehbar ist -bool position_is_valid(vector position) -{ - const int row = position[0]; - const int col = position[1]; - - bool outside_playfield = row < 0 || col < 0 || row >= kRows || col >= kCols; - - if(outside_playfield) // Erst prüfen, bevor Vector-Zugriff - { - return false; - } - - return kMaze[row][col] != '#'; -} - -// Funktion zur Bewegung des Spielers -// player_position: Position der SpielerIn vor der Bewegung -// direction: Richtungskommando -// Rückgabe: Die neue SpielerInnenposition der SpielerIn -vector move_player(vector player_position, char direction) -{ - vector potential_new_position = new_position_by_direction(player_position, direction); - - if(position_is_valid(potential_new_position)) - { - return potential_new_position; - } - else - { - cout << "Bewegung nicht moeglich!\n"; - return player_position; - } -} - -// Gibt eine kurze Hilfe aus -void display_help() -{ - cout << "Willkommen zum Labyrinth-Spiel!\n"; - cout << "Ziel des Spiels: Finde den Weg vom Startpunkt (S) zum Ziel (Z).\n"; - cout << "Spielfeld-Erklaerung:\n"; - cout << "S - Startpunkt: Hier beginnt der Spieler.\n"; - cout << "Z - Ziel: Erreiche diesen Punkt, um das Spiel zu gewinnen.\n"; - cout << "# - Wand: Diese Felder sind nicht begehbar.\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 << "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. -// player_position: die aktuelle SpielerInnenposition -// input: Ein Eingabezeichen -// Rückgabe: Die neue SpielerInnenposition, falls sie sich aendert -vector process_input(vector player_position, char input) -{ - switch(input) - { - case 'w': case 's': case 'a': case 'd': - return move_player(player_position, input); - case 'h': - display_help(); - break; - default: - cout << "Diese Eingabe kenne ich nicht. Gib 'h' ein, um eine Hilfe zu erhalten.\n"; - break; - } - return player_position; -} - -// Gibt true zurueck, wenn das Ziel erreicht wurde -// player_position: Die aktuelle SpielerInnenposition -// Rückgabe: true gdw. das Ziel erreicht wurde -bool reached_goal(vector player_position) -{ - return kMaze[player_position[0]][player_position[1]] == 'Z'; -} - -// Die Hauptschleife des Spiels -// player_position: Die aktuelle SpielerInnenposition -void game_loop(vector player_position) -{ - char input; - bool not_won = true; - while(cin && not_won) - { - display_maze(player_position); - cin >> input; - if(cin) - { - player_position = process_input(player_position, input); - - if(reached_goal(player_position)) - { - display_maze(player_position); - cout << "Ziel erreicht! Herzlichen Glueckwunsch!\n"; - not_won = false; - } - } - } -} int main() { - game_loop(kPlayerStartPosition); + const Maze maze = Maze(); + // 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; + + // Durchlaufe die Hauptschleife des Spiels + while (true) + { + // Zeige dem Spieler das Spielfeld + maze.render(player.get_pos()); + + // Kontrolliere, ob der Spieler schon das Ziel erreicht hat + if (maze.is_player_at_goal(player.get_pos())) + { + // Ziel erreicht! Herzlichen Glückwunsch! + cout << "Ziel erreicht! Herzlichen Glueckwunsch!\n"; + break; + } + + // Lese Eingabe des Spielers + cin >> input; + + // Erstelle einen Vector mit einer Bewegung von 0 und 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) + { + case 'w': + movement_vector.update(0, -1); + break; + case 'a': + movement_vector.update(-1, 0); + break; + case 's': + movement_vector.update(0, 1); + break; + case 'd': + movement_vector.update(1, 0); + 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"; + break; + default: + // Die gewollte Aktion kenne ich nicht. Schreibe eine Fehlernachricht in die Konsole + cout << "Diese Eingabe kenne ich nicht. Gib 'h' ein, um eine Hilfe zu erhalten.\n"; + break; + } + + // Kontrolliere gewollte Bewegung und setze sie um. + player.move(maze, movement_vector); + } + // Beende das Programm return 0; -} \ No newline at end of file +}