[Klassen]Variable erhält falschen Wert von Konstruktor?
-
peel schrieb:
Das einzige was mir spontan einfallen würde ist einfach von Jeder Variable die Adresse public machen, jedoch gibt es da sicher eine bessere Lösung, oder nicht?
Wie schon ein paar mal gesagt, wäre eine schiffklasse wohl das allerbeste...
class Tpoint { private: size_t x; size_t y; public: size_t Get_X() const {return x;} size_t Get_Y() const {return y;} void Set(size_t _x, size_t _y) {x=_x; y=_y;} Tpoint(size_t _x, size_t _y) : x(_x), y(_y) {} Tpoint() : x(0), y(0) {} }; class Tship_base { protected: static const npos = ~0; public: Tship_base() {} ~Tship_base() {} }; template <size_t length> class Tship : Tship_base { private: const Tpoint first; bool isHurt[length]; const bool horizontal; static size_t isStrike(const size_t coord_stepping, const size_t coord_solid, size_t shipPoint_step, const size_t shipPoint_solid) { if (shipPoint_solid != coord_solid) return npos; for (size_t last_shipPoint(shipPoint_step+lenth); last_shipPoint != shipPoint_step; ++shipPoint_step) { if (shipPoint_step == coord_stepping) return last_shipPoint - shipPoint_step; } return npos; } size_t getStrikingPos(const Tpoint& point) const { if (horizontal) return isStrike(point.Get_X(), point.Get_Y(), first.Get_X(), first.Get_Y()); else return isStrike(point.Get_Y(), point.Get_Y(), first.Get_Y(), first.Get_X()); } void createShipHealth() { for (size_t i(0); i != length; ++i) isHurt[i] = false; } public: Tship(const Tpoint &startingpoint, bool _horizontal) : first(startingpoint), horizontal(_horizontal) {createShipHealth();} bool IsStrike(const Tpoint& point) const { const size_t pos = getStrikingPos(point); if (pos == Tship::npos) return false; assert (pos < length); //man weiß ja nie... bool hurting(!isHurt[pos]); //wenn es true ist (schon getroffen wurde), dann geben wir "kein treffer" zurück - wenn es noch nicht getroffen ist, sagen wir dem spieler, dass er etwas getroffen hat isHurt[pos] = true; //in beiden fällen wurde das stück schiff getroffen return hurting; } };dann nimmste deine CPlayer-Klasse und bastelst so was: (eigtl würd ich es noch in ne klasse Tfield oder so was auslagern, aber naja ^^)
class Tplayer { private: std::vector <Tship*> ships; void destroy() { while(!ships.empty()) { delete ships.last(); ships.pop_last(); }; } public: //... Tplayer//kA ~Tplayer() {destroy();} };so in etwa würd ich es machen ^^ ich wollt eigtl erst nur 2 sätze dazu schreiben aber dann hat mich die lange weile gepackt xD
@peel: natürlich führen viele wege nach rom und meiner ist mit sicherheit nicht der allerbeste...
na denn, bb ^^
-
Da ich nicht mehr viel Zeit habe aber heute noch was zusammenbringen möchte, mach ich aus dem Schiffe versenken nun ein Minesweeper.Die Eingabe der Koordinaten über den Benutzer macht verdammt viel Arbeit.
Danke für eure Hilfe.
-
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.
-
na toll ><
da hab ich es sooo toll gemacht - glaube ich ^^bb
-
Da sieht man einmal mehr, wie viel es für beide Seiten bringt, wenn man einfach Code schreibt. Hinweise und Tipps sind grundsätzlich besser, denn nicht selten versteht man fremden Code nicht und hat länger, diesen nachzuvollziehen, als wenn man eigenen schreiben würde. Ausserdem lernt man beim Selberschreiben wohl mehr.
-
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::stringstattchar*undstd::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.
-
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