"Deadlock" in Klassendeklarationen



  • vorwärts deklaration

    class A;
    
    class B
    {
    public:
       A a;
    };
    
    class B;
    class A
    {
    public:
       B b;
    };
    


  • Was sind denn Guards? Google spuckt nix brauchbares aus

    Hast du ein Beispiel dazu?



  • Hallo

    Nein Include Guards helfen da nicht weiter (und ich vermute das PHPFreak diese bereits eingebaut hat)
    Sondern wie BigNeal bereits geschrieben hat, das Zauberwort heißt Forward Declaration. Allerdings erzwingt das die Verwendung von Zeigern oder Referenzen bei den Membern. Siehe Suchfunktion hier im BCB-Forum und im C++ Forum.

    bis bald
    akari



  • Ich verwende schon ausschließlich Pointer als Attribute. Trotzdem komme ich so nicht weiter...

    Das Problem ist, dass ich nicht nur den Namen der Klasse verwende (z.b. so: methode(B* objekt)), sondern dass ich auch auf dessen Methoden zugreifen muss



  • Forward Deklartion hatte ich vor dem Editeren, PHPfreak hat dann darauf geantwortet, ich hab nicht mitbekommen und geändert auf die Guards ;o

    PHPfreak schrieb:

    Was sind denn Guards? Google spuckt nix brauchbares aus

    Hast du ein Beispiel dazu?

    Oben. Die #ifndef #define #endif Gerüst.



  • Ah ok, danke für den Hinweis!

    Aber wie löse ich mein Problem? 😃 😃



  • Hallo

    Indem du Forward Declaration verwendest. Zeiger benutzen reicht nicht aus.

    bis bald
    akari



  • Aber das hab ich doch... Mal ein Beispiel:

    class Controller;
    
    //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
    

    Aber in der Klasse Computer wird die Methode getCounter() von Controller aufgerufen. Und die kennt er nicht



  • Da musst du leider mehr zeigen. Aus deinem Beispiel seh ich keine Probleme.



  • Ok, hier die gesamte Datei:

    class Controller;
    
    //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
            }
          }
    
          bool move(SpielfeldModel* model, Feld* feld, Controller* controller)
          {
           Sleep(300);
           switch(this->getSchwierigkeit())
           {
            case 1:  this->pcEasy(model);                    //PC spielt mit pcEasy()
                     break;
            case 2:  this->pcDifficult(controller, model);               //PC spielt mit pcDifficult()
                     break;
            default: throw "Crashed due to wrong difficulty";
                     break;
            }
    
           return true;
          }
    
          /*
          Die einfache Spielmethode setzt an einer zufälligen Stelle im Spielfeld
          einen Stein, falls das Feld frei ist.
          */
          void pcEasy(SpielfeldModel* model)
          {
           Feld *feld = NULL;  //Temporärer Speicher für Feld
           int x = 0, y = 0;   //Koordinaten des PC-Zugs
    
           randomize();        //Zufallsgenerator starten
    
           do
           {
            x =  random(CONST__anzX);                //zufällige x-Koordinate
            y =  random(CONST__anzY);                //zufällige y-Koordinate
            if(model->isValidCoord(x,y))       //Prüfen, ob Koordinate erlaubt ist
            {
             feld = model->getFeld(x,y);      //Das Feld (x|y) holen
            }
           }
           //solange das Spiel nicht unentschieden, beendet oder das Feld voll ist.
           while(feld != NULL && !feld->isEmpty());
    
           feld->setPlayer(this);            //Dem gefundenen Feld den Besitzer zuweisen
          }
    
          /*
          Die schwere Spielmethode versucht, eigene Kombinationen zu erstellen und zu
          vervollständigen.
          Außerdem findet diese Methode die "Gewinnversuche" des Gegners und versucht,
          diese zu verhindern.
          */
          void pcDifficult(Controller* controller, SpielfeldModel* model)
          {
           Feld *feld = NULL;  //Temporärer Speicher für Feld
    
           //Die ersten beiden Züge sollen nicht mit der KI gemacht werden,
           //weil der PC sonst am Anfang immer den gleichen Zug macht, wenn der Mensch
           //das auch macht.
           if(controller->getCounter() <= 1)
           {
            this->pcEasy(model);
            return;         //Abbruch, da Zug gemacht wurde.
           }
    
           //Als erstes wird versucht, eigene 3er-Kombinationen zu erzeugen
           feld = controller->findCombinations(controller->getCurrentPlayer(), 2);
    
           if(feld == NULL)
           { //Dann wird versucht, gegnerische 2er zu blockieren, so dass keine 3er entstehen
             feld = controller->findCombinations(controller->getNextPlayer(), 2);
           }
    
           if(feld == NULL)
           { //Andernfalls werden aus eigenen 1ern 2er gemacht
             feld = controller->findCombinations(controller->getCurrentPlayer(), 1);
           }
    
           if(feld == NULL)
           {  //Sonst gegnerische 1er blockieren, so dass keine 2er entstehen
              feld = controller->findCombinations(controller->getNextPlayer(), 1);
           }
    
           if(feld == NULL)
           { //Sollte nichts geklappt haben, wird die Zufallsmethode versucht
             this->pcEasy(model);
           }
           else
           { //Wenn die KI zugeschlagen hat, wird das Feld belegt
             feld->setPlayer(this);
           }
          }
    };
    


  • Sieht so aus, als wäre mein Problem wirklich unlösbar 😃
    Ich hab jetzt nochmal rumprobiert und es nicht hingekriegt



  • du musst mehrere dateien verwenden.
    statt alles inline zu definieren, trennst du es
    in deklaration und definition:

    in a.h

    #includeguard
    
    class b;
    class a
    {
    public:
        void func(b *);
    };
    

    in a.cpp

    #include "a.h"
    #include "b.h"
    
    a::func(b *ptr)
    {
      ptr->b_only();
    }
    


  • So geht es bei zyklischen Abhängigkeiten:

    // App.cpp
    #pragma hdrstop
    
    #include <tchar.h>
    #include <iostream>
    #include "A.h"
    #include "B.h"
    
    using namespace std;
    
    #pragma argsused
    int _tmain(int argc, _TCHAR* argv[])
    {
    
    	A* a = new A();
    	B* b = new B();
    
    	a->where(b);
    	b->where(a);
    
    	cin.get();
    	return 0;
    }
    
    // A.h
    #ifndef AH
    #define AH
    
    #include "B.h"
    
    class B;
    
    class A
    {
    public:
    	void where(B* b) const;
    };
    
    #endif
    
    // A.cpp
    #pragma hdrstop
    
    #include <iostream>
    #include "A.h"
    
    using std::cout;
    using std::endl;
    
    void A::where(B* b) const
    {
    	cout << "B is there:" << b << endl;
    }
    #pragma package(smart_init)
    
    // B.h
    #ifndef BH
    #define BH
    
    #include "A.h"
    
    class A;
    
    class B
    {
    public:
    	void where(A* a) const;
    };
    
    #endif
    
    //B.cpp
    #pragma hdrstop
    
    #include<iostream>
    #include "B.h"
    
    using std::cout;
    using std::endl;
    
    void B::where(A* a) const
    {
    	cout << "A is there:" << a << endl;
    }
    #pragma package(smart_init)
    


  • Sorry Zeus, aber du hast immer noch zyklische Abhängigkeiten:
    A.h bindet B.h ein und umgekehrt!!!

    #include "A.h" bzw. #include "B.h" müssen aus den jeweiligen Headerdateien gelöscht werden, und (wie akari schon zweimal schrieb) nur Vorwärtsdeklarationen (forward declarations) verwendet werden:

    // A.h
    #ifndef AH
    #define AH
    
    class B;
    
    class A
    {
    public:
        void where(B* b) const;
    };
    #endif
    
    // B.h
    #ifndef BH
    #define BH
    
    class A;
    
    class B
    {
    public:
        void where(A* a) const;
    };
    
    #endif
    

    Nur wenn A ein Objekt von B enthält (als Member) wird die komplette Klassendeklaration von B benötigt (und damit ein #include "B.h").



  • Ist das ein Problem?



  • So, hab jetzt alles in Header-Dateien und in "Body"-Dateien aufgesplitetet.
    Das war vielleicht ne Drecksarbeit, aber sie hat nichts gebracht.

    Ich include als Beispiel so:

    include "controller.h"
    include "controller.cpp"
    

    In Controller.h ist die Klasse definiert und in controller.cpp sind die Methoden ausformuliert. Leider erhalte ich jetzt diese schöne Fehlermeldung:

    [C++ Fehler] controller.cpp(1): E2090 Qualifizierer 'Controller' ist kein Name einer Klasse oder einer Struktur

    bool Controller::columnIsFull(int column)
    {
      return this->getHighestField(column) == NULL;
    }
    

    Wie krieg ich das denn jetzt hin?
    Müsste doch theoretisch so gehen, oder nicht?



  • Hallo

    include "controller.cpp"

    Warum das denn? Das Einbinden von Implementations-Dateien (im Gegensatz zu Header-Dateien) ist sehr unüblich und meistens ein Fehler.

    Also ich werde dir dann auch noch die Verwendung der Suchfunktion abnehmen, und finde gleich diesen Thread, wo ich den Einsatz von Forward Declaration an einem Beispiel erläutere.

    bis bald
    akari



  • Bin ich jetzt komplett bescheuert?

    Ich bin mir sicher, dass ich die Forward Declarations richtig angwendet habe.

    Die includes habe ich jetzt übrigens entfernt. Hat nichs an der Situation geändert. Werden cpp-Dateien autmatisch includiert oder warum braucht man die nicht?

    Also in PHP gibts sowas nicht 😃 Vielleicht ist das daher für mich grad alles so schwer verständlich



  • cpp Dateien werden nicht inkludiert sondern vom Compiler einzeln in Objektcode überstetzt. Der Linker fügt dann zu Schluss alles zusammen.
    Wenn es immer noch nicht geht, dann hast du wahrscheinlich noch Definitionen in deinen Headerdateien drin. In die Headerdateien gehören bei dir nur Deklarationen.
    Zeig bitte mal deinen Code.



  • Also die Header-Datei vom Controller:

    /*
       Der Controller regelt den Spielablauf
    */
    class Controller                                 //Kontrolliert den Spielablauf
    {
       public:
         Controller(TForm1*);                        //Konstruktor
         Spieler* getSpieler(int);                   //Liefert einen Spieler
         TForm1* getGUI();                            //GUI zurückgeben
         SpielfeldModel* getModel();                 //Liefert das Model (Speicher aller Felder)
         void reset();                               //Spiel zurücksetzten (Neustart)
         void setPlayerType(int id, AnsiString type); //Typ setzen
         void refreshStatus();                       //Aktualisiert die Statusanzeige
         void refreshTimer();                        //Aktualisiert die Zeitanzeige
         void resetTimer();                          //Uhr auf 0 setzen
         int getCounter();                           //gibt Anzahl der Spielzüge zurück
         Feld* findCombinations(Spieler*, unsigned int);      //Findet Kombinationen
         Spieler* getCurrentPlayer();                //Den aktuellen Spieler zurückgeben
         Spieler* getNextPlayer();                   //Nächstes Spieler-Objekt
    
       private:
         TForm1 *gui;                                //Die GUI
         Spieler *spieler[2];                        //Das Array der Spieler
         int counter;                                //Zählt die Spielzüge
         SpielfeldModel* model;                      //Model
         unsigned int timer_min;                     //Minutenanzeige
         unsigned int timer_sec;                     //Sekundenanzeige
         bool finished;                              //Spiel beendet?
         bool isCombination(Spieler*, int, int, int,
                                 int, unsigned int); //Kombination?
         void __fastcall onClick(TObject *Sender);   //Behandelt einen Klick
         void __fastcall adjust(TObject *Sender);    //Passt das Spielfeld an
         void setFinished(bool);                     //Spiel beenden / starten
         bool isFinished();                          //Spiel beendet?
         void zugPC();                               //Zug des PC-Spielers
         void pcEasy();                              //PC-Schwierigkeit Einfach
         void pcDifficult();                         //PC-Schwierigkeit Schwer
         bool isUnentschieden();                     //Ist das Spiel unentschieden?
         int getCurrentPlayerID();                   //Die ID des aktuellen Spielers zurückgeben
         int getNextPlayerID();                      //Die nächste Spieler-ID
         void searchWinner();                        //Sucht gewinner / Unentschieden
         void incrementCounter();                    //1 Spielzug mehr
         void decrementCounter();                    //1 Spielzug weniger
         void resetCounter();                        //Setzt die Spielzüge auf 0 zurück
         Spieler* checkField();                      //Ermittelt einen Gewinner
         Feld* getHighestField(int);
         bool columnIsFull(int);
    };
    

    Dann der Body (achtung, laang)

    //Konstruktor initialisiert Variablen und erstellt Objekte
    Controller::Controller(TForm1* Form1)
    {
       this->gui = Form1;                   //Das erste Formular als GUI verwenden
    
       //Die Breite der GUI fetlegen
       this->gui->Width = (CONST__anzX+1)* (CONST__breite_label + CONST__abstand) + CONST__abstand;
    
       //Die Höhe der GUI festlegen
       this->gui->Height = (CONST__anzY+1)* (CONST__hoehe_label + CONST__abstand) + CONST__abstand + 10
                           + this->gui->label_spieldauer->Height
                           + this->gui->label_status->Height;
    
       //Eventhandler des Feldcontainers beim Resize (auch beim Hinzufügen von Elementen)
       this->gui->panel_feldcontainer->OnResize = adjust;
    
       //Eventhandler der GUI beim Resitze = OnResize von Feldcontainer
       this->gui->OnResize = this->gui->panel_feldcontainer->OnResize;
    
       //Das Model, das das Spielfeld enthält, wird erstellt.
       this->model = new SpielfeldModel(
                                      this->gui, this->gui->panel_feldcontainer,
                                      CONST__breite_label,
                                      CONST__hoehe_label,
                                      (CONST__hoehe_label + CONST__abstand),
                                      (CONST__breite_label + CONST__abstand)
                                     );
    
       //Jetzt muss noch jedem Feld die onClick-Methode zugewiesen werden.
       for(int y = 0; y < CONST__anzY; y++)
       {
         for(int x = 0; x < CONST__anzX; x++)
         {
           model->getFeld(x,y)->OnClick = onClick;
         }
       }
    
       this->spieler[0] = new Mensch;                             //Ersten Spieler erzeugen
       this->spieler[0]->setColor(CONST__default_spieler1_color); //Farbe setzen
    
       this->spieler[1] = new Computer(2);                        //Zweiten Spieler erzeugen
       this->spieler[1]->setColor(CONST__default_spieler2_color); //Farbe setzen
    
       this->counter = 0;                   //Es wurde noch nicht gespielt
       this->finished = false;              //Das Spiel ist noch nicht beendet
       this->timer_min = 0;                 //Timer zurücksetzen
       this->timer_sec = 0;                 //Timer zurücksetzen
       this->refreshStatus();               //Statusanzeige aktualisieren (bzw. initialisieren)
    }
    
    bool Controller::columnIsFull(int column)
    {
      return this->getHighestField(column) == NULL;
    }
    
    Feld* Controller::getHighestField(int column)
    {
      if(this->model->isValidCoordX(column))
      {
         int x = column;
         for(int y = CONST__anzY-1; y >= 0; y--)
         {
           if(this->model->getFeld(x,y)->isEmpty())
           {
             return this->model->getFeld(x,y);
           }
         }
         return NULL;
      }
      else
      {
        throw "Illegal column given";
      }
    }
    
    // Diese Methode regelt den Spielablauf. Mit dem ersten Klick eines Spielers beginnt das Spiel.
    // Ist der zweite Spieler der PC, wird automatisch die entsprechende Methode aufgerufen.
    void __fastcall Controller::onClick(TObject *Sender)
    {
     Feld *feld = NULL;                  //Temporärer Speicher für Felder
     feld = dynamic_cast<Feld*>(Sender); //Sender-Objekt wird zu Feld-Objekt gecastet
    
     /*
     if(feld != NULL && !this->columnIsFull(this->model->getX(feld)))
     {
      this->getHighestField(this->model->getX(feld))->setColor(this->getCurrentPlayer()->getColor());
      this->getHighestField(this->model->getX(feld))->setPlayer(this->getCurrentPlayer());
     }
     */
    
     if(this->isFinished())  //Falls das Spiel beendet wurde, wird abgebrochen
     {
       return;               //Abbrechen
     }
    
     bool worked = this->getCurrentPlayer()->move(this->model, feld, this);
     this->incrementCounter();
    
     if(worked && !this->isFinished())
     {
       if(this->getCurrentPlayer()->getType() == "Computer")
       {
         this->onClick(NULL);
       }
     }
     return;
    
     if(this->getCurrentPlayer()->getType() == "Mensch"  //Falls der Spieler ein Mensch ist
     && (Sender == NULL || feld == NULL))                //Und kein Feld angeklickt wurde
     {
       return;                                           //Abbrechen
     }
    
     if(this->getCurrentPlayer()->getType() == "Mensch"
        && !feld->isEmpty()) //Falls der Spieler ein Mensch ist und das Feld belegt ist
     {
       return;                         //Wird die Methode abgebrochen, da nur leere
     }                                 //Felder angeklickt werden dürfen
    
     //Sollte der aktuelle Spieler der PC sein, muss der Zug des PCs mit der Methode zugPC ausgeführt werden
     if(this->getCurrentPlayer()->getType() == "Computer" && !this->isFinished())
     {
      Sleep(300);  //PC setzt nicht sofort, sondern wartet 300ms.
    
      this->zugPC(); //Der PC führt den Zug aus
     }
     //Der aktuelle Spieler ist kein PC, sondern ein Mensch. Klick wird ausgewertet
     else if(this->getCurrentPlayer()->getType() == "Mensch" && !this->isFinished())
     {
        if(feld->isEmpty())  //Man darf nur leere Felder anklicken
        {
         feld->setColor(this->getCurrentPlayer()->getColor());  //Angeklicktes Feld einfärben
         feld->setPlayer(this->getCurrentPlayer());             //Besitzer des Felds eintragen
        }
     }
     else
     {
         ShowMessage("Der Spielertyp des aktuellen Spielers ist undefiniert. Spielabbruch");
         this->setFinished(true);   //Spiel beenden, da Fehler auftrat.
     }
    
     this->searchWinner();    //Schauen, ob es einen Gewinner gibt / Unentschieden
    
     //Nur weitermachen, wenn das Spiel nicht beendet wurde.
     if(!this->isFinished())
     {
       //Spielzähler erhöhen. Der aktuelle Spieler ist jetzt der nächste Spieler
       this->incrementCounter();
    
       //Status aktualisieren. Es wird jetzt der aktuelle (also der nächste) Spieler
       //im Status angezeigt.
       this->refreshStatus();
    
       //Diese Stelle ist sehr wichtig für den Fall, dass ein Computerspieler an der Reihe ist.
       //Der PC löst nämlich kein onClick-Event aus, deshalb machen wir das hier manuell.
       if(this->getCurrentPlayer()->getType() == "Computer")
       {
         //Hier wird das onClick-Event manuell ausgelöst (als ob der PC geklickt hätte)
         this->onClick(NULL);
       }
     }
     else
     {
       this->incrementCounter(); //Wenn Spiel beendet wurde, muss der Spielzähler
                                 //noch um 1 erhöht werden, weil er sonst den
                                 //beendenden Zug nicht enthält.
     }
    }
    
    //Diese Methode wird aufgereufen, wenn der PC dran ist.
    void Controller::zugPC()
    {
     //Aktueller Spieler ist PC?
     if(this->getCurrentPlayer()->getType() != "Computer")
     {
       ShowMessage("Es ist ein schwerwiegender Fehler aufgetreten");
       this->setFinished(true);                                      //Spiel beenden
     }
    
     //getCurrentPlayer gibt einen Pointer auf ein Spieler-Objekt zurück.
     //Die Klasse Spieler kennt aber die Methoden von Computer nicht.
     //Deshalb müssen wir einen Type-Cast zu einem Computer*-Pointer durchführen
     //Danach sind alle Methoden und Attribute des Objekts ansprechbar.
     Computer* spieler = dynamic_cast<Computer*>(this->getCurrentPlayer());
    
     //Je nach Schwierigkeit andere Methode ausführen
     switch(spieler->getSchwierigkeit())
     {
        case 1:  this->pcEasy();                    //PC spielt mit pcEasy()
                 break;
        case 2:  this->pcDifficult();               //PC spielt mit pcDifficult()
                 break;
        default: ShowMessage("Undefinierte Schwierigkeitsstufe");
                 break;
     }
    }
    
    /*
       Die einfache Spielmethode setzt an einer zufälligen Stelle im Spielfeld
       einen Stein, falls das Feld frei ist.
    */
    void Controller::pcEasy()
    {
     Feld *feld = NULL;  //Temporärer Speicher für Feld
     int x = 0, y = 0;   //Koordinaten des PC-Zugs
    
     randomize();        //Zufallsgenerator starten
    
     do
     {
       x =  random(CONST__anzX);                //zufällige x-Koordinate
       y =  random(CONST__anzY);                //zufällige y-Koordinate
       if(this->model->isValidCoord(x,y))       //Prüfen, ob Koordinate erlaubt ist
       {
         feld = this->model->getFeld(x,y);      //Das Feld (x|y) holen
       }
     }
     //solange das Spiel nicht unentschieden, beendet oder das Feld voll ist.
     while(!this->isUnentschieden() && feld != NULL && !feld->isEmpty() && !this->isFinished());
    
     feld->setColor(this->getCurrentPlayer()->getColor()); //Dem gefundenen Feld die Farbe zuweisen
     feld->setPlayer(this->getCurrentPlayer());            //Dem gefundenen Feld den Besitzer zuweisen
    }
    
    /*
       Die schwere Spielmethode versucht, eigene Kombinationen zu erstellen und zu
       vervollständigen.
       Außerdem findet diese Methode die "Gewinnversuche" des Gegners und versucht,
       diese zu verhindern.
    */
    void Controller::pcDifficult()
    {
     Feld *feld = NULL;  //Temporärer Speicher für Feld
    
     //Die ersten beiden Züge sollen nicht mit der KI gemacht werden,
     //weil der PC sonst am Anfang immer den gleichen Zug macht, wenn der Mensch
     //das auch macht.
     if(this->getCounter() <= 1)
     {
      this->pcEasy();
      return;         //Abbruch, da Zug gemacht wurde.
     }
    
     //Als erstes wird versucht, eigene 3er-Kombinationen zu erzeugen
     feld = this->findCombinations(this->getCurrentPlayer(), 2);
    
     if(feld == NULL)
     { //Dann wird versucht, gegnerische 2er zu blockieren, so dass keine 3er entstehen
      feld = this->findCombinations(this->getNextPlayer(), 2);
     }
    
     if(feld == NULL)
     { //Andernfalls werden aus eigenen 1ern 2er gemacht
      feld = this->findCombinations(this->getCurrentPlayer(), 1);
     }
    
     if(feld == NULL)
     {  //Sonst gegnerische 1er blockieren, so dass keine 2er entstehen
      feld = this->findCombinations(this->getNextPlayer(), 1);
     }
    
     if(feld == NULL)
     { //Sollte nichts geklappt haben, wird die Zufallsmethode versucht
       this->pcEasy();
     }
     else
     { //Wenn die KI zugeschlagen hat, wird das Feld belegt
       feld->setColor(this->getCurrentPlayer()->getColor());
       feld->setPlayer(this->getCurrentPlayer());
     }
    }
    
    /*
      Diese Methode durchsucht das Spielfeld nach Kombinationen mit der Länge "length"
      Anschließend gibt sie das Feld, das "vor" der Kombination kommt und leer ist,
      zurück.
    */
    Feld* Controller::findCombinations(Spieler* player, unsigned int length)
    {
    
     for(int y = 0; y < CONST__anzY; y++)
     {
       for(int x = 0; x < CONST__anzX; x++)
       {
         if(this->model->isValidCoord(x,y) &&            //Koordinate gültig?
            this->model->getFeld(x,y)->isEmpty()         //Feld leer?
            )
         {
           if(this->isCombination(player, x, y+1, 0, 1, length)     //Senkrecht
           || this->isCombination(player, x+1, y, 1, 0, length)     //Waagrecht
           || this->isCombination(player, x, y-1, 0, -1, length)    //Senkrecht
           || this->isCombination(player, x-1, y, -1, 0, length)    //Waagrecht
           || this->isCombination(player, x+1, y+1, 1, 1, length)   //Diagonal
           || this->isCombination(player, x+1, y-1, 1, -1, length)  //Diagonal
           || this->isCombination(player, x-1, y+1, -1, 1, length)  //Diagonal
           || this->isCombination(player, x-1, y-1, -1, -1, length) //Diagonal
           )
           {
              return this->model->getFeld(x,y);             //Feld zurückgeben
           }
         }
       }
      }
      return NULL;                                         //Kein Feld gefunden
    }
    
    /**
      Hier wird geprüft, ob einer der Spieler das Spiel gewonnen hat oder ob das
      Spiel unentschieden ist.
    */
    void Controller::searchWinner()
    {
    Spieler *winner = NULL;             //Temporärer Speicher für den Gewinner-Spieler
    winner = this->checkField(); //Das Spielfeld nach dem Gewinner durchsuchen
    if(winner != NULL)        //Wenn winner != NULL ist, gibt es einen Gewinner.
    {
      this->gui->setStatusMessage(winner->getName()+" hat gewonnen!");   //Gewinner ausgeben
      this->gui->setSpielerfarbeVorschau(winner->getColor());            //Farbe des Gewinners anzeigen
      this->setFinished(true);                                           //Spiel beenden
    }
    else
    {
      //Auf unentschieden prüfen
      if(this->isUnentschieden())                           //Prüfen, ob unentschieden ist
      {
       this->gui->setStatusMessage("Untentschieden");       //Unentschieden ausgeben
       this->gui->resetSpielerfarbeVorschau();              //Farbe zurücksetzen
       this->setFinished(true);                             //Spiel beenden
      }
    }
    }
    
    /**
       Gibt die ID (index im spieler-Array) des aktuellen Spielers zurück
    */
    int Controller::getCurrentPlayerID()
    {
      return  this->counter % 2 == 0 ? 0 : 1;
    }
    
    /*
       gibt den aktuellen Spieler zurück (Pointer auf Spieler-Objekt)
    */
    Spieler* Controller::getCurrentPlayer()
    {
      //Das Spieler-Objekt mit dem ermittelten Index wird returned
      return this->spieler[this->getCurrentPlayerID()];
    }
    
    /**
       Gibt die ID (index im spieler-Array) des nächsten Spielers zurück
    */
    int Controller::getNextPlayerID()
    {
      return (this->getCurrentPlayerID() == 1) ? 0 : 1;   //"toggle"
    }
    
    /*
       gibt den nächsten Spieler zurück (Pointer auf Spieler-Objekt)
    */
    Spieler* Controller::getNextPlayer()
    {
      //Das Spieler-Objekt mit dem ermittelten Index wird returned
      return this->spieler[this->getNextPlayerID()];
    }
    
    /*
       Prüft, ob alle Felder belegt sind und das Spiel somit beendet ist.
    */
    bool Controller::isUnentschieden()
    {
       int volle_felder = 0;  //Wieviele Felder sind bereits belegt?
    
       //In dieser verschachtelten for-Schleife wird ermittelt, wieviele Felder belegt sind.
       for(int y = 0; y < CONST__anzY; y++)
       {
         for(int x = 0; x < CONST__anzX; x++)
         {
           if(this->model->isValidCoord(x,y) &&     //Gültige Koordinate &&
             !this->model->getFeld(x,y)->isEmpty()  //Feld belegt?
             )
           {
             volle_felder++;    //Ein belegtes Feld mehr
           }
         }
       }
    
     //Entspricht die Anzahl der vollen Felder der Anzahl der vorhandenen Spielfelder?
     return (volle_felder >= (CONST__anzX * CONST__anzY));
    }
    
    //Diese Methode leitet den Aufruf direkt an die GUI weiter (Delegation)
    void __fastcall Controller::adjust(TObject *Sender)
    {
      this->gui->placeElements();
    }
    
    /*
      isCombination überprüft, ob es ab der Stelle (startX|startY) eine
      Feldkombination des Spielers player gibt.
    
      Mit dirX und dirY kann man steuern, ob waagrechte, senkrechte oder diagonale
      Kombinationen gesucht werden sollen.
      length gibt die Mindestlänge der Kombinationen an.
    */
    bool Controller::isCombination(Spieler* player, int startX, int startY, int dirX, int dirY, unsigned int length)
    {
       //Teste, ob Startkoordinaten gültig sind
       if(!this->model->isValidCoord(startX, startY))
       {
           return false; //Ungültige Koordinaten
       }
    
       //Teste, ob Endkoordinaten gültig sind
       if(!this->model->isValidCoord(startX + (dirX*(length-1)), startY + (dirY*(length-1))))
       {
          return false; //Ungültige Koordinaten
       }
    
       //jetzt wird eine Schleife length mal wiederholt.
       for(unsigned int i = 0; i < length; i++)
       {
         //Es wird geprüft, ob das Feld NICHT vom Spieler ist.
         if(this->model->getFeld(startX + (dirX*i), startY + (dirY*i))->getPlayer() != player)
         {
           return false;    //Wenn es nicht vom Spieler ist, wird false zurückgegeben,
                            //da dann keine Kombination existiert.
         }
       }
       return true;         //Eine Kombination existiert!
    }
    
    /*
        Überprüft, ob es im Spielfeld einen Gewinner gibt, der length
        Felder in einer Kombination hat.
     */
    Spieler* Controller::checkField()
    {
      unsigned int length = 3;    //TicTacToe erfordert 3er-Kombinationen
    
      Spieler* player = NULL;     //Pointer auf spieler-Objekt
    
      //Spielfeld durchlaufen
      for(unsigned int y = 0; y < CONST__anzY; y++)
      {
          for(unsigned int x = 0; x < CONST__anzX; x++)
          {
               if(this->model->isValidCoord(x,y))      //Ist die Koordinate gültig?
               {
    
                player =  this->model->getFeld(x, y)->getPlayer();    //Besitzer des Felds holen
                if(player != NULL)                                    //Wenn es einen Besitzer gibt
                {
                 if(   this->isCombination(player, x, y, 0, 1, length)   //Senkrecht
                    || this->isCombination(player, x, y, 1, 0, length)   //Waagrecht
                    || this->isCombination(player, x, y, 1, 1, length)   //Diagonal
                    || this->isCombination(player, x, y, 1, -1, length)  //Diagonal
                    || this->isCombination(player, x, y, -1, 1, length)  //Diagonal
                    || this->isCombination(player, x, y, -1, -1, length) //Diagonal
                    )
                 {
                   return player;                    //Gewinner zurückgeben
                 }
                }
               }
          }
      }
      return NULL;                                   //Kein Gewinner
    }
    
    /*
      Gibt entweder Spieler 1 oder Spieler 2 zurück.
    */
    Spieler* Controller::getSpieler(int nr)
    {
      if(nr != 1 && nr != 2)         // nr muss 1 oder 2 sein, weil es nur 2 Spieler gibt
      { throw "Dieser Spieler existiert nicht"; }
    
      return this->spieler[nr-1];   //Array beginnt bei 0 --> -1
    }
    
    /*
       Gibt Pointer auf Model-Objekt zurück
    */
    SpielfeldModel* Controller::getModel()
    {
      return this->model;
    }
    
    /*
       Erhöht den Counter, der die Spielzüge zählt.
    */
    void Controller::incrementCounter()
    {
      this->counter++;
      this->gui->showSpielzuege(this->counter);  //Neue Anzahl anzeigen
    }
    
    /*
      Verringert den Counter, der die Spielzüge zählt
    */
    void Controller::decrementCounter()
    {
      this->counter--;
      this->gui->showSpielzuege(this->counter);   //Neue Anzahl anzeigen
    }
    
    /*
      Counterstand zurückgeben
    */
    int Controller::getCounter()
    {
      return this->counter;
    }
    
    /**
       Counter zurückstetzen
    */
    void Controller::resetCounter()
    {
      this->counter = 0;
      this->gui->showSpielzuege(this->counter);   //Neue Anzahl anzeigen
    }
    
    /*
       Controller zurücksetzten und neues Spiel starten
    */
    void Controller::reset()
    {
      this->resetCounter();       //Counter zurücksetzen
      this->model->reset();       //Spielfeld leeren
      this->refreshStatus();      //Status aktualisieren
      this->gui->RefreshField();  //Feld neu anzeigen
      this->resetTimer();
      this->setFinished(false);   //Spiel starten
    }
    
    /*
       Spiel beenden / starten
    */
    void Controller::setFinished(bool finished)
    {
      this->finished = finished;
    }
    
    /**
      Wurde das Spiel beendet?
    */
    bool Controller::isFinished()
    {
      return this->finished;
    }
    
    /*
       Status aktualisieren
       Status enthält den aktuellen Spielernamen und seine Farbe
    */
    void Controller::refreshStatus()
    {
      //Spielername anzeigen
      this->gui->setStatusMessage("Spieler '" + this->getCurrentPlayer()->getName() + "' ist dran!");
    
      //Farbe anzeigen
      this->gui->setSpielerfarbeVorschau(this->getCurrentPlayer()->getColor());
    
      //Änderungen von der GUI verarbeiten lassen
      Application->ProcessMessages();
    }
    
    /*
       Mit dieser Methode kann man den Typ (Computer / Mensch)
       eines Spielers ändern.
       Die ID entspricht hier nicht der ID im array, sondern ist 1 höher.
    */
    void Controller::setPlayerType(int id, AnsiString type)
    {
      //Spieler 1 oder SPieler 2 NICHT?
      if(id != 1 && id != 2)
      {
        throw "Dieser Spieler existiert nicht!";
      }
    
      id--;  //ID entspricht jetzt Array-Index von this->spieler
    
      if(type == "Mensch")
      {
        this->spieler[id] = new Mensch;   //MEnsch-Objekt hineinschreiben
      }
      else if(type == "Computer")
      {
         this->spieler[id] = new Computer;   //Computer-Objekt hineinschreiben
      }
      else
      {
        throw "Dieser Spielertyp existiert nicht";
      }
    }
    
    //Gibt einen Zeiger auf die GUI (Form1) zurück
    TForm1* Controller::getGUI()
    {
      return this->gui;
    }
    
    //Diese Methode wird aufgerufen, wenn der Timer aktiv wird.
    void Controller::refreshTimer()
    {
        if(!this->isFinished()) //Zeit nur zählen, wenn Spiel nicht beendet ist
        {
           this->timer_sec++;         //Bei jedem Aufruf soll der Sekundenzähler erhöht werden.
           if(this->timer_sec >= 60)  //Prüfen, ob eine Minute vorbei ist
           {
             this->timer_sec = 0;     //Wenn ja, Sekunden auf 0 setzen
             this->timer_min++;       //und Minuten erhöhen
           }
        }
    
        this->gui->showTimerData(this->timer_min, this->timer_sec); //Die GUI soll die Daten jetzt ausgeben
    }
    
    /*
        Setzt die Spieldauer auf 0:00 min zurück
     */
    void Controller::resetTimer()
    {
      this->timer_sec = 0;      //0 Sekunden
      this->timer_min = 0;      //0 Minuten
      this->gui->showTimerData(this->timer_min, this->timer_sec);  //Anzeige neu beschreiben lassen
    }
    

    Die anderen Dateien brauchst du nich auch noch, oder?
    Hier vllt noch meine "God-Header-Datei"

    #include <stdio.h>
         #include <IniFiles.hpp>
         #include "anleitung.h"
         #include "viergewinnt.h"
         #include "spieler.h"
         #include "model_feld.h"
         #include "model_spielfeld.h"
         #include "computer.h"
         #include "mensch.h"
         #include "controller.h"
         #include "model_options.h"
         #include "optionen.h"
         #include "controller_options.h"
    
         /*#include "spieler.cpp"
         #include "mensch.cpp"
         #include "computer.cpp"
         #include "model_feld.cpp"
         #include "model_spielfeld.cpp"
         #include "controller.cpp"    */
    

    In den Header-Dateien ist keine Methode mehr drin, nur die Signaturen (oder wie man das nennen mag)


Anmelden zum Antworten