feat: added maze parser, added keys and doors

This commit is contained in:
moonleay 2024-12-16 23:20:04 +01:00
parent 7c116c8cfc
commit ec8b851078
Signed by: moonleay
GPG key ID: 82667543CCD715FB
9 changed files with 232 additions and 46 deletions

View file

@ -14,4 +14,6 @@ add_executable(epr24pr3_ojanssen2
std_lib_inc.h std_lib_inc.h
Maze.h Maze.h
Maze.cpp Maze.cpp
MazeParser.h
MazeParser.cpp
) )

View file

@ -3,29 +3,38 @@
// //
#include "Maze.h" #include "Maze.h"
#include "Player.h"
namespace game namespace game
{ {
Maze::Maze() Maze::Maze(const vector<vector<char>> play_field, const vector<int> 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) 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 return false; // Zielposition ist außerhalb des Spielfelds
if (field[pos.y][pos.x] == '#') if (field[pos.y][pos.x] == '#')
return false; // Zielposition ist eine Wand 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) 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) for (int y = 0; y < field.size(); ++y)
{ {
@ -33,7 +42,7 @@ namespace game
for (int x = 0; x < field[y].size(); ++x) for (int x = 0; x < field[y].size(); ++x)
{ {
// ... schreibe für jedes Element ... // ... 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 cout << "S"; // ... 'S' wenn der aktuelle Eintrag die Position des Spielers ist
else // sonst else // sonst
cout << field[y][x]; // ... den tatsächlichen Eintrag cout << field[y][x]; // ... den tatsächlichen Eintrag
@ -42,4 +51,20 @@ namespace game
cout << "\n"; // Beende die Reihe mit einem Zeilenumbruch 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<int> Maze::get_player_start_position()
{
return this->player_start_position;
}
} // game } // game

61
Maze.h
View file

@ -7,43 +7,70 @@
#ifndef MAZE_H #ifndef MAZE_H
#define MAZE_H #define MAZE_H
namespace game
{
class Maze
{ // class -> members private by default
/// Das Spielfeld
vector<vector<char>> field = {
{'#', '.', '.', '.', '.'},
{'#', '.', '#', '.', '.'},
{'.', 'Z', '#', '.', '.'},
{'.', '#', '#', '#', '.'},
{'.', '.', '.', '.', '.'}
// ^ Spieler startet hier (4,0)
};
/* Legende /* Legende
* S - Spieler * S - Spieler
* Z - Ziel * Z - Ziel
* . - Leerer Raum (begehbar) * . - Leerer Raum (begehbar)
* # - Wand * # - 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
private:
/// Das Spielfeld
vector<vector<char>> field;
/// Die Startposition des Spielers
vector<int> player_start_position;
public: public:
/// Das Spielfeld /// Das Spielfeld
Maze(); Maze(vector<vector<char>> play_field, vector<int> 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 /// Kontrolliere, ob der Spieler am Ziel ist
/// @param pos Die Position des Spielers /// @param pos Die Position des Spielers
/// @return Ob der Spieler am Ziel ist /// @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 /// Kontrolliere, ob eine bestimmte Position begehbar ist
/// @param pos Die Position, die überprüft werden soll /// @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 /// @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 /// Zeige das Spielfeld in der Konsole an
/// @param pos Die Position des Spielers /// @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<int> get_player_start_position();
}; };
} // game } // game

43
MazeParser.cpp Normal file
View file

@ -0,0 +1,43 @@
//
// Created by moonleay on 12/16/24.
//
#include "MazeParser.h"
namespace game {
vector<int> MazeParser::request_numbers_from_user(const int amount_of_numbers)
{
int input;
vector<int> 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<int> maze_size = request_numbers_from_user(2);
char input;
vector<vector<char>> field;
for (int i = 0; i < maze_size[0]; ++i)
{
vector<char> 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<int> player_start_pos = request_numbers_from_user(2);
return Maze(field, player_start_pos);
} // `4 3 #.# #.K #T# #Z# 0 1`
} // game

29
MazeParser.h Normal file
View file

@ -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<int> 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

View file

@ -8,7 +8,7 @@
namespace game 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 // 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 // Berechne die Position, zu der der Spieler sich bewegen möchte
const PositionVector target_position = PositionVector(this->get_pos().x + move_vector.x, 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 // 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); this->update_position(target_position);
else
cout << "Bewegung nicht moeglich!\n"; // 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
throw runtime_error("Bewegung nicht moeglich!");
return maze;
}
bool Player::has_key_available() const
{
return this->keys_in_inventory > 0;
}
} // game } // game

View file

@ -2,19 +2,20 @@
// Created by moonleay on 12/12/24. // Created by moonleay on 12/12/24.
// //
#include "PositionVector.h" #include "PositionVector.h"
#include "Maze.h"
#ifndef PLAYER_H #ifndef PLAYER_H
#define PLAYER_H #define PLAYER_H
namespace game namespace game
{ {
class Maze; // use instead of include to speed up the compiler :)
/// Ein Spieler. /// Ein Spieler.
/// Besitzt einen veränderbaren Positionsvektor /// Besitzt einen veränderbaren Positionsvektor
class Player class Player
{ {
/// Die Position des Spielers /// Die Position des Spielers
PositionVector pos; PositionVector pos;
int keys_in_inventory;
public: public:
/// Ein Spieler. /// Ein Spieler.
@ -35,7 +36,11 @@ namespace game
/// @param maze Das Maze /// @param maze Das Maze
/// @param move_vector Die gewollte Bewegung /// @param move_vector Die gewollte Bewegung
/// @return Die neue Position des Spielers /// @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 } // maze

View file

@ -10,7 +10,8 @@ namespace game
/// Ein Vector aus zwei zahlen. /// Ein Vector aus zwei zahlen.
/// Kann eine Position oder eine Differenz zwischen zwei Positionen darstellen. /// Kann eine Position oder eine Differenz zwischen zwei Positionen darstellen.
struct PositionVector struct PositionVector
{ // struct -> members public by default {
// struct -> members public by default
// Die beiden Variablen des Vectors // Die beiden Variablen des Vectors
int x; int x;
int y; int y;

View file

@ -1,34 +1,51 @@
#include "Maze.h"
#include "std_lib_inc.h" #include "std_lib_inc.h"
#include "Player.h" #include "Player.h"
#include "PositionVector.h" #include "PositionVector.h"
// #include "Maze.h" #include "MazeParser.h"
// Muss nicht inkludiert werden, da es schon in Player.h inkludiert wird
using game::Player; using game::Player;
using game::PositionVector; using game::PositionVector;
using game::Maze; using game::Maze;
using game::MazeParser;
// Ein Programm, welches dir erlaubt ein Labyrinth zu erkunden mit 'w', 'a', 's', und 'd'. // Ein Programm, welches dir erlaubt ein Labyrinth zu erkunden mit 'w', 'a', 's', und 'd'.
const vector<int> player_start_position = {4, 0}; const vector<int> player_start_position = {4, 0};
int main() 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<int> player_start_position = maze.get_player_start_position();
// Erstelle einen Spieler mit der gegebenen start position // Erstelle einen Spieler mit der gegebenen start position
Player player = Player(player_start_position[1], player_start_position[0]); Player player = Player(player_start_position[1], player_start_position[0]);
// Erstelle eine Variable für den Input // Erstelle eine Variable für den Input
char input; char game_input;
// Durchlaufe die Hauptschleife des Spiels // Durchlaufe die Hauptschleife des Spiels
while (true) while (true)
{ {
// Zeige dem Spieler das Spielfeld // 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 // 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! // Ziel erreicht! Herzlichen Glückwunsch!
cout << "Ziel erreicht! Herzlichen Glueckwunsch!\n"; cout << "Ziel erreicht! Herzlichen Glueckwunsch!\n";
@ -36,7 +53,7 @@ int main()
} }
// Lese Eingabe des Spielers // Lese Eingabe des Spielers
cin >> input; cin >> game_input;
// Erstelle einen Vector mit einer Bewegung von 0 und 0 // Erstelle einen Vector mit einer Bewegung von 0 und 0
PositionVector movement_vector = PositionVector(0, 0); PositionVector movement_vector = PositionVector(0, 0);
@ -44,7 +61,7 @@ int main()
// Kontrolliere, was der Spieler machen möchte. Speichere die erforderte Bewegung im Bewegungsvektor. // Kontrolliere, was der Spieler machen möchte. Speichere die erforderte Bewegung im Bewegungsvektor.
// Schreibe nachrichten in die Konsole, // Schreibe nachrichten in die Konsole,
// wenn nach Hilfe gefragt wird oder eine unbekannte Eingabe eingegeben wurde // wenn nach Hilfe gefragt wird oder eine unbekannte Eingabe eingegeben wurde
switch (input) switch (game_input)
{ {
case 'w': case 'w':
movement_vector.update(0, -1); movement_vector.update(0, -1);
@ -60,7 +77,10 @@ int main()
break; break;
case 'h': case 'h':
// Schreibe hilfsreiche Tipps in die Konsole // 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; break;
default: default:
// Die gewollte Aktion kenne ich nicht. Schreibe eine Fehlernachricht in die Konsole // 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. // 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 // Beende das Programm
return 0; return 0;