ein kleines spiel



  • Ich habe jetzt endlich mal angefangen was zu programmieren und würde mich freuen, wenn jemand drüber guckt. Ob das seine Richtigkeit hat und was so die gängigen Arten beim Programmieren sind. Ich habe leider nur Internetforen als einzige Ansprechpartnern in diesen Dingen.

    Zum "Spiel" ist zu sagen, das es eine Art Tamagotchi sein soll, wo mit jeder Aktion der Magier älter wird. Das Ziel ist es bevor er 60 wird die Weltherrschaft zu erlangen. Es ist noch lange nicht fertig.

    #include <iostream>
    #include <windows.h> 
    #include "all.h"
    
    using namespace std;
    
    int main () {
    	int haus = 0, jahre = 0, arbeit = 0, geld = 0, mana = 0, intelligenz = 0, fitness = 0, zufriedenheit = 0, magiebegabung = 0, tagesp;
    
    	for(jahre; jahre < 60;) {
    
    	ausgabe (haus, jahre, arbeit, geld, mana, intelligenz, fitness, zufriedenheit, magiebegabung);
    
    	cin >> tagesp;
    
    		switch (tagesp) {	
    			case 1:  
    				geld = arbeiten (arbeit, geld);
    				jahre ++;
    				break;
    			case 2:   
    				cout <<"falsche Eingabe"    ; 
    				break;
    			case 3:  
    				cout <<"falsche Eingabe"    ; 
    				break;
    			case 4:  
    				cout <<"falsche Eingabe"   ; 
    				break;
    			case 5:  
    				cout <<"falsche Eingabe"   ; 
    				break;
    			case 6:   
    				cout <<"falsche Eingabe"   ; 
    				break;
    			case 7:   
    				cout <<"falsche Eingabe"    ; 
    				break;
    		}
    
    	}
    
    	return 0;
    }
    
    #include <iostream>
    #include "all.h"
    
    using namespace std;
    
    void ausgabe(int haus,int jahre,int arbeit,int geld,int mana,int intelligenz,int fitness,int zufriedenheit,int magiebegabung){
    	cout << "o   o                      o   o               " << endl
    		 << "|L /|                      |L /|               " << endl
    		 << "| O |  oo o-o   oo         | O |  oo o--o o-o  " << endl
    		 << "|   | | | |  | | |         |   | | | |  | |-'  " << endl
    		 << "o   o o-o-o  o o-o         o   o o-o-o--O o-o  " << endl
    		 << "                                        |      " << endl
    		 << "                                     o--o      " << endl << endl;
    
    	cout << "#-------------------------------------------#"  << endl
    		 << "|J:"<<jahre<<"  |G:"<<geld<<"  |M:"<<mana<<"  |I:"<<intelligenz
    		 <<"  |F:"<<fitness<<"  |Z:"<<zufriedenheit<<"  |MB:"<<magiebegabung<< endl << endl;       
    
    	cout << "1 - Arbeiten gehen" << endl
    		 << "2 - Ein Buch Lesen" << endl
    		 << "3 - Muskeln Trainiren" << endl
    		 << "4 - Freizeit genießen" << endl
    		 << "5 - Magie experimente" << endl
    		 << "6 - Magie wirken" << endl
    		 << "7 - Infos" << endl
    		 << "Was möchtest du tun" << endl;
    
    }
    
    #include <iostream>
    #include <windows.h> 
    #include "all.h"
    
    using namespace std;
    
    int arbeiten(int arbeit, int geld){
    
    		switch (arbeit) {	
    			case 0:  
    				cout <<"Als Strassenfeger ist dein verdienst gering" << endl << "Gold + 2" << endl;
    				geld = geld +2;
    
    				Sleep (3000); 
                    system("cls"); 
    				break;
    			case 1:   
    				cout <<"Als Schreiner ist dein verdienst gering" << endl << "Gold + 3" << endl;
    				geld = geld +3;
    
    				Sleep (5000); 
                    system("cls"); 
    				break;
    			case 2:  
    				cout <<"Als Metzger ist dein verdienst gering" << endl << "Gold + 5" << endl;
    				geld = geld +5;
    
    				Sleep (5000); 
                    system("cls"); 
    				break;
    			case 3:  
    				cout <<"Als Metzger ist dein verdienst gering" << endl << "Gold + 6" << endl;
    				geld = geld +6;
    				Sleep (5000); 
                    system("cls"); 
    				break;
    			case 4:  
    				cout <<"Als Politiker ist dein verdienst sehr gut " << endl << "Gold + 9" << endl;
    				geld = geld +8;	
    				Sleep (5000); 
                    system("cls"); 
    				break;
    				}
    	return geld;
    
    }
    
    void ausgabe(int haus,int jahre,int arbeit,int geld,int mana,int intelligenz,int fitness,int zufriedenheit,int magiebegabung);
    int arbeiten(int arbeit, int geld);
    

  • Mod

    Ein Königreich für Objektorientierung!

    Weitere Tipps:
    -Codeduplikation vermeiden. Bei deinem Monsterswitch sind 2 Zeilen immer gleich (ich nehme mal an, das eine Sleep(3000) ist eine direkte Folge davon, dass du zu oft das gleiche tippen musstest und das keine Absicht war).
    -include "all.h" klingt gefährlich. Lass mich raten: Ein Header, in dem alle Header stehen? Falls ja: Tu das nicht. Binde jeweils genau die Header ein, die du brauchst. Keinen mehr, keinen weniger.
    -Und zu guter Letzt: endl macht was anderes als du denkst. '\n' ist ein Zeilenumbruch. endl ist '\n' + flush. Und für einen sinnvollen Anwendungsfall eines flush muss man schon arg konstruierte Beispiele heranziehen. Somit ist auch endl eher konktraproduktiv (denn ein flush kostet oft).

    Das sind allesamt stilistische Tipps (nach echten Fehlern habe ich gar nicht gesucht, da dein Programm nicht übersetzbar ist), aber das sind keine willkürlichen Stilfragen, sondern solche, die dir das Leben wesentlich einfacher (und deine Programme fehlerfreier) machen, wenn du dich daran hältst.



  • Das ist Betrug!

    case 4:  
                    cout <<"Als Politiker ist dein verdienst sehr gut " << endl << "Gold + 9" << endl;
                    geld = geld +8;
                    Sleep (5000);
                    system("cls");
                    break;
                    }
    

  • Mod

    troll1337 schrieb:

    Das ist Betrug!

    case 4:  
                    cout <<"Als Politiker ist dein verdienst sehr gut " << endl << "Gold + 9" << endl;
                    geld = geld +8;
                    Sleep (5000);
                    system("cls");
                    break;
                    }
    

    Das ist übrigens auch noch ein exzellenter Punkt, den ich nicht erwähnt habe. Diese Art von Fehler kann man auch sehr gut vermeiden. Wie, das überlasse ich dem Threadersteller als Übungsaufgabe (es gibt viele Lösungen).



  • Kleines 1x1 der Spiele-Programmierung / effektiver C++-Programmierung:

    - Keine For-Loop nehmen, sondern eine while-loop mit entsprechendem Abbruch-Kriterium. Wenn du dich entscheidest das dein Magier älter als 60 werden darf, oder du ein ganz anderes Spiel machen willst, wirst du so oder so eine while-loop brauchen. Zum Anfang tuts auch ein

    int alter = 0;
    while(true) {
      [...]
    
      if( *irgendeine-bedingung* )
        ++alter;  
    
      if( alter > 60)
        break;
    }
    

    - Nicht alles in Main packen, Objektorientiert oder Funktional arbeiten. Bei einem solchen simplen Spiel kann man gerne auch nur mit Funktionen und globalen Variablen arbeiten, aber ich empfehle von Anfang an alles OO zu designen. Erzeuge eine Klasse 'Game', welche alle notwendigen Funktionen und Member-Variablen enthält. Später können weitere Klassen für spezielle Aufgaben hinzukommen. Wenn du ein 'größeres' Spiel programmieren willst, wirst du dankbar sein es direkt OO zu designen.

    - Lerne korrekte Funktionale Programmierung. Eine Funktion sollte immer nur eine Aufgabe haben und nicht viele. Deine Funktion 'Ausgabe' nimmt zwei Funktionen war, spalte sie lieber in zwei Funktionen auf zur Anzeige der Daten und eine weiter Funktion zur Anzeige des Menüs und der entgegennahme des User-Input. Wenn dieses Prinzip immer an, dies macht Code modularer und besser wartbar.

    - Monster-Konstrukte vermeiden. Deine Funktion 'ausgabe' hat eine riesen Signatur. Sowas ist im Programmier-Alltag nicht tragbar. Durch ein OO Design mit Klassen-Membern würde sich das von selbst erübrigen. Deine Switch-Anweisung enthält redundanten Code. Dieser kann problemlos nach der switch durchgeführt werden. Wenn es geht auf switch-Anweisungen verzichten und lieber mit std::map, std::function und std::bind arbeiten (fortgeschrittenes Thema, aber eine sehr gute Programm-Architektur).

    - KEIN using namespace <irgendein-namespace>! Namespaces existieren aus gutem Grund. Und gerade der Namespace std ist nicht gerade klein und müllt den globalen Namespace zu. Wenn du funktional programmierst wirst du dann schnell auf Namenskonflikte stoßen. Besser direkt lernen mit namespaces zu arbeiten. Und wenn mal ein Namespace einen langen namen hat, benutze einen namespace alias. Alles ist besser als ein using...

    - Apropos Namensgebung: Mische keine deutschen begriffe in den Code. Dies macht den Code nur für deutsche verständlich. Verwende nur englische Begriffe und eigne dir ordentliches Camel-Casing für die Namen an. Hier gibt es zwar mehrere 'Wahrheiten', verwende diejenige welche dir am besten gefällt und verwende sie Konsequent.

    - Versuche auf den Windows.h-Header zu verzichten. Enthält viel komisches und vor allem plattform-abhängiges zeug. Eine der großen Stärken von Standard-C++ ist, das es OS-Unabhängig ist. Es gibt immer einen Standard-C++ weg etwas zu erreichen, keine Plattform-Header notwendig. Hier geht es wohl im speziellen um die Funktionen Sleep() und System(). Beide sind schlecht! Nutze den chrono-header und std::chrono für Zeit-Funktionen und vermeide System() komplett ( http://www.cplusplus.com/articles/j3wTURfi/ )

    - Vermeide Bolier-Plate Code. Bspw. das return 0; am Ende der main-function. Erstens nicht ganz korrekt ( return EXIT_SUCCESS ist genauer ) und zweitens schlicht unnötig da es der Compiler sowieso hinzufügt wenn es fehlt.

    Ach und prinzipiell, lerne erst mal effektives und modernes C++ (C++11!, OO-Design, function-binding) bevor du mit Spiele-Programmierung anfängst. Diese Grundlagen werden dich danach durch jedes Spiel begleiten. Und wenn du dann Lust hast statt einfacher Text-Spiele (welche übrigens garnicht mal so einfach zu programmieren sind 😉 ) auch mal 2D Grafik zu programmieren kann ich dir wärmstens SFML Game Development empfehlen. Modernes C++11 mit einer modernen C++ Multimedia API und sehr gut verständlich.

    Ich hoffe ich habe einem angehenden Spiele-Programmierer geholfen 😉



  • SeriousITGuy schrieb:

    while(true)
    

    Sieht aus wie Pascal.



  • SeriousITGuy schrieb:

    Wenn es geht auf switch-Anweisungen verzichten und lieber mit std::map, std::function und std::bind arbeiten (fortgeschrittenes Thema, aber eine sehr gute Programm-Architektur).

    Nein. switch ist allgemein vollkommen legitim. Aber std::function / std::bind würde ich mir lieber drei Mal überlegen.

    SeriousITGuy schrieb:

    - KEIN using namespace <irgendein-namespace>! Namespaces existieren aus gutem Grund. Und gerade der Namespace std ist nicht gerade klein und müllt den globalen Namespace zu. Wenn du funktional programmierst wirst du dann schnell auf Namenskonflikte stoßen. Besser direkt lernen mit namespaces zu arbeiten. Und wenn mal ein Namespace einen langen namen hat, benutze einen namespace alias. Alles ist besser als ein using...)

    std::placeholders ?

    SeriousITGuy schrieb:

    Erstens nicht ganz korrekt ( return EXIT_SUCCESS ist genauer ) und zweitens schlicht unnötig da es der Compiler sowieso hinzufügt wenn es fehlt.

    §3.6.1.5 schrieb:

    A return statement in main has the effect of leaving the main function (destroying any objects with automatic
    storage duration) and calling std::exit with the return value as the argument. If control reaches the end
    of main without encountering a return statement, the effect is that of executing
    return 0;

    SeriousITGuy schrieb:

    SFML Game Development

    ...was übrigens in C++03 geschrieben ist.



  • switch ist natürlich korrektes C++, aber in der Spiele-Programmierung sollte man von switch eher abstand nehmen, da es selbst in kleinen Projekten schnell zu Monster-Konstrukten kommt die nicht mehr wartbar sind. Mit std::bind, std::function und einer std::map die die function-pointer basierend auf game-events dispatched fährt man auf Dauer wesentlich besser, und deutlich flexibler sogar. Gerade in Text-Spielen habe ich diese Erfahrung gemacht.

    Ich kenne kein Argument std::bind und std::function nicht zu verwenden (außer dieser ominöse VS2013 Compiler-Bug, dieser lässt sich mit einem Lambda aber umgehen).

    std::placeholders ist ein legitimer gebrauch eines usings, das habe ich vergessen 😉 Aber sonst nutze ich es nicht, und man sollte es auch nicht.

    Und nein SFML Game Development nutzt C++11 Techniken wie std::unique_ptr und std::bind.

    Kommen jetzt noch mehr konstruierte Gegenargumente oder wars das?



  • SeriousITGuy schrieb:

    switch ist natürlich korrektes C++, aber in der Spiele-Programmierung sollte man von switch eher abstand nehmen, da es selbst in kleinen Projekten schnell zu Monster-Konstrukten kommt die nicht mehr wartbar sind. Mit std::bind, std::function und einer std::map die die function-pointer basierend auf game-events dispatched fährt man auf Dauer wesentlich besser, und deutlich flexibler sogar. Gerade in Text-Spielen habe ich diese Erfahrung gemacht.

    Deine Erfahrung ist falsch. switch hat legitime Anwendungsfaelle, genauso wie Makros und goto.

    SeriousITGuy schrieb:

    Ich kenne kein Argument std::bind und std::function nicht zu verwenden (außer dieser ominöse VS2013 Compiler-Bug, dieser lässt sich mit einem Lambda aber umgehen).

    Ich keine kein Argument, std::bind und std::function zu verwenden. Lambdas sind besser.



  • Na irgendwo müssen die Lambdas doch hin, da eignet sich oft eine std::function?



  • Aeh, frag mich nicht, wieso ich da std::function dazugeschrieben hab. std::function ist nett, std::bind nicht.



  • Kellerautomat schrieb:

    Ich keinekenne kein Argument, std::bind und std::function zu verwenden. Lambdas sind besser.

    Compiler der keine Lambdas kann?



  • Kellerautomat schrieb:

    SeriousITGuy schrieb:

    switch ist natürlich korrektes C++, aber in der Spiele-Programmierung sollte man von switch eher abstand nehmen, da es selbst in kleinen Projekten schnell zu Monster-Konstrukten kommt die nicht mehr wartbar sind. Mit std::bind, std::function und einer std::map die die function-pointer basierend auf game-events dispatched fährt man auf Dauer wesentlich besser, und deutlich flexibler sogar. Gerade in Text-Spielen habe ich diese Erfahrung gemacht.

    Deine Erfahrung ist falsch. switch hat legitime Anwendungsfaelle, genauso wie Makros und goto.

    SeriousITGuy schrieb:

    Ich kenne kein Argument std::bind und std::function nicht zu verwenden (außer dieser ominöse VS2013 Compiler-Bug, dieser lässt sich mit einem Lambda aber umgehen).

    Ich keine kein Argument, std::bind und std::function zu verwenden. Lambdas sind besser.

    Wow falsche Erfahrung, wusste gar nicht das es sowas gibt. Da lernt man jeden Tag noch was neues hier 😕

    Das switch legitime Anwendungsfälle hat, habe ich nicht bestritten. ABER im vorliegenden Beispiel (und in diesem Kontext habe ich es gemeint) des Themenerstellers sind switch-Anweisungen kontraproduktiv zur Code-Wartbarkeit und Erweiterbarkeit. Wenn du das nicht glauben willst schreibe mal einen Zork-like Command-Parser (Von mir aus vereinfacht auf <verb> <active object> <passive object>) mit nem switch, viel Spaß dabei.
    Ein std::mapstd::string,std::function - Konstrukt ist hier deutlich eleganter und flexibler von der Code-Architektur her. Und ob man dabei std::bind oder Lambdas verwendet ist egal, das Ergebnis ist das Gleiche. Und warum sollen Lambdas dann bitte besser sein als std::bind?
    Ich verwende täglich beides und habe (bis auf den VS2013 Compiler-Bug mit std::bind) noch nie Probleme damit gehabt. Der Code leidet unter keiner der beiden Varianten, im Gegenteil ich finde std::bind sogar leserlicher als Lambdas (subjektiv!).

    Eine weiterführende Erklärung warum std::bind schlechter als Lambdas für die Zwecke von Function Pointers sein soll wäre nett, dann lernen vielleicht ein paar was dazu.



  • hustbaer schrieb:

    Kellerautomat schrieb:

    Ich keinekenne kein Argument, std::bind und std::function zu verwenden. Lambdas sind besser.

    Compiler der keine Lambdas kann?

    Sowas gibts noch? 🤡

    SeriousITGuy schrieb:

    Das switch legitime Anwendungsfälle hat, habe ich nicht bestritten. ABER im vorliegenden Beispiel (und in diesem Kontext habe ich es gemeint) des Themenerstellers sind switch-Anweisungen kontraproduktiv zur Code-Wartbarkeit und Erweiterbarkeit.

    SeriousITGuy schrieb:

    switch ist natürlich korrektes C++, aber in der Spiele-Programmierung sollte man von switch eher abstand nehmen, da es selbst in kleinen Projekten schnell zu Monster-Konstrukten kommt die nicht mehr wartbar sind.

    Ja, war voll konkret und spezifisch fuer das Problem des TE gemeint!!!!!!111

    Mal ganz ehrlich, ich hab noch nie jemanden gesehen, der es geschafft hat, mit so wenigen Forenposts so viel Bullshit zu verzapfen. Ich gratuliere dir zu dieser Kunst. 👍


  • Mod

    Mal ganz ehrlich, ich hab noch nie jemanden gesehen, der es geschafft hat, mit so wenigen Forenposts so viel Bullshit zu verzapfen.

    Hey, den Rekord dafür halte ich!

    switch ist natürlich korrektes C++, aber in der Spiele-Programmierung sollte man von switch eher abstand nehmen, da es selbst in kleinen Projekten schnell zu Monster-Konstrukten kommt die nicht mehr wartbar sind.

    Für switch -Konstrukte kann der Compiler ggf. eine jump table erstellen. Oder (binäre) Intervallschachtelung anwenden. Also recht performant.



  • Wäre interessant ab wie vielen Cases ein Funktionszeiger schneller wird als switch .



  • SeriousITGuy schrieb:

    switch ist natürlich korrektes C++, aber in der Spiele-Programmierung sollte man von switch eher abstand nehmen, da es selbst in kleinen Projekten schnell zu Monster-Konstrukten kommt die nicht mehr wartbar sind.

    Dann lern halt programmieren.

    SeriousITGuy schrieb:

    Ich kenne kein Argument std::bind und std::function nicht zu verwenden (außer dieser ominöse VS2013 Compiler-Bug, dieser lässt sich mit einem Lambda aber umgehen).

    Performance ist kein Argument?

    SeriousITGuy schrieb:

    Aber sonst nutze ich es nicht, und man sollte es auch nicht.

    Wie du aus meinem Gegenbeispiel hättest entnehmen sollen ist Verallgemeinerung nicht gut.

    SeriousITGuy schrieb:

    Und nein SFML Game Development nutzt C++11 Techniken wie std::unique_ptr und std::bind.

    Ups, das ist ein Buch und nicht ein Link zu SFML (das in C++03 geschrieben ist, was ich auch eig. meinte). Mein Fehler.

    SeriousITGuy schrieb:

    Kommen jetzt noch mehr konstruierte Gegenargumente oder wars das?

    Soll das jetzt lustig sein?

    Kellerautomat schrieb:

    Mal ganz ehrlich, ich hab noch nie jemanden gesehen, der es geschafft hat, mit so wenigen Forenposts so viel Bullshit zu verzapfen. Ich gratuliere dir zu dieser Kunst. 👍

    👍



  • Not so serious schrieb:

    SeriousITGuy schrieb:

    switch ist natürlich korrektes C++, aber in der Spiele-Programmierung sollte man von switch eher abstand nehmen, da es selbst in kleinen Projekten schnell zu Monster-Konstrukten kommt die nicht mehr wartbar sind.

    Dann lern halt programmieren.

    Er hat Recht. Schade, dass dir kein Gegenargument einfällt.

    Not so serious schrieb:

    Performance ist kein Argument?

    Nicht bevor du mir zeigst, dass bind/function so viel langsamer ist, dass es zum problematischen Bottleneck im ganzen Spiel wird. Wenn du das schaffst, "dann lern halt programmieren"


Anmelden zum Antworten