Vector in Array speichern



  • SeppJ schrieb:

    Samyboy schrieb:

    SeppJ schrieb:

    Samyboy schrieb:

    Könnteste bitte konkret werden mit Design?

    Ja, wenn du mit deinen Anforderungen konkret wirst. Was soll das Programm denn können?

    Naja, ich meinte was vom Design her schlecht ist bei dieser Klasse 😃

    Wie du schon selbst schreibst, ist dein Programm praktisch unlesbar. Außerdem fuchtelst du ohne erkennbaren Grund mit rohen Pointern rum, was gerade bei unerfahrenen Programmierern fast immer schief geht, so auch bei dir. Ich werde mir mal kurz Gedanken machen wie ich ein Schachbrett mit deinen Methoden modellieren würde. Melde mich in ein paar Minuten wieder.

    Nunja, sollte man nicht möglichs jedes Objekt auf dem Heap speichern und dann die Pointer herumschicken?



  • Samyboy schrieb:

    Nunja, sollte man nicht möglichs jedes Objekt auf dem Heap speichern und dann die Pointer herumschicken?

    Nein, auf keinen Fall! Denk dran, Value-Semantik ist gut. Benutze möglichst automatische Objekte. Wenn du besitzende Zeiger brauchst, kapsle sie (Smart-Pointer, Pointer-Container oder ähnliches). Aber im Anwendungscode sollte new und delete so wenig wie nur möglich auftauchen.


  • Mod

    Hier ist mal ein Anfang, mit dem man arbeiten kann. Du musst nur noch die Methoden Implementieren, was in den meisten Fällen trivial sein sollte. Das schwierige, wo die Magie passiert, ist Coordinates::valid_move().

    #include <vector>
    #include <cassert>
    
    enum Color{white, black};
    enum Value{empty, pawn, knight, bishop, rook, queen, king};
    
    // A set of coordinates on a chess board
    class Coordinates
    {
    private:
      unsigned rank, file;
    public:
      Coordinates(unsigned x, unsigned y): file(x), rank(y)
      {
        assert(x<8);
        assert(y<8);
      };
    
      // Checks if other square is a valid move for a <v> from this coordinate:
      bool valid_move(Value v, Coordinates c) const;
    
    };
    
    class Square
    {
    private:
      // What kind of piece is on the square?
      Value i_value;
      // The color of the piece on the square. Ignored if value is empty.
      Color i_color; 
    
    public:
      Square(Value v, Color c=white): i_value(v), i_color(c){};
    
      // Draw the piece on output
      void draw(sf::RenderWindow* window) const;
    
      void set(Value v, Color c=white);
    
      // Getter:
      Value get_piece();
      Color get_color();
    };
    
    // A chessboard
    class Board
    {
    private:
      std::vector<Square> i_squares[64];
      // Returns reference to square at coordinates
      Square& at(Coordinates c);
    public:
      // Draws a chessboard and calls draw for all squares
      void draw(sf::RenderWindow* window) const;
    
      // Set piece at position
      void AddPiece(Coordinates coordinates, Value v, Color c=white);
    
      // Check if move is legal for player
      bool MoveIsLegal(Coordinates from, Coordinates to, Color player) const;
    
      // Move a piece
      void DoMove(Coordinates from, Coordinates to);
    
      // Sets all squares to empty
      void Reset();
    
      // Resets all squares to the initial value for a standard game
      void InitialPosition();
    
    };
    


  • Bis auf

    std::vector<Square> i_squares[64];
    

    ein (ganz) gutes Design.

    SeppJ, du meinst wohl eher, daß der vector mit 64 Feldern (Square) initialisiert wird, und nicht, daß du 64 vectors erzeugst?

    Und Coordinates::valid_move() wird ohne Zugriff auf das Schachbrett auch nicht sehr sinnvoll sein (d.h. z.B. bei Bauern und Königen ist auch die Farbe entscheidend).

    Und ich würde auch noch die Trennung von GUI (SFML) und Logic (ChessBoard) vornehmen...



  • Samyboy schrieb:

    Nunja, sollte man nicht möglichs jedes Objekt auf dem Heap speichern und dann die Pointer herumschicken?

    Wenn ein Objekt erst einmal mit new angelegt wurde ist alles was von diesem Objekt ausgeht (Member) automatisch im Heap und nicht im Stack des Programms (des main-Funktionsaufrufes). Geht gar nicht anders. Nur wenn du etwas dann mit new anlegst ist es eben ein beliebiger Speicherbereich und nicht mehr zusammenhängend.



  • Wenn ich zum Design noch was anmerken dürfte:
    Die Methoden Draw( ... ) gehören da eindeutig nicht zu Square und auch nicht zu Board, du solltest Logik und Darstellung trennen.


  • Mod

    Th69 schrieb:

    Bis auf

    std::vector<Square> i_squares[64];
    

    ein (ganz) gutes Design.

    SeppJ, du meinst wohl eher, daß der vector mit 64 Feldern (Square) initialisiert wird, und nicht, daß du 64 vectors erzeugst?

    Ups, ähh, ja.

    Und Coordinates::valid_move() wird ohne Zugriff auf das Schachbrett auch nicht sehr sinnvoll sein (d.h. z.B. bei Bauern und Königen ist auch die Farbe entscheidend).

    Ich hatte mir gedacht, dass die Methode Board::MoveIsLegal() das macht und dabei Coordinates::valid_move() benutzt um überhaupt zu prüfen, wohin gegangen werden kann. Ist sicherlich noch suboptimal, da ich mir nicht wirklich Gedanken gemacht habe, wie man das implementieren würde.

    Und ich würde auch noch die Trennung von GUI (SFML) und Logic (ChessBoard) vornehmen...

    Ahh, die Urfrage der Spieleprogrammierung. Irgendwie konnte mir nie jemand sagen, welche Vorgehensweise besser ist (ich programmiere aber auch nicht viele Spiele). Ist momentan die Trennung von Logik und Darstellung wieder in?



  • SeppJ schrieb:

    Ist momentan die Trennung von Logik und Darstellung wieder in?

    Ich glaube dies war eigentlich nie out. Wobei man abwägen muss: Ist Wartbarkeit und Portierbarkeit wichtiger als der zusätzliche Aufwand?

    Ich arbeite derzeit in einem Projekt, wo es besser gewesen wäre auf ersteres zu setzen. Eine nachträgliche Änderung ist in loser gekoppelten Systemen immer einfacher als in fest "verdrahteten" Systemen. Selbst wenn es nicht um den Austausch der Ausgabeform geht, sondern nur der Anpassung.



  • Dankeschön für die tollen Tipps und Ideen! (Vor allem Dir Sepp)

    Ich guck mir das ganze mal in RUhe an 🙂



  • SeppJ schrieb:

    Ahh, die Urfrage der Spieleprogrammierung. Irgendwie konnte mir nie jemand sagen, welche Vorgehensweise besser ist (ich programmiere aber auch nicht viele Spiele). Ist momentan die Trennung von Logik und Darstellung wieder in?

    Ich bevorzuge die Trennung ganz klar (zumindest sobald ein Projekt etwas grösser ist). Ich denke, der Aufwand ist nur gerade am Anfang höher, wenn man eine Schnittstelle einrichten muss, die eine Verbindung von Logik- und Grafikobjekten herstellt. Aber ich sehe ab und zu Leute, die das nicht so genau nehmen. Oft sind das auch jene, die sich mehr für fertige Spiele als unnötige technische Details wie C++ interessieren. 😉

    Aber auch bei fortgeschrittenenen Programmierern sieht man diese Vorgehensweise nicht selten. Natürlich kann es Fälle geben, in denen eine Trennung unangebracht ist. Doch meiner Meinung nach ist der Hauptgrund einfach oft eine kurzsichtige Denkweise (betrifft übrigens generell Designfragen).



  • Nexus schrieb:

    Aber auch bei fortgeschrittenenen Programmierern sieht man diese Vorgehensweise nicht selten.

    Logisch wenn die Planung nur auf schnelle Ergebnisse ausgelegt ist. Im Endeffekt wird es aber für alle teurer. Sei es nun, weil nicht erwartet wurde das man später nochmal auf das Design aufsetzen muss, sei es weil ein Projekt einen deutlich längeren Lebenszyklus erfährt, oder weil man den Kunden immer alle Wünsche gestern erfüllen will.

    Es wird blauäugig an Projekte herangegangen. Die Faulheit einiger Programmierer fördert dies zusätzlich (Warum den Aufwand betreiben "unnötige" Dokumentationen zu verfassen, Schichtentrennungen vorzunehmen oder "unsinnige" Diskussionen am Tisch durchzuführen). Das Ergebnis ist zwar vielleich (wenn alles gut geht) tatsächlich schneller auf dem Weg zum Kunden, aber in der Regel dauert die Fehlerkorrektur, der Einbau neuer Features etc. immer länger.

    Am Schluß hat man ein Flickenteppich. Ein Programm das nach außen hin wie ein schillerndes 200-Etagen Hochhaus mit Hightech-Glasaufzug aussieht, aber sich im inneren auf eine morsche Bruchbude aufstützt, bei der Fehler seltener behoben als "umschifft" werden, da die Zeit zum Beheben nicht eingeräumt wird.

    Nexus schrieb:

    Natürlich kann es Fälle geben, in denen eine Trennung unangebracht ist. Doch meiner Meinung nach ist der Hauptgrund einfach oft eine kurzsichtige Denkweise (betrifft übrigens generell Designfragen).

    Vollkommen richtig. Der Haken daran ist nur, das solche "hirnrissigen", "idealistischen" Gedankengänge ja nur von den "Programmierern" stammen können, die über "keinerlei wirtschaftliches Denkvermögen" verfügen.

    Ich behaupte das mancherlei Programmierer/Softwareentwickler mehr Ahnung von einer sinnvollen Projektplanung haben, als Manager. Ein Erfolg einer Software setzt sich halt nicht nur aus dem schnellen "Cashflow" zusammen, sondern auch langfristigeren Aspekten wie Kundenzufriedenheit und teilweise daraus resultierender Kundenbindung.



  • Also die Trennung von Darstellung (z.B. GUI), Logik und eigentlich sogar Datenspeicherung (DAL) sollte eigentlich bei jedem Projekt von vornherein feststehen. Auch wenn ich selber kein Verfechter einzelner Design-Patterns (MVC, MVVM, PAC, ...) bin, so sollte die modulare Software-Entwicklung generell immer im Vordergrund stehen. Das liegt bei mir auch daran, daß ich in meinem Berufsleben bisher häufig bestehende Systeme umschreiben bzw. neuschreiben mußte, weil es nicht möglich war dort einzelne Teile auszutauschen (z.Z. arbeite ich an einem C# Projekt, dessen Vorgänger in VB geschrieben wurde und dort einfach von internen Klassen direkt auf die GUI-Form zugegriffen wurde!?! Bis auf das GUI-Layout habe ich keine einzige Zeile übernommen...).

    Was aus einem einfachen Satz für eine Diskussion entstehen kann 🙂

    P.S: asc, ich stimme dir voll zu!

    Hier noch ein Auszug aus einem Skript, daß ich mal verfasst hatte (leider hatte der Verlag damals die dafür angedachte Buchreihe eingestellt):

    Th69 schrieb:

    Als Entwickler sollte man ein Programm nicht zu starr konstruieren, da sich sonst Sonderwünsche und notwendige Erweiterungen nur widerwillig einbringen lassen. Ein Architekt, der nicht weiß, daß sein Haus auf unebenen Grund gebaut wird, kann nach dem Bau auch keine Änderungen mehr vornehmen, außer das Haus abzureißen und wieder von vorn anzufangen. Genau diese Vorgehensweise habe ich in der Praxis jedoch schon oft erlebt - das kostet Zeit und viele, viele Nerven.


Anmelden zum Antworten