Konsolenspiel - Snake Race(v2.0)



  • Und die Funktion werde ich dann auch so machen, wie du gesagt hast

    Das waren nur Beispiele, schau den Code durch und überlege dir, was du doppelt und dreifach machst (wie wäre es z.B. mit einer Funktion erstelleSpielfeld(anzahlBrunnen)? um ein weiteres Bsp. zu nennen). Es geht ja darum, dass du merkst für was man Funktionen brauchen könnte um den Code übersichtlicher zu machen.



  • Habs mir nur kurz angesehen, vielleicht schaue ich später noch mal über den Code..
    Aber nur so nebenbei.. sind die CS-Sounds eigentlich frei? 😉



  • Keine Ahnung, die sind noch vom alten 1.6 (das ist von 2003). Aber ich glaube das stört wohl niemanden 😉



  • Soo, falls es noch jemanden interessiert, ich arbeite jetzt wieder an dem Projekt weiter.
    Ich hab die Ratschläge von lustig berücksichtigt und erstmal alle #define direktiven durch const und Funktionen ersetzt. Außerdem habe ich statt zb. 'Aj' einfach 'j' genommen, womit die Konstanten dafür nutzlos werden.
    Außerdem bin ich nun dabei, für jede Modus-Datei eine Header zu schreiben, die den jeweiligen Bewegen-Funktion-Prototyp beinhaltet. Ich konnte keine allgemeine Funktion schreiben, da bei jedem Modus ja in eine andere Highscore-Datei geschrieben wird, ander Sounds verwendet werden und noch diverse andere Kleinigkeiten anders ablaufen. Die Definition dieser 'speziellen' Bewegungsfunktion schreibe ich dann gleich mit in die selbige Datei 'Modus_X.cpp', Ich habs nämlich so gelernt, dass in Headerdateien niemals Funktions-Definitionen stehen.
    Ich werd das dann morgen oder so hochladen, eben wenn ich fertig bin.
    Und ich werd das ganze mal bei sourceforge.net hochladen, file-upload gefällt mir nicht besonders.

    ps. Ich hab schon fast 70 Downloads, das hätte ich nicht erwartet 😉

    EDIT: Mir ist gerade noch etwas aufgefallen. Wenn ich eine Funktion "void bewegen(...)" schreiben will, muss ich fast 10 Variablen übergeben. Ich werde wohl eine Struktur Player oder so schreiben müssen, wo das dann gespeichert wird.



  • Ach was, mir ist es jetzt egal, wie lange das dauern wird: ich schreibe mir jetzt eine geeignete Klasse und mache das ganze Objektorientiert. Wenn mir dann etwas auffällt oder ich Fragen hab, werde ich mich hier wieder melden 😉



  • Also endlich, ich habs dann doch noch geschafft. Snake Race ist nun Objektorientiert^^
    Jetzt brauch ich natürlich wieder eure Hilfe. Ich muss natürlich wissen, was ihr von meinem Code haltet. Die Größe hat sich mal eben halbiert, und das bei gleichbleibender Leistung.
    Ich hab jetzt 2 Klassen: Einmal Spiel (Spiel.h und Spiel.cpp) und Feld (Feld.h und Feld.cpp).
    Wie hätte man es besser lösen können?
    Und wie findet ihr den Codestil, hab ich was falsch gemacht?
    Und dann eben die selben Fragen wie vorher auch.
    Ich hab das ganze jetzt nicht auf sourceforge.net hochgeladen, sondern wieder auf file-upload.net:
    Snake Race feat cs 1.8
    Ich freu mich schon auf Rückmeldungen im Bezug auf den Quellcode 😉



  • Lustiges Teil 😉 Aber mir viel was auf

    Die Highscore als Klartext in einer exe halte ich eher für gewöhnungsbedürftig.
    Nehm lieber sowas wie .dat und speichere die Daten binär.
    Außerdem gibts da noch ein Formatierungsproblem mit dem Datum >>> 030.1.2011

    Es gibt einen Grafikbug bei Modus 1, nach dem das Spiel gewonnen wurde und
    der Name eingegeben wurde.
    Bestätigt man das weitere Spielen mit "n" so gibt es lustige Effekte. Die Schlange
    beginnt trotzdem von neuem

    > auch bei Modus 2

    Modus 3: Nach dem einsammeln des ersten Objekts ist das Spiel bereits zu Ende

    Code hab ich mir allerdings nur mal grob angeschaut aber mir viel auf:

    1. modus_eins bis modus_vier
    sieht mir bissl nach Copy-Paste aus und abändern, was anders ist.
    Hier bieten sich ebenfalls Klassen / Vererbung an. z.B. Basisklasse Modus und dann Kindklassen ModusFoo und ModusBar.

    2. es gibt viele identische Codesegmente, die überall auftauchen. Zentralisiere solche Sachen und nutze Funktionen.
    Beispiel:

    system("cls");
                        make_rang();
                        gotoxy(1, 1);
                        std::cout << "Gebe deinen Namen ein (keine Leerzeichen und max. 10 Zeichen): "; //gewinner bekannt machen
                        std::string gewinner;
                        std::cin >> gewinner;
                        set_name(gewinner);
                        if(gewinner.size() > 10) {
                            color_cout(10, 30, GELB, "Name zu gross!");
                            Sleep(1000);
                            while(kbhit()) getch();
                            continue;
    

    Schreib dir hier eine Funktion enterName, dann musst du diesen Schnipsel nicht überall reinkopieren 😉

    3. Entscheide dich für eine Sprache
    -Deutsch
    -Englisch
    -Denglisch (natürlich nicht :D)

    4. vielleicht an der ein oder anderen Stelle sinnvollere Namen wie get_durch() << ??? denn ich muss lange suchen, was "durch" ist

    5. Bei Übergabe von Objekten an Klassen nutze Referenzen
    von char Feld::get_zeichen(const Spiel para) const
    nach char Feld::get_zeichen(const Spiel &para) const

    Mach dir vielleicht mal ein Klassendiagramm. Was brauchst du für Klassen. Was sollen diese für Daten kapseln. Was muss wo drauf zugreifen. Quasi ein Konzept, bevor du anfängst wild umzubauen.



  • Erst mal danke für die Anwort!

    Highscore als Klartext in einer .exe hab ich deswegen gemacht, damit man die Highscores nicht so einfach abändern kann (sowas wie rechtsklick -> öffnen mit geht bei einer .exe ja nicht), und außerdem weiß ich auch nicht, was binär speichern bedeutet^^

    Der Formatierungsfehler war ein kleiner Tippfehler, den hab ich eben behoben.

    Einen Grafikfehler gibts bei mir nicht 😕
    Ich hab jetzt einfach mal ein break dabei eingefügt, und noch einige Dinge so abgeändert, dass es funktionieren sollte.

    Zu 3: Sowas aber auch :D:D Ich war noch beim debuggen, und weil ich nicht jedes mal 15 Punkte einsammeln wollte hab ich die Punkte gleich mal auf 14 gesetzt, damit ich sehen konnte, ob alles funktioniert. Ist jetzt wieder auf 0 😉

    Zum Code:

    Naja es sind eben viele Kleinigkeiten die geändert werden, sodass es schwierig für mich war, das ganze nur einmal zu schreiben. Da ich jetzt nicht so ein Profi mit Klassen bin, ist mir das noch zu kompliziert.

    Gut, die Variablennamen sind vielleicht nicht immer beste Wahl - statt y_minus hätte man auch stumpf RECHTS benutzen können, aber was solls. Darauf achte ich dann beim nächsten mal 😉

    Eigentlich hab ich immer auf die richtige Verwendung von const und Referenzen geachtet, ich kann das aber auch mal übersehen haben.

    Ich hab das geupdatete nochmal hochgeladen, tritt der Grafikfehler bei dir immer noch auf?
    Link: Download



  • Incocnito schrieb:

    damit man die Highscores nicht so einfach abändern kann (sowas wie rechtsklick -> öffnen mit geht bei einer .exe ja nicht)

    😃 Wo hast du das denn gelesen. Öffne mal notepad und zieh des Teil per drag&drop rein 😉

    Ja, der Grafikfehler ist bei mir nun behoben ... löblich löblich.
    Dann bin ich mal auf Version 2.0 gespannt



  • Ich hab nochmal ordendlich optimiert. Die main.cpp ist von 16kB auf 6kB gesunken und in jeder modus_x.cpp - datei stehen nun weniger als 200 zeilen 😃
    Ohne eure Hilfe hätt' ich das nicht geschafft.
    Ich hab also nochmal einige neue Funktionen geschrieben zu so ziemlich allem, was doppelt vorkam. Wenn ihr noch irgendwas findet, was doppelt vorkommt und woraus man eine Funktion basteln könnte, wär das wirklich cool. Mal sehen, ob ihr noch irgendwas findet, es ist jetzt ja schon extrem wenig geworden ^^

    Ach ja, natürlich kann man leicht eine exe im Editor öffnen, ich weiß das schon. Nur wissen das eben viele Computer-Noobs nicht, und deswegen ist es wenigstens ein kleiner Schutz. Außerdem ist die Endung ja prinzipiell egal - und wenn die exe in diesem Fall das leichte rumeditieren um einige Sekunden verzögert, warum nicht^^
    Ansonsten würde ich gerne wissen, wie ich das "binär" speichern sollte, und wie ich das dann wieder lesen kann.

    Hier der aktuelle Downloadlink: Download



  • Zu Dateien in C++ gibts auch einen FAQ Eintrag, da steht auch wie man eine Datei binär öffnet / liest / schreibt.

    http://www.c-plusplus.net/forum/39469



  • Ich hab jetzt nochmal wieder ordentlich was geändert und alles erreicht, was ich in Version 1.8 erreichen wollte.

    Ich werde dann weiterhin etwaige Aktualisierungen und neue Versionen hier posten, und dann könnt ihr auch noch Kritik/Verbesserungsvorschläge zum Spiel äußern. Nachdem ich nun tagelang mit dem Code beschäftigt war, wirds Zeit wieder was neues zu adden 😉
    Den neuesten Downloadlink findet ihr dann immer auf der ersten Seite dieses Threads.
    Die neueste Version mit dem umgekrempelten Menu findet ihr auch dort.



  • So, Version 1.9 ist nun fertig.

    Es gibt nun Items, die einem Spielvorteile verschaffen können. Außerdem wurde das Menu komplett umgekrempelt und benutzerfreundlicher gestaltet.
    Was geändert wurde steht aber auch noch mal im Programm beim ersten Start und natürlich in der Versions.log. Downloadlink auf der ersten Thread-Seite zu finden.

    Viel Spaß^^



  • Ich habe mal kurz über den neuen Sourcecode geschaut (naja, über einen Teil zumindest 😉 ).
    Erst mal Kompliment, das sieht schon viel aufgeräumter auf.

    2 Dinge fallen trotzdem auf. Du hast jetzt eine Klasse geschrieben für dein Spiel. Im Moment ist das aber vom Prinzip her eher eine Struktur mit ein paar Hilfsfunktionen. Du hast für _jede_ Variable eine get und set funktion, die nichts anderes machen als den Wert der Variable ohne überprüfung zu setzen bzw. zurückzugeben.
    So könntest du ja gerade so gut alle Variablen public machen und hättest dir eine Menge Schreibarbeit gespart.

    Ein Beispiel was z.B. verändert werden könnte.

    drei.set_points(drei.get_points()+1);
                gotoxy(13, 55);
                cout << drei.get_points() << " von 15 Punkten";
    

    Immer wenn du die Punkte veränderst willst du auch die Punkteanzeige am Bildschirm updaten. Lass das doch deine Klasse (bzw. die set Funktion) machen. Die Set Funktion würde dann immer wenn die Punkte geändert werden, diese auch an die entsprechende Position am Bildschirm schreiben. So weisst du dass die Anzeige immer mit der Punktzahl übereinstimmt.
    Evt. sollte die set Funktion sogar noch mehr machen, z.B. überprüfen ob du schon gewonnen hast. Wenn ja kannst du ja true zurückgeben sonst false.
    Ausserdem bietet es sich auch an eine erhoehe_Punkte Funktion zu schreiben, wenn du öfters so etwas wie oben machst.
    Oder

    zwei.setze_richtung(c);
            } //if(kbhit())
    
            if(!zwei.get_x_plus()  && !zwei.get_y_plus()
            && !zwei.get_x_minus() && !zwei.get_y_minus()) continue;
    

    wie wäre es, wenn setze_richtung die überprüfung gleich durchführt und das Ergebnis zurückliefert?
    Das gilt nicht nur für die Spiel Klasse sondern für alle, das zieht sich durch den ganzen Code... z.B. auch wenn du den Spieler auf ein Feld setzt machst du jede Menge abfragen (ist da ein Brunnen? Spieler nicht zeichnen. Ist da eine Wand? Spieler vor der Wand zeichnen. etc.). Wäre es nicht einfacher, wenn du eine Funktion hättest die all das erledigt sobald du die Position des Spielers änderst (und dann halt zurückgibt, ob er noch lebt).

    Zweiter Punkt: Deine modus1, modus2, modus3, etc. files sind fast identisch. Da brauchst du eigentlich nur eine Funktion, bei dem du das Spiel je nach Modus mit unterschiedlichen Start / Gewinn Werten initialisierst.



  • Ich find das Spiel gut! Sourcecode hab ich nur überflogen. Auslachen lassen musst Du dich deswegen keinesfalls!

    Tipps/Anmerkungen/Prinzipienreiterei:

    • Verwende enums (z.B.: rest.h, "farben für die color-funktion")
    • Vermeide Wiederholungen (Ja, damit wiederhole ich nur, was mein Vorredner schon sagte^^)
    • in den einzelnen Modus-Funktionen setzt Du jeweils den random-seed. Mach das doch einmal ganz vorne in main(), dann brauchst Du das im ganzen Programm nicht wieder.
    • Das zusammenstückeln der Strings wie z.B. in rest::hilfe_modus ist unästhetisch und macht den Code nicht lesbarer. Ich glaube, derartige Optimierung ist in einem solchen Programm an dieser Stelle etwas übertrieben.
    • Bei den get/set-Funktionen kann ich meinem Vorredner nur zustimmen.
    • Es wäre gut, wenn Du deinen Einrückstil etwas konsequenter befolgen würdest - das macht den Code lesbarer
    • using namespace std; ist zwar eine Tipperleichterung, aber es hat einen Grund, dass die Funktionen in einen namespace gelegt wurden: Diesen nun in den global namespace auszukippen macht den Vorteil zunichte, dass man bei der Benennung eigener Variablen/Funktionen nicht darum scheren muss, ob es vielleicht eine gleichnamige Funktion in der Standardbibliothek gibt. -> Ich weiß, es ist mühsam, immer std:: davor zu schreiben, aber ich würds mir angewöhnen.
    • int main() fehlt das return 0; am Ende. Ja, (leider) muss man es nichtmehr schreiben, aber ich finds inkonsequent, es wegzulassen.

    mfg
    Mr X



  • Hm das mit den Überprüfungen in den get/set Methoden scheint sinnfoll, das werde ich wohl machen. Gute Idee^^

    Ja, eine Funktion die das ganze zeichnet und überprüft ob er noch lebt, bzw. wie er gestorben ist, wäre auch nicht schlecht.

    Zu den verschiedenen Modi:
    Es sind ja nicht nur die Anzahl der Objekte und die Geschwindigkeit die sich unterscheiden. Die Schrittbegrenzung ist anders und in Modus 3 und 4 gar nicht erst vorhanden, die maximale Punktzahl ist anders, bei Modus 4 wird beim verlieren immer der Name abgefragt, bei Modus 1, 2, und 3 nicht, im vierten Modus gibt's ab 20 Punkten noch extra-Objekte, bei jedem Modus-Start wird ein anderer text animiert und andere Farben verwendet uvm.
    Es wäre ziemlich schwierig, eine Funktion zu schreiben, die dann jedes mal überprüfen müsste, welcher Modus gemeint ist (Ist es der vierte? Keine Schrittbgrenzung, kein Punktelimit, Text animieren usw., ist es der zweite? Schrittlimit 800, kein Punktelimmit, den Text animieren usw.).
    Das sind eben alles Kleinigkeiten die in den Modi anders sind.

    Okay, enums könnte ich verwenden, gute Idee.
    Und stimmt, srand() vorzubereiten brauche ich nur einmal. Das mir das nicht aufgefallen ist o.o
    Echt nicht? es sind nämlich nur 2 Wörter die sich unterscheiden, sodass es mir sinnfoll schien, einfach zu überprüfen welcher Modus übergeben wurde.
    Ja, ich bin zwischendurch auf 2 Leerzeichen pro Tab umgestiegen, da hast du recht.
    Das stört mich allerdings schon mit using namespace std;
    Denn bei einem Quellcode mit knapp 500 Zeilen (was ja noch sehr wenig ist) über 100 mal std:: schreiben zu müssen, nur damit ich ein paar Funktionsnamen und Variablennamen selbst definieren kann, scheint mir eigentlich unsinnig. Außerdem, wenn etwas mal schon in std steht, dann mache ich halt den Anfangsbuchstaben groß o.ä. Und außerdem gilt using namespace std; ja nur für die aktuelle Datei.
    Und zu return 0; -> wenn main sowieso 0 zurückgibt wenn mans nicht angibt, wozu sollte ichs dann angeben? 😉

    Also danke für die Tipps, da sind einige wirklich gut bei 🙂



  • Incocnito schrieb:

    Hm das mit den Überprüfungen in den get/set Methoden scheint sinnfoll, das werde ich wohl machen. Gute Idee^^

    Ja, eine Funktion die das ganze zeichnet und überprüft ob er noch lebt, bzw. wie er gestorben ist, wäre auch nicht schlecht.

    Zu den verschiedenen Modi:
    Es sind ja nicht nur die Anzahl der Objekte und die Geschwindigkeit die sich unterscheiden. Die Schrittbegrenzung ist anders und in Modus 3 und 4 gar nicht erst vorhanden, die maximale Punktzahl ist anders, bei Modus 4 wird beim verlieren immer der Name abgefragt, bei Modus 1, 2, und 3 nicht, im vierten Modus gibt's ab 20 Punkten noch extra-Objekte, bei jedem Modus-Start wird ein anderer text animiert und andere Farben verwendet uvm.
    Es wäre ziemlich schwierig, eine Funktion zu schreiben, die dann jedes mal überprüfen müsste, welcher Modus gemeint ist (Ist es der vierte? Keine Schrittbgrenzung, kein Punktelimit, Text animieren usw., ist es der zweite? Schrittlimit 800, kein Punktelimmit, den Text animieren usw.).
    Das sind eben alles Kleinigkeiten die in den Modi anders sind.

    Okay, enums könnte ich verwenden, gute Idee.
    Und stimmt, srand() vorzubereiten brauche ich nur einmal. Das mir das nicht aufgefallen ist o.o
    Echt nicht? es sind nämlich nur 2 Wörter die sich unterscheiden, sodass es mir sinnfoll schien, einfach zu überprüfen welcher Modus übergeben wurde.
    Ja, ich bin zwischendurch auf 2 Leerzeichen pro Tab umgestiegen, da hast du recht.
    Das stört mich allerdings schon mit using namespace std;
    Denn bei einem Quellcode mit knapp 500 Zeilen (was ja noch sehr wenig ist) über 100 mal std:: schreiben zu müssen, nur damit ich ein paar Funktionsnamen und Variablennamen selbst definieren kann, scheint mir eigentlich unsinnig. Außerdem, wenn etwas mal schon in std steht, dann mache ich halt den Anfangsbuchstaben groß o.ä. Und außerdem gilt using namespace std; ja nur für die aktuelle Datei.
    Und zu return 0; -> wenn main sowieso 0 zurückgibt wenn mans nicht angibt, wozu sollte ichs dann angeben? 😉

    Also danke für die Tipps, da sind einige wirklich gut bei 🙂

    Sorry, konnte einfach nicht widerstehen ^^



  • Hä?

    EDIT: Boa, nach über einem Monat seh ich gerade das mir ein rechtschreib-super-gau gelungen ist. Ufff..



  • So, ich hab jetzt Version 2.0 fertig. Änderungen sind (unter anderem):

    -Fenster vergrössert
    -Schwierigkeit wird nun angegeben
    -einzelne Modi verändert/verbessert
    -Items angepasst (auch an die modi)

    Für detailliertere Informationen siehe Einblendung beim ersten Spielstart oder Versions.log.

    Da ich es jetzt endlich geschafft habe, das Fenster der Konsole unter dem GCC zu vergrößern, kann man endlich mal alles erkennen 😉
    Das hat aber leider ein ziemlich unschönes copy & paste Chaos in der main.cpp verursacht. Naja, was will man machen..

    Ich hab den Code nochmal leicht überarbeitet und das 100-mal (exakt) vorkommende std:: gelöscht und überall using namespace std; geschrieben, gegen den Hinweis von Mr X. Den Grund habe ich ja bereits genannt..
    Außerdem hab ich die Farben in eine enum gesteckt.

    Viel Spaß 😉



  • Die massenweisen überflüssigen get/set-Methoden gibts immernoch...

    Und außerdem gilt using namespace std; ja nur für die aktuelle Datei.

    Das ist blödsinn, wenn Du es in Header schreibst (Was Du ja auch getan hast). Dann gilt es nämlich für jede .cpp-Datei, die diesen Header includiert genauso.


Anmelden zum Antworten