[Klassen]Variable erhält falschen Wert von Konstruktor?



  • jopp ^^ aber mir war halt gerad langweilig und ich hatte keine lust, was anstrengendes zu schreiben - so mit nachdenken und so... das liegt mir nicht so 😉

    bb



  • Um nochmals auf das Anfangsproblem zurückzukommen:

    Die Schleifen zum Setzen der Feld-Arrays sind falsch:

    char cFeld[10][10]={}; 
    
        for(int iZeile=0; iZeile<11; iZeile++) 
        { 
            for(int iSpalte=0; iSpalte<11; iSpalte++) 
            { 
                cFeld[iZeile][iSpalte]='0'; 
            } 
        }
    

    Die beiden Zähler dürfen natürlich nur von 0 bis 9 laufen (d.h. iZeile < 10 und iSpalte < 10)!!!

    Somit war ein Speicherüberschreiber drin und daher wurden die Werte der beiden Klassenvariablen geändert (da sie ebenso auf dem Stack liegen).



  • Nexus schrieb:

    Denk daran, ein MineSweeper ist nicht gerade einfach zu programmieren.

    Wenn man ein Feld aufdeckt, bei dem die angrenzenden 8 Felder leer sind, werden darum herum ebenfalls alle aufgedeckt, bis man zu Feldern gelangt, die Minen als Nachbarn haben. Das ist am einfachsten mit einer Rekursion zu lösen, wobei vom Aufdeckpunkt ausgegangen wird und jeweils die Nachbarn aufgedeckt werden, sofern sie es noch nicht sind.

    Bei Feldern, die Minen als Nachbarn haben, steht beim Aufdecken eine Zahl. Diese gibt an, wie viele der acht umliegenden Felder eine Mine beinhalten.

    Mein Minesweeper ist eben ein wenig vereinfacht, man denkt nur ein Feld auf, und es wird überprüft ob eine Mine drunter ist oder nicht, ohne Nachbarn aufdecken und so.

    Hier mal der Code:

    #include <iostream>
    #include <stdlib.h>
    #include <time.h>
    
    using namespace std;
    
    void feld_ausgeben(char *pcFeld);
    void spiel_starten();
    
    int main()
    {
    	char cAuswahl='0';
    
    	while(cAuswahl!=3)
    	{
    		cout<<"Minesweeper!"<<endl;
    		cout<<"---------------------------"<<endl;
    		cout<<"1: Spiel starten"<<endl;
    		cout<<"2: Anleitung"<<endl;
    		cout<<"3: Beenden"<<endl;
    		cout<<"---------------------------"<<endl;
    
    		cin>>cAuswahl;
    		cout<<endl;
    
    		if (cAuswahl=='1')
    		{
    			spiel_starten();
    		}
    		else if(cAuswahl=='2')
    		{
    			cout<<endl;
    			cout<<"Das Ziel dieses Spiel ist es alle Felder unter denen sich keine Minen befinden anzuwählen. Wenn Sie ein Mine auswählen haben sie das Spiel verloren. Für jedes angewählte Feld ohne Mine bekommen sie einen Punkt."<<endl;
    			cout<<"Das Feld ist folgendermaßen aufgebaut:"<<endl<<endl;
    			cout<<"  | 1 2 3 4 5 6 7 8 9 10"<<endl;
    			cout<<"1 | --------------------"<<endl;
    			cout<<"2 | 0 0 0 0 0 0 0 0 0 0"<<endl;
    			cout<<"3 | 0 0 0 0 0 0 0 0 0 0"<<endl;
    			cout<<"4 | 0 0 0 0 0 0 0 0 0 0"<<endl;
    			cout<<"5 | 0 0 0 0 0 x 0 0 0 0"<<endl;
    			cout<<"6 | 0 0 0 0 0 0 0 0 0 0"<<endl;
    			cout<<"7 | 0 0 0 0 0 0 0 0 0 0"<<endl;
    			cout<<"8 | 0 0 0 0 0 0 0 0 0 0"<<endl;
    			cout<<"9 | 0 0 0 0 0 0 0 0 0 0"<<endl;
    			cout<<"10| 0 0 0 0 0 0 0 0 0 0"<<endl;
    			cout<<endl;
    			cout<<"Um des x anzuwählen müssen Sie bei der x: Abfrage 6 und bei der y: Abfrage 5 eintippen."<<endl;
    			cout<<"Wenn sie ein Feld angewählt haben und es keine Mine ist, verwandelt es sich in ein o statt einer 0."<<endl;
    			cout<<endl<<endl;
    		}
    		else if(cAuswahl=='3')
    		{
    			break; //aus while ausbrechen und somit Programm beenden
    		}
    		else
    		{
    			cout<<"Falsche Eingabe!"<<endl<<endl;
    			main(); //Programm neustarten
    			break; //und aus while ausbrechen
    		}
    
    	}
    	return 0;
    }
    void feld_ausgeben(char *pcFeld)
    {
    	for(int i=1; i<101; pcFeld++, i++) //*pcFeld zeigt auf cFeld[0][0], wenn dies um 1(4 byte) erhöht wird, kommt man zum nächsten Speicherplatz des Arrays
    	{
    			cout<<*pcFeld<<" ";
    			if(i%10==0) //immer bei 10 eine neue Zeile beginnen
    			{
    				if(i==100)
    				{
    					cout<<endl; //bei 100 eine Leerzeile dazu
    				}
    				cout<<endl;
    			}
    	}
    }
    void spiel_starten()
    {
    	int i_tippx=0; //Variablen die die Werte speichern die der Benutzer angibt wo er denkt das sich keine Mine befindet
    	int i_tippy=0;
    
    	int iScore=0; //Punktezahl
    
    	bool bVerloren=false;
    
    	char cFeld_minen[10][10]={}; //Array das die Koordinaten der Minen enthält
    	char cFeld[10][10]={}; //Array das dem Benutzer ausgegeben wird
    
    	const int rand_max=10;
    
    	srand(time(NULL)); //rand() initialisieren
    
    	int iAnzahl_minen=0;
    
    	for(int iZeile=0; iZeile<10; iZeile++) //beide Felder mit 0en füllen
    	{
    		for(int iSpalte=0; iSpalte<10; iSpalte++)
    		{
    			cFeld[iZeile][iSpalte]='0';
    			cFeld_minen[iZeile][iSpalte]='0';
    		}
    	}
    
    	cout<<"Anzahl der Minen:";
    	cin>>iAnzahl_minen;
    
    	if(iAnzahl_minen>=1 && iAnzahl_minen<=99) //min. eine Mine; min. ein Feld noch frei
    	{		
    		for(int i=0; i<iAnzahl_minen; i++)
    		{
    			int iZeile=(rand()%rand_max); //Zufallszahlen für Koordinaten der Mine
    			int iSpalte=(rand()%rand_max);
    
    			if(cFeld_minen[iZeile][iSpalte]=='1') //wenn der Platz schon besetzt ist, keinen Wert hineinschreiben
    			{
    				i--;							  //sondern nur Schleifendurchläufe um 1 verringern, damit diese Runde nicht gezählt wird
    			}
    			else
    			{
    				cFeld_minen[iZeile][iSpalte]='1'; //wenn nicht im Feld ist: eine 1 hineinschreiben
    			}
    		}
    	}
    	else
    	{
    		cout<<"Falsche Eingabe!"<<endl<<endl;
    		bVerloren=true; //damit man nicht in die nächste while-Schleife gelangt und das Programm neugestartet wird
    	}
    	feld_ausgeben(&(cFeld_minen[0][0]));
    	while(bVerloren==false)
    	{
    		cout<<"x:";
    		cin>>i_tippx;
    		cout<<"y:";
    		cin>>i_tippy;
    
    		--i_tippx;
    		--i_tippy; //auf Array anpassen
    
    		if(i_tippx<10 && i_tippx>-1 && i_tippy<10 && i_tippy>-1) //die Tipps dürfen nicht kleiner als 0, und nicht größer als 9 sein.
    		{
    			if(cFeld_minen[i_tippy][i_tippx]=='1')
    			{
    				cout<<"Leider eine Mine getroffen!"<<endl;
    				cout<<"Erreichte Punkte: "<<iScore<<endl<<endl;
    				bVerloren=true; //wenn eine Mine getroffen wird, Score ausgeben, und Programm neustarten
    			}
    			else if(cFeld_minen[i_tippy][i_tippx]=='0') //wenn keine Mine getroffen wurde
    			{
    				iScore++;						//Score um eins erhöhen
    				if(iAnzahl_minen+iScore==100) //bevor das Feld bearbeitet und ausgegeben wird überprüfen ob der Spieler schon gewonnen hat
    				{
    					cout<<"SIEG! Sie haben alle Felder ohne Minen geöffnet."<<endl;
    					cout<<"Sie haben somit "<<iScore<<" Punkte erreicht."<<endl<<endl;
    					break;
    					main();
    				}
    				cFeld[i_tippy][i_tippx]='o'; //an die Stelle ein o schreiben
    				feld_ausgeben(&(cFeld[0][0])); //das Feld ausgeben
    			}
    		}
    		else
    		{
    			cout<<"Falsche Eingabe!"<<endl;
    		}
    	}
    }
    

    Irgendwelche Stilbrüche oder sonstige Sachen die man verbessern könnte? Bitte mit Erklärung. Danke schonmal.

    EDIT:
    Ich weiß es hat nichts mehr mit dem eigentlichem Topic zu tun, aber ich wollte dafür nicht extra einen Thread aufmachen.



  • Ich hab einige Dinge, die mir grad aufgefallen sind, aufgeschrieben:

    #include <iostream>
    #include <stdlib.h>   // C++-Standardheader: <cstdlib>
    #include <time.h>     //                     <ctime>
    
    // ...
    
    int main()
    {
    	char cAuswahl='0';
    
    	while(cAuswahl!=3) // Vergleich auf int-Literal 3, nicht Zeichen '3'
    	{
            // ...
    
    		if (cAuswahl=='1')   // switch wäre besser
    		{
    			spiel_starten();
    		}
            // ...
    					main(); // was soll das?
    

    Du solltest vielleicht ein bisschen weniger C-Style programmieren, d.h. weniger rohe Arrays und Zeiger, sondern std::string statt char* und std::vector , und so weiter. Klassen wären sicher auch keine schlechte Idee...

    Vielleicht ist es auch noch etwas zu früh, aber du solltest dir das im Hinterkopf behalten. 😉



  • Nexus schrieb:

    Ich hab einige Dinge, die mir grad aufgefallen sind, aufgeschrieben:

    while(cAuswahl!=3) // Vergleich auf int-Literal 3, nicht Zeichen '3'
    

    Ich glaube, dass das durchaus gewollt (und richtig) war, auf '3' zu prüfen...

    Ich würd die Schleife ja fast so machen:

    for (;;) //endlosschleife
    {
    char eingabe;
    std::cin >> eingabe;
    switch eingabe:
    {
    case '1':
    //zeugs in ne fkt auslagern
    break;
    
    case '2':
    //zeugs in ne fkt auslagern
    break;
    
    case '3':
    default:
    break; //ende
    }
    }
    
    std::cout << "Sie haben das Programm beendet... blabla" << std::endl;
    

    statt dem char würde ich auch nen int oder so was in der richtung nehmen, wenn geplant ist, dass es auch mal mehr, als 9 punkte geben kann ^^

    außerdem würde ich die zahlen auf jeden fall als globale konstante definieren...

    und die ungültigen werte würde ich definitiv anders abfangen als durch den abbruch des programms - aber so sollte es vor erst gehen und bissl was muss dir ja auch noch an aufgaben übrig bleiben 😉

    außerdem habe ich den eindruck, dass du das mit der schleife noch nicht ganz verstanden hast ?! warum nimmst du ne schleife, wenn du eh jedes ma nen break drin hast?!!? Und dann kommt dann noch der ganze Krempel, den Nexus schon aufgeführt hat...

    hf gl - und kopf hoch, jeder hat ma klein angefangen ^^



  • Wenn man eine Mine trifft, braucht es die While-Schleife um wieder in das Programm zu kommen.

    Ich verwende für Menüabfragen etc meist char um den Endlosschleifen durch falsche Eingaben aus dem Weg zu gehen(zb "u" gibt bei int ja dann Endlossschleife).

    Diese break;'s hab ich eingebaut weil das Programm sonst nicht beendet wird, bzw erst nach dem zweiten mal "3" eingeben. Das eine break bei else hab ich entfernt, dort steht jetzt nur

    cout<<"Falsche Eingabe!"<<endl<<endl;
    

    .

    Ich lese noch ein Buch über c++ und Vektoren kommen erst ein wenig später.

    Und wegen den Bibiotheken: ich habe in den man-Pages nachgesehen wie ich die Zufallszahlen unter Linux hinbekomme, und dort stand das ich diese 2 Bibiotheken einbinden muss.



  • zb "u" gibt bei int ja dann Endlossschleife

    hmm... nein?
    dafür gibts default (bei nem switch) und nen else beim if ^^

    Zufallszahlen unter Linux

    std::rand ist C++ - das heißt, es ist plattformunabhängig...

    und die header stdlib.h und time.h sind ja auch nicht immer falsch - aber wenn man C++ programmieren will, dann sollte man diese (unbedingt!) vermeiden - wir haben dafür cstdlib, ctime, .... etc - der gleiche funktionsumfang etc, aber eben die C++-header wohingegen sämtliche standard-header mit ner endung den C-prgroammieren vorenthalten sind ^^ (hat vor allem etwas mit namespaces zu tun - das ist aber erst mal egal - hauptsache, man macht es ^^)

    bb



  • Irgendwie schon, wenn ich die erste while-Schleife genau so lasse, nur statt char cAuswahl int iAuswahl nehme und das dem enstprechend verlinke(halt ohne ') kommt bei einer Eingabe von "u" eine Endlosschleife(und ich hab ja else).



  • das liegt an der fehlerhaften eingabe...
    du musst erst die failbits des streams resetten bevor du die nächste eingabe machen kannst...

    std::cin.clear();
    std::cin./*irgendwas mit goodbit, glaube ich xD schon ewig nix mehr mit streams zu tun gehabt ^^*/
    

    bb



  • std::cin.clear() sollte reichen. Das setzt alle Fehlerflags zurück.


  • Administrator

    Nexus schrieb:

    std::cin.clear() sollte reichen. Das setzt alle Fehlerflags zurück.

    Man sollte aber noch eine Synchronisierung des Streams erzwingen, da das 'u' immer noch im Stream wäre und einfach bei der nächsten Eingabe wieder einen Fehler auslösen könnte. Daher diese zwei Möglichkeiten:

    std::cin.ignore(std::cin.rdbuf()->in_avail());
    // Überspringt alles, was noch im Stream ist.
    
    // Oder, aber funktioniert anscheinend nicht überall und immer:
    std::cin.sync();
    

    Grüssli


Anmelden zum Antworten