Objektkoordinaten 2d Array
-
Photon schrieb:
Hey,
Moin!

Photon schrieb:
Soweit ich es verstanden habe benutzt du die const variable als schutz damit sie nicht ausversehen geändert wird?
So kannst Du es sehen.
Ein "tödliches print()" ist ein Bug, den ich so schon beim Kompilieren finde.Man spricht von "const-correctness" und die finde ich so wichtig, dass ich sie Dir von Beginn an nahelege.
Photon schrieb:
Zu den zwei funktionen. Bei der zweiten ist mir denke ich klar was zu tun ist aber bei der ersten weiss ich nicht so genau. Willst du einfach ur ne print funktionen die sagt held a mit den werten x,y,z greift held b mmit werten x,y,z an? und dann halt in er der nächsten wirklich "kämpfen" lasssen?
In der ersten sollst Du einen Angriff
attacker->attackedschreiben. Den richtigen Angriff. D.h. den Schaden berechnen und evtl.attackedLebenspunkte abziehen.
Aber es ist gar nicht der Inhalt auf den ich hinauswill: ich will nur sehen, dass Du die Funktionssignatur änderst - speziell die Typen der formalen Parameter.Dto. in der zweiten - wobei Du bei der Implementierung hoffentlich auf die erste Funktion zurückgreifst - dann wird's ein Zwei- oder Dreizeiler.
-
Ich habe jetzt eine weile nachgedacht aber ich komme nicht drauf.
Für die erste funktion habe ich das hier verwendet:
void attack(hero attacking, hero& attacked){ attacking.dmg -= attacked.def; if(attacking.dmg>0){ attacked.hp -= attacking.dmg;} }Aber wie du das jetzt mit der zweiten meinst verstehe ich nicht. Was genau soll jetzt noch bei showdown passieren? Ich steh irgendwie auf dem schlauch

-
Photon schrieb:
void attack(hero attacking, hero& attacked){ attacking.dmg -= attacked.def; if(attacking.dmg>0){ attacked.hp -= attacking.dmg;} }Aber wie du das jetzt mit der zweiten meinst verstehe ich nicht. Was genau soll jetzt noch bei showdown passieren? Ich steh irgendwie auf dem schlauch

Hmm...ja, mein Fehler. Ich dachte in
showdown()würde a erst b schlagen und b dann a - aber meinattack()ist Deinshowdown().
Egal:
Wieso übergibst Duattackingper Value?
Wieso ziehst Du vonattacking.dmgab? Verliert der Angreifer (bzw. dessen Kopie) Schadenspunkte?Meine Implementierungen:
void attack(const hero& attacker, hero& attacked){ assert(alive(attacker) && alive(attacked)); int dmg = attacked.def < attacker.dmg ? attacker.dmg - attacked.def : 0; attacked.hp = dmg < attacked.hp ? attacked.hp - dmg : 0; } void showdown(hero& a, hero& b){ attack(a, b); if(alive(b)) attack(b, a); }Der Ternäre Operator ist wahrscheinlich noch neu für Dich.
Funktioniert ganz einfach:
Bedingung ? Wert_wenn_Bedingung_wahr : Wert_wenn_Bedingung_falsch;Ist klar warum ich die Parameter so deklariere, wie ich sie deklariere?
-
achso ok ähm ja nein also:
void attack(hero attacking, hero& attacked){ attacking.dmg -= attacked.def; //<- dmg - def! if(attacking.dmg>0){ attacked.hp -= attacking.dmg;} //attacked als referenz weil von ihm hp dauerhaft abgezogen werden sollen }Also ich erzeuge für den Angreifer eine kopie damit der schaden nicht dauerhaft beeinflusst wird.
Ich habe mich die ganze zeit gefragt ob es gut ist eine funktion in einen funktion zu packen und war auch schon kurz davor das zu machen aber dann dachte ich mir das es vieleicht zu verschachtelt wird und man wieder leicht den überblick verlieren könnte.
WAs && || usw. machen weiss ich

Allerdings mag ich diese Assertmeldung nicht. Bzw. nich in diesem beispiel weil das ein hero in der rauen welt da draussen stribt ist eine zulässige aktion. Zumindest wenn es nach dem Universum geht.
-
Sry für den doppelpost hier is meine version
#include <iostream> #include <string> #include <cassert> struct hero { std::string name; int dmg, def, hp; }; bool is_alive(hero h) { bool alive = 0<h.hp; /** if(alive) std::cout << h.name << " is alive.\n"; else std::cout << h.name << " is dead.\n"; **/ return alive; } void print(const hero& h){ std::cout << h.name << " (hp:" << h.hp << ", dmg:" << h.dmg << ", def:" << h.def << ')'; } void attack(hero attacking, hero& attacked){ attacking.dmg -= attacked.def; if(attacking.dmg>0){ attacked.hp -= attacking.dmg;} } void showdown(hero& a, hero& b){ while (is_alive(a) && is_alive(b)){ attack(a,b); if (is_alive(b)){ attack(b,a);} print(a); std::cout << "\n"; print(b); std::cout << "\n"; } } int main(){ hero red = { "red", 10, 10, 10 }; hero blue = { "blue", 8, 8, 8 }; showdown(red,blue); }
-
Photon schrieb:
Also ich erzeuge für den Angreifer eine kopie damit der schaden nicht dauerhaft beeinflusst wird.
Guter, richtiger Gedanke. Allein das gesamte struct hero zu kopieren, ist aufwändiger, als über eine Referenz auf const nur lesend zuzugreifen. Siehe mein Beispiel.
Photon schrieb:
Ich habe mich die ganze zeit gefragt ob es gut ist eine funktion in einen funktion zu packen
Das ist absolut üblich. Komplexe Funktionen lassen sich so bequem aus einfachen anderen Funktionen zusammenbauen. Siehe z.B. mein showdown(): 3 Zeilen für einen Schlagabtausch von zwei Helden.
Photon schrieb:
Allerdings mag ich diese Assertmeldung nicht. Bzw. nich in diesem beispiel weil das ein hero in der rauen welt da draussen stribt ist eine zulässige aktion. Zumindest wenn es nach dem Universum geht.
Das assert() steht als erste Zeile in der Funktion. Ich will also nur verhindern, dass auf Tote eingeschlagen wird, bzw. das Tote noch angreifen.
Aha: ich sehe Du hast jetzt einen Kampf auf Leben und Tod daraus gemacht.Ich geh jetzt nochmal einen Schritt weiter.
Bisher istheroein POD - plain old data - sowas benutzt man schon seit 40 Jahren in C.C++ lässt mich zusätzlich zu Daten (name, hp, def, dmg) noch Methoden in ein struct einfügen.
Betrachte z.B. folgenden Code
#include <iostream> #include <string> struct hero { std::string name_; int hp_, dmg_, def_; // default constructor (ohne Parameter) hero() : name_("Unsung hero"), hp_(180), dmg_(80), def_(70) { /* body */ } // Ein Konstruktor mit Parametern hero(std::string name, int hp, int dmg, int def) : name_(name), hp_(hp), dmg_(dmg), def_(def) {} }; void print(const hero& h, std::ostream& out) { out << h.name_ << " (hp:" << h.hp_ << ", dmg:" << h.dmg_ << ", def:" << h.def_ << ')' << '\n'; } int main(){ using namespace std; hero h{"Parzival", 200, 100, 100}; print( h, cout ); hero hh; // default konstruierter Held print( hh, cout ); }Das sieht erstmal wild aus.
Ist allerdings im großen und ganzen unserstruct hero, wie vorher auch (ich habe nur die Daten member umbenannt z.B.name->name_)Diesmal hat hero allerdings 2 Konstruktoren. Konstruktoren sorgen dafür, dass Objekte der Klasse zu der sie gehören von Anfang an in einem definierten Zustand sind. Sieh Dir die Konstruktoren an (Zeilen 8-10 u. 12-14). Ist leicht einzusehen, was da passiert, oder?
In Zeile 24 benutze ich den Konstruktor, der Parameter entgegennimmt. In Zeile 27 benutze ich den ohne Parameter.
Beachte auch, dass jeder Konstruktor noch einen body hat - da könnte noch Code zwischen den geschweiften Klammern stehen - wie in einer Funktion.Fragen bis jetzt?
Schreib Dir vielleicht eine andere Klasse mit ein paar Membern. Schau, ob Du den Konstruktor implementiert bekommst.Das nächste Ziel heisst wohl Methoden - damit wird's richtig spannend.

-
Ok das mit dem kontruktor habe ich denke ich soweit verstanden. aber die printfunktion macht mir zu schaffen ich weiß nicht so genau was ostream da macht und meine nachforchung hat mit auch nicht schlauer gemacht.
So oder so werde ich morgen mal was kleines schreiben um das bisher gelernte anzuweden.

-
Photon schrieb:
aber die printfunktion macht mir zu schaffen ich weiß nicht so genau was ostream da macht und meine nachforchung hat mit auch nicht schlauer gemacht.
Das war eine kleine Gemeinheit, die ich eingebaut habe.
Schauen wir uns die mal an:void print(const hero&, std::ostream&);print()ist also eine Funktion, die nichts zurückgibt und zwei Parameter erwartet.
Der erste Parameter ist eine Referenz aufconst hero.
Der zweite Parameter ist eine Referenz aufstd::ostream.Dieser zweite Parameter gibt das "Ziel" für
print()vor.
std::ostreamist ein Typ aus der Standardbibliothek von C++. Dieser Typ stellt verschiedene Funktionen bereit um in einen Stream zu schreiben. Ein Stream bezeichnet in diesem Zusammenhang einen Strom von Zeichen.
std::coutist vom Typstd::ostreamund wird im Header<iostream>deklariert.
D.h.std::coutist ein vortreffliches Argument für den zweiten Parameter vonprint().
Aber auch jeder andere ostream geht, z.B. Schreiben in eine Datei:#include <iostream> #include <fstream> #include <string> struct hero { std::string name_; int hp_, dmg_, def_; hero() : name_("Unsung hero"), hp_(180), dmg_(80), def_(70) {} hero(std::string name, int hp, int dmg, int def) : name_(name), hp_(hp), dmg_(dmg), def_(def) {} }; void print(const hero& h, std::ostream& out) { out << h.name_ << " (hp:" << h.hp_ << ", dmg:" << h.dmg_ << ", def:" << h.def_ << ')' << '\n'; } int main(){ using namespace std; hero heroes[] = { {"Parzival", 200, 100, 100}, {"Bors", 205, 90, 100}, {"Lancelot", 190, 90, 90}, {}, }; ofstream file{"heroes.txt"}; // ein ofstream ist ein spezieller ostream if( !file ) return -1; // Datei konnte nicht geoeffnet werden for(const hero& h : heroes) print( h, file ); }Hausaufgabe: Schau Dir den Header
<iostream>Deiner Standardbibliothek an.
-
Hey,
ich sitze gerade an dem projekt und habe nun 2 kleine probleme
#include <iostream> struct pos{int x, y;}; struct creep {int hp; char pic;}; creep() : hp(30), pic('o') {} // Warum geht das nicht? ich bekomme fehler //error: expected unqualified-id before ')' token void print (const creep& cr){ std::cout << cr.hp << cr.pic << std::endl; } int main() { creep paul; print(paul); return 0; }Wenn ich jetzt noch pos in die sctruct creep argumente nehme:
#include <iostream> struct pos{int x, y;}; struct creep {pos p; int hp; char pic;}; creep() : pos(5,5) hp(30), pic('o') {} // Wie initialisiert man pos ? void print (const creep& cr){ std::cout << cr.p << cr.hp << cr.pic << std::endl; } int main() { creep paul; print(paul); return 0; }Desweiteren sieht der konstruktor mit werten für mich aus als ob man einfach das struct initialisiert bis auf den part in den geschweiften klammern also
creep paul={5,6,7};So wäre es ja ganz normal bei deinen konstruktor sieht es wie gesagt eigentlich genau so aus in der main ich verstehe nicht so wirklich denn sinn.
Grüße
Photon
-
Photon schrieb:
Hey,
ich sitze gerade an dem projekt und habe nun 2 kleine probleme
#include <iostream> struct pos{int x, y;}; struct creep {int hp; char pic;}; creep() : hp(30), pic('o') {} // Warum geht das nicht? ich bekomme fehler //error: expected unqualified-id before ')' token void print (const creep& cr){ std::cout << cr.hp << cr.pic << std::endl; }Der Konstruktor muss in die struct, vor dem };.
Wenn ich jetzt noch pos in die sctruct creep argumente nehme:
#include <iostream> struct pos{int x, y;}; struct creep {pos p; int hp; char pic;}; creep() : pos(5,5) hp(30), pic('o') {} // Wie initialisiert man pos ? void print (const creep& cr){ std::cout << cr.p << cr.hp << cr.pic << std::endl; } }Du kannst pos so initialisieren, wenn pos einen Konstruktor hat, der int, int nimmt.
Desweiteren sieht der konstruktor mit werten für mich aus als ob man einfach das struct initialisiert bis auf den part in den geschweiften klammern also
creep paul={5,6,7};So wäre es ja ganz normal bei deinen konstruktor sieht es wie gesagt eigentlich genau so aus in der main ich verstehe nicht so wirklich denn sinn.
Ja, so kann man auch initialisieren.
Aber wenn einige Elemente private sind, das heißt, man auf sie nicht zugreifen kann, geht das nicht.
Überhaupt ist man mit Konstruktoren viel flexibler (und btw: Seit C++11 ist das lediglich eine andere Syntax zum Konstruktoraufruf).
Aber ich rate dir wirklich, ein GUTES Lehrbuch zu C++(11) zu kaufen.
-
Jop danke nathan für die schnelle antwort
ich werde das mal mit den konstruktor bei pos. bekomme ich bestimmt hin.Ja ich war heut im laden und hab mir Primer c++ bestellt und es kommt morgen schon
leider die englische ausgabe aber das geht denke ich klar
-
Photon schrieb:
So wäre es ja ganz normal bei deinen konstruktor sieht es wie gesagt eigentlich genau so aus in der main ich verstehe nicht so wirklich denn sinn.
Ja. Anstatt einfacher Memberfunktionen ausgerechnet Konstruktoren für den Anfang zu wählen war wirklich nicht schlau von mir...

-
Ich hoffe übrigens, dass ich die Zeit bis zum Primer gut zu überbrücken geholfen habe.

Mir ist noch eine Nutzung von Konstruktoren eingefallen, die Dir ohne weiteren Stoff einleuchten wird. Die Benutzung des Bodies für über die Initialisierung hinausgehende Aktionen.
Siehe z.B. hier, wo Heroes sich in Burgen registrieren können:#include <iostream> #include <vector> #include <string> struct castle{ std::string name; std::vector<std::string> heroes; // Dies ist ein default constructor // Er kann prinzipiell ohne Parameter aufgerufen werden, // weil ich einen "Default Wert" bereitstelle castle(std::string name="Grayskull") : name(name) {} }; void enlist(const std::string& name, castle& c){ c.heroes.push_back(name); std::cout << name << " enlisted at castle " << c.name <<'\n'; } struct hero{ std::string name; hero() :name("Attila") { std::cout << name << " is a rogue hero!\n"; } hero(std::string name, castle& where) :name(name) { enlist(name, where); } }; void print(const castle& c, std::ostream& out){ switch(c.heroes.size()){ case 0: out << "No heroes enlisted at castle " << c.name << '\n'; break; case 1: out << "There is one hero enlisted at castle " << c.name << '\n'; break; default: out << "There are " << c.heroes.size() << " heroes enlisted at castle " << c.name << '\n'; } } int main(){ castle c; // default c'tor hero heroes[]={ {"Lancelot", c}, {"Parzival", c}, {} // default c'tor }; print( c, std::cout ); }
-
Ja das leuchtet ein man könnte auch weitere funktionen anheften damit direkt etwas mit dem hero passiert oder?
Jedenfall hier mal mein aktueller stand ich bin wieder etwas am hapern:
#include <iostream> #include <vector> struct pos{int x, y; //Speicher positionen für spielfeld pos(int y,int x) : y(y),x(x){} }; struct field { char map_[30][30]; std::vector<pos> cre; //Die Vektoren sind bisher noch überflüssig. std::vector<pos> twr; }; void draw_map(field& maze){ //Zeichnet das spielfeld. Gibt es etwas schnelleres als 2 schleifen? for(int y=0;y<30;y++){ for(int x=0;x<30;x++){ if (x==0 || x==29 || y==0 || y==29 ){maze.map_[y][x]='@';} else{if (maze.map_[x][y]!='O' || maze.map_[x][y] != 'X') maze.map_[y][x]= ' ';} std::cout << maze.map_[y][x]; } std::cout << "\n"; } } struct creep {pos p; int hp; char pic; //Ja die creeps creep() : p(15,15), hp(30), pic('O') {} }; void cr_move (creep& cr, field& fl){ //Hier liegt der fehler irgendwo cr.p.y+=5; fl.map_[cr.p.y][cr.p.x]='O'; fl.map_[cr.p.y-1][cr.p.x]=' '; draw_map(fl); } void cr_stats (const creep& cr){ std::cout << "<creep pos x: "<< cr.p.x << " pos y: " << cr.p.y << " hp: " << cr.hp << " pic: " << cr.pic << " >\n\n"; } struct creep_wave{std::vector<creep> wave;}; //Sowas in der art würde ich gerne benutzen. int main() { field lvl1; creep paul; draw_map(lvl1); cr_stats(paul); cr_move(paul,lvl1); cr_stats(paul); return 0; }Leider wird der creep nicht auf der map gedruckt

Teilweise bin ich noch etwas unsicher welche funktion in welche soll damit es übersichtlic bleibt. Auch habe ich das gefühl nicht alles sollte void und mit referenzübergabe sein aber es scheint am praktischsten für die bisher implementierten sachen.
Ja du hast mich stark motiviert und ich bekomme langsam eine vorstellung wie das alles funktioniert und warum. Danke nochmal

-
Ich glaube, die Bedingung in Zeile 18 ist verquer - die ODER Verknüpfung sollte wohl ein UND sein.
Ausserdem solltest Du map_ initialisieren:field() : map_{{0}}{} //initialisiert jeden char von map_ mit 0, doppelte Klammer muss bei Array-InitialisierungPhoton schrieb:
Teilweise bin ich noch etwas unsicher welche funktion in welche soll damit es übersichtlic bleibt. Auch habe ich das gefühl nicht alles sollte void und mit referenzübergabe sein aber es scheint am praktischsten für die bisher implementierten sachen.
Wow. Jetzt bin ich beeindruckt.
Das stimmt total und liegt - zumindest was den hinteren Teil anbelangt - daran, dass wir noch immer einen Mischmasch aus C und C++ benutzen.
Der vordere Teil ist Teil der Trennlinie zwischen guten und schlechten Entwicklern.
Also nochmal die Ohren gespitzt:
Wie schon gesagt lässt uns C++ neben Daten auch Methoden (member functions) in Klassen definieren.
Die Deklaration sieht aus wie immer, wird nur in die Klassendefinition gezogen.
In Methoden kann ich ohne weiteres auf weitere Member zugreifen.
Siehe z.B:#include <iostream> struct pos { int x, y; }; struct creep{ pos p; creep() : p{0,0} {} void move( pos newpos ) { p=newpos; } void print( std::ostream& out ) const { // const hier bedeutet: veraendert creep nicht out << "Creep @" << p.x << ' ' << p.y << '\n'; } }; int main(){ creep c; c.print( std::cout ); // Notation fuer Methodenaufruf - wie fuer alle member c.move( {1,2} ); c.print( std::cout ); }Wie Du siehst: viel übersichtlicher.
Aber achte auf const correctness: "read-only" Funktionen deklarierst Du alsconst! (Zeile 10)Photon schrieb:
Ja du hast mich stark motiviert und ich bekomme langsam eine vorstellung wie das alles funktioniert und warum. Danke nochmal

Fein!

PS: Sei nicht zu geizig mit vertikalem Platz z.B. Z. 18 bei Dir finde ich zu lang. Und wenn möglich nutz einen Editor, der Dir bei der Einrückung hilft.