Das Kartenspiel Mau-Mau

1. Aufgabe

Wir betrachten in dieser Aufgabe den bekannten Kartenspiel-Klassiker Mau-Mau. Für das Spiel sind eine Reihe von C++-Klassen zu entwickeln, die wir in den ersten Abschnitten zunächst unabhängig voneinander betrachten. Erst am Schluss der Aufgabe fügen wir diese Klassen zu einem Ganzen zusammen. Die Grundregeln des Spiels sind folgende:

Spielvorbereitung::

  • An einem Kartentisch sitzen mehrere Kartenspieler zusammen. Auf dem Tisch liegt ein Stapel mit verdeckten Spielkarten.

  • Jeder Spieler erhält vor Spielbeginn fünf Karten. Dann wird eine Karte vom Stapel genommen und aufgedeckt. Während des Spiels gibt es also zwei Kartenstapel: Einen Stapel zum Ziehen von (verdeckten) Karten (Ziehstapel), einen zweiten Stapel zum Ablegen von (aufgedeckten) Karten (Ablagestapel).

Spielverlauf:

  • Derjenige Spieler, der an der Reihe ist, legt eine seiner Karten offen auf den Ablagestapel. Die Karte, die der Spieler ablegen möchte, muss entweder die gleiche Farbe (Karo, Herz, Pik, Kreuz) oder das gleiche Bild (Sieben, Acht, Neun, Zehn, Dame, König, Ass) wie die zuletzt aufgedeckte Karte haben. Auf die Pik-10 darf also entweder eine andere Pik-Karte oder eine andere 10 gelegt werden. Kann ein Spieler keine Karte ablegen, muss er eine Karte vom Ziehstapel ziehen und warten, bis er erneut an der Reihe ist.

  • Ist der Stapel mit den verdeckten Karten irgendwann aufgebraucht, so werden die abgelegten Karten, außer der obersten, gemischt und als neuer Stapel ausgelegt.

  • Gewonnen hat, wer zuerst alle seine Karten abgelegt hat. Das Spiel wird mit den übrigen Spielern solange fortgesetzt, bis nur noch ein Spieler übrig bleibt.

Karten mit besonderer Bedeutung:

  • Karte mit Bild Acht:

    Legt ein Spieler eine Acht auf die aufgedeckte Karte, dann muss der nächste Spieler einmal aussetzen. Bei zwei Spielern ist dann derjenige, der die Acht ausgelegt hat, erneut an der Reihe.

  • Karte mit Bild Bube:

    Ein Bube kann grundsätzlich auf alle Karten gelegt werden. Legt ein Spieler einen Buben, darf er sich eine Farbe wünschen. Die nächste Karte, die auf den Buben gelegt wird, muss dann diese Farbe haben. Besitzt der nächste Spieler diese Farbe nicht, so muss er – wie gehabt – eine Karte ziehen.

  • Karte mit Bild Sieben:

    Legt ein Spieler eine Sieben auf die aufgedeckte Karte, dann muss der nächste Spieler zwei Karten ziehen. Sollte er jedoch ebenfalls eine Sieben haben, dann kann er sie auf die bereits abgelegte Sieben legen und braucht keine Karten zu ziehen. Der nächste Spieler muss dann aber vier Karten ziehen, der übernächste 6 usw.

Mau-Mau kennt neben dem oben beschriebenen Standardregelwerk zahlreiche weitere Varianten, sie spielen für diese Aufgabe keine Rolle! Wir beschreiben nun im Folgenden eine mögliche Strukturierung der Aufgabenstellung an Hand des geschickten Entwurfs einiger zentraler Klassen detaillierter.

1.1. Klasse Card

Wir gehen als erstes auf die Klasse Card ein. Jede Karte des Kartenspiels wird durch ein Card-Objekt repräsentiert. Ein Card-Objekt wird durch die Kartenfarbe und das Kartenbild eindeutig beschrieben. Zu diesem Zweck definieren wir zunächst die folgenden zwei Aufzählungstypen CardColor und CardPicture:

enum class CardColor { Empty, Karo, Herz, Pik, Kreuz };
enum class CardPicture { Empty, Sieben, Acht, Neun, Zehn, Bube, Dame, König, Ass };

Mit ihrer Hilfe können Sie nun die Klasse Card realisieren, entnehmen Sie weitere Hilfestellungen dazu Tabelle 1:

Methode

Schnittstelle und Beschreibung

Konstruktor

Card (CardColor color, CardPicture picture);

Der Konstruktor dient zum Erzeugen eines Kartenobjekts. Die beiden Parameter color und picture legen die Details der Karte fest.

GetColor

CardColor GetColor();

Liefert die Farbe einer Spielkarte zurück.

GetPicture

CardPicture GetPicture();

Liefert das Bild einer Spielkarte zurück.

Tabelle 1. Zentrale Methoden der Klasse Card.


Mit welchem programmiersprachlichen Konstrukt lassen sich zwei Spielkarten geeignet auf Gleichheit vergleichen? Implementieren Sie ferner den <<-Operator, um ein Card-Objekt auf der Konsole ausgeben zu können.

Testrahmen für Klasse Card:

Card c1(CardColor::Kreuz, CardPicture::Neun);
Card c2(CardColor::Pik, CardPicture::König);

cout << c1 << endl;
cout << c2 << endl;

cout << "Farbe: " << c1.GetColorAsString() << endl;
cout << "Bild: " << c1.GetPictureAsString() << endl;

if (c1 == c2)
    cout << "Die Karten sind gleich" << endl;
else
    cout << "Die Karten sind verschieden" << endl;

Ausgabe:

Kreuz Neun
Pik Koenig
Farbe: Kreuz
Bild: Neun
Die Karten sind verschieden

2. Klasse CardDeck

Auf dem Spieltisch liegen während eines Mau-Mau-Spiels zwei Kartenstapel: Einer zum Ziehen von Karten und ein zweiter zum Ablegen. Die Verwaltung eines solchen Kartenstapels obliegt der Klasse CardDeck. Implementieren Sie – auf möglichst einfache Weise – die Klasse CardDeck mit den in Tabelle 2 beschriebenen Methoden. Da ein Kartenspiel nicht mehr als 32 Karten besitzt, dürfen Sie zum Beispiel die vereinfachende Annahme zu Grunde legen, dass CardDeck-Objekte niemals mehr als 32 Karten-Elemente aufnehmen können.

Neben den üblichen Methoden Push, Pop und IsEmpty gibt es auch die Methode TopOfDeck. Sie ist vor allem für den Stapel mit den abgelegten Karten relevant, da die Spieler stets sehen müssen, welche Karte als letztes abgelegt wurde und damit als oberste Karte aufliegt. Für den Stapel zum Ziehen von Karten ist ein Aufruf der TopOfDeck-Methode natürlich tabu.

Element

Schnittstelle und Beschreibung

Methode Push

void Push (Card card);

Dient zum Ablegen einer Karte card auf dem Kartenstapel.

Methode Pop

Card Pop();

Dient zum Abheben einer Karte vom Kartenstapel.

Methode TopOfDeck

Card TopOfDeck();

Dient zum Betrachten der obersten Karte eines Kartenstapels. Die Karte wird nicht vom Stapel entfernt!

Eigenschaft Size

int Size() const;

Liefert die Anzahl der Karten des Stapels zurück.

Eigenschaft IsEmpty

bool IsEmpty() const;

Mit IsEmpty lässt sich feststellen, ob ein Kartenstapel noch Karten enthält oder leer ist.

Methode Fill

void Fill();

Dient zum Vorbelegen des Stapels mit allen verfügbaren Karten.

Methode Shuffle

void Shuffle();

Mit Shuffle lassen sich die Karten des Stapels mischen.

Operator <<

friend ostream& operator<< (ostream& os, const CardDeck& deck);

Gibt ein CardDeck-Objekt geeignet auf der Konsole aus.

Tabelle 2. Zentrale Methoden der Klasse CardDeck.


Testrahmen für Klasse CardDeck:

Card c1(CardColor::Kreuz, CardPicture::Neun);
Card c2(CardColor::Pik, CardPicture::König);
Card c3(CardColor::Herz, CardPicture::Sieben);

CardDeck deck;
deck.Push(c1);
deck.Push(c2);
deck.Push(c3);

cout << deck << endl;

Ausgabe:

1: Kreuz Neun
2: Pik Koenig
3: Herz Sieben

3. Klasse CardSet

Beim Spielen von Mau-Mau hält jeder Spieler eine bestimmte Menge von Spielkarten in der Hand. Die Klasse CardDeck ist dazu nicht geeignet. Möchte man beispielsweise eine Karte ablegen, kann man von einem Stapel nur die oberste Karte entfernen. Eine beliebige Karte innerhalb des Stapels kann nicht entfernt werden. Wir benötigen deshalb eine zweite einfache Hilfsklasse CardSet. Legen Sie wiederum vereinfachende Annahmen wie bei der Klasse CardDeck zu Grunde. Eine Beschreibung der wichtigsten Methoden und Eigenschaften der Klasse CardSet finden Sie in Tabelle 3 vor:

Element

Schnittstelle und Beschreibung

Methode Add

void Add(Card card);

Fügt eine Karte card zur Kartenmenge hinzu.

Methode Remove

void Remove(int index);

Entfernt die Karte mit dem Index index aus der Kartenmenge.

Eigenschaft Size

int Size() const;

Ermittelt die Anzahl der Karten in der Kartenmenge.

Operator []

Card operator[] (int index) const;

Liefert die Karte mit dem Index index aus der Kartenmenge zurück. Die Karte wird dabei aus der Menge nicht entfernt.

Operator <<

friend ostream& operator<< (ostream& os, const CardSet& set);

Gibt ein CardSet-Objekt geeignet auf der Konsole aus.

Tabelle 3. Zentrale Methoden der Klasse CardSet.


Testrahmen für Klasse CardSet:

// test frame for a single CardSet object
Card card1 (CardColor::Kreuz, CardPicture::Neun);
Card card2 (CardColor::Pik, CardPicture::König);
Card card3 (CardColor::Herz, CardPicture::Sieben);

CardSet set;
set.Add(card1);
set.Add(card2);
set.Add(card3);

for (int i = 0; i < set.Size(); i++)
{
    cout << "Karte " << i << ": " << set[i] << endl;
}

cout << "Karten auf der Hand:" << endl << set << endl;
set.Remove(1);
cout << "Karten auf der Hand:" << endl << set << endl;

Ausgabe:

Karte 0: Kreuz Neun
Karte 1: Pik Koenig
Karte 2: Herz Sieben
Karten auf der Hand:
Kreuz Neun, Pik Koenig, Herz Sieben
Karten auf der Hand:
Kreuz Neun, Herz Sieben

4. Klasse Player

Jeder Spieler von Mau-Mau hat einen Namen und kennt zwei Kartenstapel: Einen Stapel zum Ablegen der Karten und einen zweiten Stapel zum Ziehen von Karten – also zwei CardDeck-Objekte. Ein Spieler hält eine bestimmte Menge von Karten in der Hand, er hat also ein CardSet-Objekt. Die Methoden des Spielers, die in Tabelle 4 spezifiziert sind, sind auf die Logik des Mau-Mau-Spiels abgestimmt. Mit der Methode ChooseAColor wird beispielsweise ein bestimmter Spieler gefragt, welche Farbe als nächstes für die abzulegenden Karten zu Grunde zu legen ist (wenn zuvor ein Bube abgelegt wurde).

Element

Schnittstelle und Beschreibung

Konstruktor/Destruktor

Player();
~Player();

Der Konstruktor dient zum Erzeugen eines Player-Objekts. Sollten im Objekt Daten dynamisch angelegt werden – zum Beispiel für den Spielernamen –, benötigt die Klasse auch einen Destruktor.

Eigenschaft PlayingDeck

void SetPlayingDeck(CardDeck* playing);

Dient zum Setzen des Kartenstapels zum Ablegen.

Eigenschaft DrawingDeck

void SetDrawingDeck(CardDeck* drawing);

Dient zum Setzen des Kartenstapels zum Ziehen.

Eigenschaft Name

void SetName(const char* name);
void GetName(char name[], int len);

Dient zum Lesen und Schreiben des Spielernamens.

Eigenschaft NumberCards

int GetNumberCards();

Liefert die Anzahl der Karten zurück, die ein Spieler in der Hand hält.

Eigenschaft IsPlaying

void IsPlaying(bool isPlaying);
bool IsPlaying();

Zeigt an, ob der Spieler noch „im Spiel“ ist (true) oder bereits all seine Karten ablegen konnte (false). Die Überladung der IsPlaying-Methode mit Parameter dient zum Schreiben des Eigenschaftswerts.

Methode ChooseAColor

CardColor ChooseAColor();

Wird ein Bube abgelegt, darf sich der aktuelle Spieler eine Farbe wünschen, die für den nächsten Spieler gilt. Es empfiehlt sich, eine Farbe zu wählen, von der der aktuelle Spieler Karten besitzt.

Methode DrawCards

void DrawCards(int number);

Kann ein Spieler keine Karte ablegen, muss er eine Karte ziehen – im Falle einer zuvor abgelegten Sieben sind es sogar mehrere. Der Parameter number gibt an, wie viele Karten der Spieler ziehen muss.

Methode PlayCard

bool PlayCard(CardColor requestedColor, CardPicture requestedPicture);

Mit PlayCard wird ein Spieler aufgefordert, eine Karte abzulegen. Die beiden Parameter requestedColor und requestedPicture spezifizieren die oberste Karte des Kartenstapels zum Ablegen. Die abzulegende Karte muss also entweder diese Farbe oder dieses Bild haben. Kann ein Spieler keine Karte ablegen, verlässt er die Methode PlayCard mit dem Rückgabewert false, andernfalls mit true.

Methode PlayCard

bool PlayCard(CardColor requestedColor);

Ist im Prinzip wie die zuvor beschriebene Überladung von PlayCard zu implementieren, nur: Liegt auf dem Kartenstapel zum Ablegen der Karten ein Bube, ist von den nachfolgenden Spielern eine Karte an Hand der Kartenfarbe abzulegen. Das Kartenbild kann also ignoriert werden, die Methode enthält nur einen einzigen Parameter requestedColor für die Kartenfarbe.

Methode PlayArbitraryCard

void PlayArbitraryCard();

Nach dem Aufnehmen von einer oder mehreren Karten auf Grund einer zuvor gespielten Sieben kann der aktuelle Spieler mit dem Ablegen einer beliebigen Karte fortfahren. Zu diesem Zweck gibt es die PlayArbitraryCard-Methode.

Methode CounterSeven

bool CounterSeven(int numCardsToDraw);

Wurde eine Sieben abgelegt, ist es für den nächsten Spieler von Vorteil, ebenfalls eine Sieben abzulegen. Zu diesem Zweck gibt es die Methode CounterSeven mit einem int-Parameter numCardsToDraw. Besitzt der aktuelle Spieler eine Sieben, legt er diese ab und verlässt die Methode mit Rückgabe von true (der Parameter numCardsToDraw wird ignoriert). Im anderen Fall muss der Spieler die Anzahl von numCardsToDraw Spielkarten auf dem Kartenstapel zum Ablegen ablegen. Die Methode liefert in diesem Fall true zurück.

Operator <<

friend ostream& operator<< (ostream& os, const Player& player);

Gibt ein Player-Objekt geeignet auf der Konsole aus.

Tabelle 4. Zentrale Methoden und Eigenschaften der Klasse Player.

5. Klasse MauMaster

Ein MauMaster-Objekt besitzt zwei Kartenstapel zum Ablegen und Ziehen und verwaltet mehrere Spieler. Das Kernstück der Klasse MauMaster ist die Methode Play (Tabelle 5). Sie simuliert den kompletten Ablauf eines Mau-Mau-Spiels. Im einfachsten Fall wird die jeweils aufliegende Karte betrachtet und ihre Information an den aktuellen Spieler mit PlayCard weitergereicht. Kann ein Spieler keine Karte ablegen, wird er mit DrawCards zum Ziehen einer entsprechenden Anzahl von Karten aufgefordert. Die Spezialkarten Sieben, Acht und Bube sind von der Play-Methode ebenfalls zu behandeln.

Methode

Schnittstelle und Beschreibung

Konstruktor

MauMaster(const char* names[], int len);

Im Konstruktor werden zuerst zwei CardDeck-Objekte und mehrere Player-Objekte erzeugt. Die Namen und Anzahl der Player-Objekte wird durch den Parameter names bestimmt. Jeder der Spieler nimmt fünf Karten in seine Hand auf.

Methode Play

void Play();

Führt den kompletten Ablauf eines Mau-Mau-Spiels durch.

Tipp: Implementieren Sie die Methode Play zunächst ohne Betrachtung der Sonderregeln für die drei Karten Sieben, Acht und Bube. Ergänzen Sie dann eine Sonderregel nach der anderen.

Tabelle 5. Zentrale Methoden der Klasse MauMaster.

6. Ein Beispiel

Das folgende Beispiel demonstriert, wie der Ablauf Ihres Programms aussehen könnte. Es sind für alle zentralen Aktionen wie Karte ziehen oder ablegen entsprechende Ausgaben zu machen. An Hand der Ausgaben muss die Einhaltung der Spielregeln von Mau-Mau nachvollziehbar sein:

------------------------------------------------------------------
    Simple Mau-Mau Cards Game (Version 1.00)
------------------------------------------------------------------
Player 1: Hans[X]: Kreuz Sieben, Kreuz Zehn, Herz Koenig, Pik Dame, Pik Acht
Player 2: Sepp[X]: Kreuz Acht, Herz Acht, Herz Bube, Pik Bube, Pik Koenig
Player 3: Ulli[X]: Herz Neun, Kreuz Koenig, Karo Zehn, Pik Zehn, Pik Ass
------------------------------------------------------------------
Topmost card: Kreuz Ass
------------------------------------------------------------------
--> Hans[X]: Kreuz Sieben, Kreuz Zehn, Herz Koenig, Pik Dame, Pik Acht
    Sepp[X]: Kreuz Acht, Herz Acht, Herz Bube, Pik Bube, Pik Koenig
    Ulli[X]: Herz Neun, Kreuz Koenig, Karo Zehn, Pik Zehn, Pik Ass
------------------------------------------------------------------
>   Hans plays Kreuz Sieben
------------------------------------------------------------------
Topmost card: Kreuz Sieben
------------------------------------------------------------------
    Hans[X]: Pik Acht, Kreuz Zehn, Herz Koenig, Pik Dame
--> Sepp[X]: Kreuz Acht, Herz Acht, Herz Bube, Pik Bube, Pik Koenig
    Ulli[X]: Herz Neun, Kreuz Koenig, Karo Zehn, Pik Zehn, Pik Ass
------------------------------------------------------------------
>   '7' is on top of deck
>   Sepp cannot respond to '7', draws 2 card(s)!
>   Sepp draws Pik Neun from drawing deck!
>   Sepp draws Kreuz Bube from drawing deck!
>   Sepp plays Kreuz Bube
>   Sepp has choosen color Kreuz
------------------------------------------------------------------
Topmost card: Kreuz Bube
------------------------------------------------------------------
    Hans[X]: Pik Acht, Kreuz Zehn, Herz Koenig, Pik Dame
    Sepp[X]: Kreuz Acht, Herz Acht, Herz Bube, Pik Bube, Pik Koenig, Pik Neun
--> Ulli[X]: Herz Neun, Kreuz Koenig, Karo Zehn, Pik Zehn, Pik Ass
------------------------------------------------------------------
>   Ulli plays Kreuz Koenig
------------------------------------------------------------------
Topmost card: Kreuz Koenig
------------------------------------------------------------------
--> Hans[X]: Pik Acht, Kreuz Zehn, Herz Koenig, Pik Dame
    Sepp[X]: Kreuz Acht, Herz Acht, Herz Bube, Pik Bube, Pik Koenig, Pik Neun
    Ulli[X]: Herz Neun, Pik Ass, Karo Zehn, Pik Zehn
------------------------------------------------------------------
>   Hans plays Kreuz Zehn
------------------------------------------------------------------
Topmost card: Kreuz Zehn
------------------------------------------------------------------
    Hans[X]: Pik Acht, Pik Dame, Herz Koenig
--> Sepp[X]: Kreuz Acht, Herz Acht, Herz Bube, Pik Bube, Pik Koenig, Pik Neun
    Ulli[X]: Herz Neun, Pik Ass, Karo Zehn, Pik Zehn
------------------------------------------------------------------
>   Sepp plays Kreuz Acht
>   '8' is on top of deck - skip next player
------------------------------------------------------------------
Topmost card: Kreuz Acht
------------------------------------------------------------------
--> Hans[X]: Pik Acht, Pik Dame, Herz Koenig
    Sepp[X]: Pik Neun, Herz Acht, Herz Bube, Pik Bube, Pik Koenig
    Ulli[X]: Herz Neun, Pik Ass, Karo Zehn, Pik Zehn
------------------------------------------------------------------
>   Hans plays Pik Acht
>   '8' is on top of deck - skip next player
------------------------------------------------------------------
Topmost card: Pik Acht
------------------------------------------------------------------
    Hans[X]: Herz Koenig, Pik Dame
    Sepp[X]: Pik Neun, Herz Acht, Herz Bube, Pik Bube, Pik Koenig
--> Ulli[X]: Herz Neun, Pik Ass, Karo Zehn, Pik Zehn
------------------------------------------------------------------
>   Ulli plays Pik Ass
------------------------------------------------------------------
Topmost card: Pik Ass
------------------------------------------------------------------
--> Hans[X]: Herz Koenig, Pik Dame
    Sepp[X]: Pik Neun, Herz Acht, Herz Bube, Pik Bube, Pik Koenig
    Ulli[X]: Herz Neun, Pik Zehn, Karo Zehn
------------------------------------------------------------------
>   Hans plays Pik Dame
==> Hans says 'Mau'
------------------------------------------------------------------
Topmost card: Pik Dame
------------------------------------------------------------------
    Hans[X]: Herz Koenig
--> Sepp[X]: Pik Neun, Herz Acht, Herz Bube, Pik Bube, Pik Koenig
    Ulli[X]: Herz Neun, Pik Zehn, Karo Zehn
------------------------------------------------------------------
>   Sepp plays Pik Neun
------------------------------------------------------------------
Topmost card: Pik Neun
------------------------------------------------------------------
    Hans[X]: Herz Koenig
    Sepp[X]: Pik Koenig, Herz Acht, Herz Bube, Pik Bube
--> Ulli[X]: Herz Neun, Pik Zehn, Karo Zehn
------------------------------------------------------------------
>   Ulli plays Herz Neun
------------------------------------------------------------------
Topmost card: Herz Neun
------------------------------------------------------------------
--> Hans[X]: Herz Koenig
    Sepp[X]: Pik Koenig, Herz Acht, Herz Bube, Pik Bube
    Ulli[X]: Karo Zehn, Pik Zehn
------------------------------------------------------------------
>   Hans plays Herz Koenig
##> Hans says 'MAU MAU !!!'
>   Hans quits game !
------------------------------------------------------------------
Topmost card: Herz Koenig
------------------------------------------------------------------
    Hans[-]
--> Sepp[X]: Pik Koenig, Herz Acht, Herz Bube, Pik Bube
    Ulli[X]: Karo Zehn, Pik Zehn
------------------------------------------------------------------
>   Sepp plays Pik Koenig
------------------------------------------------------------------
Topmost card: Pik Koenig
------------------------------------------------------------------
    Hans[-]
    Sepp[X]: Pik Bube, Herz Acht, Herz Bube
--> Ulli[X]: Karo Zehn, Pik Zehn
------------------------------------------------------------------
>   Ulli plays Pik Zehn
==> Ulli says 'Mau'
------------------------------------------------------------------
Topmost card: Pik Zehn
------------------------------------------------------------------
    Hans[-]
--> Sepp[X]: Pik Bube, Herz Acht, Herz Bube
    Ulli[X]: Karo Zehn
------------------------------------------------------------------
>   Sepp plays Pik Bube
>   Sepp has choosen color Herz
------------------------------------------------------------------
Topmost card: Pik Bube
------------------------------------------------------------------
    Hans[-]
    Sepp[X]: Herz Bube, Herz Acht
--> Ulli[X]: Karo Zehn
------------------------------------------------------------------
>   Ulli cannot serve, draws 1 card(s)
>   Ulli draws Kreuz Dame from drawing deck!
------------------------------------------------------------------
Topmost card: Pik Bube
------------------------------------------------------------------
    Hans[-]
--> Sepp[X]: Herz Bube, Herz Acht
    Ulli[X]: Karo Zehn, Kreuz Dame
------------------------------------------------------------------
>   Sepp plays Herz Acht
==> Sepp says 'Mau'
>   '8' is on top of deck - skip next player
------------------------------------------------------------------
Topmost card: Herz Acht
------------------------------------------------------------------
    Hans[-]
--> Sepp[X]: Herz Bube
    Ulli[X]: Karo Zehn, Kreuz Dame
------------------------------------------------------------------
>   Sepp plays Herz Bube
##> Sepp says 'MAU MAU !!!'
>   Sepp has choosen color Herz
>   Sepp quits game !
Ulli has lost --- Game over [17 rounds]

2. Lösung

Quellcode: Siehe auch github.com/peterloos/Cpp_MauMau.git.

Die Hilfestellungen im Aufgabenteil waren sehr umfangreich gestaltet – wir können uns daher im Lösungsteil direkt der Realisierung der einzelnen Klassen Card, CardDeck, CardSet, Player, MauMaster und Program zuwenden (Listing 1, Listing 2, Listing 3, Listing 4, Listing 5, Listing 6, Listing 7, Listing 8, Listing 9, Listing 10 und Listing 11):

01: class Card
02: {
03: private:
04:     CardColor    m_color;
05:     CardPicture  m_picture;
06: 
07: public:
08:     static const char* CardColorDisplay[];
09:     static const char* CardPictureDisplay[];
10: 
11: public:
12:     // c'tors
13:     Card();
14:     Card(CardColor, CardPicture);
15: 
16:     // getter
17:     CardColor GetColor() { return m_color; }
18:     CardPicture GetPicture() { return m_picture; }
19:     const char* GetColorAsString() { return CardColorDisplay[(int) m_color]; }
20:     const char*  GetPictureAsString() { return CardPictureDisplay[(int) m_picture]; }
21: 
22:     // operators
23:     friend bool operator== (const Card&, const Card&);
24:     friend bool operator!= (const Card&, const Card&);
25:     friend ostream& operator<< (ostream&, const Card&);
26: };

Beispiel 1. Klasse Card: Header-Datei


01: #include <iostream>
02: using namespace std;
03: 
04: #include "CardColor.h"
05: #include "CardPicture.h"
06: #include "Card.h"
07: 
08: // note: list of strings must match enumeration type CardColor !
09: const char* Card::CardColorDisplay[] =
10: {
11:     "Empty", "Karo", "Herz", "Pik", "Kreuz"
12: };
13: 
14: // note: list of strings must match enumeration type CardPicture !
15: const char* Card::CardPictureDisplay[] = 
16: { 
17:     "Empty", "Sieben", "Acht", "Neun", "Zehn", "Bube", "Dame", "Koenig", "Ass"
18: };
19: 
20: // c'tors
21: Card::Card()
22: {
23:     m_color = CardColor::Empty;
24:     m_picture = CardPicture::Empty;
25: }
26: 
27: Card::Card(CardColor color, CardPicture picture)
28: {
29:     m_color = color;
30:     m_picture = picture;
31: }
32: 
33: // operators
34: bool operator== (const Card& c1, const Card& c2)
35: {
36:     return c1.m_color == c2.m_color && c1.m_picture == c2.m_picture;
37: }
38: 
39: bool operator!= (const Card& c1, const Card& c2)
40: {
41:     return !(c1 == c2);
42: }
43: 
44: // output
45: ostream& operator<< (ostream& os, const Card& card)
46: {
47:     const char* colorDisplay = Card::CardColorDisplay[(int) card.m_color];
48:     const char* pictureDisplay = Card::CardPictureDisplay[(int) card.m_picture];
49:     os << colorDisplay << ' ' << pictureDisplay;
50:     return os;
51: }

Beispiel 2. Klasse Card: Implementierung


01: class CardDeck
02: {
03: private:
04:     Card m_deck[32];
05:     int m_top;
06:     int m_randomSeed;
07: 
08: private:
09:     static const int RandomSeed = 1;
10: 
11: public:
12:     // c'tor
13:     CardDeck();
14: 
15:     // properties
16:     void SetRandomSeed();
17:     void SetRandomSeed(int seed);
18:     bool IsEmpty() const;
19:     int Size() const;
20: 
21:     // methods
22:     void Push(Card);
23:     Card Pop();
24:     Card TopOfDeck();
25:     void Fill();
26:     void Clear();
27:     void Shuffle();
28: 
29:     // output
30:     friend ostream& operator<< (ostream&, const CardDeck&);
31: };

Beispiel 3. Klasse CardDeck: Header-Datei


001: #include <time.h>
002: #include <iostream>
003: using namespace std;
004: 
005: #include "CardColor.h"
006: #include "CardPicture.h"
007: #include "Card.h"
008: #include "CardDeck.h"
009: 
010: // used for all players
011: CardDeck::CardDeck()
012: {
013:     m_top = 0;
014: 
015:     // initialize random number generator "by random"
016:     ::srand((unsigned int) time(NULL));
017: }
018: 
019: void CardDeck::Push(Card c)
020: {
021:     m_deck[m_top] = c;
022:     m_top++;
023: }
024: 
025: Card CardDeck::Pop()
026: {
027:     m_top--;
028:     return m_deck[m_top];
029: }
030: 
031: bool CardDeck::IsEmpty() const
032: {
033:     return m_top == 0;
034: }
035: 
036: int CardDeck::Size() const
037: {
038:     return m_top;
039: }
040: 
041: void CardDeck::SetRandomSeed()
042: {
043:     m_randomSeed = CardDeck::RandomSeed;
044:     ::srand(m_randomSeed);
045: }
046: 
047: void CardDeck::SetRandomSeed(int seed)
048: {
049:     m_randomSeed = seed;
050:     ::srand(m_randomSeed);
051: }
052: 
053: Card CardDeck::TopOfDeck()
054: {
055:     if (m_top == 0)
056:         throw "ERROR (PlaySeven): Should never be reached";
057: 
058:     int index = m_top - 1;
059:     return m_deck[index];
060: }
061: 
062: void CardDeck::Fill()
063: {
064:     // fill stack with all available cards
065:     Card c01(CardColor::Karo, CardPicture::Sieben);
066:     Card c02(CardColor::Karo, CardPicture::Acht);
067:     Card c03(CardColor::Karo, CardPicture::Neun);
068:     Card c04(CardColor::Karo, CardPicture::Zehn);
069:     Card c05(CardColor::Karo, CardPicture::Bube);
070:     Card c06(CardColor::Karo, CardPicture::Dame);
071:     Card c07(CardColor::Karo, CardPicture::König);
072:     Card c08(CardColor::Karo, CardPicture::As);
073: 
074:     Card c09(CardColor::Herz, CardPicture::Sieben);
075:     Card c10(CardColor::Herz, CardPicture::Acht);
076:     Card c11(CardColor::Herz, CardPicture::Neun);
077:     Card c12(CardColor::Herz, CardPicture::Zehn);
078:     Card c13(CardColor::Herz, CardPicture::Bube);
079:     Card c14(CardColor::Herz, CardPicture::Dame);
080:     Card c15(CardColor::Herz, CardPicture::König);
081:     Card c16(CardColor::Herz, CardPicture::As);
082: 
083:     Card c17(CardColor::Pik, CardPicture::Sieben);
084:     Card c18(CardColor::Pik, CardPicture::Acht);
085:     Card c19(CardColor::Pik, CardPicture::Neun);
086:     Card c20(CardColor::Pik, CardPicture::Zehn);
087:     Card c21(CardColor::Pik, CardPicture::Bube);
088:     Card c22(CardColor::Pik, CardPicture::Dame);
089:     Card c23(CardColor::Pik, CardPicture::König);
090:     Card c24(CardColor::Pik, CardPicture::As);
091: 
092:     Card c25(CardColor::Kreuz, CardPicture::Sieben);
093:     Card c26(CardColor::Kreuz, CardPicture::Acht);
094:     Card c27(CardColor::Kreuz, CardPicture::Neun);
095:     Card c28(CardColor::Kreuz, CardPicture::Zehn);
096:     Card c29(CardColor::Kreuz, CardPicture::Bube);
097:     Card c30(CardColor::Kreuz, CardPicture::Dame);
098:     Card c31(CardColor::Kreuz, CardPicture::König);
099:     Card c32(CardColor::Kreuz, CardPicture::As);
100: 
101:     Push(c01);
102:     Push(c02);
103:     Push(c03);
104:     Push(c04);
105:     Push(c05);
106:     Push(c06);
107:     Push(c07);
108:     Push(c08);
109:     Push(c09);
110:     Push(c10);
111:     Push(c11);
112:     Push(c12);
113:     Push(c13);
114:     Push(c14);
115:     Push(c15);
116:     Push(c16);
117:     Push(c17);
118:     Push(c18);
119:     Push(c19);
120:     Push(c20);
121:     Push(c21);
122:     Push(c22);
123:     Push(c23);
124:     Push(c24);
125:     Push(c25);
126:     Push(c26);
127:     Push(c27);
128:     Push(c28);
129:     Push(c29);
130:     Push(c30);
131:     Push(c31);
132:     Push(c32);
133: }
134: 
135: void CardDeck::Shuffle()
136: {
137:     // now mix stack per random
138:     int repetitionCount = 30;
139:     while (repetitionCount--)
140:     {
141:         int x1 = rand() % m_top;
142:         int x2 = rand() % m_top;
143: 
144:         Card temp = m_deck[x1];
145:         m_deck[x1] = m_deck[x2];
146:         m_deck[x2] = temp;
147:     }
148: }
149: 
150: void CardDeck::Clear()
151: {
152:     m_top = 0;
153: }
154: 
155: // output
156: ostream& operator<< (ostream& os, const CardDeck& deck)
157: {
158:     for (int i = 0; i < deck.m_top; i++)
159:         os << (i+1) << ": " << deck.m_deck[i] << endl;
160: 
161:     return os;
162: }

Beispiel 4. Klasse CardDeck: Implementierung


01: class CardSet
02: {
03: private:
04:     Card m_set[32];
05:     int m_top;
06: 
07: public:
08:     // c'tor
09:     CardSet();
10: 
11:     // properties
12:     int Size() const;
13: 
14:     // methods
15:     void Add(Card c);
16:     void Remove(int index);
17:     void Clear();
18: 
19:     // operators
20:     Card operator[](int index) const;
21: 
22:     // output
23:     friend ostream& operator<< (ostream&, const CardSet&);
24: };

Beispiel 5. Klasse CardSet: Header-Datei


01: #include <iostream>
02: using namespace std;
03: 
04: #include "CardColor.h"
05: #include "CardPicture.h"
06: #include "Card.h"
07: #include "CardSet.h"
08: 
09: CardSet::CardSet()
10: {
11:     m_top = 0;
12: }
13: 
14: int CardSet::Size() const
15: {
16:     return m_top;
17: }
18: 
19: void CardSet::Add(Card c)
20: {
21:     m_set[m_top] = c;
22:     m_top++;
23: }
24: 
25: void CardSet::Remove(int index)
26: {
27:     if (index < 0 || index >= m_top)
28:     {
29:         throw "Wrong Index";
30:     }
31: 
32:     m_set[index] = m_set[m_top - 1];
33:     m_top--;
34: }
35: 
36: void CardSet::Clear()
37: {
38:     m_top = 0;
39: }
40: 
41: Card CardSet::operator[] (int index) const
42: {
43:     return m_set[index];
44: }
45: 
46: // output
47: ostream& operator<< (ostream& os, const CardSet& set)
48: {
49:     for (int i = 0; i < set.Size(); i++)
50:     {
51:         os << set[i];
52:         if (i < set.Size() - 1)
53:             os << ", ";
54:     }
55: 
56:     return os;
57: }

Beispiel 6. Klasse CardSet: Implementierung


01: class Player
02: {
03: private:
04:     CardSet    m_hand;       // player's hand of cards (usually 5)
05:     CardDeck*  m_playing;    // deck to play (Kartenstapel zum Ablegen)
06:     CardDeck*  m_drawing;    // deck to draw (Kartenstapel zum Ziehen)
07:     char*      m_name;       // players name
08:     bool       m_isPlaying;  // false, after getting rid of all cards
09: 
10: public:
11:     // c'tor / d'tor
12:     Player();
13:     ~Player();
14: 
15:     // properties
16:     void SetPlayingDeck(CardDeck* playing);
17:     void SetDrawingDeck(CardDeck* drawing);
18:     int GetNumberCards();
19:     void IsPlaying(bool isPlaying);
20:     bool IsPlaying();
21:     void SetName(const char* name);
22:     void GetName(char name[], int len);
23: 
24:     // methods
25:     CardColor ChooseAColor();
26:     void DrawCards(int number);   
27:     bool PlayCard(CardColor requestedColor, CardPicture requestedPicture);
28:     bool PlayCard(CardColor requestedColor);
29:     void PlayArbitraryCard();
30:     bool CounterSeven(int numCardsToDraw);
31:     void ReleaseAllCards();
32: 
33:     // private helper methods
34: private:
35:     Card DrawCard();
36:     bool HasSeven();
37:     void PlaySeven();
38:     void PrintMauMauIf();
39: 
40:     // output
41:     friend ostream& operator<< (ostream&, const Player&);
42: };

Beispiel 7. Klasse Player: Header-Datei


001: #include <iostream>
002: using namespace std;
003: 
004: #include "CardColor.h"
005: #include "CardPicture.h"
006: #include "Card.h"
007: #include "CardSet.h"
008: #include "CardDeck.h"
009: #include "Player.h"
010: #include "MauMaster.h"
011: 
012: Player::Player()
013: {
014:     m_name = (char*) 0;
015:     m_isPlaying = true;
016:     m_playing = (CardDeck*) 0; 
017:     m_drawing = (CardDeck*) 0; 
018: }
019: 
020: Player::~Player()
021: {
022:     delete m_name;
023: }
024: 
025: // properties
026: void Player::SetPlayingDeck(CardDeck* playing)
027: {
028:     m_playing = playing;
029: }
030: 
031: void Player::SetDrawingDeck(CardDeck* drawing)
032: {
033:     m_drawing = drawing;
034: }
035: 
036: int Player::GetNumberCards()
037: {
038:     return m_hand.Size();
039: }
040: 
041: void Player::IsPlaying(bool isPlaying)
042: {
043:     m_isPlaying = isPlaying;
044: }
045: 
046: bool Player::IsPlaying()
047: {
048:     return m_isPlaying;
049: }
050: 
051: void Player::GetName(char name[], int len)
052: {
053:     strcpy_s(name, len, m_name);
054: }
055: 
056: void Player::SetName(const char* name)
057: {
058:     int len = 0;
059:     while (name[len] != '\0')
060:         len++;
061: 
062:     m_name = new char[len + 1];
063:     strcpy_s(m_name, len + 1, name);
064: }
065: 
066: // public interface
067: CardColor Player::ChooseAColor()
068: {
069:     if (m_hand.Size() > 0)
070:     {
071:         // players has (still) some cards in his hand
072:         return m_hand[0].GetColor();
073:     }
074:     else
075:     {
076:         // players has no more cards, chooses arbitrary card color
077:         return CardColor::Herz;
078:     }
079: }
080: 
081: void Player::DrawCards(int number)
082: {
083:     for (int i = 0; i < number; i++)
084:     {
085:         Card card = DrawCard();
086:         m_hand.Add(card);
087: 
088:         MauMaster::Log(">   %s draws %s %s from drawing deck!", 
089:             m_name, card.GetColorAsString(), card.GetPictureAsString());
090:     }
091: }
092: 
093: bool Player::CounterSeven(int numCardsToDraw)
094: {
095:     if (HasSeven())
096:     {
097:         // players holds '7' in his hand
098:         MauMaster::Log(">   %s counters '7' with another '7' !", m_name);
099:         PlaySeven();
100:         return true;
101:     }
102:     else
103:     {
104:         MauMaster::Log(">   %s cannot respond to '7', draws %d card(s)!", 
105:             m_name, numCardsToDraw);
106:         DrawCards(numCardsToDraw);
107:         return false;
108:     }
109: }
110: 
111: bool Player::PlayCard(CardColor requestedColor, CardPicture requestedPicture)
112: {
113:     for (int i = 0; i < m_hand.Size(); i++)
114:     {
115:         Card card = m_hand[i];
116:         if (card.GetColor() == requestedColor || card.GetPicture() == requestedPicture)
117:         {
118:             m_hand.Remove(i);
119:             m_playing->Push(card);
120:             MauMaster::Log(">   %s plays %s %s", 
121:                 m_name, card.GetColorAsString(), card.GetPictureAsString());
122:             PrintMauMauIf();
123:             return true;
124:         }
125:     }
126: 
127:     // 'Bube' maybe played upon every card!
128:     for (int i = 0; i < m_hand.Size(); i++)
129:     {
130:         Card card = m_hand[i];
131:         if (card.GetPicture() == CardPicture::Bube)
132:         {
133:             m_hand.Remove(i);
134:             m_playing->Push(card);
135:             MauMaster::Log(">   %s plays %s %s", 
136:                 m_name, card.GetColorAsString(), card.GetPictureAsString());
137:             PrintMauMauIf();
138:             return true;
139:         }
140:     }
141: 
142:     return false;
143: }
144: 
145: bool Player::PlayCard(CardColor requestedColor)
146: {
147:     for (int i = 0; i < m_hand.Size(); i++)
148:     {
149:         Card card = m_hand[i];
150: 
151:         // 'Bube' upon 'Bube' not allowed ("Bube auf Bube" stinkt)
152:         if (card.GetPicture() == CardPicture::Bube)
153:             continue;
154: 
155:         if (card.GetColor() == requestedColor)
156:         {
157:             m_hand.Remove(i);
158:             m_playing->Push(card);
159:             MauMaster::Log(">   %s plays %s %s", 
160:                 m_name, card.GetColorAsString(), card.GetPictureAsString());
161:             PrintMauMauIf();
162:             return true;
163:         }
164:     }
165: 
166:     return false;
167: }
168: 
169: void Player::PlayArbitraryCard()
170: {
171:     int lastIndex = m_hand.Size() - 1;
172:     Card card = m_hand[lastIndex];
173:     m_hand.Remove(lastIndex);
174:     m_playing->Push(card);
175:     MauMaster::Log(">   %s plays %s %s", 
176:         m_name, card.GetColorAsString(), card.GetPictureAsString());
177:     PrintMauMauIf();
178: }
179: 
180: void Player::ReleaseAllCards()
181: {
182:     m_hand.Clear();
183: }
184: 
185: // private helper methods
186: Card Player::DrawCard()
187: {
188:     // turn over playing deck to serve as new drawing deck
189:     if (m_drawing->Size() == 0)
190:     {
191:         MauMaster::Log (">   turn over playing deck to serve as new drawing deck");
192: 
193:         // save topmost card of playing stack
194:         Card topmostPlayingCard = m_playing->Pop();
195: 
196:         // copy rest of playing deck to drawing deck
197:         while (!m_playing->IsEmpty())
198:         {
199:             Card tmp = m_playing->Pop();
200:             m_drawing->Push(tmp);
201:         }
202: 
203:         // shuffle drawing stack
204:         m_drawing->Shuffle();
205: 
206:         // restore topmost card of playing stack
207:         m_playing->Push(topmostPlayingCard);
208:     }
209: 
210:     return m_drawing->Pop();
211: }
212: 
213: bool Player::HasSeven()
214: {
215:     for (int i = 0; i < m_hand.Size(); i++)
216:     {
217:         Card c = m_hand[i];
218:         if (c.GetPicture() == CardPicture::Sieben)
219:             return true;
220:     }
221: 
222:     return false;
223: }
224: 
225: void Player::PlaySeven()
226: {
227:     for (int i = 0; i < m_hand.Size(); i++)
228:     {
229:         Card card = m_hand[i];
230:         if (card.GetPicture() == CardPicture::Sieben)
231:         {
232:             m_hand.Remove(i);
233:             m_playing->Push(card);
234: 
235:             MauMaster::Log(">   %s drops %s %s onto deck!", 
236:                 m_name, card.GetColorAsString(), card.GetPictureAsString());
237: 
238:             PrintMauMauIf();
239:             return;
240:         }
241:     }
242: 
243:     throw "ERROR (PlaySeven): Should never be reached";
244: }
245: 
246: void Player::PrintMauMauIf()
247: {
248:     if (m_hand.Size() == 1)
249:     {
250:         MauMaster::Log("==> %s says 'Mau'", m_name);
251:     }
252:     else if (m_hand.Size() == 0)
253:     {
254:         MauMaster::Log("##> %s says 'MAU MAU !!!'", m_name);
255:     }
256: }
257: 
258: // output
259: ostream& operator<< (ostream& os, const Player& player)
260: {
261:     os << player.m_name << "[" << (player.m_isPlaying ? "X" : "-") << "]";
262:     if (player.m_hand.Size() > 0)
263:     {
264:         os << ": ";
265:         os << player.m_hand;
266:     }
267: 
268:     return os;
269: }

Beispiel 8. Klasse Player: Implementierung


01: #define VERBOSE
02: // #define SINGLE_STEP
03: 
04: class MauMaster
05: {
06: private:
07:     static const char* Version;
08:     static const int MaxCardsAtBegin;
09: 
10:     CardDeck m_playing;     // deck to play (Kartenstapel zum Ablegen - offen)
11:     CardDeck m_drawing;     // deck to draw (Kartenstapel zum Ziehen - verdeckt)
12:     Player*  m_players;     // array of players
13:     int      m_numPlayers;  // number of players
14:     int      m_rounds;      // counting rounds of a game
15: 
16: public:
17:     // c'tor
18:     MauMaster(const char* names[], int len);
19:     ~MauMaster();
20: 
21:     // properties
22:     int GetRounds();
23: 
24:     // public interface
25:     void Init(int randomSeed);
26:     void Play();
27: 
28: private:
29:     // private helper methods
30:     int NextPlayer(int currentPlayer);
31:     int IncrementNumberOfCardsToDraw(int numberOfCardsToDraw);
32:     void LogGameStatusDebug(Card topMostCard, int currentPlayer);
33:     void LogFinalGameStatus(char* name);
34: 
35: public:
36:     // general purpose helpers
37:     static void PrintVersion();
38: 
39:     // logging methods
40:     static void Log(char const* message);
41:     static void Log(char const* message, char const* string);
42:     static void Log(char const* message, char const* string, CardColor col);
43:     static void Log(char const* message, char const* string, int n);
44:     static void Log(char const* message, char const* string1, char const* string2, char const* string3);
45: };

Beispiel 9. Klasse MauMaster: Header-Datei


001: #include <string>
002: #include <conio.h>
003: #include <time.h>
004: #include <iostream>
005: using namespace std;
006: 
007: #include "CardColor.h"
008: #include "CardPicture.h"
009: #include "Card.h"
010: #include "CardSet.h"
011: #include "CardDeck.h"
012: #include "Player.h"
013: #include "MauMaster.h"
014: 
015: // initialization of static class members
016: const char* MauMaster::Version = "    Simple Mau-Mau Cards Game (Version 1.00)";
017: const int MauMaster::MaxCardsAtBegin = 5;  // used for testing - should be 5 regularly
018: 
019: // c'tor
020: MauMaster::MauMaster(const char* names[], int len)
021: {
022:     // create array of players
023:     m_numPlayers = len;
024:     m_players = new Player[m_numPlayers];
025:     for (int i = 0; i < m_numPlayers; i++)
026:     {
027:         m_players[i].SetName(names[i]);
028:         m_players[i].SetPlayingDeck(&m_playing);
029:         m_players[i].SetDrawingDeck(&m_drawing);
030:         m_players[i].IsPlaying(true);
031:     }
032: }
033: 
034: MauMaster::~MauMaster()
035: {
036:     delete[] m_players;
037: }
038: 
039: // properties
040: int MauMaster::GetRounds()
041: {
042:     return m_rounds;
043: }
044: 
045: // public interface
046: void MauMaster::Init(int randomSeed)
047: {
048:     // create new random generator (prefer unique results to make testing more easier)
049:     m_playing.SetRandomSeed(randomSeed);
050:     m_drawing.SetRandomSeed(randomSeed);
051: 
052:     // intialize card decks
053:     m_playing.Clear();
054:     m_drawing.Clear();
055:     m_drawing.Fill();       // fill deck with all available cards ...
056:     m_drawing.Shuffle();    // ... and mix them ...
057: 
058:     for (int i = 0; i < m_numPlayers; i++)
059:     {
060:         m_players[i].IsPlaying(true);
061:         m_players[i].ReleaseAllCards();
062:         m_players[i].DrawCards(MaxCardsAtBegin);  // draw initial amount of cards
063:     }
064: 
065:     m_rounds = 0;
066: 
067:     // print summary of players at begin of game
068: #if defined (VERBOSE)
069:     cout << "------------------------------------------------------------------" << endl;
070:     for (int i = 0; i < m_numPlayers; i++)
071:     {
072:         cout << "Player " << (i + 1) << ": " << m_players[i] << endl;
073:     }
074: #endif
075: }
076: 
077: void MauMaster::Play()
078: {
079:     // controlling game variables
080:     int numberOfCardsToDraw = 2;
081:     int activePlayers = m_numPlayers;
082:     int currentPlayer = 0;
083:     CardColor requestedCardColor = CardColor::Empty;
084: 
085:     // controlling special games state 'Bube'
086:     bool bubeIsActive = false;
087: 
088:     // controlling special games states '8'
089:     bool eightIsActive = false;
090:     bool skipNextPlayer = false;
091: 
092:     // uncover first card
093:     Card firstCard = m_drawing.Pop();
094:     m_playing.Push(firstCard);
095: 
096:     // and now lets play an aweful game
097:     while (activePlayers > 1)
098:     {
099:         Card topMostCard = m_playing.TopOfDeck();
100:         LogGameStatusDebug(topMostCard, currentPlayer); 
101: 
102:         m_rounds ++;  // count rounds of this game
103: 
104:         // handle special cards
105:         if (topMostCard.GetPicture() == CardPicture::Sieben)
106:         {
107:             // '7' is on top of card deck
108:             MauMaster::Log(">   '7' is on top of deck");    
109:             if (m_players[currentPlayer].CounterSeven(numberOfCardsToDraw))
110:             {
111:                 numberOfCardsToDraw = IncrementNumberOfCardsToDraw(numberOfCardsToDraw);
112:             }
113:             else
114:             {
115:                 numberOfCardsToDraw = 2;  // no more '7' on top of card deck
116: 
117:                 // player may now draw a card, if he can
118:                 m_players[currentPlayer].PlayArbitraryCard();
119: 
120:                 Card peek = m_playing.TopOfDeck();
121: 
122:                 if (peek.GetPicture() == CardPicture::Bube)
123:                 {
124:                     requestedCardColor = m_players[currentPlayer].ChooseAColor();
125: 
126:                     bubeIsActive = true; 
127: 
128:                     char name[32];
129:                     m_players[currentPlayer].GetName(name, 32);
130:                     MauMaster::Log(">   %s has choosen color %s", name, requestedCardColor);
131:                 }
132:                 else if (peek.GetPicture() == CardPicture::Acht)
133:                 {
134:                     MauMaster::Log(">   '8' is on top of deck - skip next player");  
135: 
136:                     eightIsActive = true;
137:                     skipNextPlayer = true;
138:                 }
139:             }
140:         }
141:         else if (topMostCard.GetPicture() == CardPicture::Acht && !eightIsActive)
142:         {
143:             MauMaster::Log(">   '8' is on top of deck - skip next player");
144: 
145:             eightIsActive = true;
146:             skipNextPlayer = false;
147:         }
148:         else if (topMostCard.GetPicture() == CardPicture::Bube && !bubeIsActive)
149:         {
150:             requestedCardColor = m_players[currentPlayer].ChooseAColor();
151: 
152:             bubeIsActive = true;   
153: 
154:             char name[32];
155:             m_players[currentPlayer].GetName(name, 32);
156:             MauMaster::Log(">   %s has choosen color %s", name, requestedCardColor);
157:         }
158:         else
159:         {
160:             // regular mode -- no special cards --
161:             // current player plays according to "standard" rules
162:             bool success;
163:             if (!bubeIsActive)
164:             {
165:                 // picture or color can be played
166:                 success =
167:                     m_players[currentPlayer].PlayCard(topMostCard.GetColor(), topMostCard.GetPicture());
168:             }
169:             else
170:             {
171:                 // a color has been choosen right before, only color must match
172:                 success =
173:                     m_players[currentPlayer].PlayCard(requestedCardColor);
174:             }
175: 
176:             if (success)
177:             {
178:                 // reset special state(s), if any 
179:                 eightIsActive = false;
180:                 bubeIsActive = false;
181: 
182:                 Card peek = m_playing.TopOfDeck();
183:                 if (peek.GetPicture() == CardPicture::Bube)
184:                 {
185:                     requestedCardColor = m_players[currentPlayer].ChooseAColor();
186: 
187:                     bubeIsActive = true;
188: 
189:                     char name[32];
190:                     m_players[currentPlayer].GetName(name, 32);
191:                     MauMaster::Log(">   %s has choosen color %s", name, requestedCardColor);
192:                 }
193:                 else if (peek.GetPicture() == CardPicture::Acht)
194:                 {
195:                     MauMaster::Log(">   '8' is on top of deck - skip next player");   
196: 
197:                     eightIsActive = true;
198:                     skipNextPlayer = true;
199:                 }
200:             }
201:             else
202:             {
203:                 char name[32];
204:                 m_players[currentPlayer].GetName(name, 32);
205:                 MauMaster::Log(">   %s cannot serve, draws %d card(s)", name, 1);
206: 
207:                 m_players[currentPlayer].DrawCards(1);
208:             }
209:         }
210: 
211:         // test, if current player quits
212:         if (m_players[currentPlayer].GetNumberCards() == 0)
213:         {
214:             char name[32];
215:             m_players[currentPlayer].GetName(name, 32);
216:             MauMaster::Log(">   %s quits game !", name);
217: 
218:             m_players[currentPlayer].IsPlaying(false);
219:             activePlayers--;
220:         }
221: 
222:         // switch to next player
223:         currentPlayer = NextPlayer(currentPlayer);
224: 
225:         // '8' has just been played, next player pauses
226:         if (skipNextPlayer == true)
227:         {
228:             // skip next player
229:             currentPlayer = NextPlayer(currentPlayer);
230: 
231:             skipNextPlayer = false;
232:         }
233:     }
234: 
235:     // last player loses the game
236:     char name[32];
237:     m_players[currentPlayer].GetName(name, 32);
238:     LogFinalGameStatus(name);
239: }
240: 
241: // private helper methods
242: int MauMaster::NextPlayer(int currentPlayer)
243: {
244:     // move to next player
245:     currentPlayer++;
246:     if (currentPlayer == m_numPlayers)
247:         currentPlayer = 0;
248: 
249:     // search next array slot with still active player
250:     while (!m_players[currentPlayer].IsPlaying())
251:     {
252:         currentPlayer++;
253:         if (currentPlayer == m_numPlayers)
254:             currentPlayer = 0;
255:     }
256: 
257:     return currentPlayer;
258: }
259: 
260: int MauMaster::IncrementNumberOfCardsToDraw(int numberOfCardsToDraw)
261: {
262:     numberOfCardsToDraw += 2;
263:     return numberOfCardsToDraw;
264: }
265: 
266: void MauMaster::LogFinalGameStatus(char* name)
267: {
268: #if defined (VERBOSE)
269:     cout << name << " has lost --- Game over [" << m_rounds << " rounds]" << endl;
270: #endif
271: }
272: 
273: void MauMaster::LogGameStatusDebug(Card topMostCard, int currentPlayer)
274: {
275: #if defined (VERBOSE)
276:     cout << "------------------------------------------------------------------" << endl;
277:     cout << "Topmost card: " << topMostCard << endl;
278:     cout << "------------------------------------------------------------------" << endl;
279: 
280:     for (int i = 0; i < m_numPlayers; i++)
281:     {
282:         const char* prefix = (i == currentPlayer) ? "-->" : "   ";
283:         cout << prefix << " " << m_players[i] << endl;
284:     }
285: 
286:     cout << "------------------------------------------------------------------" << endl;
287: #endif
288: 
289: #if defined (SINGLE_STEP)  
290:     _getch();   // just for testing
291: #endif
292: }
293: 
294: void MauMaster::PrintVersion()
295: {
296:     cout << "------------------------------------------------------------------" << endl;
297:     cout << MauMaster::Version << endl;
298:     cout << "------------------------------------------------------------------" << endl;
299: }
300: 
301: // logging methods
302: void MauMaster::Log(char const* message)
303: {
304: #if defined (VERBOSE)
305:     cout << message << endl;
306: #endif
307: }
308: 
309: void MauMaster::Log(char const* message, char const* string)
310: {
311: #if defined (VERBOSE)
312:     char buf[128];
313:     snprintf(buf, sizeof(buf), message, string);
314:     cout << buf << endl;
315: #endif
316: }
317: 
318: void MauMaster::Log(char const* message, char const* string, int n)
319: {
320: #if defined (VERBOSE)
321:     char buf[128];
322:     snprintf(buf, sizeof(buf), message, string, n);
323:     cout << buf << endl;
324: #endif
325: }
326: 
327: void MauMaster::Log(char const* message, char const* string, CardColor col)
328: {
329: #if defined (VERBOSE)
330:     const char* colorDisplay[] =
331:     {
332:         "Empty", "Karo", "Herz", "Pik", "Kreuz"
333:     };
334: 
335:     char buf[128];
336:     snprintf(buf, sizeof(buf), message, string, colorDisplay[(int)col]);
337:     cout << buf << endl;
338: #endif
339: }
340: 
341: void MauMaster::Log(char const* message, char const* string1, char const* string2, char const* string3)
342: {
343: #if defined (VERBOSE)
344:     char buf[128];
345:     snprintf(buf, sizeof(buf), message, string1, string2, string3);
346:     cout << buf << endl;
347: #endif
348: }

Beispiel 10. Klasse MauMaster: Implementierung


001: #include <limits>
002: #include <iostream>
003: using namespace std;
004: 
005: #include "CardColor.h"
006: #include "CardPicture.h"
007: #include "Card.h"
008: #include "CardDeck.h"
009: #include "CardSet.h"
010: #include "Player.h"
011: #include "MauMaster.h"
012: 
013: void TestUnit_01_Cards()
014: {
015:     Card c1(CardColor::Kreuz, CardPicture::Neun);
016:     Card c2(CardColor::Pik, CardPicture::König);
017: 
018:     cout << c1 << endl;
019:     cout << c2 << endl;
020: 
021:     cout << "Farbe: " << c1.GetColorAsString() << endl;
022:     cout << "Bild: " << c1.GetPictureAsString() << endl;
023: 
024:     if (c1 == c2)
025:         cout << "Die Karten sind gleich" << endl;
026:     else
027:         cout << "Die Karten sind verschieden" << endl;
028: }
029: 
030: void TestUnit_02_CardDeck()
031: {
032:     Card c1(CardColor::Kreuz, CardPicture::Neun);
033:     Card c2(CardColor::Pik, CardPicture::König);
034:     Card c3(CardColor::Herz, CardPicture::Sieben);
035: 
036:     CardDeck deck;
037:     deck.Push(c1);
038:     deck.Push(c2);
039:     deck.Push(c3);
040: 
041:     cout << deck << endl;
042: }
043: 
044: void TestUnit_03_CardDeck()
045: {
046:     // test frame for CardDeck objects
047:     CardDeck deck;
048:     deck.SetRandomSeed(1);
049:     deck.Fill();
050:     cout << deck << endl;
051:     deck.Shuffle();
052:     cout << deck << endl;
053: }
054: 
055: void TestUnit_04_CardSet()
056: {
057:     // test frame for a single CardSet object
058:     Card card1 (CardColor::Kreuz, CardPicture::Neun);
059:     Card card2 (CardColor::Pik, CardPicture::König);
060:     Card card3 (CardColor::Herz, CardPicture::Sieben);
061: 
062:     CardSet set;
063:     set.Add(card1);
064:     set.Add(card2);
065:     set.Add(card3);
066: 
067:     for (int i = 0; i < set.Size(); i++)
068:     {
069:         cout << "Karte " << i << ": " << set[i] << endl;
070:     }
071: 
072:     cout << "Karten auf der Hand:" << endl << set << endl;
073:     set.Remove(1);
074:     cout << "Karten auf der Hand:" << endl << set << endl;
075: }
076: 
077: void TestUnit_10_PlayTheGame()
078: {
079:     MauMaster::PrintVersion();
080:     const char* names[] = { "Hans", "Sepp", "Ulli" };
081:     MauMaster mm (names, 3);
082:     mm.Init(111);
083:     mm.Play();
084: }
085: 
086: void TestUnit_11_SingleTestMauMaster()
087: {
088:     MauMaster::PrintVersion();
089:     const char* names[] = { "Hans", "Sepp", "Ulli" };
090:     MauMaster mm(names, 3);
091:     int randomSeed = 71;
092:     mm.Init(randomSeed);
093:     mm.Play();
094: }
095: 
096: void TestUnit_12_StressTestMauMaster()
097: {
098:     MauMaster::PrintVersion();
099:     const char* names[] = { "Hans", "Sepp", "Ulli" };
100:     MauMaster mm(names, 3);
101: 
102:     int minRounds = numeric_limits<int>::max();
103:     int minRoundsIndex = -1;
104:     int maxRounds = -1;
105:     int maxRoundsIndex = -1;
106: 
107:     for (int i = 1; i < 1000; i++)
108:     {
109:         mm.Init(i);
110:         mm.Play();
111: 
112:         if (mm.GetRounds() < minRounds)
113:         {
114:             minRounds = mm.GetRounds();
115:             minRoundsIndex = i;
116:         }
117: 
118:         if (mm.GetRounds() > maxRounds)
119:         {
120:             maxRounds = mm.GetRounds();
121:             maxRoundsIndex = i;
122:         }
123: 
124:         cout << "Game at " << i << ": " << mm.GetRounds() << endl;
125:     }
126: 
127:     cout << "Minumum number of rounds: " << minRounds << " [Index " << minRoundsIndex << "]" << endl;
128:     cout << "Maximum number of rounds: " << maxRounds << " [Index " << maxRoundsIndex << "]" << endl;
129: }
130: 
131: int main()
132: {
133:     TestUnit_01_Cards();
134:     TestUnit_02_CardDeck();
135:     TestUnit_03_CardDeck();
136:     TestUnit_04_CardSet();
137:     TestUnit_10_PlayTheGame();
138:     TestUnit_11_SingleTestMauMaster();
139:     // TestUnit_12_StressTestMauMaster();
140: 
141:     return 0;
142: }

Beispiel 11. Klasse Program