Anfängerproblem: TicTacToe



  • Ich grüße euch,

    bin neu hier im Forum und dachte mir, dass ihr mir vielleicht bei meinem Problem helfen könntet. Ich befinde mich im ersten Lehrjahr meiner Ausbildung zum Anwendungsentwickler und habe bisher leider nur praktische Erfahrung in der Schule sammeln können. Aber das ist jetzt nicht der entscheidene Punkt. Jedenfalls haben wir uns bisher nur mit den Basics beschäftigt und im nächsten Block stehen Arrays an. Da ich die Phasen des Leerlaufs überbrücken möchte, dachte ich mir, dass ich mich schon ein wenig in das Thema einarbeite, dabei habe ich an TicTacToe gedacht und habe einfach mal mit Hilfe eines Fachbuches (etwas älter) meiner Fantasie freien Lauf gelassen.

    //============================================================================
    // Name        : TicTacToe.cpp
    // Author      : Bla
    // Description : 10.01.2011
    //============================================================================
    
    #include <iostream.h>
    #include <conio.h>
    #include <string>
    
    using namespace std;
    
    void menue();
    void neuesSpiel();
    void spielernamen();
    void beenden();
    
    int choice;
    string spieler1 = "Spieler 1";
    string spieler2 = "Spieler 2";
    int aktionsfeld[9];
    int spielerzug[2];
    bool zug;
    bool spielende;
    int wahl;
    
    void main(){
      menue();
    }
    
    void menue(){
      cout << "~~~~~~~~ TIC TAC TOE ~~~~~~~~" << endl;
      cout << "~~~~~~ by Bla Bla ~~~~~~" << endl;
      cout << "~~" << endl;
      cout << "~~ (1) Neues Spiel ~" << endl;
      cout << "~~ (2) Spielernamen ~" << endl;
      cout << "~~ (3) Beenden ~" << endl;
      cout << "~~" << endl << endl;
    
      choice = 0;
    
      while(choice != 1 || choice != 2 || choice != 3){
        cout << "Bitte Option waehlen (1-3): ";
        cin >> choice;
        switch(choice){
          case 1: neuesSpiel(); break;
          case 2: spielernamen(); break;
          case 3: beenden(); break;
          default: cout << "Bitte gueltigen Wert eingeben.";
        }
      }
    }
    
    void neuesSpiel(){
      aktionsfeld[0] = 0;
      aktionsfeld[1] = 0;
      aktionsfeld[2] = 0;
      aktionsfeld[3] = 0;
      aktionsfeld[4] = 0;
      aktionsfeld[5] = 0;
      aktionsfeld[6] = 0;
      aktionsfeld[7] = 0;
      aktionsfeld[8] = 0;
      spielerzug[0] = 0;
      spielerzug[1] = 0;
    
      zug = false; // False = Spieler 1 ist am Zug
      spielende = false;
    
      while(spielende != true){
        cout << endl << "     *     *     " << endl;
        if(aktionsfeld[0] == 0){
          cout << " (1) ";
        }
        else{
          if(aktionsfeld[0] == 1){
            cout << "  X  ";
          }
          else{
            cout << "  O  ";
          }
        }
        cout << "*";
        if(aktionsfeld[1] == 0){
          cout << " (2) ";
        }
        else{
          if(aktionsfeld[1] == 1){
            cout << "  X  ";
          }
          else{
            cout << "  O  ";
          }
        }
        cout << "*";
        if(aktionsfeld[2] == 0){
          cout << " (3) " << endl;
        }
        else{
          if(aktionsfeld[2] == 1){
            cout << "  X  " << endl;
          }
          else{
            cout << "  O  " << endl;
          }
        }
        cout << "     *     *     " << endl;
        cout << "*****************" << endl;
        cout << "     *     *     " << endl;
        if(aktionsfeld[3] == 0){
          cout << " (4) ";
        }
        else{
          if(aktionsfeld[3] == 1){
            cout << "  X  ";
          }
          else{
            cout << "  O  ";
          }
        }
        cout << "*";
        if(aktionsfeld[4] == 0){
          cout << " (5) ";
        }
        else{
          if(aktionsfeld[4] == 1){
            cout << "  X  ";
          }
          else{
            cout << "  O  ";
          }
        }
        cout << "*";
        if(aktionsfeld[5] == 0){
          cout << " (6) " << endl;
        }
        else{
          if(aktionsfeld[5] == 1){
            cout << "  X  " << endl;
          }
          else{
            cout << "  O  " << endl;
          }
        }
        cout << "     *     *     " << endl;
        cout << "*****************" << endl;
        cout << "     *     *     " << endl;
        if(aktionsfeld[6] == 0){
          cout << " (7) ";
        }
        else{
          if(aktionsfeld[6] == 1){
            cout << "  X  ";
          }
          else{
            cout << "  O  ";
          }
        }
        cout << "*";
        if(aktionsfeld[7] == 0){
          cout << " (8) ";
        }
        else{
          if(aktionsfeld[7] == 1){
            cout << "  X  ";
          }
          else{
            cout << "  O  ";
          }
        }
        cout << "*";
        if(aktionsfeld[8] == 0){
          cout << " (9) " << endl;
        }
        else{
          if(aktionsfeld[8] == 1){
            cout << "  X  " << endl;
          }
          else{
            cout << "  O  " << endl;
          }
        }
        cout << "     *     *     " << endl << endl;
        if(zug == false){
          cout << spieler1 << " ist am Zug!" << endl;
        }
        else{
          cout << spieler2 << " ist am Zug!" << endl;
        }
    
        wahl = 0;
    
        while(wahl < 1 || wahl > 9){
          cout << "Aktionsfeld waehlen (1-9): ";
          cin >> wahl;
        }
    
        if(aktionsfeld[wahl-1] != 0){
          cout << "Dieses Aktionsfeld ist bereits belegt!" << endl;
        }
        else{
          if(zug == true){
            aktionsfeld[wahl-1] = 1;
            spielerzug[0] = spielerzug[0] + 1;
            zug = false;
          }
          else{
            aktionsfeld[wahl-1] = 2;
            spielerzug[1] = spielerzug[1] + 1;
            zug = true;
          }
        }
    
        if((aktionsfeld[0] == aktionsfeld[1] == aktionsfeld[2]) != 0){
          cout << "win placeholder";
        }
        else{
          if((aktionsfeld[3] == aktionsfeld[4] == aktionsfeld[5]) != 0){
            cout << "win placeholder";
          }
          else{
            if((aktionsfeld[6] == aktionsfeld[7] == aktionsfeld[8]) != 0){
              cout << "win placeholder";
            }
            else{
              if((aktionsfeld[0] == aktionsfeld[3] == aktionsfeld[6]) != 0){
                cout << "win placeholder";
              }
              else{
                if((aktionsfeld[1] == aktionsfeld[4] == aktionsfeld[7]) != 0){
                  cout << "win placeholder";
                }
                else{
                  if((aktionsfeld[2] == aktionsfeld[5] == aktionsfeld[8]) != 0){
                    cout << "win placeholder";
                  }
                  else{
                    if((aktionsfeld[0] == aktionsfeld[4] == aktionsfeld[8]) != 0){
                      cout << "win placeholder";
                    }
                    else{
                      if((aktionsfeld[2] == aktionsfeld[4] == aktionsfeld[6]) != 0){
                        cout << "win placeholder";
                      }
                      else{
                        if(aktionsfeld[0] != 0 && aktionsfeld[1] != 0 &&
                        aktionsfeld[2] != 0 && aktionsfeld[3] != 0 &&
                        aktionsfeld[4] != 0 && aktionsfeld[5] != 0 &&
                        aktionsfeld[6] != 0 && aktionsfeld[7] != 0 &&
                        aktionsfeld[8] != 0){
                          cout << "draw placeholder";
                        }
                        else{
                          cout << "spiel fortführen placeholder";
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
      menue();
    }
    
    void spielernamen(){
      cout << endl << "***** Spielernamen *****" << endl;
      cout << "Spieler 1: " << spieler1 << endl;
      cout << "Spieler 2: " << spieler2 << endl;
      cout << "Bitte Namen fuer Spieler 1 eingeben: ";
      cin >> spieler1;
      cout << "Bitte Namen fuer Spieler 2 eingeben: ";
      cin >> spieler2;
      cout << "Namen erfolgreich geaendert!" << endl;
      cout << "Spieler 1: " << spieler1 << endl;
      cout << "Spieler 2: " << spieler2 << endl;
      cout << "************************" << endl << endl;
      menue();
    }
    
    void beenden(){
      exit(0);
    }
    

    Wir haben uns mit der Objektorientierung und Vererbung noch nicht wirklich beschäftigt, von daher habe ich einfach alles in eine Klasse geklatscht.

    Um meine Gedankengänge ein wenig zu erläutern:

    • Man hat die Möglichkeit das Spiel zu Starten (neuesSpiel()), die Spielernamen zu verändern (spielernamen()) und das Programm zu beenden (beenden())
    • Die beiden Arrays habe ich für die einzelnen Aktionsfelder (9 Stück) und die Anzahl der Spielzüge der einzelnen Spieler (2 Stück) angelegt, einfach um mich damit vertraut zu machen
    • Der aktuelle Spielzug wird durch die Variable zug angegeben
    • Das Spielende wird durch die Variable spielende angegeben

    So nun zu meinen Problemen und Fragen:

    • Ist die Verwendung der Funktionen sinnvoll? (daran zweifele ich nämlich)
    • Sind die Aufrufe der Funktionen richtig gesetzt? Habe mit dem Debugger verschiedene Programmabläufe versucht, allerdings weiß ich nicht, ob ich die Funktionsaufrufe einfach so in eine Funktion setzen kann und ob es so sinnig ist
    • Jetzt zu meinem Hauptproblem: Die If-Abfragen spucken immer Win aus, obwohl ich mir mit den Abfragen eigentlich ziemlich sicher bin. Habe eigentlich alle Win Szenarien abgehandelt und bei jeder Abfrage noch eine Überprüfung eingebaut, dass das Feld nicht leer ist. Draw funktioniert übrigens auch, aber eigentlich sollte nach einem Zug, wenn das Spiel nicht gerade gewonnen wird nur folgendes ausgegeben werden: 'spiel fortführen placeholder'

    Mir ist bewusst, dass das Programm nicht fertig ist, aber dieses Problem hält mich einfach auf, weiterzumachen. Vermutlich ein dämlicher Fehler, wie immer, wenn man am verzweifeln ist.

    Habe leider keinen Anfängerbereich gefunden, ich hoffe es ist in Ordnung, wenn ich das hier so reinschreibe.

    Danke und LG
    Karotte



  • if((aktionsfeld[0] == aktionsfeld[1] == aktionsfeld[2]) != 0)
    

    Das tut nicht das, was du glaubst.

    Hier wird aktionsfeld[0] mit aktionsfeld[1] verglichen, und das Ergebnis dieses Vergleiches (true oder false) wird mit aktionsfeld[2] verglichen.

    Was du meinst, geht so:

    if(aktionsfeld[0] == aktionsfeld[1] && aktionsfeld[0] == aktionsfeld[2])
    

  • Mod

    a==b==c ergibt nicht wahr, wenn a, b und c gleich sind. Benutze (a==b) and (b==c) . Compiliere auch immer mit höchsten Warnstufen, gute COmpiler hätten dich dann auf die Fehler aufmerksam gemacht.

    Und schmeiß dein Buch und deine Entwicklungsumgebung weg, sie sind Jahrzehnte veraltet.



  • Und noch ein Hinweis:

    Gerade wenn man mit Arrays (oder Containertypen) arbeitet, sind Schleifen (for, while) geradezu prädestiniert dafür, denn diese verkürzen den Programmcode enorm 😉

    Nur so als Beispiel:

    for(int i=0; i<8; i++)
        aktionsfeld[i] = 0;
    

    Und insbesondere der Rest der Funktion neuesSpiel() läßt sich ebenso gut verkürzen...



  • Da gibt´s jede Menge zu verbessern, aber man kann natürlich nicht erwarten, dass ein Neuling guten Code schreibt. Was mir beim Überfliegen so aufgefallen ist:

    - globale Variablen sollten soweit wie möglich vermieden werden

    - schlechte bis garkeine logische Gliederung in Funktionen. Es gibt bei dir eine Godfunktion, die das komplette Spiel abwickelt. Du solltest eigene Funktionen für die Eingabe von Spielzügen oder die Ausgabe des Spielfelds benutzen. Es gibt eine Regel, die besagt: one function, one purpose, also eine Funktion sollte genau eine Aufgabe erfüllen. Ob das immer umsetzbar ist sei mal dahingestellt, aber wenn eine Funktion über 200 Zeilen lang ist dann kann man sie mit Sicherheit in Teilfunktionen unterteilen.

    - Funktionen können Werte zurückgeben. Statt globale Variablen zu verwenden und dort Eingaben zu speichern kannst du Rückgabewerte von Funktionen benutzen, deren Ergebnis nur lokal relevant ist. Ein heißer Kandidat dafür ist die Funktion menue(), es gibt keinen Grund die Auswahl des Spielers global zu speichern, wo sie ja doch nur lokal gebraucht wird.

    - C++ besitzt Schleifen. Statt statisch jedes Spielfeldelement zu prüfen kannst du mit einer Schleife über alle Elemente laufen und dann das jeweilige Element ausgeben. Mittels / und % Operator kannst du den linearen Index in einen Pseudo 2D Index für die Zeile und Spalte umrechnen.

    - die STL erfordert zwar einiges an Einarbeitungszeit, aber bietet für alltägliche Problem fast immer ein kurze Lösung. Z.B. lässt sich das Array mit einem Einzeiler füllen:

    #include <algorithm>
    
    int arr[9];
    
    int main()
    {
       std::fill( arr, arr +9, 0 );
    }
    

    Karotte schrieb:

    Wir haben uns mit der Objektorientierung und Vererbung noch nicht wirklich beschäftigt, von daher habe ich einfach alles in eine Klasse geklatscht.

    Ich sehe keine Klasse nirgendwo nicht. Eine Quelltextdatei, aber keine Klasse.

    Ansonsten hat SeppJ ja schon gesagt, was du mit deinem Buch und Compiler anstellen solltest. Es gibt von Microsoft das Visual Studio in der freien Express Variante, damit hast du einen modernen und vor allem standardkonformen Compiler.


Anmelden zum Antworten