Skip to content
Snippets Groups Projects
Commit 602b97b8 authored by Guttmann, Michael's avatar Guttmann, Michael
Browse files

Merge branch 'master' of gitlab.tugraz.at:coding_tug/coding-streams

parents fbcd9ae7 e5f88b98
No related branches found
No related tags found
No related merge requests found
Showing
with 1019 additions and 0 deletions
CXX := clang++
CXXFLAGS := -Wall -Wextra -pedantic -std=c++17 -g -c -o
ASSIGNMENT := oop1
BUILDDIR := build
SOURCES := $(wildcard *.cpp)
OBJECTS := $(patsubst %,$(BUILDDIR)/%,${SOURCES:.cpp=.o})
TESTRUNNER_EXECUTABLE := ../testrunner
.DEFAULT_GOAL := help
.PHONY: reset clean bin lib all run test help
prepare:
mkdir -p $(BUILDDIR)
$(BUILDDIR)/%.o: %.cpp
@echo -e "[\033[36mINFO\033[0m] Compiling object:" $<
$(CXX) $(CXXFLAGS) $@ $< -MMD -MF ./$@.d
$(ASSIGNMENT) : $(OBJECTS)
@echo -e "[\033[36mINFO\033[0m] Linking objects:" $@
$(CXX) -pthread -o $@ $^
clean: ## cleans up project folder
@echo -e "[\033[36mINFO\033[0m] Cleaning up folder..."
rm -f $(ASSIGNMENT)
rm -rf ./$(BUILDDIR)
rm -rf testreport.html
rm -rf ./valgrind_logs
bin: prepare $(ASSIGNMENT) ## compiles project to executable binary
@echo -e "[\033[36mINFO\033[0m] Compiling binary..."
chmod +x $(ASSIGNMENT)
all: clean bin ## all of the above
run: all ## runs the project
@echo -e "[\033[36mINFO\033[0m] Executing binary..."
./$(ASSIGNMENT)
test: clean ## runs public testcases on the project
@echo -e "[\033[36mINFO\033[0m] Executing testrunner..."
chmod +x testrunner
./$(TESTRUNNER_EXECUTABLE) -c test.toml
help: ## prints the help text
@echo -e "Usage: make \033[36m<TARGET>\033[0m"
@echo -e "Available targets:"
@awk -F':.*?##' '/^[a-zA-Z_-]+:.*?##.*$$/{printf " \033[36m%-10s\033[0m%s\n", $$1, $$2}' $(MAKEFILE_LIST)
-include $(wildcard *.d)
# Assignment 2
## Einleitung
In Assignment 2 (A2) der Konstruktionsübung (KU) wird ein Spiel programmiert, welches dem Brettspiel "Das verrückte Labyrinth" nachempfunden wurde.
Das Spielprinzip wird auf dieser Seite allgemein beschrieben und sollte bei der Implementierung auch eingehalten werden. Die exakte Angabe zu einzelnen Assignments können Sie den folgenden Links entnehmen:
- [Ass2](description/Ass2.md)
Ganz unten auf dieser Seite finden Sie die Spezifikationen, die einzuhalten sind!
## Spielaufbau
Gespielt wird auf einem Spielfeld, welches 49 Felder umfasst (7x7 Felder). 16 dieser Felder sind mit jeweils einer Gangkarte belegt, welche nicht verschoben werden kann. Die anderen 33 Felder sind auch mit jeweils einer Gangkarte belegt, jedoch können diese Gangkarten verschoben werden, die Zuordnung einer Gangkarte zu einem Feld ist in diesem Fall also nicht fix.
Es gibt insgesamt 50 Gangkarten: 16 fixe und 33 mobile (wie oben beschrieben) und eine zusätzliche Gangkarte, welche zum Verschieben verwendet wird.
Außerdem gibt es in diesem Spiel 24 Schatzkarten und Schätze und 4 Spielfiguren.
## Spielbeginn
Am Anfang des Spiels werden die Gangkarten auf den freien Plätzen des Spielfelds verteilt. Dabei bleibt eine Gangkarte übrig.
Als nächstes werden die Schatzkarten verdeckt und gleichmäßig an die Spielenden verteilt.
Danach wählen alle Spielenden eine Spielfigur und jede Spielfigur wird auf die zugehörige Startposition auf dem Spielfeld gestellt.
## Spielziel
Ziel des Spiels ist es, alle Schätze zu sammeln, welche auf den eigenen Schatzkarten abgebildet sind und wieder an den Start zurückzukehren. Gewonnen hat also, wer zuerst alle Schätze eingesammelt hat und wieder an der eigenen Startposition steht.
## Spielablauf
Die Spielenden führen abwechselnd ihren Spielzug aus, bis das Spielende erreicht ist. Wie ein Zug im Detail aussieht, wird nun erklärt.
Am Anfang eines Zuges benötigt man eine aktuelle Schatzkarte. Wenn man noch keine hat, so schaut man (geheim) die oberste Schatzkarte des eigenen Stapels an, diese wird zur aktuellen Schatzkarte, bis dieser Schatz aufgesammelt wurde.
Ein Spielzug besteht immer aus zwei Schritten:
1. Gänge verschieben
2. Spielfigur ziehen
### 1. Gänge verschieben
Die freie (das heißt nicht am Spielfeld liegende) Gangkarte wird genommen und in beliebiger Orientierung von der Seite an einer der markierten Stellen eingeschoben. Dadurch wird eine andere Gangkarte herausgeschoben. Die Gangkarten dürfen aber nicht so verschoben werden, dass sich das Spielfeld wieder im Zustand des vorherigen Zuges befindet. (Das heißt, "Zurückschieben" ist nicht erlaubt.)
Wird beim Verschieben eine Spielfigur aus dem Spiel herausgeschoben, so landet sie auf der anderen Seite – also auf der Gangkarte, welche gerade hineingeschoben wurde.
Es muss immer eine Gangkarte eingeschoben werden; ein Überspringen dieses Schrittes ist nicht erlaubt.
### 2. Spielfigur ziehen
Nun darf die eigene Spielfigur an jeden Punkt im Labyrinth verschoben werden, welcher von der aktuellen Position mit einem durchgängigen Gang verbunden ist, auch wenn dort schon eine Spielfigur steht. Das Verschieben der Spielfigur ist optional, das heißt, dieser Schritt kann ausgelassen werden.
Wenn die Spielfigur am Ende des Zuges auf der Gangkarte mit dem gesuchten Schatz steht, gilt dieser als gefunden (bzw. aufgesammelt). Beim Beginn des nächsten Zuges darf die oberste Schatzkarte des eigenen Stapels aufgedeckt werden, der darauf abgebildete Schatz ist dann der aktuelle Schatz, der gefunden werden muss.
## Spielende
Wer alle eigenen Schätze gefunden hat, muss wieder an die eigene Startposition zurück. Wer das als Erste*r schafft, hat das Spiel gewonnen und das Spiel ist zu Ende.
## Spezifikation
- nur geforderte Ausgaben
- Die Random-Klasse (Random.cpp, Random.hpp) darf nicht verändert werden
### Erlaubte Bibliotheken
- alle Bibliotheken der C++ Standard Library
### Abgabe und Bewertung
- Push auf das Gitlab Repository des Teams **auf einen Branch namens `submission`**
- **Abzugeben bis: 29.5.2021 um 23:59 Uhr**
#### Ausbesserung
Auch wenn Sie die benötigten 50 % der öffentlichen Test Cases am Ende der Abgabefrist noch nicht bestehen, bedeutet das noch nicht das Ausscheiden aus der KU für Sie. Es gibt die Möglichkeit einer **Ausbesserung**, welche anstelle von Assignment 3 gemacht werden kann. Diese Ausbesserung _ersetzt_ die bei A2 erreichten Punkte. Informationen dazu gab es im ersten Stream des Semesters am 3.3.2021.
#### Teilnahmevoraussetzung für Assignment 3
Da Assignment 3 auf A2 aufbauen wird, sind für eine Teilnahme an Assignment 3 zumindest zwei Drittel aller Test Cases in A2 zu bestehen.
#### Bewertung
- Bei Assignment 2 können 64 Punkte erreicht werden.
- Die Kriterien, nach welchen Ihr Programm bewertet wird, können Sie folgenden Dokumenten entnehmen:
- [Beurteilungsschema](https://tc.tugraz.at/main/mod/page/view.php?id=157196)
- [Bewertung von Übungsbeispielen](https://tc.tugraz.at/main/mod/page/view.php?id=138898)
- [Style Guide](https://tc.tugraz.at/main/mod/page/view.php?id=138899)
#### Updates
Während das Assignment von Ihnen bearbeitet wird, wird es noch zu dem einen oder anderen Update kommen (z. B. etwaige Änderungen/Präzisierungen an der Angabe oder vielleicht sogar die Veröffentlichung einer Möglichkeit auf Bonuspunkte). An diese Updates gelangen Sie über einen Upstream-Pull mit Git.
///---------------------------------------------------------------------------------------------------------------------
/// Random.cpp
///
/// Authors: Tutors
///---------------------------------------------------------------------------------------------------------------------
#include "Random.hpp"
#include <stdexcept>
#include <iostream>
#include <cstdlib>
#include <sstream>
#include <string>
using Oop::Random;
///---------------------------------------------------------------------------------------------------------------------
std::string const Random::CARD_SEED{"RAND_SEED"};
std::string const Random::ORIENTATION_SEED{"RAND_SEED"};
size_t const Random::ORIENTATION_MIN{0};
size_t const Random::ORIENTATION_MAX{3};
///---------------------------------------------------------------------------------------------------------------------
Random::Random() :
card_mersenne_twister_{getSeed(CARD_SEED)},
orientation_mersenne_twister_{getSeed(ORIENTATION_SEED)},
orientations_{ORIENTATION_MIN, ORIENTATION_MAX}
{
}
///---------------------------------------------------------------------------------------------------------------------
Random& Random::getInstance()
{
static Random instance{};
return instance;
}
///---------------------------------------------------------------------------------------------------------------------
size_t Random::getRandomCard(size_t const number_of_cards_left)
{
std::uniform_int_distribution<size_t> normal_distribution{1, number_of_cards_left};
return normal_distribution(card_mersenne_twister_);
}
///---------------------------------------------------------------------------------------------------------------------
size_t Random::getRandomOrientation()
{
return orientations_(orientation_mersenne_twister_);
}
///---------------------------------------------------------------------------------------------------------------------
size_t Random::getSeed(std::string const environment_variable)
{
size_t seed{};
try
{
seed = getEnvironmentSeed(environment_variable);
}
catch (std::runtime_error& exception)
{
seed = getHardwareSeed();
}
return seed;
}
///---------------------------------------------------------------------------------------------------------------------
size_t Random::getEnvironmentSeed(std::string const environment_variable)
{
char* environment_seed{getenv(environment_variable.c_str())};
if (environment_seed)
{
return parseSeed(environment_seed);
}
throw std::runtime_error{"Environment variable \"" + environment_variable + "\" does not exist."};
}
///---------------------------------------------------------------------------------------------------------------------
size_t Random::getHardwareSeed()
{
std::random_device seed_from_hardware{};
return seed_from_hardware();
}
///---------------------------------------------------------------------------------------------------------------------
size_t Random::parseSeed(std::string const seed)
{
std::stringstream seed_stream{seed};
size_t seed_value{};
seed_stream >> seed_value;
if (seed_stream.eof() && !seed_stream.bad())
{
return seed_value;
}
throw std::runtime_error{"Could not parse the seed \"" + seed + "\"."};
}
///---------------------------------------------------------------------------------------------------------------------
/// Random.hpp
///
/// Authors: Tutors
///---------------------------------------------------------------------------------------------------------------------
#ifndef RANDOM_HPP
#define RANDOM_HPP
#include <random>
#include <string>
namespace Oop
{
///-------------------------------------------------------------------------------------------------------------------
/// Random class
/// Generates random values representing the card to distribute or the rotation of the card.
///
class Random final
{
public:
///---------------------------------------------------------------------------------------------------------------
/// Returns an instance of the Random class (Singleton pattern).
/// Example: Random::getInstance().getRandomOrientation()
///
static Random& getInstance();
///---------------------------------------------------------------------------------------------------------------
/// Deleted copy constructor.
///
Random(Random const&) = delete;
///-----------------------------------------------------------------------------------------------------------------
/// Deleted assignment operator.
///
Random& operator=(Random const&) = delete;
///-----------------------------------------------------------------------------------------------------------------
/// Get the number of the next card to be distributed.
///
/// @param number_of_cards_left The total number of cards left to distribute.
/// @return A random number in range [1, number_of_cards_left].
///
size_t getRandomCard(size_t const number_of_cards_left);
///-----------------------------------------------------------------------------------------------------------------
/// Get the number of times the card should be rotated clock-wise.
///
/// @return A random number in range [0, 3]
///
size_t getRandomOrientation();
private:
///---------------------------------------------------------------------------------------------------------------
/// Mersenne Twister pseudo-random generators
///
std::mt19937 card_mersenne_twister_;
std::mt19937 orientation_mersenne_twister_;
///---------------------------------------------------------------------------------------------------------------
/// Uniform distribution of all possible orientations
///
std::uniform_int_distribution<size_t> orientations_;
///---------------------------------------------------------------------------------------------------------------
/// Names of the environment seeds
///
static std::string const CARD_SEED;
static std::string const ORIENTATION_SEED;
///---------------------------------------------------------------------------------------------------------------
/// Orientation number range: [min, max]
///
static size_t const ORIENTATION_MIN;
static size_t const ORIENTATION_MAX;
///---------------------------------------------------------------------------------------------------------------
/// Private constructor. Access to class possible only through getInstance.
///
Random();
///---------------------------------------------------------------------------------------------------------------
/// Gets the seed from environment.
/// If the environment variable does not exist it falls back to generating the seed.
///
/// @param environment_variable Name of the environment variable holding the seed.
/// @returns A number suitable to seed the random number generator.
///
size_t getSeed(std::string const environment_variable = nullptr);
///---------------------------------------------------------------------------------------------------------------
/// Gets the seed from environment.
///
/// @param environment_variable Name of the environment variable holding the seed.
/// @return A number from the environment_variable suitable to seed the random number generator.
///
size_t getEnvironmentSeed(std::string const environment_variable);
///---------------------------------------------------------------------------------------------------------------
/// Generates a random seed.
///
/// @return A hardware generated number suitable to seed the random number generator.
///
size_t getHardwareSeed();
///---------------------------------------------------------------------------------------------------------------
/// Parses the seed to a numerical value.
///
/// @param environment_seed Seed to parse.
/// @return The numerical value of the seed parameter.
///
size_t parseSeed(std::string const seed);
};
}
#endif
\ No newline at end of file
# A2 Angabe
Assignment 2 ist in zwei Milestones unterteilt. Es wird empfohlen, zuerst Milestone 1 abzuschließen, bevor mit Milestone 2 begonnen wird, da Milestone 2 auf Milestone 1 aufbaut.
Die genaue Beschreibung zu den einzelnen Milestones finden sich unter:
- [Milestone 1](Milestone_1.md)
## Milestone 1
Hier wird die Grundstruktur des Spiels (welche zum Teil schon vorgegeben ist) und einige Basisfunktionen erstellt.
\ No newline at end of file
# Angabe Milestone 1
Ziel des 1. Milestones ist es, eine Grundstruktur für das Spiel zu erstellen und das Spielfeld auszugeben. Im Rahmen von Milestone 1 werden demnach grundlegende Klassen erstellt.
## Vorgegebene Klassen
Mit dem Erstellen der Klassen sollte am besten begonnen werden, um schon eine Grundstruktur für das Spiel zu haben.
Die hier angegebenen Klassen können so implementiert werden, wie sie hier beschrieben werden. Es ist erlaubt, diese auch zu erweitern wenn dies notwendig erscheint oder eine andere (objektorientierte) Programmstruktur zu wählen.
Wenn in den folgenden Ausführungen kein Datentyp vorgegeben ist, soll eigenständig ein passender Datentyp gewählt werden.
Sofern nicht näher beschrieben, sind bei den Klassen Konstruktor(en) und Destruktor sowie Getter und Setter für alle Attribute zu implementieren.
### Klasse `Treasure`
Klasse, die einen Schatz repräsentiert. (Beim originalen Brettspiel wird zwischen Schatzkarten von Spielenden – sie geben die zu suchenden Schätze an – und Schätzen auf Gangkarten unterschieden. Im Gegensatz zum Brettspiel müssen wir hier nicht zwischen Schatzkarten und den tatsächlichen Schätzen unterscheiden – wir können aus dem Kontext erkennen, worum es sich handeln muss. Schätze im `card_stack_`s des `Player`s sind immer Schatzkarten, am Spielfeld sind es Schätze.)
#### Attribute
- `name_` (Name des Schatzes)
- `treasure_id_` (Nummer des Schatzes)
- `found_` (zeigt an, ob die Karte gefunden wurde)
#### Methoden
- Konstruktor mit den Parametern `(name, treasure_id)`
- Getter für `name_`, `treasure_id_` und `found_`
- Setter für `found_` (keine Setter für `name_` und `treasure_id_`)
### Klasse `Tile`
Klasse, die eine Gangkarte repräsentiert.
#### Enums
Diese Enum Klassen dienen zur Beschreibung der Gangkarten und können vor der Definition von Tile definiert werden.
```C++
enum class TileType {T, L, I, O, U, X};
```
Die Buchstaben stehen für die verschiedenen möglichen Spielfeldkarten:
- T oben geschlossen, sonst überall offen
- L oben und rechts offen, sonst geschlossen
- I oben und unten offen, sonst geschlossen
- U oben offen, sonst geschlossen
- X überall offen
- O überall geschlossen
Die Rotation beschreibt, um wie viel eine Gangkarte gedreht wurde. Das heißt ein "L" mit einer Rotation von "DEG180" ist dann links und unten offen und ansonsten geschlossen. Die Rotation enspricht immer der Rotation in mathematisch positive Richtung, das heißt Drehung gegen den Urzeigersinn.
```C++
enum class Rotation {DEG0 = 0, DEG90 = 1, DEG180 = 2, DEG270 = 3};
```
#### Attribute
- TileType `type_` (Richtung, wo offen)
- Rotation `rotation_` (Gibt die Orientierung des Tiles an)
- vector `players_` (enthält Zeiger zu `Player`n, welche sich auf der Gangkarte befinden)
#### Methoden
- pure virtual Methode `getTileString()` (gibt den String zurück, welcher das aktuelle Spielfeld auf der Map repräsentiert)
- `getRotationValue()` Gibt die Orientierung (rotation_) in Grad zurück. 0 -> 0°, 1 -> 90°, 2 -> 180° und 3 -> 270°.
- `string getTileTypeString()` (gibt den Kartentyp in Form eines Strings zurück. (entweder "T", "L", "I", "O", "U" oder "X").
- Getter für `type_`, `rotation_` und `players_`
- Setter für `rotation_` (kein Setter für `type_`)
### Klasse `StartTile` (Subklasse von `Tile`)
Klasse, die eine Gangkarte repräsentiert, welche auch als Startfeld für eine*n Spielende*n dienen kann.
#### Attribute
- `player_color_` (Zeigt an, für welchen `Player` die Gangkarte als Startfeld dient)
#### Methoden
- ein Getter für `player_color_`
- `getTileString()` (gibt den String zurück, welcher das aktuelle Spielfeld für die Map repräsentiert).
### Klasse `TreasureTile` (Subklasse von `Tile`)
Klasse, die eine Gangkarte mit einem Schatz repräsentiert.
#### Attribute
- `treasure_` (verweist auf den `Treasure`)
- `collected_` (zeigt an, ob der Schatz aufgesammelt wurde)
#### Methoden
- Setter, Getter
- `collectTreasure()` (für das Aufsammeln des Schatzes)
- `getTileString()` (gibt den String zurück, welcher das aktuelle Spielfeld für die Map repräsentiert).
### Klasse `NormalTile` (Subklasse von `Tile`)
Klasse, die eine Gangkarte repräsentiert, die kein Startfeld sein kann und auch keinen Gegenstand enthält.
#### Methoden
- `getTileString()` (gibt den String zurück, welcher das aktuelle Spielfeld für die Map repräsentiert).
### Klasse `Player`
Klasse, die eine Spielfigur bzw. eine*n Spielende*n repräsentiert.
#### Attribute
- `covered_stack_` Vektor mit dem (Treasure *) stack für die zu suchenden Schätze
- `nr_found_treasures_` Anzahl der gefundenen Schätze
### Klasse `Game`
Die Spielklasse regelt das Spiel und hat die Daten, die es dazu braucht. Diese gibt es nur ein Mal, weshalb sie als Singleton realisiert werden sollte. ([Erklärung Singleton Klasse](Singleton.md))
## Spielstart
Das Spiel wird ohne Kommandozeilenparameter gestartet und gibt zunächst folgenden Text aus:
```
Welcome to the Wild OOP Labyrinth!!!\n
```
Dann wird abgefragt, mit wie vielen Personen das Spiel gespielt wird:
```
Player Count (2-4):
```
Als Eingabe ist hier nur '2', '3' oder '4' erlaubt, also eine Zahl zwischen 2 und 4. Wird etwas anderes eingegeben, soll die folgende Meldung ausgegeben werden und wieder nach der Anzahl der Mitspielenden gefragt werden.
```
Wrong Input only a Number from 2 to 4 is allowed!\n
```
Nun muss eine Instanz der `Random` Klasse, welche zur Verfügung gestellt wird, erstellt werden, um eine zufällige Verteilung der Karten und des Spielfeldes zu erhalten. Es muss unbedingt die von der LV-Leitung bereitgestellte Klasse verwendet werden – ansonsten funktionieren die automatischen Tests nicht!
Die Instanz sollte solange das Spiel läuft verfügbar sein. Sie sollte also nur einmal erstellt werden und dann immer genutzt werden, wenn eine Zufallszahl benötigt wird.
Danach werden die Schatzkarten verteilt (wie im Abschnitt "Verteilen der Schatzkarten" beschrieben).
Nach dem Verteilen der Schatzkarten wird das Spielfeld belegt (wie unter "Belegen des Spielplans" beschrieben).
Anschließend wird das Spielfeld ein Mal ausgegeben. Danach wird ein Prompt mit dem aktiven `Player` ausgegeben und auf den Input des/der Spielenden gewartet.
Die Reihenfolge der `Player` ist Red, Yellow, Green und dann Blue.
Die Prompt sieht wie folgt aus:
```
PLAYERCOLOR >
```
Dies wäre am Anfang also:
```
RED >
```
Wird `EOF` eingegeben (End of File, nicht der String "EOF"), wird das Programm mit dem Returnwert 0 beendet.
In Milestone 1 müssen noch keine weiteren Usereingaben für den Prompt behandelt werden. Für Milestone 1 reicht es also, nur auf `EOF` bei der Usereingabe zu achten.
## Verteilen der Schatzkarten
Die Schatzkarten haben eine feste ID.
Die IDs der einzelnen Schätze können folgender Tabelle entnommen werden:
| Schatznummer | Schatzname |
| ------------- |:-------------:|
| 01 | Totenkopf |
| 02 | Schwert |
| 03 | Goldsack |
| 04 | Schlüsselbund |
| 05 | Sombrero |
| 06 | Ritterhelm |
| 07 | Buch |
| 08 | Krone |
| 09 | Schatztruhe |
| 10 | Kerzenleuchte |
| 11 | Schatzkarte |
| 12 | Goldring |
| 13 | Eule |
| 14 | Hofnarr |
| 15 | Eidechse |
| 16 | Käfer |
| 17 | Flaschengeist |
| 18 | Kobold |
| 19 | Schlange |
| 20 | Geist |
| 21 | Fledermaus |
| 22 | Spinne |
| 23 | Ratte |
| 24 | Motte |
Am Anfang jedes Spieles werden diese Karten genau in dieser Reihenfolge erstellt und dann folgendermaßen auf die Mitspielenden aufgeteilt.
Es wird mit der bereitgestellten `Random`-Klasse eine Zahl von 1 bis zur Anzahl der noch nicht ausgeteilten Schatzkarten bestimmt (Aufruf der Funktion get `getRandomCard()`) und die Karte, welche am Stapel (Array/Vector) diese Position hat, an den nächsten Player ausgeteilt. Die Reihenfolge der Farben ist Rot, Gelb, Grün und Blau.
Spielen weniger als 4 Personen das Spiel, so wird/werden die Farbe/Farben am Ende ausgelassen. Bei einem Spiel mit 3 Personen gibt es also nur die Farben Rot, Gelb und Grün, bei 2 Personen nur Rot und Gelb.
Die Karten werden der Reihe nach ausgegeben, bis es keine Karte mehr zum Verteilen gibt, also alle Karten einem Player zugewiesen wurden.
## Ausgabe des Spielfelds
Das Spielfeld, auf dem die Karten liegen, soll als ASCII-Art im Terminal ausgegeben werden. Eine Karte besteht dabei aus 9*5 Zeichen (9 Spalten und 5 Zeilen). Sofern sich etwas/jemand auf der Karte befindet, soll dies mit einem Charakter angezeigt werden:
`P` für einen Player direkt gefolgt vom Anfangsbuchstaben der Farbe; für Player Blue also `PB`. Stehen mehrere Player auf einer Karte so wird P direkt gefolgt von den Buchstaben der Player ausgegeben. Wären also Player Red und Yellow auf der gleichen Karte, wäre die entsprechende Ausgabe `PRY`.
Die Ausgabe erfolgt in der 4. Zeile der Karte mit einem Abstand von 3 Zeichen zum linken Rand außer bei 4 Playern, dort sind es nur 2 Zeichen Abstand.
Ein Schatz auf einer Karte wird mit `T` und der zweistelligen Schatz-ID angezeigt; für Schatz 1 wäre dies z. B. `T01`.
Befindet sich auf einer Karte ein Schatz, so wird dies in der 3. Zeile der Karte angezeigt mit einem Abstand von 3 Zeichen von links.
Wichtig: Wurde ein Schatz schon gefunden, das heißt `collected_` ist `true`, so wird der Schatz nicht mehr angezeigt.
Die ASCII-Karten bestehen aus den Zeichen ``'█'`` und ``' '``.
Folgende ASCII-Karten existieren: (Die Buchstaben zeigen dabei an, wo sich die Zeichen für einen Schatz / Player befinden sollen.)
Gerader Gang horizontal
```
█████████
T01
PY
█████████
```
Gerader Gang vertikal
```
██ ██
██ ██
██ ██
██ ██
██ ██
```
Verzweigung nach rechts
```
██ ██
██
██
██
██ ██
```
Verzweigung nach oben
```
██ ██
█████████
```
Verzweigung nach links
```
██ ██
██
██
██
██ ██
```
Verzweigung nach unten
```
█████████
██ ██
```
Kurve oben-rechts
```
██ ██
██
██
██
█████████
```
Kurve oben-links
```
██ ██
██
██
██
█████████
```
Kurve unten-links
```
█████████
██
██
██
██ ██
```
Kurve unten-rechts
```
█████████
██
██
██
██ ██
```
## Spielfeld
Beispiel:
```
Player Red(R) | | | Player Yellow(Y)
Treasure: X/6 V V V Treasure: X/6
1 2 3 4 5 6 7
███████████ █████████████ █████████████████████████████
██ (R) ██ ██ ██ ██ (Y) ██
1██ ██ ██ T01 ██ ██ T02 ██
██ ██ ██ ██ ██ ██
██ ████ ████ ████ ████ █████████████ ██
██ ████ ████ █████████████ ████ ████ ██
████ ████ ██ ██
-->2 ████ ████ T13 T14 ██ S15 ██ <--
████ ████ ██ ██
███████████ ████████████████████████████████████████ ██
██ █████████████ ████ █████████████ ████ ██
██ ██ ██
3██ T03 ██ T04 T05 T06 ██
██ ██ ██
██ █████████████ █████████████ █████████████ ██
███████████ ████ ████ █████████████ ████ ██
████ ██ ██ ██
-->4 ████ T16 ██ ██ T17 ██ <--
████ ██ ██ ██
██████████████████████████████████████ ████████████████████
██ ████ ████ █████████████ █████████████ ██
██ ██ ████ ██
5██ T07 T18 ██ T08 T09 ████ T19 T10 ██
██ ██ ████ ██
██ ██████████████████████ ████ ████ ████ ██
██ █████████████ ████ ████ ████ ███████████
██ ████ ██ ██
-->6 T20 ██ T21 ████ ██ T22 ██ T23 <--
██ ████ ██ ██
████████████████████ ████ █████████████████████████████
██ ████ ████ ████ ████ █████████████ ██
██ (G) ██ ██ (B) ██
7██ ██ T11 T24 ██ T12 ██
██ ██ ██ ██
███████████ ███████████████████████████████ ███████████
Player Green(G) Ʌ Ʌ Ʌ Player Blue(B)
Treasure: X/6 | | | Treasure: X/6
```
Wie beim Brettspiel soll angezeigt werden, wo Karten verschoben werden können und wo nicht. Pfeile rund um das Spielfeld markieren bewegliche Reihen und Spalten.
Für jede&ast;n Spielende&ast;n wird auch angezeit, wie viele Schätze schon eingesammelt wurden und wie viele noch zu finden sind. Dabei steht das X in dem obigen Beispiel für die Anzahl der von dem jeweiligen Spielenden bereits gefundenen Schätze und die 6 steht für die insgesamt zu findenden Schätze. Die Anzahl der zu findenden Schätze pro Person, hängt von der Anzahl der Spielenden ab. Bei zwei Speilenden müsste jede&ast;r Spielende nicht 6 Schätze einsammeln sondern 12 und bei 3 Spielenden müssen alle 8 Schätze finden. Es werden nur für mitspielende Player Informationen zu Farbe und Schätzen angezeigt. Player, die nicht im Spiel sind (dieser Fall tritt bei weniger als 4 Spielenden auf) würden in obiger Ausgabe keine Erwähnung finden.
## Belegen des Spielplans
Es gibt auf dem Spielplan fix zugewiesene Karten. Diese sind:
1:1, 1:3, 1:5, 1:7\
3:1, 3:3, 3:5, 3:7\
5:1, 5:3, 5:5, 5:7\
7:1, 7:3, 7:5, 7:7
Dadurch ergibt sich der folgende vorgegebene Spielplan:
```
█████████ █████████ █████████ █████████
██ ██
██ (R) T01 T02 (Y) ██
██ ██
██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ █████████ ██ ██
██ ██ ██
██ T03 ██ T04 T05 T06 ██
██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██
██ T07 T08 T09 ██ T10 ██
██ ██ ██
██ ██ █████████ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██
██ ██
██ (G) T11 T12 (B) ██
██ ██
█████████ █████████ █████████ █████████
```
Diese Karten des Spielplans können auch nicht verschoben werden und sind dadurch immer fix vorgegeben.
Die restlichen Karten werden am Anfang des Spieles immer neu verteilt.
Karten, welche nicht fix auf dem Spielfeld sind: 11 Karten mit Kurve, 11 Karten mit Durchgang und 12 Karten mit Verzweigung und Schatz.
Zum Belegen des Spielplans werden diese Karten nun aneinander in einem Array/Vector gereiht, damit aus diesen zufällig eine Karte ausgesucht werden kann. So kommen zuerst alle Karten mit Kurve, dann alle Karten mit Durchgang und schlussendlich alle Karten mit Verzweigung.
Nun soll mit der zur Verfügung gestellten Klasse "Random" und der Funktion "getRandomCard" eine Zahl von 1 bis zur Anzahl der noch nicht gelegten Karten auf dem Stapel gewählt werden. Dann wird die Karte, welche im Stapel an der zuvor bestimmten Zahl ist, genommen und auf die erste noch freie Stelle gelegt mit einer Orientierung welche ebenfalls durch die Random Klasse (Funktion: "getRandomOrientation") bestimmt wird. Hier ist die erste freie Stelle immer diejenige welche von oben links beginnend noch keine Karte auf dem Spielplan hat. "getRandomOrientation" gibt ein zufällige Zahl von der Menge {0, 1 , 2, 3} zurück, dabei steht diese Zahl für die Anzahl wie oft das jeweilige Feld um 90° nach links gedreht werden soll.
Am Anfang wäre dies 1:2, dann 1:4, dann 1:6, dann 2:1 usw.
Dies wird solange wiederholt, bis nur noch eine Karte übrig und das Spielfeld voll bedeckt ist. Die nicht gelegte Karte ist jene, welche als freie Karte zum Verschieben verwendet wird.
# Singleton
Ziel dieser Seite ist es, den Befriff Singleton für das Assignement so weit zu erklären, dass Singletons verstanden und für den gewünschten Zweck verwendet werden können. Genauere Infos über Singletons in C++, können selbständig in Büchern oder im Internet recherchiert werden.
## Algemein
Ein Singleton stellt sicher, dass es nur genau ein Objekt einer Klasse gibt. Wird eine Klasse also als Singleton implementiert, so kann es nicht mehr als eine Instanz der Klasse geben.
## Beispiel ein Singleton Klasse
```C++
#ifndef TEST_SINGLETONCLASS_H
#define TEST_SINGLETONCLASS_H
class SingletonClass
{
public:
static SingletonClass& instance()
{
static SingletonClass instance_;
return instance_;
}
~ SingletonClass() {}
void function();
private:
SingletonClass() :variable(20) {}
SingletonClass( const SingletonClass& );
SingletonClass& operator = (const SingletonClass&);
int variable;
};
#endif
```
Dadurch, dass Konstruktor, Copy-Konstruktor und Copy-Assignment-Operator`private` sind, kann eine Instanz von `SingletonClass` nur innerhalb dieser Klasse erstellt werden. Von außen kann ein `SingletonClass`-Objekt über die `static`-Methode `instance` angefordert werden. Auch bei wiederholten Aufrufen von `instance` wird immer die selbe Instanz zurückgegeben.
## Verwendung der Klasse
Um die Singleton Klasse zu verwenden kann entweder die Instanz als Referenz verwendet werdenoder direkt auf die Instanz verwendet werden.
```C++
SingletonClass &singleton = SingletonClass::instance();
singleton.function();
// oder dierekte Verwendung
SingletonClass::instance().function();
```
Um die Singleton Klasse zu verwenden muss diese lediglich inkludiert werden. Sobald diese irgendwo inkludiert und verwendet wurde wird auch eine Instanz dafür angelegt.
File added
File added
File added
File added
#include <cstdio>
#include <cstring>
// TODO: destructor (free memory allocated on the heap (for name_))
// TODO: copy constructor (implement deep copy)
class Pig {
char* name_; // longer names now supported (compared to `char name_[10];`)
unsigned age_;
public:
Pig(const char* name, unsigned age)
: name_{ new char[strlen(name) + 1] }, age_{age}
{
strcpy(name_, name);
}
void drink() { printf("%s drinks\n", name_); }
void bath() { printf("Bath! :D\n"); }
unsigned getAge() { return age_; }
void setAge(unsigned age) { age_ = age; }
};
class Cat { // not a pig
char* name_;
unsigned age_;
public:
Cat(const char* name, unsigned age)
: name_{ new char[strlen(name) + 1] }, age_{age}
{
strcpy(name_, name);
}
void drink() { printf("%s drinks\n", name_); }
void play() { printf("Play! :D\n"); } // cats play and don't take a bath
unsigned getAge() { return age_; }
void setAge(unsigned age) { age_ = age; }
};
int main()
{
Pig pig{ "Babe", 0 };
pig.bath();
pig.drink();
Cat cat{ "Grumpy", 7 };
cat.play();
// cat.bath(); // <-- error: no member named 'bath' in 'Cat' (Cats don't enjoy taking a bath – only pigs do)
cat.drink();
return 0;
}
# include "pet.hpp"
// TODO: add include guards (see pet.hpp for an example)
class Cat : public Pet {
public:
Cat(const char* name, unsigned age)
: Pet{ name, age } {}
Cat(const Cat& other) = delete;
~Cat() = default;
void play() { printf("%s plays!\n", name_); }
};
\ No newline at end of file
#include <cstdio>
#include "pig.hpp"
#include "cat.hpp"
int main()
{
Pig pig{ "Babe", 0 };
pig.drink();
pig.bath();
Cat cat{ "Grumpy", 0 };
cat.drink();
cat.play();
return 0;
}
\ No newline at end of file
#ifndef PET_H
#define PET_H
#include <cstring>
#include <cstdio>
class Pet {
unsigned age_;
protected:
char* name_;
public:
Pet(const char* name, unsigned age)
: age_{age}, name_{ new char[strlen(name) + 1] }
{
strcpy(name_, name);
}
Pet(const Pet& other) = delete;
~Pet() { delete[] name_; }
unsigned getAge() { return age_; }
void setAge(unsigned age) { age_ = age; }
void drink() { printf("%s drinks\n", name_); }
};
#endif
\ No newline at end of file
# include "pet.hpp"
// TODO: add include guards (see pet.hpp for an example)
class Pig : public Pet {
public:
// using Pet::Pet; // possible instead of defining the constructor for Pig. Using this line, Pig inherits Pet's constructor.
Pig(const char* name, unsigned age)
: Pet{ name, age } {}
Pig(const Pig& other) = delete;
~Pig() = default;
void bath() { printf("%s enjoys taking a bath!\n", name_); }
};
\ No newline at end of file
# include "pet.hpp"
// TODO: add include guards (see pet.hpp for an example)
class Cat : public Pet {
public:
Cat(const char* name, unsigned age)
: Pet{ name, age } {}
Cat(const Cat& other) = delete;
~Cat() = default;
void play() { printf("%s plays!\n", name_); }
};
#include <cstdio>
#include "pig.hpp"
#include "cat.hpp"
int main() {
Pet* pets[2]{
new Pig{ "Babe", 0 },
new Cat{ "Grumpy", 7}
};
for (auto pet : pets) {
pet->drink();
}
return 0;
}
#ifndef PET_H
#define PET_H
#include <cstring>
#include <cstdio>
class Pet {
unsigned age_;
protected:
char* name_;
public:
Pet(const char* name, unsigned age)
: age_{age}, name_{ new char[strlen(name) + 1] }
{
strcpy(name_, name);
}
Pet(const Pet& other) = delete;
virtual ~Pet() { delete[] name_; }
unsigned getAge() { return age_; }
void setAge(unsigned age) { age_ = age; }
// virtual void drink() = 0; // if we use this line instead of the next, Pet becomes an abstract class.
virtual void drink() { printf("%s drinks.\n", name_); }
};
#endif
# include "pet.hpp"
// TODO: add include guards (see pet.hpp for an example)
class Pig : public Pet {
public:
// using Pet::Pet; // possible instead of defining the constructor for Pig. Using this line, Pig inherits Pet's constructor.
Pig(const char* name, unsigned age)
: Pet{ name, age } {}
Pig(const Pig& other) = delete;
~Pig() = default;
void bath() { printf("%s enjoys taking a bath!\n", name_); }
void drink() override { printf("%s drinks water.\n", name_); }
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment