Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- PABS
- Courses
- Grundlagen der Programmierung
- People's Democratic Dictatorship
- Richard Wiebe (s364302) (Logout)
- People's Democratic Dictatorship
- People's Democratic Dictatorship (dt. Demokratische Volksdiktatur) ist ein erfundenes Kartenspiel für mindestens zwei Spieler, in dem es ähnlich wie bei Mau-Mau darum geht, alle seine Handkarten abzuwerfen. Zu Beginn denkt sich jeder Spieler eine Spielregel aus, nach der die Karten auf den Ablagestapel gelegt werden dürfen, ohne sie den anderen Spielern zu verraten. Gespielt wird im Uhrzeigersinn. Jeder Spieler darf wahlweise eine Karte ablegen, ohne dabei gegen die von ihm aufgestellte Regel zu verstoßen, oder eine Karte vom Nachziehstapel ziehen. Verletzt ein Spieler beim Ablegen eine oder mehrere Regeln der anderen Spieler, weisen diese ihn darauf hin und er muss eine Karte ziehen. Das Spiel endet entweder, wenn ein Spieler keine Karten mehr hat, oder der Nachziehstapel aufgebraucht ist und auch nicht mehr durch abgeworfene Karten aufgefüllt werden kann.
- Implementierung
- Die Implementierung gliedert sich grob in drei Teile. Zuerst modellieren wir die Karten, sowie Nachzieh- und Ablagestapel. Danach bauen wir einige Klassen zur Darstellung der Regeln, die die Spieler sich ausdenken können. Zuletzt gilt es die Spielmechanik sowie eine simple KI für das Spiel zu implementieren.
- Teil 1: Deck
- Das Enum Suit
- Um die Farben der Spielkarten darzustellen, erstellen Sie im Paket de.uniwue.gdp.pdd.deck das Enum Suit. In Java werden Enums wie normale Klassen deklariert, indem man statt des Schlüsselwortes class das Schlüsselwort enum verwendet. Ein Enum ist eine Aufzählung einer festen Anzahl von Werten. In diesem Fall die möglichen Farben einer Spielkarte im klassischen französischen Blatt Karo, Kreuz, Herz und Pik. Wir wählen für unsere Konstanten die englischen Bezeichnungen und schreiben sie, wie für Konstanten in Java üblich, in Großbuchstaben.
- DIAMONDS
- CLUBS
- HEARTS
- SPADES
- Die Klasse Card
- Erstellen Sie im Paket de.uniwue.gdp.pdd.deck die Klasse Card, die eine einzelne Spielkarte in unserem Deck modelliert.
- Erstellen Sie mindestens den folgenden Konstruktor.
- public Card(Suit suit, int value): Erstellt eine neue Karte mit der angegebenen Farbe und dem angegebenen Wert. Die Werte entsprechen dabei den Zahlenwerten mit Ass = 1, Bube = 11, Dame = 12 und König = 13.
- Erstellen Sie weiterhin die folgenden Methoden.
- public Suit suit(): Gibt die Farbe der Karte zurück.
- public int value(): Gibt den Wert der Karte zurück.
- Überschreiben Sie außerdem die Methoden equals und hashCode, sodass zwei Karten genau dann als gleich angesehen werden, wenn sie in Wert und Farbe übereinstimmen. (Tipp: Enums können mit == verglichen werden.) Überschreiben Sie die Methode toString nach dem Schema "<value> of <suit>".
- Das Interface Deck
- Erstellen Sie im Paket de.uniwue.gdp.pdd.deck das Interface Deck, das gemeinsame Methoden für verschiedene Kartenblätter bereitstellt.
- Das Interface soll genau die folgenden Methoden definieren.
- Card take();: Eine Karte vom Nachziehstapel ziehen.
- void play(Card card);: Eine Karte auf den Ablagestapel spielen.
- Card top();: Gibt die oberste Karte des Ablagestapels zurück.
- Die Klasse OutOfCardsException
- Erstellen Sie im Paket de.uniwue.gdp.pdd.deck die Klasse OutOfCardsException, die von der Klasse RuntimeException erbt und immer dann geworfen werden soll, wenn uns die Karten ausgehen.
- Implementieren Sie die folgenden Konstruktoren der Basisklasse.
- public OutOfCardsException(): Erstellt eine neue Instanz der Klasse.
- public OutOfCardsException(String message): Erstellt eine neue Instanz der Klasse mit der angegebenen Fehlermeldung.
- public OutOfCardsException(String message, Throwable cause): Erstellt eine neue Instanz der Klasse mit der angegebenen Fehlermeldung und Ursache.
- public OutOfCardsException(Throwable cause): Erstellt eine neue Instanz der Klasse mit der angegebenen Ursache.
- Es ist keine Funktionalität gefordert, die über die der Basisklasse hinausgeht.
- Die Klasse Standard52
- Erstellen Sie im Paket de.uniwue.gdp.pdd.deck die Klasse Standard52, die das Interface Deck implementiert und den Standardkartensatz mit 52 unterschiedlichen Karten darstellt.
- Erstellen Sie einen privaten Konstruktor, mit dem Sie Ihre Klasse instantiieren können.
- Erstellen Sie zwei statische Methoden für einen einfachen und einen doppelten Kartensatz. Diese Methoden ersetzen einen öffentlichen Konstruktor.
- public static Deck singleDeck(): Erstellt ein neues Deck mit dem einfachen Kartensatz.
- public static Deck doubleDeck(): Erstellt ein neues Deck mit doppeltem Kartensatz (jede Karte kommt zweimal vor, insgesamt also 104 Karten).
- Nach dem Erstellen eines neuen Decks soll eine zufällige erste Karte auf dem Ablagestapel liegen. Alle anderen Karten sollen gemischt den Nachziehstapel bilden. Ist der Nachziehstapel aufgebraucht, wird der Ablagestapel, bis auf die oberste Karte, gemischt und bildet den neuen Nachziehstapel. Besteht der Ablagestapel zu diesem Zeitpunkt aus nur einer Karte, wird eine OutOfCardsException geworfen.
- Teil 2: Regeln
- Eine Regel im Sinne dieses Spiels ist eine Funktion, die als Parameter die letzte oberste Karte des Ablagestapels sowie die gerade gespielte Karte erhält und einen Wahrheitswert zurück gibt, ob diese Kombination der Regel entspricht. Eine Funktion mit zwei Parametern und Rückgabetyp boolean heißt im Java Jargon BiPredicate. Die Parametertypen werden dabei über zwei generische Typparameter angegeben. Unsere Regeln sind entsprechend vom Typ BiPredicate<Card, Card>. Die einzige abstrakte Methode, deren Implementierung das Interface vorschreibt ist damit boolean test(Card t, Card u). Der erste Parameter t stellt dabei die oberste Karte des Ablagestapels dar, der zweite u die gerade ausgespielte Karte. Wenn Sie die Methode in Ihren Implementierungen überschreiben, sollten Sie von der Möglichkeit gebrauch machen, die beiden Parameter sinnvoll zu benennen. Das Interface BiPredicate definiert bereits einige Operationen, die sich auf solchen Funktionen definieren lassen. Für diese Aufgabe wichtig werden die booleschen Operatoren and, or und not.
- Die Klasse Rules
- Erstellen Sie im Paket de.uniwue.gdp.pdd.rules die Klasse Rules. Hier werden wir einige Beispielregeln implementieren, die sich durch boolesche Operatoren zu komplexeren Regelgebilden zusammenfügen lassen.
- Erstellen Sie mindestens die folgenden Methoden.
- public static BiPredicate<Card, Card> sameValueRule(): Es dürfen nur Karten mit gleichem Wert abgelegt werden.
- public static BiPredicate<Card, Card> sameSuitRule(): Es dürfen nur Karten mit gleicher Farbe abgelegt werden.
- public static BiPredicate<Card, Card> increasingValueRule(): Es dürfen nur Karten mit höherem Wert abgelegt werden.
- public static BiPredicate<Card, Card> jokerRule(Card joker): Es dürfen nur Karten des angegebenen Kartentyps abgelegt werden, egal welche Karte gerade auf dem Ablagestapel liegt.
- Die Klasse ExtensibleRule
- Für die KI, die unser Spiel spielen soll, ist es sinnvoll eine Regel zu schaffen, die sich nach und nach um das gesammelte Wissen über erlaubte Spielzüge erweitern lässt.
- Erstellen Sie im Paket de.uniwue.gdp.pdd.rules die Klasse ExtensibleRule, die das Interface BiPredicate<Card, Card> implementiert.
- Erstellen Sie mindestens den folgenden Konstruktor.
- public ExtensibleRule(BiPredicate<Card, Card> base): Erstellt eine neue Instanz mit der angegebenen Basisregel. Die Basisregel ist die vom entsprechenden Spieler selbst aufgestellte Regel gegen die auf keinen Fall verstoßen werden darf.
- Erstellen Sie weiterhin die folgenden Methoden.
- public void addAllowed(Card top, Card played): Fügt der Wissensbasis eine Zuordnung von einer erlaubten Kombination aus oberster und gespielter Karte hinzu.
- public void addForbidden(Card top, Card played): Fügt der Wissensbasis eine Zuordnung von einer Kombination aus oberster und gespielter Karte hinzu, die gegen eine der Regeln verstößt.
- public boolean test(Card top, Card played): Testet ob die angegeben Kombination aus oberster und gespielter Karte der Basisregel entspricht und nach dem Wissensstand über verbotene Spielzüge nicht verboten ist. Diese Methode überschreibt die Methode boolean test(T t, U u) aus dem Interface BiPredicate. Kennzeichnen Sie dies durch die Annotation @Override, sodass der Compiler die Deklaration auf Korrektheit prüft.
- public boolean testAllowed(Card top, Card played): Testet ob die angegeben Kombination aus oberster und gespielter Karte der Basisregel entspricht und nach dem Wissensstand über erlaubte Spielzüge erlaubt ist.
- Teil 3: Spielmechanik und KI
- Die Klasse IllegalMoveException
- Erstellen Sie im Paket de.uniwue.gdp.pdd die Klasse IllegalMoveException, die von der Klasse RuntimeException erbt und immer dann geworfen werden soll, wenn ein Spieler gegen seine eigene Regel verstößt.
- Implementieren Sie die folgenden Konstruktoren der Basisklasse.
- public IllegalMoveException(): Erstellt eine neue Instanz der Klasse.
- public IllegalMoveException(String message): Erstellt eine neue Instanz der Klasse mit der angegebenen Fehlermeldung.
- public IllegalMoveException(String message, Throwable cause): Erstellt eine neue Instanz der Klasse mit der angegebenen Fehlermeldung und Ursache.
- public IllegalMoveException(Throwable cause): Erstellt eine neue Instanz der Klasse mit der angegebenen Ursache.
- Es ist keine Funktionalität gefordert, die über die der Basisklasse hinausgeht.
- Das Interface GameListener
- Um die einzelnen Spieler, vor allem die, die von einer KI gesteuert werden, über den Spielverlauf zu informieren verwenden wir das Interface GameListener. Dieses definiert Methoden, über die unser Game Klasse später den Spielern Informationen zukommen lassen kann. Dieses Entwurfsmuster wird oft auch Observer genannt.
- Erstellen Sie im Paket de.uniwue.gdp.pdd das Interface GameListener.
- Das Interface soll genau die folgenden Methoden definieren.
- void validMove(Player player, Card top, Card played);: Wird aufgerufen, wenn ein Spielzug nicht zu beanstanden war.
- void invalidMove(Player player, Card top, Card played);: Wird aufgerufen, wenn ein Spielzug gegen eine der Regeln verstieß.
- Die abstrakte Klasse Player
- Erstellen Sie im Paket de.uniwue.gdp.pdd die abstrakte Klasse Player, die das Interface GameListener implementiert. Sie bildet die Basis für verschiedene Implementierung von Spielerstrategien.
- Erstellen Sie mindestens das folgende Feld, das erbende Klassen nutzen dürfen.
- protected final List<Card> hand;: Die Hand des Spielers, also die Menge von Karten, die er momentan auf der Hand hält.
- Erstellen Sie den folgenden Konstruktor.
- protected Player(String name): Erstellt einen neuen Spieler mit dem angegebenen Namen.
- Erstellen Sie mindestens die folgenden Methoden.
- public void startNewGame(Collection<Card> hand): Ersetzt die aktuellen Handkarten durch die Übergebenen. Wird am Beginn eines jeden Spiels aufgerufen. Achten Sie darauf, dass Änderungen an der übergebenen Collection nicht zu Änderungen der Handkarten führen.
- public void take(Card card): Der Spieler nimmt die angegebene Karte auf die Hand.
- public int numberOfCards(): Gibt zurück, wie viele Karten der Spieler aktuell auf der Hand hat.
- public String name(): Gibt den Namen des Spielers zurück.
- public abstract BiPredicate<Card, Card> invent();: Wird von erbenden Klassen überschrieben; Gibt die erfundene Spielregel dieses Spielers zurück.
- public abstract Optional<Card> play(Card top);: Wird von erbenden Klassen überschrieben; Spielt abhängig von der obersten Karte des Ablagestapels eine Karte aus der Hand aus oder nicht. Die Klasse Optional wird verwendet um anzuzeigen, ob eine Karte gespielt wurde oder nicht.
- Überschreiben Sie außerdem die toString Methode so, dass der Name des Spielers ausgegeben wird.
- Die Klasse Game
- Erstellen Sie im Paket de.uniwue.gdp.pdd die Klasse Game. Sie bildet das Rückgrat dieser Implementierung und setzt einen Großteil der Spielmechanik um. Ein Spielzug läuft dabei folgendermaßen ab.
- Reihum, der Reihenfolge der übergebenen Liste folgend, ist jeder Spieler einmal dran.
- Der Spieler, der dran ist, kann eine Karte ablegen. Tut er das nicht, muss er eine Karte ziehen.
- Hat er eine Karte abgelegt, wird beurteilt, ob der Zug regelkonform war. Verstößt der Zug gegen die Regel des Spielers selbst, ist er ungültig und die entsprechende Exception wird geworfen. Verstößt der Zug gegen eine der anderen Regeln, muss der Spieler eine Karte ziehen.
- Hat der Spieler keine Handkarten mehr und musste auch keine neuen ziehen, ist das Spiel vorbei und er hat gewonnen.
- Alle Spieler werden über die gerade gespielte Karte informiert.
- Erstellen Sie den folgenden Konstruktor.
- public Game(List<Player> players, Deck deck): Erstellt ein neues Spiel mit den angegebenen Spielern und dem gegebenen Kartensatz.
- Erstellen Sie mindestens die folgenden Methoden.
- public void start(): Startet ein neues Spiel. Jeder Spieler erhält zu Beginn sieben Karten. Die Methode soll solange Spielzüge durchführen, bis das Spiel zu Ende ist.
- public Optional<Player> winner(): gibt den Sieger des aktuellen Spiels aus oder empty wenn noch kein Sieger feststeht.
- Die Klasse SimplePlayer
- Zuletzt erstellen wir eine beispielhafte Implementierung einer einfachen KI, die das Spiel spielt.
- Erstellen Sie im Paket de.uniwue.gdp.pdd die Klasse SimplePlayer, die von Player erbt.
- Erstellen Sie folgende Konstruktoren.
- public SimplePlayer(String name): Erstellt eine neue Instanz der Klasse mit dem angegebenen Namen. Instanzen, die mit diesem Konstruktor erstellt wurden, sollen immer folgende Regel verwenden. Wie bei Mau-Mau dürfen Karten mit gleicher Farbe oder gleichem Wert abgelegt werden. Zusätzlich sind alle Buben Joker, dürfen also immer abgelegt werden.
- public SimplePlayer(String name, BiPredicate<Card, Card> rule): Erstellt eine neue Instanz der Klasse mit dem angegeben Namen und der angegebenen Spielregel.
- Überschreiben Sie die Methode invent, sodass immer die entsprechende Spielregel zurückgegeben wird.
- Überschreiben Sie die Methode play, sodass folgende Strategie umgesetzt wird.
- Gibt es keine Handkarte die im Einklang mit der eigenen Spielregel gespielt werden könnte spiele nicht.
- Sonst spiele eine Handkarte mit entsprechender Präzedenz:
- Die Kombination aus oberster Karte und zu spielender Karte ist als erlaubt bekannt.
- Die Kombination aus oberster Karte und zu spielender Karte ist unbekannt.
- Die Kombination aus oberster Karte und zu spielender Karte ist als verboten bekannt.
- Verwenden Sie die Methoden aus dem GameListener Interface, um Ihre Wissenssammlung aktuell zu halten.
- Bewertung
- In dieser Aufgabe können Sie auch durch unvollständige oder fehlerhafte Abgaben Punkte erlangen. Die Punktzahl richtet sich dabei nach den bestandenen Unit-Tests. PABS wird Ihre Abgabe als `accepted` markieren, egal wie viele PABS Tests Sie bestanden haben. Lassen Sie sich von einer grün markierten Aufgabe also nicht in die Irre führen.
- Viel Spaß!
- PABS 3.1 - University of Würzburg
Advertisement
Add Comment
Please, Sign In to add comment