main #1

Merged
Jbrass merged 10 commits from University/epr24pr42-ojanssen2:main into main 2025-01-19 18:30:16 +01:00
23 changed files with 299 additions and 393 deletions

8
.idea/.gitignore generated vendored
View file

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View file

@ -1,5 +1,5 @@
#include "Entity.h" #include "Entity.h"
#include "../Environment/Maze.h" #include "Maze.h"
namespace game { namespace game {
Entity::Entity(const Vector2d starting_position, const char display_character): pos(starting_position), display_character(display_character), move_left(true){} Entity::Entity(const Vector2d starting_position, const char display_character): pos(starting_position), display_character(display_character), move_left(true){}

View file

@ -1,4 +1,4 @@
#include "../Util/Vector2d.h" #include "Vector2d.h"
#ifndef ENTITY_H #ifndef ENTITY_H
#define ENTITY_H #define ENTITY_H

View file

@ -1,8 +1,8 @@
#include "Game.h" #include "Game.h"
#include "Maze.h" #include "Maze.h"
#include "../Exceptions/MovementNotPossible.h" #include "MovementNotPossible.h"
#include "../Exceptions/UnkownAction.h" #include "UnkownAction.h"
#include "../Util/Vector2d.h" #include "Vector2d.h"
using game::Player; using game::Player;
using game::Maze; using game::Maze;

View file

@ -1,7 +1,7 @@
#include "Maze.h" #include "Maze.h"
#include "../Entities/Player.h" #include "Player.h"
#include "../Util/GameState.h" #include "GameState.h"
#include "../Entities/Entity.h" #include "Entity.h"
#ifndef GAME_H #ifndef GAME_H
#define GAME_H #define GAME_H
@ -11,6 +11,7 @@ namespace game
/// Eine Instanz des Spiels /// Eine Instanz des Spiels
class Game class Game
{ {
private: private:
/// Das Labyrinth /// Das Labyrinth
Maze maze; Maze maze;
@ -23,6 +24,7 @@ namespace game
/// ///
bool infomode_enabled; bool infomode_enabled;
public: public:
explicit Game(Maze& maze); explicit Game(Maze& maze);

View file

@ -1,4 +1,4 @@
#include "../std_lib_inc.h" #include "std_lib_inc.h"
#ifndef MALFORMEDMAZE_H #ifndef MALFORMEDMAZE_H
#define MALFORMEDMAZE_H #define MALFORMEDMAZE_H

162
Maze.cpp Normal file
View file

@ -0,0 +1,162 @@
#include "Maze.h"
#include "Entity.h"
#include "Player.h"
#include "MalformedMaze.h"
using game_exceptions::MalformedMaze;
namespace game {
/// Ist eine Konstante, darf also in global scope
static const vector<char> valid_maze_elements = {'Z', '.', '#', 'A', 'K', 'T', 'B', 'C'};
/// Welche Geistertypen es gibt
static const vector<char> valid_enemies = {'A', 'B', 'C'};
/// Die Maximale Labyrinthgröße
static constexpr int MAX_MAZE_SIZE = 20;
Maze::Maze(const vector<vector<char>> &play_field, const vector<int> &player_start_position,
const vector<Entity> &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<Entity> &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;
return 1 + std::min({this->calculate_steps_until_win(position.get_new_updated(0, -1), steps - 1),
this->calculate_steps_until_win(position.get_new_updated(0, 1), steps - 1),
this->calculate_steps_until_win(position.get_new_updated(1, 0), steps - 1),
this->calculate_steps_until_win(position.get_new_updated(-1, 0), steps - 1)});
}
vector<Entity> Maze::get_entities() { return this->enemies; }
vector<int> Maze::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 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 Maze::validate_maze_element(const char &target) {
for (const char c: valid_maze_elements)
if (c == target)
return true;
return false;
}
bool Maze::is_valid_enemy(const char &target) {
for (const char c: valid_enemies)
if (c == target)
return true;
return false;
}
Maze Maze::request_maze_from_user() {
vector<int> maze_size = request_numbers_from_user(2);
char input;
vector<vector<char>> field;
vector<Entity> enemies;
for (int y = 0; y < maze_size[0]; ++y) {
vector<char> 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<int> player_start_pos = request_numbers_from_user(2);
return {field, player_start_pos, enemies};
} // Beispieleingabe: `4 3 #.# #.K #T# #Z# 0 1`
} // namespace game

113
Maze.h Normal file
View file

@ -0,0 +1,113 @@
#ifndef MAZE_H
#define MAZE_H
#include "Vector2d.h"
#include "std_lib_inc.h"
namespace game {
class Player;
class Entity;
/// Ist eine Konstante, darf also in global scope
/// Ein Labyrinth.
/// Besitzt ein Feld
class Maze {
// class -> members private by default
private:
/// Das Spielfeld
vector<vector<char>> field;
/// Die Startposition des Spielers
Vector2d player_start_position;
/// Eine Liste an Gegnern
vector<Entity> enemies;
/// Erlaubte Zeichen in einem Labyrinth
/* Legende
* S - Spieler
* Z - Ziel
* . - Leerer Raum (begehbar)
* # - Wand
* A - Animaltronic
* K - Schlüssel
* T - Tür
* B - Bowie
* C - Connellys
*/
public:
/// Das Spielfeld
Maze(const Vector<Vector<char>> &play_field, const Vector<int> &player_start_position,
const Vector<Entity> &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<Entity> &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<Entity> get_entities();
/// 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();
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(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);
};
} // namespace game
#endif // MAZE_H

View file

@ -1,4 +1,4 @@
#include "../std_lib_inc.h" #include "std_lib_inc.h"
#ifndef MOVEMENTNOTPOSSIBLE_H #ifndef MOVEMENTNOTPOSSIBLE_H
#define MOVEMENTNOTPOSSIBLE_H #define MOVEMENTNOTPOSSIBLE_H

View file

@ -1,8 +1,8 @@
#include "Player.h" #include "Player.h"
#include "../Environment/Maze.h" #include "Maze.h"
#include "../Exceptions/MovementNotPossible.h" #include "MovementNotPossible.h"
#include "../Util/GameState.h" #include "GameState.h"
#include "../Environment/Game.h" #include "Game.h"
using game::GameState; using game::GameState;
using game::Game; using game::Game;

View file

@ -1,5 +1,5 @@
#include "../Util/Vector2d.h" #include "Vector2d.h"
#include "../Util/GameState.h" #include "GameState.h"
#ifndef PLAYER_H #ifndef PLAYER_H
#define PLAYER_H #define PLAYER_H

View file

@ -1,4 +1,4 @@
#include "../std_lib_inc.h" #include "std_lib_inc.h"
#ifndef UNKOWNACTION_H #ifndef UNKOWNACTION_H
#define UNKOWNACTION_H #define UNKOWNACTION_H

View file

@ -1,11 +1,9 @@
#include "Environment/Maze.h" #include "Maze.h"
#include "Util/MazeParser.h" #include "Game.h"
#include "Environment/Game.h" #include "MalformedMaze.h"
#include "Exceptions/MalformedMaze.h" #include "GameState.h"
#include "Util/GameState.h"
using game::Maze; using game::Maze;
using game::MazeParser;
using game::Game; using game::Game;
using game::GameState; using game::GameState;
@ -20,7 +18,7 @@ int main()
GameState state = GameState::RUNNING; GameState state = GameState::RUNNING;
try try
{ {
Maze maze = MazeParser::request_maze_from_user(); Maze maze = Maze::request_maze_from_user();
Game game = Game(maze); Game game = Game(maze);

View file

@ -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<vector<char>>& play_field, const vector<int>& player_start_position, const vector<Entity>& 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<Entity>& 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<int> 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<Entity> Maze::get_entities() {
return this->enemies;
}
} // game

View file

@ -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<vector<char>> field;
/// Die Startposition des Spielers
Vector2d player_start_position;
/// Eine Liste an Gegnern
vector<Entity> enemies;
public:
/// Das Spielfeld
Maze(const Vector<Vector<char>>& play_field, const Vector<int>& player_start_position, const Vector<Entity>& 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<Entity>& 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<Entity> get_entities();
};
} // game
#endif //MAZE_H

View file

@ -1,11 +0,0 @@
#include "MathUtil.h"
namespace game {
int MathUtil::get_min(const vector<int>& numbers) {
int i = numbers[0];
for (const int j : numbers)
if (j < i)
i = j;
return i;
}
} // game

View file

@ -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<int>& numbers);
};
} // game
#endif //UTIL_H

View file

@ -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<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 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<int> maze_size = request_numbers_from_user(2);
char input;
vector<vector<char>> field;
vector<Entity> enemies;
for (int y = 0; y < maze_size[0]; ++y)
{
vector<char> 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<int> player_start_pos = request_numbers_from_user(2);
return {field, player_start_pos, enemies};
} // Beispieleingabe: `4 3 #.# #.K #T# #Z# 0 1`
} // game

View file

@ -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<char> valid_maze_elements = {'Z', '.', '#', 'A', 'K', 'T', 'B', 'C'};
/// Welche Geistertypen es gibt
static const vector<char> 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<int> 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