Der beste Weg zum Spieleprogrammieren



  • Jetzt habe ich ein Programm gebastelt, das irgendwo einen Fehler hat:

    #include <iostream>
    #include <map>
    #include <fstream>
    #include <string>
    
    void Auslesen(std::map<std::string, std::string> &text, std::string &s)
    {
        using namespace std;
    	ifstream MeineDatei ("TestDokument.txt");
    	int i = 0;
    
    	while (!MeineDatei.eof())
    	{
    		MeineDatei.get();
    		MeineDatei >> s[i];
    		MeineDatei.get();
    		MeineDatei >> text[s];
    		i++;
    	}
    }
    
    int main()
    {
        using namespace std;
    
    	map <string, string> Text;
        ifstream MeineDatei ("TestDokument.txt");
    	string schluessel[5];
    	int i = 0;
    
        if (MeineDatei.is_open())
    	{
            Auslesen(Text, schluessel[0, 1, 2, 3, 4]);
    
    		for(Text.begin(); Text.begin() != Text.end(); Text.begin()++, i++)
    		{
    			cout << Text[(string)schluessel[i]];
    		}
    	}
    	else
            cout << "Fehler beim Auslesen der Daten!" << endl
            << "Bitte pruefen sie ob \"TestDokument.txt\" existiert und im richtigen Ordner ist!";
    
    	cin.clear();
        cin.ignore();
    }
    

    Ich vermute das sich der Fehler in der while-Schleife versteckt hat.

    cooky451 schrieb:

    (Falls du lieber erstmal eine "Musterlösung" sehen möchtest, weil du das Gefühl hast so nicht weiter zu kommen sag Bescheid.)

    Wenn der Fehler in meinem Programm groß genug ist, würde ich mir gern die Musterlösung ansehen 🙂 .

    Weiterhin danke für eure Hilfe.

    -Skotchy



  • Skotchy schrieb:

    Wenn der Fehler in meinem Programm groß genug ist, würde ich mir gern die Musterlösung ansehen 🙂 .

    Nja, jetzt hast du es fast.

    Skotchy schrieb:

    #include <iostream>
    #include <map>
    #include <fstream>
    #include <string>
    
    void Auslesen(std::map<std::string, std::string> &text, std::string &s) // Wozu der std::string?
    {
        using namespace std;
    	ifstream MeineDatei ("TestDokument.txt"); // Das hier soll als Parameter übergeben werden. Entweder als "const char *", oder als "const std::string&")
    	int i = 0; // Wozu das?
    
    	while (!MeineDatei.eof()) // Tipp: http://www.cplusplus.com/reference/iostream/ios/operator_voidpt/
    	{
    		MeineDatei.get(); // Wozu?
    		MeineDatei >> s[i]; // Warum s[i]?
    		MeineDatei.get(); // Wozu?
    		MeineDatei >> text[s]; // Hier bist du auf dem richtigen Weg.
    		i++; // ..?
    	}
    }
    
    int main()
    {
        using namespace std;
    
    	map <string, string> Text; // Korrekt
        ifstream MeineDatei ("TestDokument.txt"); // Hä? Du öffnest die Datei doch in der Funktion!
    	string schluessel[5]; // Hä?
    	int i = 0; // ??
    
        if (MeineDatei.is_open())
    	{
            Auslesen(Text, schluessel[0, 1, 2, 3, 4]); // Was soll "schluessel"?
    
    		for(Text.begin(); Text.begin() != Text.end(); Text.begin()++, i++) // Siehe Beispiel unten. :)
    		{
    			cout << Text[(string)schluessel[i]];
    		}
    	}
    	else
            cout << "Fehler beim Auslesen der Daten!" << endl
            << "Bitte pruefen sie ob \"TestDokument.txt\" existiert und im richtigen Ordner ist!";
    
    	cin.clear();
        cin.ignore();
    }
    

    Über einen Vektor kannst du mit Iteratoren z.B. so iterieren:

    std::vector<int> v;
    v.push_back(5);
    ...
    for (std::vector<int>::const_iterator i = v.begin(); // i zeigt auf das erste Element des Vektors
      i != v.end(); ++i) // v.end() zeigt genau ein Element hinter das Letzte
    {
      std::cout << *i;
    }
    

    Letzter Versuch. Du solltest dir nur im Klaren darüber sein, wenn du Spiele programmieren möchtest, sollte man die Aufgabe in ~2-3 Minuten gut lösen können. 😉



  • So, dann ist das hier also die letzte Version meines Programms, allerdings mit ein paar Unklarheiten

    #include <iostream>
    #include <map>
    #include <fstream>
    #include <string>
    #include <vector>
    
    void Auslesen(std::map<std::string, std::string> &text)
    {
        using namespace std;
    	ifstream MeineDatei ("TestDokument.txt");
    	//const string &a = MeineDatei ("TestDokument.txt"); //Total falsch oder?!
    
    	if ((void*)MeineDatei == 0){
    		cerr << "Fehler beim Auslesen der Daten!" << endl
    		<< "Bitte pruefen sie ob \"TestDokument.txt\" existiert und im richtigen Ordner ist!";
    		return;
    	}
    
    	while (MeineDatei.good())
    	{
    		MeineDatei >> text/*Hier müsste das erste Wort der Zeile aus der Datei als Schlüssel rein, oder?*/;
    		//Hier müsste ich den vector v mit text[] füttern oder?
    	}
    }
    
    int main()
    {
        using namespace std;
    
    	vector<int> v;
    	v.push_back (5);
    	map <string, string> Text;
    
        Auslesen(Text);
    
    	for(vector<int>::const_iterator i = v.begin(); i != v.end(); ++i)
    	{
    		cout << *i;
    	}
    
    	cin.clear();
        cin.ignore();
    }
    

    Ich denke mal, dass alles was mir unklar ist im code als Kommentar steht. Bin jetzt eigendlich nur noch gespannt, ob diese oder die vorherige Version näher an der Lösung bzw. an der Musterlösung ist.

    Edit: LoL warum wird der Code nicht als Code angezeigt ?!

    Weiterhin danke für eure Hilfe.

    -Skotchy



  • Skotchy schrieb:

    #include <iostream>
    #include <map>
    #include <fstream>
    #include <string>
    #include <vector>
    
    void Auslesen(std::map<std::string, std::string> &text)
    {
        using namespace std;
    	ifstream MeineDatei ("TestDokument.txt");
    	//const string &a = MeineDatei ("TestDokument.txt"); //Total falsch oder?!
    
    	if ((void*)MeineDatei == 0){
    		cerr << "Fehler beim Auslesen der Daten!" << endl
    		<< "Bitte pruefen sie ob \"TestDokument.txt\" existiert und im richtigen Ordner ist!";
    		return;
    	}
    
    	while (MeineDatei.good())
    	{
    		MeineDatei >> text/*Hier müsste das erste Wort der Zeile aus der Datei als Schlüssel rein, oder?*/;
    		//Hier müsste ich den vector v mit text füttern oder?
    	}
    }
    
    int main()
    {
        using namespace std;
    
    	vector<int> v;
    	v.push_back (5);
    	map <string, string> Text;
    
        Auslesen(Text);
    
    	for(vector<int>::const_iterator i = v.begin(); i != v.end(); ++i)
    	{
    		cout << *i;
    	}
    
    	cin.clear();
        cin.ignore();
    }
    

    Der Syntaxhighlighter verträgt sich wohl nicht mit [].
    Joa, schon besser. 🙂

    Blubb:

    void readFile(std::map<std::string, std::string>& keys, const char *filename)
    {
      std::ifstream in(filename);
      if (in.good())
      {
        std::string s;
        while (in >> s && in >> keys[s])
          ;
      }
      else
      {
        std::cerr << "Could not open file: " << filename << "\n";
      }
    }
    
    int main()
    {
      std::map<std::string, std::string> keys;
      readFile(keys, "keys.txt");
      // auto ist hier gleichbedeutend mit: std::map<std::string, std::string>::const_iterator
      for (auto i = keys.begin(); i != keys.end(); ++i)
        std::cout << i->first << ": " << i->second << "\n";
      getchar(); // Ganz böse!
    }
    

    Frag, falls du irgendetwas nicht verstehst, oder wenn du noch eine Aufgabe willst.



  • Eine paar Fragen hab ich da schon zu dem Code:
    1. Hätte ich gerne die while-Schleife erläutert bekommen.
    2. Was ist der Unterschied zwischen 'endl' und '"\n"' ? Beide sorgen dafür das der folgende Text in der nächsten Zeile ausgegeben, richtig? Aber wo liegt der unterschied?(oder gibt es da keinen?)
    3. Eine Verständnisfrage: 'i->first' Zeigt auf das erste Element aus 'i' und 'i->second' auf das zweite bzw. indem Fall auf die folgenden Elemente, richtig?
    4. Warum ist 'getchar()' so böse?

    So, dass waren dann meine Fragen und über noch eine Aufgabe würde ich mich auch freuen 🙂 .

    Weiterhin danke für eure Hilfe.

    -Skotchy



  • Skotchy schrieb:

    1. Hätte ich gerne die while-Schleife erläutert bekommen.

    Das war der Tipp mir operator void*. operator >> gibt selbst eine Referenz auf das Objekt zurück.
    (z.B: istream& operator>> (float& val ); )
    Und bei dem Objekt selbst ist wieder operator void* überladen:

    Return Value
    null pointer if either failbit or badbit is set.
    A non-null pointer otherwise.

    Das wird da ausgenutzt. Letztendlich besteht die Schleife also nur aus "lies bis zum Ende."

    Dazu wird im linken Teil der Schlüssel ausgelesen und im rechten Teil wird dann der Wert des Schlüssels in der std::map gesetzt.

    Skotchy schrieb:

    2. Was ist der Unterschied zwischen 'endl' und '"\n"' ? Beide sorgen dafür das der folgende Text in der nächsten Zeile ausgegeben, richtig? Aber wo liegt der unterschied?(oder gibt es da keinen?)

    std::endl ist ein "\n" + std::flush. Wenn du das flush nicht brauchst, nutze "\n", da das logischerweise performanter ist.

    Skotchy schrieb:

    3. Eine Verständnisfrage: 'i->first' Zeigt auf das erste Element aus 'i' und 'i->second' auf das zweite bzw. indem Fall auf die folgenden Elemente, richtig?

    Nein. Der Iterator über eine std::map zeigt auf ein std::pair. first/second sind member von std::pair, bei einer std::map sind sie gleichbedeutend mit "Schlüssel" und "Wert".

    Skotchy schrieb:

    4. Warum ist 'getchar()' so böse?

    1. Weil es C ist und man C und C++ nicht unbedingt mischen sollte und 2. weil man Konsolen-Programme normalerweise einfach zu Ende laufen lässt. (Nur ist das unter Windows zum testen halt nervig.)



  • Vor der nächsten Aufgabe hätte ich da nochmal eine Frage.
    Dieses Auslesen einer Datei kann man doch (auch) dazu benutzen um Spielstände zu laden, richtig? Dann müsste man für das Speicher die Datei einfach ändern bzw. neu beschreiben, oder?

    Weiterhin danke für eure Hilfe.

    -Skotchy



  • Will mich nicht großartig einmischen, finde gut, dass du ihm hilfst, habe nur mal an dich (cooky451) ne Frage ..

    cooky451 schrieb:

    ifstream hat einen Konstruktor, da brauchst du dann kein open mehr:

    ifstream datei("text.txt");
    

    Das close am Ende kannst du hier auch sparen, das macht der Destruktor. (RAII ist schon was Tolles. :p)

    Gehen wir davon aus wir haben einen Gültigkeitsbereich, in dem ein Stream geöffnet wird. Irgendwo zwischendrin schmiert das Programm ab, ohne eine Exception zu werfen. Die Dateioperationen sind längst abgeschloßen. Was ist die Folge: Destruktor wird nicht aufgerufen, Datei wird nicht geschloßen, lässt sich vom User nicht mehr bearbeiten/verschieben/löschen weil Windoof sagt "Datei in Verwendung". Warum dann kein close () verwenden um sowas zu vermeiden? Oder sehe ich da was Falsch?

    Also z.B.

    { // Gültigkeitsbereich
        ifstream bla ("datei.txt");
    
        /* Mache hier alle gewünschten Operationen mit der Datei */
    
        /* Hier könnte jetzt ein bla.close () rein, weil alle Dateioperationen erledigt wurden */
    
        /* Hier schmiert das Programm ab, ohne Exception, d.h. Destruktor für bla wird nicht mehr aufgerufen */
    }
    


  • Fake oder Echt schrieb:

    Gehen wir davon aus wir haben einen Gültigkeitsbereich, in dem ein Stream geöffnet wird. Irgendwo zwischendrin schmiert das Programm ab, ohne eine Exception zu werfen. Die Dateioperationen sind längst abgeschloßen. Was ist die Folge: Destruktor wird nicht aufgerufen, Datei wird nicht geschloßen, lässt sich vom User nicht mehr bearbeiten/verschieben/löschen weil Windoof sagt "Datei in Verwendung". Warum dann kein close () verwenden um sowas zu vermeiden? Oder sehe ich da was Falsch?

    Wenn das Programm abstürzt (!= es wird eine Exception geworfen), dann liegt es im Tätigkeitsbereich des Betriebssystems alle Resourcen freizugeben. Du kannst an der Stelle nichts mehr machen. Im Grunde verlagerst du das Problem nur an einen anderen Ort. Der Code der vorher durch ctor/dtor eingerahmt wurde wird jetzt durch ctor/close() oder open()/close() eingerahmt. Macht keinen Unterschied. Natürlich solltest du trotzdem darauf achten, dass du Resourcen nicht zu lange verwendest. Aber das erreichst du durch modularisierung einfacher, als durch das willkürliche Einstreuen von close().

    Mal dein Beispiel weiter kommentiert:

    { // Gültigkeitsbereich
        ifstream bla ("datei.txt");
    
        /* Mache hier alle gewünschten Operationen mit der Datei */
    
        /* Hier könnte jetzt ein bla.close () rein, weil alle Dateioperationen erledigt wurden */
        //das war ein deutlicher Gedanke, dass hier eine Bedeutungseinheit zuende ist, also ist alles was drüber kommt, eine Funktion
    
        /* Hier schmiert das Programm ab, ohne Exception, d.h. Destruktor für bla wird nicht mehr aufgerufen */
    }
    

    =>

    SomeType readFile(){
        ifstream bla ("datei.txt");
        /* Mache hier alle gewünschten Operationen mit der Datei */
    
        /* lass den Destruktor aufräumen*/
    }
    
    { // Gültigkeitsbereich
        SomeType type = readFile();
    
        /* Hier schmiert das Programm ab, ohne Exception, aber bla ist bereits kaputt, awesome!*/
    }
    


  • Skotchy schrieb:

    Vor der nächsten Aufgabe hätte ich da nochmal eine Frage.
    Dieses Auslesen einer Datei kann man doch (auch) dazu benutzen um Spielstände zu laden, richtig? Dann müsste man für das Speicher die Datei einfach ändern bzw. neu beschreiben, oder?

    Ja, natürlich geht das. Du musst dir halt ein Format für die Spielstände überlegen in dem alle wichtigen Informationen abgespeichert werden (Punkte, Fortschritt, ...) und die schreibst du dann in eine Datei und kannst sie später wieder auslesen.

    Das spezielle Beispiel mit der std::map hier könnte man für Werte und ähnliches sogar direkt benutzen, auch wenn der Zusammenhang eigentlich gar nicht geplant war. 🙂



  • cooky451 schrieb:

    auch wenn der Zusammenhang eigentlich gar nicht geplant war. 🙂

    ist doch supper, dann hab ich also indirekt mehr gelernt als vorgesehen war. 🙂

    cooky451 schrieb:

    Du musst dir halt ein Format für die Spielstände überlegen in dem alle wichtigen Informationen abgespeichert werden (Punkte, Fortschritt, ...) und die schreibst du dann in eine Datei und kannst sie später wieder auslesen.

    wird zwar noch ein bisschen früh sein aber welche Formate empfehlen sich da besonders gut, oder gibt es da je nach Spiel andere die sich besser eignen?

    Jetzt freue ich mich aber auf die nächste Aufgabe(und natürlich wieder, falls vorhanden, auf den 'versteckten' unbeabsichtigten Inhalt 😉 ).

    Weiterhin danke für eure Hilfe.

    -Skotchy



  • otze schrieb:

    SomeType readFile(){
        ifstream bla ("datei.txt");
        /* Mache hier alle gewünschten Operationen mit der Datei */
        
        /* lass den Destruktor aufräumen*/
    }
    
    { // Gültigkeitsbereich
        SomeType type = readFile();
    
        /* Hier schmiert das Programm ab, ohne Exception, aber bla ist bereits kaputt, awesome!*/
    }
    

    Ja gut, dass da da bereits aufgeräumt wurde ist ja klar, da ja der Gültigkeitsbereich von bla nur innerhalb von readFile () liegt. Mein Gedanke war nur, Handle wird in dem entsprechenden Bereich nicht geschloßen, also geht Windoof davon aus, dass die Datei nicht beendet wurde, aber du hast ja erklärt, dass dieses Problem von offenen unbenutzten Handles nach dem Programmabsturz an das OS weitergegeben wird bzw. bearbeitet werden soll, also is meine Frage geklärt, danke 🙂



  • Skotchy schrieb:

    wird zwar noch ein bisschen früh sein aber welche Formate empfehlen sich da besonders gut, oder gibt es da je nach Spiel andere die sich besser eignen?

    Mit denk dir ein Format aus war genau das gemeint, denk dir ein Format aus. Für so etwas gibt es keinen Standard (wehe jetzt kommt mir einer mit xml. ;)).
    Das kommt ganz auf das Spiel an, welches du machen möchtest. Nehmen wir ein Jump&Run Spiel. Da würde man nur die Punkte / das aktuelle Level speichern. Bei einem Rollenspiel sieht das ganz anders aus, da kommen so Sachen wie Inventar, Attribute, etc. pp. dazu.

    Skotchy schrieb:

    Jetzt freue ich mich aber auf die nächste Aufgabe(und natürlich wieder, falls vorhanden, auf den 'versteckten' unbeabsichtigten Inhalt 😉 ).

    Kleine Übung zu Iteratoren. Implementiere std::sort , allerdings nur mit Bubblesort. (Kannst natürlich auch Quicksort oder so nehmen, aber es soll ja um die Sprache und nicht um den Algorithmus gehen.)

    Als Beispiel für den generellen Umgang mit Iteratoren siehe: http://www.cplusplus.com/reference/algorithm/ .
    Sehr viele dieser Algorithmen sind sehr einfach und haben dort eine Beispielimplementierung.



  • Ok, ich glaube ich habe die Aufgabe verstanden aber in meinem Code einen (kleinen?!) Denkfehler.

    #include <iostream>
    #include <algorithm>
    
    int main()
    {
    	int Zahlen[] = {13,63,4,85,23,1,51,6790,6,2}, maenge = 10;
    
    	bool Unsortiert = false;
    	while ( !Unsortiert && maenge > 1)
    	{
    		for (int i; i > i +1; i++)
    		{
    			if (Zahlen[i] > Zahlen[++i])
    			{
    				std::sort(Zahlen[i - 1], Zahlen[++i - 1]);
    				Unsortiert = true;
    			}
    		}
    		maenge -= 1;
    	}
    
    	for(int i = 0; i >= 0; i++)
    		std::cout << Zahlen[i] << ", ";
    }
    

    Der Compiler sagt mir, dass da irgendetwas Falsch ist aber markiert nichts als Fehler 😮 .
    Ist da eventuell etwas defekt oder ist das normal so?

    Weiterhin danke für eure Hilfe.

    -Skotchy



  • Du hast weder std::sort noch die Aufgabenstellung verstanden. Um das noch mal zu betonen: Du sollst die Funktion std::sort nachbauen. Angewendet wird std::sort so:

    #include <algorithm>
    #include <vector>
    
    int main()
    {
      std::vector<int> v;
      v.push_back(5);
      v.push_back(3);
      v.push_back(76);
      v.push_back(9);
      // ...
      std::sort(v.begin(), v.end());
      // Jetzt ist der Vektor sortiert, aus, ende. Nichts mit while (unsorted) ...
    }
    


  • Skotchy schrieb:

    Der Compiler sagt mir, dass da irgendetwas Falsch ist [...]

    ... und was Dein Compiler sagt, erfahren wir sicher durch unsere Kristallkugeln.



  • Du könntest dir auch einfach mal die Anwendungsbeispiele zu std::sort durchlesen:
    http://www.cplusplus.com/reference/algorithm/sort/
    Das ist eigentlich immer der erste Schritt, wenn man Funktionen nicht kennt.



  • So, hat zwar etwas länger gedauert aber ich wollte die Aufgabe erst versuchen zu verstehen. Ich soll std::sort nicht anwenden sondern nachbauen, dabei soll ich das 'Bibblesort-Prinzip' anwenden also die Zahlen immer wieder durchgehen und nur zwei nebeneinander liegende Zahlen miteinander vertauschen wenn sie nicht in der richtigen Reihenfolge stehen. Wenn ich das nicht machen sollte habe ich die Aufgabe leider immer noch nicht verstanden.
    Und hier ist mein Programm, allerdings immer noch mit Fehlern:

    #include <iostream>
    #include <algorithm>
    #include <vector>
    
    void tausch(std::vector<int> &x, std::vector<int> &y)
    {
    	std::vector<int> ablage;
    	ablage = x;
    	x = y;
    	y = ablage;
    }
    
    int main()
    {
    	int zahlen[] = {4, 71, 74, 24, 97, 3, 388, 622, 2};
    	std::vector<int> Zahlen (zahlen, zahlen + 9);
    
    	for (std::vector<int>::iterator i = Zahlen.begin(); i != Zahlen.end(); ++i)
    	{
    		if (Zahlen[i] > Zahlen[++i])
    			tausch(Zahlen[i], Zahlen[++i]);
    	}
    
    	for (int i = 0; i != Zahlen.size(); ++i)
    		std::cout << Zahlen[i] << " ";
    }
    

    Die Fehler liegen in der for-Schleife bei 'Zahlen[i]' und ich weiß nicht wie ich den Fehler korrigieren könnte.

    Weiterhin danke für eure Hilfe.

    -Skotchy


  • Mod

    Skotchy schrieb:

    Die Fehler liegen in der for-Schleife bei 'Zahlen[i]' und ich weiß nicht wie ich den Fehler korrigieren könnte.

    du kannst den fehler korrigieren indem du rausfindest was "++i" bedeutet.



  • rapso schrieb:

    Skotchy schrieb:

    Die Fehler liegen in der for-Schleife bei 'Zahlen[i]' und ich weiß nicht wie ich den Fehler korrigieren könnte.

    du kannst den fehler korrigieren indem du rausfindest was "++i" bedeutet.

    Bedeutet das nicht einfach 'i + 1'?


Anmelden zum Antworten