Schach als Anfängertraining



  • Hi,
    bin neu hier und auch in der Materie C++. Um zu üben habe ich bisher erlaubte Züge für Schachfiguren implementiert (und das Schachfeld dazu 😉 ). Das Programm tut soweit auch (Schach, Schachmatt, Rochade und en passant wird noch implementiert hoffe ich 😉 ).

    Meine Frage bzw. Bitte an euch wäre mal den Code zu überfliegen und mir Kommentare, zu meinen Kommentaren im Code und eventueller (ja die wirds geben) "Schönheitsfehler", zu geben.

    Auch Code einsparen wäre nett ;).
    Dass ich die beiden identischen switch-teile zusammenfassen kann weiß ich ;). Wollts vorerst aber noch nicht machen.

    Ps.: Irgendwelche auskommentierten cout dienten der Fehlersuche

    #include <vector>
    #include <iostream>
    #include <string>
    
    //Instructions
    void instructions(){
        std::cout << "Dieses Programm regelt nur die erlaubten Zuege." << std::endl;
        std::cout << "Die Mitspieler spielen abwechselnd indem sie die den Wunschbefehl" << std::endl;
        std::cout << "'n' Neustart, 'e' Exit, 'z' ziehen eingeben." << std::endl;
        std::cout << "Nach einem z wird die Startposition," << std::endl;
        std::cout << "(Zeile_Leerzeichen_Spalte) angeben, gefolgt von einem Leerzeichen" << std::endl;
        std::cout << "und den Zielkoordinaten eingegeben."<< std::endl;
        std::cout << "Wollen sie stattdessen beenden geben sie nur e ein." <<std::endl << std::endl << std::endl;
    }
    
    class Schachbrett {
    
    	private:
    	char schach[9][9];
    
    	//Konstruktor, Figuren werden initial aufgestellt
    	public:
    	Schachbrett() {
    	    //Beschriftung Schabrett schach[Zeile][Spalte]
    	    schach[0][0] = ' ';
            schach[1][0] = '1';
            schach[2][0] = '2';
            schach[3][0] = '3';
            schach[4][0] = '4';
            schach[5][0] = '5';
            schach[6][0] = '6';
            schach[7][0] = '7';
            schach[8][0] = '8';
            schach[0][1] = '1';
            schach[0][2] = '2';
            schach[0][3] = '3';
            schach[0][4] = '4';
            schach[0][5] = '5';
            schach[0][6] = '6';
            schach[0][7] = '7';
            schach[0][8] = '8';
    		//weisse Figuren
    		schach[1][1] = 'T';
    		schach[1][2] = 'S';
    		schach[1][3] = 'L';
    		schach[1][4] = 'D';
    		schach[1][5] = 'K';
    		schach[1][6] = 'L';
    		schach[1][7] = 'S';
    		schach[1][8] = 'T';
    		for (int i = 1; i < 9; i++) {
    			schach[2][i] = 'B';
    		}
    
    		//schwarze Figuren
    		schach[8][1] = 't';
    		schach[8][2] = 's';
    		schach[8][3] = 'l';
    		schach[8][4] = 'd';
    		schach[8][5] = 'k';
    		schach[8][6] = 'l';
    		schach[8][7] = 's';
    		schach[8][8] = 't';
    		for (int i = 1; i < 9; i++) {
    			schach[7][i] = 'b';
    		}
    
    		//restliche Felder leer initialisieren
    		for (int spalte = 3; spalte < 7; spalte++) {
    			for (int zeile = 1; zeile < 9; zeile++) {
    				schach[spalte][zeile] = '0';
    			}
    		}
    	}
    
    	//Ausgabe des Feldes
        void printField(){
            std::cout << std::endl << std::endl;
            for (int i = 0; i < 9; i++){
                for (int j = 0; j < 9; j++)
                    std::cout << schach[i][j] << " ";
                    std::cout << std::endl;
            }
        std::cout << std::endl << std::endl;
        }
    
        //welche Figur steht auf dem Feld?
        char welcheFigur(int x1,int x2){
            char figur= schach[x1][x2];
            return figur;
        }
    
        //ist Feld leer?
        bool feldBesetzt(int y1, int y2){
            if(schach[y1][y2]!='0'){
                return true;
            }
            else return false;
        }
    
        //Ist der richtige Spieler dran? oder ist es eine Figur vom Gegner zum schlagen mit Übergabewert (spieler+1)%2
        bool richtigeFigur(char figur, int spieler){
            if ((spieler==0)&&('A'<figur&&figur<'Z')){
                return true;
            }
            if((spieler==1)&&('a'<figur&&figur<'z')){
                return true;
            }
            else {
                return false;
            }
        }
    
        //Zug ausführen, wenn wenn er gültig ist
        void zug(int x1,int x2,int y1, int y2, char figur){
            schach[x1][x2]='0';
            schach[y1][y2]=figur;
        }
    
        //lediglich Ausgabe über unerlaubte Züge
        void falscherZug(){
            std::cout << "ungueltiger Zug" << std::endl;
        }
    
        //erlaubte Züge für die Türme
        int zugTurm(int x1,int x2,int y1, int y2, char figur){
            int frei=1;
            if((x1==y1)&&(x2!=y2)){
                if(x2<y2){
                    for (int tmp=x2+1; tmp < y2; tmp++){
                        if(feldBesetzt(x1, tmp)){
                            frei=0;
                        }
                    }
                    if(frei==1){
                       zug(x1, x2, y1, y2, figur);
                       return 1;
                    }
                }
                else{
                    for (int tmp=y2+1; tmp < x2; tmp++){
                        if(feldBesetzt(y1, tmp)){
                            frei=0;
                        }
                    }
                    if(frei==1){
                        zug(x1, x2, y1, y2, figur);
                        return 1;
                    }
                }
            }
            else if((x2==y2)&&(x1!=y1)){
                if(x1<y1){
                    for (int tmp=x1+1; tmp < y1; tmp++){
                        if(feldBesetzt(tmp, x2)){
                            frei=0;
                        }
                    }
                    if(frei==1){
                        zug(x1, x2, y1, y2, figur);
                        return 1;
                    }
                }
                else{
                    for (int tmp=y1+1; tmp < x1; tmp++){
                        if(feldBesetzt(tmp, y2)){
                            frei=0;
                        }
                    }
                    if(frei==1){
                        zug(x1, x2, y1, y2, figur);
                        return 1;
                    }
                }
            }
            else{
                return 0;
            }
        }
    
        //erlaubte Züge der Springer
        int zugSpringer(int x1,int x2,int y1, int y2, char figur){
            if(((abs(x1-y1)==2)&&(abs(x2-y2)==1))||((abs(x1-y1)==1)&&(abs(x2-y2)==2))){
                zug(x1, x2, y1, y2, figur);
                return 1;
            }
            else{
                return 0;
            }
        }
    
        //erlaubte Züge der Läufer
        int zugLaeufer(int x1,int x2,int y1, int y2, char figur){
            int frei=1;
            int tmp1, tmp2;
            if(abs(x1-y1)==(abs(x2-y2))){
                tmp2 = x2;
                if(x1<y1){
                    if(x2<y2){
                        for(tmp1=x1+1; tmp1 < y1; tmp1++){
                            tmp2=tmp2+1;
                            if(feldBesetzt(tmp1, tmp2)){
                                frei=0;
                            }
                        }
                        if(frei==1){
                            zug(x1, x2, y1, y2, figur);
                            return 1;
                        }
                    }
                    else{
                        for(tmp1=x1+1; tmp1 < y1; tmp1++){
                            tmp2=tmp2-1;
                            if(feldBesetzt(tmp1, tmp2)){
                                frei=0;
                            }
                        }
                        if(frei==1){
                            zug(x1, x2, y1, y2, figur);
                            return 1;
                        }
                    }
                }
                else if(x2<y2){
                        for(tmp1=x1-1; tmp1 < y1; tmp1--){
                            tmp2=tmp2+1;
                            if(feldBesetzt(tmp1, tmp2)){
                                frei=0;
                            }
                        }
                        if(frei==1){
                            zug(x1, x2, y1, y2, figur);
                            return 1;
                        }
                    }
                    else{
                        for(tmp1=x1-1; tmp1 < y1; tmp1--){
                            tmp2=tmp2-1;
                            if(feldBesetzt(tmp1, tmp2)){
                                frei=0;
                            }
                        }
                        if(frei==1){
                            zug(x1, x2, y1, y2, figur);
                            return 1;
                        }
                    }
                }
                else{
                    return 0;
            }
        }
    
        //erlaubte Züge der Dame
        //hm das mach ich besser Außerhalb der Klasse
    
        //erlaubte Züge des Königs
        int zugKoenig(int x1,int x2,int y1, int y2, char figur){
            if((abs(x1-y1)<=1)&&(abs(x2-y2)<=1)){
                zug(x1, x2, y1, y2, figur);
                return 1;
            }
            else{
                return 0;
            }
        }
    
        //erlaubte Zühe der "großen" Bauern
        int zugBauerB(int x1,int x2,int y1, int y2, char figur){
            if((((x2==y2)&&((y1-x1)==2)&&(x1==2))||((x2==y2)&&((y1-x1)==1)))&&(!feldBesetzt(x1+1,x2))){
                zug(x1, x2, y1, y2, figur);
            }
            else if((abs(y2-x2)==1)&&((y1-x1)==1)&&(feldBesetzt(y1, y2))){
                zug(x1, x2, y1, y2, figur);
            }
            else{
                return 0;
            }
        }
    
        //erlaubte Zühe der "kleinen" Bauern
        int zugBauerb(int x1,int x2,int y1, int y2, char figur){
            if((((x2==y2)&&((x1-y1)==2)&&(x1==7))||((x2==y2)&&((x1-y1)==1)))&&(!feldBesetzt(x1-1,x2))){
                zug(x1, x2, y1, y2, figur);
            }
            else if((abs(y2-x2)==1)&&((x1-y1)==1)&&(feldBesetzt(y1, y2))){
                zug(x1, x2, y1, y2, figur);
            }
            else{
                return 0;
            }
        }
    
    };
    
    int main() {
        std::vector< std::vector<int> > image;
        char command;
        int x1, x2, y1, y2;
        std::string name;
        char figur;
        Schachbrett spiel;
        int spieler=0;
        instructions();
        spiel.printField();
        int gezogen;
    while (true) {
        gezogen=0;
        std::cout << "Spieler " << spieler+1 << " ist an der Reihe." << std::endl;
    
        std::cin >> command;
        switch (command) {
    
        case 'z':
          std::cin >> x1;
          std::cin >> x2;
          std::cin >> y1;
          std::cin >> y2;
            if ((x1>8)||(x1<1)||(x2>8)||(x2<1)||(y1>8)||(y1<1)||(y2>8)||(y2<1)){
            std::cout << "Falsche Eingabe" << std::endl;
            break;
            }
            figur=spiel.welcheFigur(x1,x2);
            if(spiel.richtigeFigur(figur,spieler)){  //Ist der Spieler dran?
                //std::cout << "Jup das geht" << std::endl;
                //erste if fragt ob das zielfeld leer ist, wenn nein wird nachgesehen ob die figur darauf eine eigene ist
                if(spiel.feldBesetzt(y1,y2)){
                    if(spiel.richtigeFigur(spiel.welcheFigur(y1,y2),(spieler+1)%2)){
                        switch(figur){
                        case 'T':  //ergeben einen Fall, da 'T' kein break hat
                        case 't':
                            gezogen=spiel.zugTurm(x1, x2, y1, y2, figur);
                            break;
    
                        case 'S':
                        case 's':
                            gezogen=spiel.zugSpringer(x1, x2, y1, y2, figur);
                            break;
    
                        case 'L':
                        case 'l':
                            gezogen=spiel.zugLaeufer(x1, x2, y1, y2, figur);
                            break;
    
                        case 'D':
                        case 'd':
                            if((x1==y1)||(x2==y2)){
                                gezogen=spiel.zugTurm(x1, x2, y1, y2, figur);
                                break;
                            }
                            else{
                                gezogen=spiel.zugLaeufer(x1, x2, y1, y2, figur);
                            }
                            break;
    
                        case 'K':
                        case 'k':
                            gezogen=spiel.zugKoenig(x1, x2, y1, y2, figur);
                            break;
    
                        case 'B':
                            gezogen=spiel.zugBauerB(x1, x2, y1, y2, figur);
                            break;
    
                        case 'b':
                            gezogen=spiel.zugBauerb(x1, x2, y1, y2, figur);
                            break;
    
                        default:
                            std::cerr << "Zuege noch nicht festgelegt für diese Figur" << std::endl;
                        }
                    }
                    else {
                        std::cout << "Dabei ihre eigene Figur schlagen?" << std::endl;
                        break;
                    }
                }
                else{
                    //Hier erlaubte Züge reinschreiben
                    switch(figur){
                        case 'T':  //ergeben einen Fall, da 'T' kein break hat
                        case 't':
                            gezogen=spiel.zugTurm(x1, x2, y1, y2, figur);
                            break;
    
                        case 'S':
                        case 's':
                            gezogen=spiel.zugSpringer(x1, x2, y1, y2, figur);
                            break;
    
                        case 'L':
                        case 'l':
                            gezogen=spiel.zugLaeufer(x1, x2, y1, y2, figur);
                            break;
    
                        case 'D':
                        case 'd':
                            if((x1==y1)||(x2==y2)){
                                gezogen=spiel.zugTurm(x1, x2, y1, y2, figur);
                                break;
                            }
                            else{
                                gezogen=spiel.zugLaeufer(x1, x2, y1, y2, figur);
                            }
                            break;
    
                        case 'K':
                        case 'k':
                            gezogen=spiel.zugKoenig(x1, x2, y1, y2, figur);
                            break;
    
                        case 'B':
                            gezogen=spiel.zugBauerB(x1, x2, y1, y2, figur);
                            break;
    
                        case 'b':
                            gezogen=spiel.zugBauerb(x1, x2, y1, y2, figur);
                            break;
    
                        default:
                            std::cerr << "Zuege noch nicht festgelegt für diese Figur" << std::endl;
                    }
    
                }
            }
            else{
                std::cout << "Das ist nicht ihre Spielfigur" << std::endl;
                break;
            }
            //std::cout << figur << std::endl;
            if (gezogen==1){
                spieler=(spieler+1)%2;
                system( "cls" );
                instructions();
                spiel.printField();
            }
            else{
                spiel.falscherZug();
            }
        break;
    
        case 'e':
          exit(0);
    
        case 'n':
            main();
    
        default:
    
          std::cerr << "Unknown command!" << std::endl;
        }
    }
          return 0;
    }
    


  • Erstens: Soviel Code auf einen Haufen sieht hier niemand besonders gerne 😉

    Zweitens: Für das Schachbrett reicht ein 8x8 Array aus - die Spalten- und Zeilenbeschriftungen sind redundant (und wenn doch - die Zeilen werden üblicherweise als 'a' bis 'h' gekennzeichnet).

    Drittens: Hast du einen bestimmten Grund, die Damen-Züge aus der Klasse auszulagern? Das kannst du sogar recht einfach zusammenfassen:

    int zugDame(...)
    { return zugTurm(...)||zugLaeufer(...); }
    


  • Beim ersten Überfliegen des Quelltextes ist mir folgendes aufgefallen:

    1. Den Konstruktor von Schachbrett kann man mit ein paar for-Schleifen vereinfachen.

    2. In Funktionen wie welcheFigur sollte überprüft werden, ob die angegebenen Koordinaten überhaupt zulässig sind (zumindestens mit einem assert).

    3. Das

    case 'n':
            main();
    

    geht nun überhaupt nicht. main()-Aufrufe sind eine außerordentlich dumme Idee. Darüber hatten wir vor einiger Zeit schon mal einen Thread (kann ihn aber im Moment nicht wiederfinden).

    4. Deine main-Funktion sieht ein bißchen voll aus. Die könnte man sicher noch weiter aufspalten.

    5. In C++ deklariert man Variablen normalerweise nicht am Anfang einer Funktion, sondern erst wenn man sie braucht.

    6. Dein Entwurf ist nicht sehr objekt-orientiert. Ich hätte an deiner Stelle eine Basisklasse Spielfigur geschrieben und dann davon die konkreten Figuren abgeleitet (Bauer, Dame, Koenig, ...).



  • Z2 schrieb:

    3. Das

    case 'n':
            main();
    

    geht nun überhaupt nicht. main()-Aufrufe sind eine außerordentlich dumme Idee. Darüber hatten wir vor einiger Zeit schon mal einen Thread (kann ihn aber im Moment nicht wiederfinden).

    5. In C++ deklariert man Variablen normalerweise nicht am Anfang einer Funktion, sondern erst wenn man sie braucht.

    6. Dein Entwurf ist nicht sehr objekt-orientiert. Ich hätte an deiner Stelle eine Basisklasse Spielfigur geschrieben und dann davon die konkreten Figuren abgeleitet (Bauer, Dame, Koenig, ...).

    zu 3. kam mir auch nicht sehr sinnvoll vor, habs dennoch gemacht, weils mir irgendwie am einfachsten vor kam, werds ändern.

    zu 5. wenn sich das auf "int gezogen" bezieht, das habe ich gemacht damit nicht bei jedem Durchgang die Variable neu erzeugt werden muss oder überschreibt er die dann eh?

    6. Also jede Figur in einer eigenen Klasse? Und dann mit Vererbungen?

    Oh man muss noch viel lernen ;). Aber ich bin dran.



  • zu 5. Zum Beispiel das hier:

    int main() {
        std::vector< std::vector<int> > image;
        char command;
        int x1, x2, y1, y2;
        std::string name;
        char figur;
        Schachbrett spiel;
        int spieler=0;
    

    Ein ganzer Haufen Variablen. Teilweise ist es sicherlich nötig, sie außerhalb der Schleife zu deklarieren (obwohl auch hier eine OOP Lösung wohlmöglich besser wäre). Aber das ist einfach zuviel.

    zu 6. Für jeden Typ von Figur eine eigene KLasse (also nicht für jeden Bauern eine). Das wäre auf jeden Fall sauberer.

    Hab noch einen Punkt:

    int zugBauerB(int x1,int x2,int y1, int y2, char figur){
            if((((x2==y2)&&((y1-x1)==2)&&(x1==2))||((x2==y2)&&((y1-x1)==1)))&&(!feldBesetzt(x1+1,x2))){
                zug(x1, x2, y1, y2, figur);
            }
            else if((abs(y2-x2)==1)&&((y1-x1)==1)&&(feldBesetzt(y1, y2))){
                zug(x1, x2, y1, y2, figur);
            }
            else{
                return 0;
            }
        }
    

    In dieser Funktion fehlen ein paar returns.



  • Z2 schrieb:

    2. In Funktionen wie welcheFigur sollte überprüft werden, ob die angegebenen Koordinaten überhaupt zulässig sind (zumindestens mit einem assert).

    hm hab ich das nicht hier:

    if ((x1>8)||(x1<1)||(x2>8)||(x2<1)||(y1>8)||(y1<1)||(y2>8)||(y2<1)){
            std::cout << "Falsche Eingabe" << std::endl;
            break;
    

    schon abgearbeitet? muss ja nicht nochmal extra gemacht werden.



  • Es geht nicht nur um falsche User-Eingaben. Ich habe mir dein Programm jetzt nicht genau angsehen, aber ich nehme an, du wirst Funktionen, die solche Koordinaten übergeben bekommen, auch an anderer Stelle aufrufen, z.B. um zu prüfen, ob ein Läufer/Turm/Damen-Zug ok ist, d.h. das kein Hindernis vorhanden ist.

    In einem so einfachen Fall ist die Sache vielleicht noch nicht kritisch. Aber wenn die Algorithmen komplexer werden, kann es leicht vorkommen, daß man versehentlich über den gültigen Bereich hinausschießt. Und wenn du erst mal so einen Bug drin hast, ist es nicht so einfach ihn ausfindig zu machen.
    Wenn du deine Funktionen andererseits geben fehlerhafte Eingaben absicherst, wird die ganze Sache sehr einfach. assert liefert dir in einem solchen Fall so ziemlich alle Informationen, die du brauchst. Da ist es häufig nicht einmal mehr nötig den Debugger anzuwerfen.

    Du hast also die Wahl. Jetzt zwei Minuten investieren, um ein paar Überprüfungen einzufügen. Oder später möglicherweise Stunden damit zu verschwenden Bugs zu jagen.



  • Okay versuche nun das ganze mit eigenen Figurenklassen zu machen.
    Bei der Dame hab ich jedoch ein Problem mit Mehrfachvererbung, hätte ja letztendlich dann dreimal ein Verweis auf die Basisklasse oder? Bzw. zweimal, denke ich kann die direkte Vererbung von Schachbrett weg lassen.

    class Dame:public Schachbrett, public Turm, public Laeufer{
        //erlaubte Züge der Dame
        int zugDame(int x1,int x2,int y1, int y2, char figur){
            return zugTurm(x1, x2, y1, y2, figur)||zugLaeufer(x1, x2, y1, y2, figur);
        }
    };
    

    Nun will er beim kompilieren, dass die Bewegungsfunktionen in Turm und Laeufer public werden. Wäre jedoch nicht sinnvoll oder?

    Kann ich dies anders lösen?



  • Die Figuren brauchen überhaupt nicht von Schachbrett zu erben (Vererbung entspricht einem "ist ein" - und eine Figur ist nunnmal kein Schachbrett) - du solltest eher der Basisklasse eine Referenz/Zeiger auf das Schachbrett mitgeben. Außerdem sollte der Zug in jeder Figurenklasse von der selben Methode ausgeführt werden:

    class Brett;
    
    class Figur
    {
    protected:
      Brett* brett;
      int posx,posy;
      bool farbe;//true = weiß, false = schwarz
    public:
      Figur(...);
      virtual void Zug(int zielx,int ziely)=0;
    }
    
    class Bauer : public Figur
    {
    public:
      Bauer(...) : Figur(...) {}
      void Zug(int zielx,int ziely) {...}
    }
    
    ///etc
    

    Eventuell kannst du mit virtueller Vererbung arbeiten (class Laufer:public virtual Figur{..}), um zu verhindern, daß die Dame die Figur-Member doppelt bekommt. Oder die Dame erbt direkt von Figur und implementiert ihre möglichen Bewegungen selber.

    PS: Wobei - ich würde aus der Methode Zug() noch die Berechnungen abtrennen, ob eine Figur ziehen darf:

    void Figur::Zug(int zielx,int ziely)
    {
      if(kann_ziehen(zielx,ziely))
      {
        aktualisiere Spielfeld
      }
    }
    

    (dann ist kann_ziehen() die virtuelle Methode und berechnet, ob eine Figur auf das gegebene Feld ziehen darf. Und besonders der König würde sich sehr darüber freuen - schließlich darf er nur auf Felder gehen, die von keinem Gegner bedroht werden)



  • Für einen Anfänger finde ich das Engagement und Programm erstaunlich.
    Beim Design kann man sicher einiges verbessern - das ist ja vollkommen normal
    und das schöne am lernen einer Programmiersprache.. man lernt immer dazu.
    Die mehr als 400 Zeilen(!) Code lassen sich um vermutlich 50-70% reduzieren.

    Ein paar Dinge die mir am Anfang auffallen. Das Spielbrett wird nicht neugezeichnet und die Eingabe ist etwas verwirrend.

    Allgemein zum Design:
    Die Methoden sind zu lang. Man sollte Methoden haben die eine ziemlich
    spezielle Aufgabe erfüllen. Meistens kann man so alles schön fein aufdrösseln
    und erspart sich viel arbeit. So wie Du es gemacht hast sind die Zug-Methoden
    für jemanden fremden sehr schwer bis unmöglich zu lesen. Wenn Du es Dir später selbst
    anschaust weißt Du nicht mehr genau was Du gemacht hast. Ansonsten bieten
    sich Kommentare an.

    Schleifen sollten möglichst nicht verschachtelt sein.
    If und else sollte möglichst eng beeinander sein. So ist
    z.B. die Frage ob die Figur dem Spieler gehört so geschrieben

    if (Figur){//viel Code} else{//nicht die Figur}
    besser ist if (!Figur) {//nicht die Figur} //viel Code
    

    So muss man nicht nach dem else suchen und hat alles an einer Stelle.

    Die Methoden bennenung von Feldbesetzt ist etwas unglücklich. Viel besser ist
    der Name: feldLeer().

    > Headerdateien verwenden
    Headerdefine #ifndef FRACTION_H
    #define FRACTION_H

    > Destruktoren verwenden

    > interne Darstellung als Array
    -> Bezeichnung überflüssig

    > using namespace std;
    Durch diesen Befehl braucht man nicht mehr std:: zu schreiben

    Vorschläge für eine zweite Version:
    Das Schachfeld besteht aus einem Array von Figuren. Man fragt dann
    die Figur ab wohin sie ziehen kann.

    Die interne Darstellung ist dann: Figur schachfeld[][].
    Der Aufruf geht dann in etwa so:

    Figur f = schachfeld[x][y];
    if(f.move_possible(x, y)) zug(x, y, f);

    Man kann dann generisch alle Züge abhandeln.
    Bei den Zugmethoden kann man auch sicher einiges durch Symmetrien kürzer
    schreiben.

    Anbei mal ein bißchen Code mit ein paar Vorschlägen.

    // Schachbrett.h
    class Schachbrett {
    
        private:
        char schach[9][9];
    
        //Konstruktor, Figuren werden initial aufgestellt
        public:
    	Schachbrett();
    	// standard Destruktor
    	~Schachbrett();
    	void printField();
    	char welcheFigur(int x1,int x2);
    	bool feldFrei(int y1, int y2);
    	bool richtigeFigur(char figur, int spieler);
    	void zug(int x1,int x2,int y1, int y2, char figur);
    	int zugTurm(int x1,int x2,int y1, int y2, char figur);
    	int zugLaeufer(int x1,int x2,int y1, int y2, char figur);
    	// examplarisch verbesserte Zugmethode
    	bool zugKoenig(int x1,int x2,int y1, int y2);
    	int zugBauerB(int x1,int x2,int y1, int y2, char figur);
    	int zugBauerb(int x1,int x2,int y1, int y2, char figur);
    	int zugSpringer(int x1,int x2,int y1, int y2, char figur);
    };
    
    // Schachbrett.cpp
    #include <vector>
    #include <iostream>
    #include <string>
    #include "Schachbrett.h"
    //Konstruktor, Figuren werden initial aufgestellt
    using namespace std;
    
    Schachbrett::Schachbrett() {
    
            //Beschriftung Schabrett schach[Zeile][Spalte]
    		// nicht nötig
    	    // die bezeichnung der Felder sollte man extern speichern
            schach[0][0] = ' ';
            schach[1][0] = '1';
            schach[2][0] = '2';
            schach[3][0] = '3';
            schach[4][0] = '4';
            schach[5][0] = '5';
            schach[6][0] = '6';
            schach[7][0] = '7';
            schach[8][0] = '8';
            schach[0][1] = '1';
            schach[0][2] = '2';
            schach[0][3] = '3';
            schach[0][4] = '4';
            schach[0][5] = '5';
            schach[0][6] = '6';
            schach[0][7] = '7';
            schach[0][8] = '8';
            //weisse Figuren
            schach[1][1] = 'T';
            schach[1][2] = 'S';
            schach[1][3] = 'L';
            schach[1][4] = 'D';
            schach[1][5] = 'K';
            schach[1][6] = 'L';
            schach[1][7] = 'S';
            schach[1][8] = 'T';
            for (int i = 1; i < 9; i++) {
                schach[2][i] = 'B';
            }
    
            //schwarze Figuren
            schach[8][1] = 't';
            schach[8][2] = 's';
            schach[8][3] = 'l';
            schach[8][4] = 'd';
            schach[8][5] = 'k';
            schach[8][6] = 'l';
            schach[8][7] = 's';
            schach[8][8] = 't';
            for (int i = 1; i < 9; i++) {
                schach[7][i] = 'b';
            }
    
            //restliche Felder leer initialisieren
            for (int spalte = 3; spalte < 7; spalte++) {
                for (int zeile = 1; zeile < 9; zeile++) {
                    schach[spalte][zeile] = '0';
                }
            }
        }
    
    Schachbrett::~Schachbrett() {}
    
    //Ausgabe des Feldes
    void Schachbrett::printField(){
        cout << endl << endl;
        for (int i = 0; i < 9; i++){
            for (int j = 0; j < 9; j++)
                cout << schach[i][j] << " ";
                cout << endl;
        }
    cout << endl << endl;
    }
    
    //welche Figur steht auf dem Feld?
    char Schachbrett::welcheFigur(int x1,int x2){
        char figur= schach[x1][x2];
        return figur;
    }
    
    //ist das Feld leer?
    bool Schachbrett::feldFrei(int y1, int y2){
        if(schach[y1][y2]!='0'){
            return true;
        }
        else return false;
    }
    
    //Ist der richtige Spieler dran? oder ist es eine Figur vom Gegner zum schlagen mit Übergabewert (spieler+1)%2
    bool Schachbrett::richtigeFigur(char figur, int spieler){
        if ((spieler==0)&&('A'<figur&&figur<'Z')){
            return true;
        }
        if((spieler==1)&&('a'<figur&&figur<'z')){
            return true;
        }
        else {
            return false;
        }
    }
    
    //Zug ausführen, wenn wenn er gültig ist
    void Schachbrett::zug(int x1,int x2,int y1, int y2, char figur){
        schach[x1][x2]='0';
        schach[y1][y2]=figur;
    }
    
    //erlaubte Züge für die Türme
    int Schachbrett::zugTurm(int x1,int x2,int y1, int y2, char figur){
        int frei=1;
        if((x1==y1)&&(x2!=y2)){
            if(x2<y2){
                for (int tmp=x2+1; tmp < y2; tmp++){
                    if(feldFrei(x1, tmp)){
                        frei=0;
                    }
                }
                if(frei==1){
                   zug(x1, x2, y1, y2, figur);
                   return 1;
                }
            }
            else{
                for (int tmp=y2+1; tmp < x2; tmp++){
                    if(feldFrei(y1, tmp)){
                        frei=0;
                    }
                }
                if(frei==1){
                    zug(x1, x2, y1, y2, figur);
                    return 1;
                }
            }
        }
        else if((x2==y2)&&(x1!=y1)){
            if(x1<y1){
                for (int tmp=x1+1; tmp < y1; tmp++){
                    if(feldFrei(tmp, x2)){
                        frei=0;
                    }
                }
                if(frei==1){
                    zug(x1, x2, y1, y2, figur);
                    return 1;
                }
            }
            else{
                for (int tmp=y1+1; tmp < x1; tmp++){
                    if(feldFrei(tmp, y2)){
                        frei=0;
                    }
                }
                if(frei==1){
                    zug(x1, x2, y1, y2, figur);
                    return 1;
                }
            }
        }
        else{
            return 0;
        }
    }
    
    //erlaubte Züge der Springer
    int Schachbrett::zugSpringer(int x1,int x2,int y1, int y2, char figur){
        if(((abs(x1-y1)==2)&&(abs(x2-y2)==1))||((abs(x1-y1)==1)&&(abs(x2-y2)==2))){
            zug(x1, x2, y1, y2, figur);
            return 1;
        }
        else{
            return 0;
        }
    }
    
    //erlaubte Züge der Läufer
    int Schachbrett::zugLaeufer(int x1,int x2,int y1, int y2, char figur){
        int frei=1;
        int tmp1, tmp2;
        if(abs(x1-y1)==(abs(x2-y2))){
            tmp2 = x2;
            if(x1<y1){
                if(x2<y2){
                    for(tmp1=x1+1; tmp1 < y1; tmp1++){
                        tmp2=tmp2+1;
                        if(feldFrei(tmp1, tmp2)){
                            frei=0;
                        }
                    }
                    if(frei==1){
                        zug(x1, x2, y1, y2, figur);
                        return 1;
                    }
                }
                else{
                    for(tmp1=x1+1; tmp1 < y1; tmp1++){
                        tmp2=tmp2-1;
                        if(feldFrei(tmp1, tmp2)){
                            frei=0;
                        }
                    }
                    if(frei==1){
                        zug(x1, x2, y1, y2, figur);
                        return 1;
                    }
                }
            }
            else if(x2<y2){
                    for(tmp1=x1-1; tmp1 < y1; tmp1--){
                        tmp2=tmp2+1;
                        if(feldFrei(tmp1, tmp2)){
                            frei=0;
                        }
                    }
                    if(frei==1){
                        zug(x1, x2, y1, y2, figur);
                        return 1;
                    }
                }
                else{
                    for(tmp1=x1-1; tmp1 < y1; tmp1--){
                        tmp2=tmp2-1;
                        if(feldFrei(tmp1, tmp2)){
                            frei=0;
                        }
                    }
                    if(frei==1){
                        zug(x1, x2, y1, y2, figur);
                        return 1;
                    }
                }
            }
            else{
                return 0;
        }
    }
    
    //erlaubte Züge der Dame
    //hm das mach ich besser Außerhalb der Klasse
    
    //erlaubte Züge des Königs
    //Zugaufruf dann extern
    bool Schachbrett::zugKoenig(int x1,int x2,int y1, int y2){
        return ((abs(x1-y1)<=1)&&(abs(x2-y2)<=1));
    }
    
    //erlaubte Zühe der "großen" Bauern
    int Schachbrett::zugBauerB(int x1,int x2,int y1, int y2, char figur){
        if((((x2==y2)&&((y1-x1)==2)&&(x1==2))||((x2==y2)&&((y1-x1)==1)))&&(!feldFrei(x1+1,x2))){
            zug(x1, x2, y1, y2, figur);
        }
        else if((abs(y2-x2)==1)&&((y1-x1)==1)&&(feldFrei(y1, y2))){
            zug(x1, x2, y1, y2, figur);
        }
        else{
            return 0;
        }
    }
    
    //erlaubte Zühe der "kleinen" Bauern
    int Schachbrett::zugBauerb(int x1,int x2,int y1, int y2, char figur){
        if((((x2==y2)&&((x1-y1)==2)&&(x1==7))||((x2==y2)&&((x1-y1)==1)))&&(!feldFrei(x1-1,x2))){
            zug(x1, x2, y1, y2, figur);
        }
        else if((abs(y2-x2)==1)&&((x1-y1)==1)&&(feldFrei(y1, y2))){
            zug(x1, x2, y1, y2, figur);
        }
        else{
            return 0;
        }
    }
    
    //main.cpp
    // main.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
    //
    #include "Schachbrett.h"
    #include "Figur.h"
    #include <vector>
    #include <iostream>
    #include <string>
    using namespace std;
    
    //Instructions
    void instructions(){
        cout << "Dieses Programm regelt nur die erlaubten Zuege." << endl;
        cout << "Die Mitspieler spielen abwechselnd indem sie die den Wunschbefehl" << endl;
        cout << "'n' Neustart, 'e' Exit, 'z' ziehen eingeben." << endl;
        cout << "Nach einem z wird die Startposition," << endl;
        cout << "(Zeile_Leerzeichen_Spalte) angeben, gefolgt von einem Leerzeichen" << endl;
        cout << "und den Zielkoordinaten eingegeben."<< endl;
        cout << "Wollen sie stattdessen beenden geben sie nur e ein." <<endl << endl << endl;
    }
    
    void getCommand(){
    
        vector< vector<int> > image;
        char command;
        int x1, x2, y1, y2;
        string name;
        char figur;
        Schachbrett spiel;
    	int spieler=0;
        instructions();
        spiel.printField();
        int gezogen;
    	while (true) {
    		gezogen=0;
    		cout << "Spieler " << spieler+1 << " ist an der Reihe." << endl;
    
    		cin >> command;
    		switch (command) {
    
        case 'z':
    	  cout << "pressed z!";
    	  cin >> x1;
          cin >> x2;
          cin >> y1;
          cin >> y2;
            if ((x1>8)||(x1<1)||(x2>8)||(x2<1)||(y1>8)||(y1<1)||(y2>8)||(y2<1)){
            cout << "Falsche Eingabe" << endl;
            break;
            }
            figur=spiel.welcheFigur(x1,x2);
    		cout << "gewählte Figur: " << figur;
            if(!spiel.richtigeFigur(figur,spieler)){  //Ist der Spieler dran?
    		            cout << "Das ist nicht ihre Spielfigur" << endl;
    		} else cout << "Das ist ihre Spielfigur" << endl;
    
                //erste if fragt ob das zielfeld leer ist, wenn nein wird nachgesehen ob die figur darauf eine eigene ist
                if(spiel.feldFrei(y1,y2)){
    				cout<<"Feld frei!";
                    if(spiel.richtigeFigur(spiel.welcheFigur(y1,y2),(spieler+1)%2)){
    					cout << "richtige Figur...";
                        switch(figur){
                        case 'T':  //ergeben einen Fall, da 'T' kein break hat
                        case 't':
                            gezogen=spiel.zugTurm(x1, x2, y1, y2, figur);
                            break;
    
                        case 'S':
                        case 's':
                            gezogen=spiel.zugSpringer(x1, x2, y1, y2, figur);
                            break;
    
                        case 'L':
                        case 'l':
                            gezogen=spiel.zugLaeufer(x1, x2, y1, y2, figur);
                            break;
    
                        case 'D':
                        case 'd':
                            if((x1==y1)||(x2==y2)){
                                gezogen=spiel.zugTurm(x1, x2, y1, y2, figur);
                                break;
                            }
                            else{
                                gezogen=spiel.zugLaeufer(x1, x2, y1, y2, figur);
                            }
                            break;
    
                        case 'K':
                        case 'k':
                            if(spiel.zugKoenig(x1, x2, y1, y2))
    							spiel.zug(x1,x2,y1,y2,figur);
                            break;
    
                        case 'B':
                            gezogen=spiel.zugBauerB(x1, x2, y1, y2, figur);
                            break;
    
                        case 'b':
                            gezogen=spiel.zugBauerb(x1, x2, y1, y2, figur);
    
                            break;
    
                        default:
                            cerr << "Zuege noch nicht festgelegt für diese Figur" << endl;
    
                        }
    					cout << "gezogen? " << gezogen;
                    }
                    else {
    					cout << "falsche Figur...";
    					cout << "Dabei ihre eigene Figur schlagen?" << endl;
                        break;
                    }
                }
    			//dieser Teil ist redundant bis ~~~
                else{
                    //Hier erlaubte Züge reinschreiben
                    switch(figur){
                        case 'T':  //ergeben einen Fall, da 'T' kein break hat
                        case 't':
                            gezogen=spiel.zugTurm(x1, x2, y1, y2, figur);
                            break;
    
                        case 'S':
                        case 's':
                            gezogen=spiel.zugSpringer(x1, x2, y1, y2, figur);
                            break;
    
                        case 'L':
                        case 'l':
                            gezogen=spiel.zugLaeufer(x1, x2, y1, y2, figur);
                            break;
    
                        case 'D':
                        case 'd':
                            if((x1==y1)||(x2==y2)){
                                gezogen=spiel.zugTurm(x1, x2, y1, y2, figur);
                                break;
                            }
                            else{
                                gezogen=spiel.zugLaeufer(x1, x2, y1, y2, figur);
                            }
                            break;
    
                        case 'K':
                        case 'k':
                            if(spiel.zugKoenig(x1, x2, y1, y2))
    							spiel.zug(x1,x2,y1,y2,figur);
                            break;
    
                        case 'B':
                            gezogen=spiel.zugBauerB(x1, x2, y1, y2, figur);
                            break;
    
                        case 'b':
                            gezogen=spiel.zugBauerb(x1, x2, y1, y2, figur);
                            break;
    
                        default:
                            cerr << "Zuege noch nicht festgelegt für diese Figur" << endl;
                    }
    				// ~~~
                }
    
           if (gezogen==1){
                spieler=(spieler+1)%2;
                system( "cls" );
                instructions();
                spiel.printField();
            }
            else{
    			// besser hier die Ausgabe
                cout << "ungueltiger Zug" << endl;
            }
        break;
    
        case 'e':
          exit(0);
    
        case 'n':
            //main();
    		return;
    
        default:
    
          cerr << "Unknown command!" << endl;
        }
    }	
    }
    
    int main(int argc, char* argv[]) {
    	instructions();
    
    	getCommand();	
    	return 0;
    }
    


  • Hm Okay thx, dann hab ich ja wieder was zu tun 😋

    Noch eine Frage, dass x1, etc. als int deklariert sind weiß ich, und wenn ich nun keine Integer eingebe ist dies ein falscher Input, soweit komm ich noch mit, jedoch meine Frage:

    Weshalb landet er dann bei falscher Eingabe in einer Endlosschleife?
    sollte er nicht nach einem Durchgang auf eine neue Eingabe warten?



  • Nein. Wenn cin eine Fehleingabe erhält, setzt es erstens ein Fehlerbit (das mußt du mit clear() zurücksetzen, bevor du irgendwas mit cin machen kannst) und lässt zweitens das falsche Zeichen im Eingabepuffer stehen (das mußt du per ignore() oder get() entsorgen).


Anmelden zum Antworten