[C++] Tic Tac Toe mit GUI (Borland c++-Builder 5 VCL) [Teil 4 von 4]

Der dritte Teil dieser Artikelserie behandelte das Optionsmenü von Tic Tac Toe.

Im vierten und letzten Teil geht es um alles, was sonst noch nicht besprochen wurde. Da wäre z.B. die Anzeige der Anleitung oder die Handhabung der verschiedenen Spielertypen. Zuerst widmen wir uns den Spielern.

Die Spielertypen und die Vererbung

uml_neuIm ersten Teil ist ein Klassendiagramm zu sehen, das zeigt, wie die Klassen miteinander in Verbindung stehen. Man sieht, dass es die Klassen Spieler, Computer und Mensch gibt. Dabei ist Spieler eine abstrakte Klasse. Man kann von ihr also keine Instanzen erzeugen.

Die Klassen Computer und Mensch erben alle Methoden und Eigenschaften von Spieler, die public oder protected sind. Hier mal die Klasse „Spieler“.

/*
   Diese Klasse ist abstrakt,
   weil sie nur als Interface für die weiteren Spielerklassen dient.
   Sie enthält alle Methoden und Attribute, die alle Kindklassen auch
   benötigen.
*/
/* abstract */ class Spieler
{
  protected:
    AnsiString name;                //Spielername
    TColor color;                   //Spielerfarbe

  public:
    Spieler(AnsiString name)        //Konstruktor mit Name als Parameter
    {
     this->setName(name);           //Name setzen
    }

    void setName(AnsiString name);  //Name setzen
    AnsiString getName();           //Name zurückgeben

    void setColor(TColor);          //Farbe setzen
    TColor getColor();              //Farbe zurückgeben

    virtual AnsiString getType() = 0;  //Abstrakte Methode zur Typermittlung
};

void Spieler::setName(AnsiString n)
{
   this->name = n;                  //Name zuweisen
}

AnsiString Spieler::getName()
{
  return this->name;               //Name zurückgeben
}

TColor Spieler::getColor()
{
       return this->color;         //Farbe zurückgeben
}

void Spieler::setColor(TColor cl)
{
 this->color = cl;                 //Farbe setzen
}

Es gibt Methoden zum Setzen und Auslesen des Namens und der Spielerfarbe. Diese Methoden werden in allen Spielerklassen benötigt und werden deshalb nur einmal implementiert.

Die Klasse Mensch, die von Spieler erbt, überschreibt die virtuelle Methode getType:

class Mensch : public Spieler
{
  public:
      Mensch() : Spieler("Mensch")        //Konstruktor setzt Standartnamen
      {
      };

      AnsiString getType()               //Für die restliche Applikation,
      {                                  //Die wissen muss, um was für einen Spieler es sich handelt
        return "Mensch";                 //Gibt den Typ zurück
      }
};

getType() gibt immer den String „Mensch“ zurück. So kann man später in der Anwendung prüfen, ob ein Spieler ein Mensch oder ein PC ist.

Und da der Computer 2 Schwierigkeitsstufen kennt, habe ich das auch direkt in die Klasse „Computer“ gepackt.

//Die Klasse Computer wird von der abstrakten Klasse Spieler abgeleitet
//und repräsentiert einen PC-Spieler.
//Es gibt verschiedene Schwierigkeitsstufen

class Computer : public Spieler
{
    private:
      unsigned int schwierigkeit; // 1 = Einfach, 2 = Schwer

    public:
      Computer() : Spieler("Computer")
      {
        this->schwierigkeit = 2;   //Standart = schwer
      };

      Computer(int s) : Spieler("Computer")
      {
        this->schwierigkeit = s;     //Schwierigkeit als Konstruktorparameter
      }

      AnsiString getType()
      {
        return "Computer";           //Spielertyp zurückgeben
      }

      unsigned int getSchwierigkeit()
      {
        return this->schwierigkeit;  //Schwierigkeit zurückgeben
      }

      void setSchwierigkeit(unsigned int s)     //Schwierigkeit setzen
      {
        if(s == 1 || s == 2)
        {
          this->schwierigkeit = s;
        }
        else
        {
          throw "Diese Schwierigkeit existiert nicht!";   //Exception
        }
      }

};

Ist doch gar nicht so schwer!

Die Handhabung von verschiedenen Typen in einem Pointer (Zeiger)

Wenn ich jetzt einen Pointer so deklariere

Spieler* spieler = new Computer

dann kann ich zwar alle Methoden aufrufen, die schon in Spieler implementiert wurden, aber ich kann z.B. nicht ermitteln, welche Schwierigkeit der PC hat. Aus diesem Grund werden bei mir an vielen Stellen im Code dynamic_casts durchgeführt.

Dadurch kann man auch Methoden ausführen, die in einer abgeleiteten Klasse daklariert wurden.

Anleitung

Auch der Anleitung habe ich ein eigenes Formular gewidmet. Es besteht nur aus einem TRichEdit und einem TButton. Der Button ist nur zum Schließen da und das RichEdit-Feld soll später die Anleitung beinhalten.

Ich habe beim Programmieren des Anleitungsfensters ganz bewusst darauf verzichtet, einen Controller und ein Model anzulegen. Denn das ist so wenig Code, dass sich das einfach nicht gelohnt hätte. Alles, was getan werden muss, ist die Datei mit dem Text einlesen und anzeigen.

Das wird beim Programmstart gemacht, wenn das Formular erzeugt wird.

//wird beim Erstellen des Formulars aufgerufen
//Liest den Text aus einer Datei ein
void __fastcall TForm3::FormCreate(TObject *Sender)
{
  //Die Datei "anleitung.ttt" enthält den Text für die Spielanleitung.
  if(FileExists("anleitung.ttt"))
  {
   this->anleitungstext->Lines->LoadFromFile("anleitung.ttt");            //Datei in Memo laden
  }
  else
  {
   this->anleitungstext->Text = "Die Datei anleitung.ttt, die den Text der Anleitung enthält, wurde leider nicht gefunden.";
  }
}

Falls die anleitung.ttt (die übrigens mit WordPad erstellt wurde) existiert, wird deren Inhalt als Anleitung angezeigt. Falls die Datei fehlt, wird ein Hinweistext angezeigt.

Und das wars auch schon!

Fazit

Insgesamt hat mir die Entwicklung dieses Spiels wirklich viel Spass gemacht und enorm viel neues Wissen über C++ beschert. Anfangs war ich C++ gegenüber noch eher negativ eingestellt, weil ich mich durch die Sache mit der starken Typisierung stark eingeschränkt fühlte, aber ich habe gemerkt, dass man auch damit leben kann. Mitlerweile macht mit C++ fast so viel Spass wie PHP. Aber auch nur fast 😉

Was hätte man besser machen können?

Sicherlich hätte man einiges einfacher programmieren können. Mit meinem derzeitigen Wissenstand war aber nichts besseres möglich. Außerdem hätte man den Code noch weiter abstrahieren und die 3 Schichten noch besser voneinander trennen können.

Aber am Ende hat dann doch der Zeitdruck gesiegt und ich habe es im derzeitigen Zustand abgegeben. Meinem Lehrer hats scheinbar gereicht, jedenfalls hat er mir 15 Punkte dafür gegeben 🙂

Erweiterungsideen

Anfangs war geplant, dass das Spiel auch übers Netzwerk/Internet spielbar sein soll. Aber das war dann zeitlich nicht mehr möglich. Aber ich nehme mir vor, dass ich diese Funktionalität in mein geplantes 4 gewinnt einbaue!

Außerdem wäre es sicher nett, wenn man einstellen könnte, wie groß das Spielfeld ist. Dazu bräuchte ich aber dynamische Arrays, die ich bisher aber noch nicht verstanden habe. Kommt aber hoffentlich mit der Zeit 🙂

Bessere KI

Die KI ist auch so ein Punkt, der mir gar nicht gefällt. Sie „denkt“ nicht mit, sondern handelt nur nach statischen Vorgaben. Also ist es eigentlich gar keine KI. Wenn es irgendwie möglich ist, das Spiel im Voraus durchzurechnen und so zu sehen, wo gesetzt werden muss, um zu gewinnen, dann wäre das eine KI!

Das ist auch mein Ziel für 4-Gewinnt. Aber bis dahin ist es noch ein weiter Weg.

The End

Das wars auch schon mit meiner Artikelreihe über Tic Tac Toe in C++. Ich hoffe, euch haben die Artikel gefallen. Ich freue mich über jegliches Feedback!

Wer sich jetzt dazu berufen fühlt, die Projektdateien anzuschauen, kann nochmal zum ersten Teil gehen und dort alles downloaden.

Umfrage

Wie findest du die 4 Artikel zu Tic Tac Toe? (Schulnoten)

View Results

Wird geladen ... Wird geladen ...
1 Star2 Stars3 Stars4 Stars5 Stars (Wurde noch nicht bewertet)
Loading...


2 Kommentare zu “[C++] Tic Tac Toe mit GUI (Borland c++-Builder 5 VCL) [Teil 4 von 4]”

  1. Eine schöne Idee solch ein Projekt vorzustellen. 🙂
    Kannst ja mal mehr davon veröffentlichen, z.B. ein PHP-Projekt mit OOP 🙂

  2. Ja, das wäre eine Überlegung wert 😉

Hinterlasse einen Kommentar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.

»Informationen zum Artikel

Autor: Simon
Datum: 19.07.2009
Zeit: 15:00 Uhr
Kategorien: C/C++
Gelesen: 9967x heute: 5x

Kommentare: RSS 2.0.
Diesen Artikel kommentieren oder einen Trackback senden.

»Meta