commit 6889d79ae8c4e07a877ab02269ee48aa19a2e0d7 Author: Fritz Bökler Date: Mon Dec 2 17:47:09 2024 +0100 Exercise-Template pushed by Artemis diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..35561a2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,38 @@ +# Source: https://github.com/alexkaratarakis/gitattributes/blob/master/C%2B%2B.gitattributes (08.12.2020) + +# Sources +*.c text diff=c +*.cc text diff=cpp +*.cxx text diff=cpp +*.cpp text diff=cpp +*.c++ text diff=cpp +*.hpp text diff=cpp +*.h text diff=c +*.h++ text diff=cpp +*.hh text diff=cpp + +# Compiled Object files +*.slo binary +*.lo binary +*.o binary +*.obj binary + +# Precompiled Headers +*.gch binary +*.pch binary + +# Compiled Dynamic libraries +*.so binary +*.dylib binary +*.dll binary + +# Compiled Static libraries +*.lai binary +*.la binary +*.a binary +*.lib binary + +# Executables +*.exe binary +*.out binary +*.app binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1f18bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +* + +!*.h +!*.hpp +!*.cpp +!*.sh +!Makefile +!.gitignore +!.gitattributes + +!*/ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..8fb2ac0 --- /dev/null +++ b/main.cpp @@ -0,0 +1,197 @@ +// 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" + +// Globale Labyrinth-Definition als konstanter Ausdruck +constexpr int kRows = 5; +constexpr int kCols = 5; + +// Labyrinth-Daten +// Interpretiere als Zeilen, dann Spalten +const vector> kMaze = { + {'#', '.', '.', '.', '.'}, + {'#', '.', '#', '.', '.'}, + {'.', 'Z', '#', '.', '.'}, + {'.', '#', '#', '#', '.'}, + {'.', '.', '.', '.', '.'}, +}; + +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); + return 0; +} \ No newline at end of file diff --git a/std_lib_inc.h b/std_lib_inc.h new file mode 100644 index 0000000..6c9ff77 --- /dev/null +++ b/std_lib_inc.h @@ -0,0 +1,299 @@ +/* +std_lib_inc.h + +Version 2024-11-07 + +simple "Programming: Principles and Practice using C++ (second edition)" course header to +be used for the first few weeks. +It provides the most common standard headers (in the global namespace) +and minimal exception/error support. + +Students: please don't try to understand the details of headers just yet. +All will be explained. This header is primarily used so that you don't have +to understand every concept all at once. + +By Chapter 10, you don't need this file and after Chapter 21, you'll understand it + +Revised April 25, 2010: simple_error() added + +Revised November 25 2013: remove support for pre-C++11 compilers, use C++11: +Revised November 28 2013: add a few container algorithms +Revised June 8 2014: added #ifndef to workaround Microsoft C++11 weakness +Revised Febrary 2 2015: randint() can now be seeded (see exercise 5.13). +Revised August 3, 2020: a cleanup removing support for ancient compilers +Revised November 7, 2024: assertions to avoid cassert and NDEBUG (Fritz) +*/ + +#ifndef H112 +#define H112 080315L + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ + + +typedef long Unicode; + +//------------------------------------------------------------------------------ + +using namespace std; + +inline bool ASSERTIONS_ACTIVE = false; + +template string to_string(const T& t) +{ + ostringstream os; + os << t; + return os.str(); +} + +struct Range_error : out_of_range { // enhanced vector range error reporting + int index; + Range_error(int i) : out_of_range("Range error: " + to_string(i)), index(i) { } +}; + + +namespace std +{ + +// trivially range-checked vector (no iterator checking): + template + struct Vector : public std::vector + { + using size_type = typename std::vector::size_type; + +/* #ifdef _MSC_VER + // microsoft doesn't yet support C++11 inheriting constructors + Vector() { } + explicit Vector(size_type n) :std::vector(n) {} + Vector(size_type n, const T& v) :std::vector(n, v) {} + template + Vector(I first, I last) : std::vector(first, last) {} + Vector(initializer_list list) : std::vector(list) {} +*/ + using std::vector::vector; // inheriting constructor + + T &operator[](unsigned int i) // rather than return at(i); + { + if(i < 0 || this->size() <= i) throw Range_error(i); + return std::vector::operator[](i); + } + + const T &operator[](unsigned int i) const + { + if(i < 0 || this->size() <= i) throw Range_error(i); + return std::vector::operator[](i); + } + }; +} + +// disgusting macro hack to get a range checked vector: +#define vector Vector + +// trivially range-checked string (no iterator checking): +struct String : std::string { + using size_type = std::string::size_type; + // using string::string; + + char& operator[](unsigned int i) // rather than return at(i); + { + if (i<0 || size() <= i) throw Range_error(i); + return std::string::operator[](i); + } + + const char& operator[](unsigned int i) const + { + if (i<0 || size() <= i) throw Range_error(i); + return std::string::operator[](i); + } +}; + +namespace std { + + template<> struct hash + { + size_t operator()(const String& s) const + { + return hash()(s); + } + }; + +} // of namespace std + + +struct Exit : runtime_error { + Exit() : runtime_error("Exit") {} +}; + +// error() simply disguises throws: +[[noreturn]] +inline void error(const string& s) +{ + throw runtime_error(s); +} + +[[noreturn]] +inline void error(const string& s, const string& s2) +{ + error(s + s2); +} + +inline void error(const string& s, int i) +{ + ostringstream os; + os << s << ": " << i; + error(os.str()); +} + + +template char* as_bytes(T& i) // needed for binary I/O +{ + void* addr = &i; // get the address of the first byte + // of memory used to store the object + return static_cast(addr); // treat that memory as bytes +} + + +inline void keep_window_open() +{ + cin.clear(); + cout << "Bitte gib ein Zeichen zum beenden ein:\n"; + char ch; + cin >> ch; + return; +} + +inline void keep_window_open(string s) +{ + if (s == "") return; + cin.clear(); + cin.ignore(120, '\n'); + for (;;) { + cout << "Bitte gib '" << s << "' ein, um zu beenden\n"; + string ss; + while (cin >> ss && ss != s) + cout << "Bitte gib '" << s << "' ein, um zu beenden\n"; + return; + } +} + + +// Assertion handling that avoids cassert and debug mode (Fritz) +#undef assert + +inline void activateAssertions() +{ + ASSERTIONS_ACTIVE = true; +} + +inline void assert(bool condition, const string& message = "") +{ + if (ASSERTIONS_ACTIVE && !condition) + { + if(message == "") + { + cout << "Assertion fehlgeschlagen!\n"; + } + else + { + cout << "Assertion fehlgeschlagen: " << message << "\n"; + } + keep_window_open(); + exit(1); + } +} + + + +// error function to be used (only) until error() is introduced in Chapter 5: +inline void simple_error(string s) // write ``error: s and exit program +{ + cout << "fehler: " << s << '\n'; + keep_window_open(); // for some Windows environments + exit(1); +} + +// make std::min() and std::max() accessible on systems with antisocial macros: +#undef min +#undef max + + +// run-time checked narrowing cast (type conversion). See ???. +// Fritz: deprecated with {}-construction +template R narrow_cast(const A& a) +{ + R r = R(a); + if (A(r) != a) error(string("informationsverlust")); + return r; +} + +// random number generators. See 24.7. + +inline default_random_engine& get_rand() +{ + static default_random_engine ran; // note: not thread_local + return ran; +}; + +inline void seed_randint(int s) { get_rand().seed(s); } + +inline int randint(int min, int max) { return uniform_int_distribution<>{min, max}(get_rand()); } + +inline int randint(int max) { return randint(0, max); } + +//inline double sqrt(int x) { return sqrt(double(x)); } // to match C++0x + +// container algorithms. See 21.9. // C++ has better versions of this: + +template +using Value_type = typename C::value_type; + +template +using Iterator = typename C::iterator; + +template +// requires Container() +void sort(C& c) +{ + std::sort(c.begin(), c.end()); +} + +template +// requires Container() && Binary_Predicate>() +void sort(C& c, Pred p) +{ + std::sort(c.begin(), c.end(), p); +} + +template +// requires Container() && Equality_comparable() +Iterator find(C& c, Val v) +{ + return std::find(c.begin(), c.end(), v); +} + +template +// requires Container() && Predicate>() +Iterator find_if(C& c, Pred p) +{ + return std::find_if(c.begin(), c.end(), p); +} + +#endif //H112