forked from University/epr24pr42-ojanssen2
162 lines
5.9 KiB
C++
162 lines
5.9 KiB
C++
#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
|