feat!: reworked former project to use classes, structs and multiple files

This commit is contained in:
moonleay 2024-12-14 17:32:06 +01:00
parent 6889d79ae8
commit 7c116c8cfc
Signed by: moonleay
GPG key ID: 82667543CCD715FB
9 changed files with 314 additions and 189 deletions

2
.gitignore vendored
View file

@ -9,3 +9,5 @@
!.gitattributes
!*/
cmake-build-debug/

17
CMakeLists.txt Normal file
View file

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

45
Maze.cpp Normal file
View file

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

50
Maze.h Normal file
View file

@ -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<vector<char>> 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

39
Player.cpp Normal file
View file

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

42
Player.h Normal file
View file

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

20
PositionVector.cpp Normal file
View file

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

31
PositionVector.h Normal file
View file

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

255
main.cpp
View file

@ -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<vector<char>> kMaze = {
{'#', '.', '.', '.', '.'},
{'#', '.', '#', '.', '.'},
{'.', 'Z', '#', '.', '.'},
{'.', '#', '#', '#', '.'},
{'.', '.', '.', '.', '.'},
};
// 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> kPlayerStartPosition = {4, 0};
// Funktion zur Anzeige des Labyrinths
// player_position: Position der SpielerIn im Labyrinth
void display_maze(vector<int> 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<int> new_position_by_direction(vector<int> 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<int> 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<int> move_player(vector<int> player_position, char direction)
{
vector<int> 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<int> process_input(vector<int> 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<int> player_position)
{
return kMaze[player_position[0]][player_position[1]] == 'Z';
}
// Die Hauptschleife des Spiels
// player_position: Die aktuelle SpielerInnenposition
void game_loop(vector<int> 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;
}