Konsolen-Simulation des Algorithmus "Türme von Hanoi" unter Verwendung einer Klasse



  • asdfsa schrieb:

    void Transfer(Tower& Lhs, Tower& Rhs)
    {
        Lhs.Add(Rhs.Top());
        Rhs.Pop();
    }
    

    Hmmm... wo hätte ich dann das Objekt Slice?



  • Cpp_Anfaeger schrieb:

    asdfsa schrieb:

    void Transfer(Tower& Lhs, Tower& Rhs)
    {
        Lhs.Add(Rhs.Top());
        Rhs.Pop();
    }
    

    Hmmm... wo hätte ich dann das Objekt Slice?

    asdfsa schrieb:

    wobei die ensprechenden funktionen (add, top etc) alle mit instanzen vom typen slice arbeiten.



  • asdfsa schrieb:

    Cpp_Anfaeger schrieb:

    asdfsa schrieb:

    void Transfer(Tower& Lhs, Tower& Rhs)
    {
        Lhs.Add(Rhs.Top());
        Rhs.Pop();
    }
    

    Hmmm... wo hätte ich dann das Objekt Slice?

    asdfsa schrieb:

    wobei die ensprechenden funktionen (add, top etc) alle mit instanzen vom typen slice arbeiten.

    achso, das muss ich noch sacken lassen ... 🙂


  • Mod

    aber man kann ja nie wissen, womit und wie etwas implementiert werden wird.

    Der Fall ist so unwahrscheinlich (und andererseits so verheerend*), dass man ihn vernachlaessigen kann. Es macht die Funktion nur komplizierter und langsamer.

    Und falls eine Exception fliegt, dann kommt diese wahrscheinlich sowieso bis zur main() durch und wird den Programmfluss mindestens unterbrechen, sodass der Status eines der Stacks keinen Unterschied mehr macht.
    Prinzipiell hast du aber Recht.

    * ~Ueberleg dir mal, wie lange das Programm noch weiterlaeuft, sobald der Speicher ausgegangen ist.~


  • Mod

    Mir war langweilig 🙂 . Das Programm erzählt die Geschichte der Türme von Hanoi. Ich habe natürlich peinlich genau darauf geachtet, dass die Scheiben wirklich verschoben werden, keine Tricksereien mit Kopien von Scheiben. Kopien von realen Gegenständen gibt es in einem wohl geformten Universum nicht. Über allem wacht der mächtige Gott Brahma, dass alle Regeln eingehalten werden:

    #include <iostream>
    #include <vector>
    #include <stdexcept>
    #include <utility>
    
    class EndOfTheWorldAsWeKnowIt { };
    
    class Disk
    {
      unsigned diameter;
      void brahma_check() const
      {
        if (diameter == 0)
          throw std::logic_error("Brahma ist zornig! "
                                 "Du hast die heiligen Regeln verletzt, "
                                 "als du eine nicht existierende Scheibe nahmst."
                                 );
      }
    public:
      Disk(unsigned diameter = 0) : diameter(diameter) { }
      Disk(Disk &&other): diameter(other.diameter) 
      {
        other.brahma_check(); 
        other.diameter = 0;
      }
      Disk(const Disk&) = delete;
      void operator=(const Disk&) = delete;
      unsigned get_diameter() const { brahma_check(); return diameter; }
    
      friend std::ostream& operator<<(std::ostream &out, Disk const& disk)
      {
        return out << "|\t" << disk.diameter << "\t|";
      }
    };
    
    class Tower
    {
      std::vector<Disk> disks;
    public:
      void add_disk(Disk &&disk) 
      { 
        if (!disks.empty() && disks.back().get_diameter() < disk.get_diameter() )
          throw std::logic_error("Brahma ist zornig! "
                                 "Du hast die heiligen Regeln verletzt, "
                                 "als du eine große Scheibe auf eine kleine legtest."
                                 );
    
        disks.push_back(std::move(disk)); 
      }
      Disk remove_top() 
      {
        if (disks.empty()) 
          throw std::logic_error("Brahma ist zornig! "
                                 "Du hast die heiligen Regeln verletzt, "
                                 "als du eine Scheibe von einem leeren Turm nahmst."
                                 );
    
        Disk disk = std::move(disks.back());
        disks.pop_back();
        return std::move(disk);
      }
      bool empty() const { return disks.empty(); }
      Tower() = default;
      Tower(const Tower&) = delete;
      void operator=(const Tower&) = delete;
    
      friend std::ostream& operator<<(std::ostream &out, Tower const& tower)
      {
        for(auto const& disk: tower.disks)
          out << disk;
        return out;
      }
    };
    
    template <unsigned num_disks> class Temple
    {
      Tower towers[3];
    public:
      Temple()
      {
        for(unsigned i = num_disks; i != 0; --i)
          towers[0].add_disk(Disk(i));
      }
      void move(unsigned from, unsigned to)
      {
        towers[to].add_disk(towers[from].remove_top());
      }
      void show_to_brahma() const
      {
        if (towers[0].empty() && (towers[1].empty() || towers[2].empty()))
          throw EndOfTheWorldAsWeKnowIt();
        else
          throw std::logic_error("Brahma ist zornig! "
                                 "Du hast die heiligen Regeln verletzt, "
                                 "als du ihm eine unvollstandige Lösung zeigtest."
                                 );
      }
    
      friend std::ostream& operator<<(std::ostream &out, Temple<num_disks> const& temple)
      {
        return out << "----------------------------------------------------------\n"
                   << temple.towers[0] << '\n' 
                   << temple.towers[1] << '\n'
                   << temple.towers[2] << '\n'
                   << "----------------------------------------------------------\n";
      }
    };
    
    class Monk
    {
      unsigned rank;
      static unsigned other_tower(unsigned t1, unsigned t2)
      {
        if (t1 == 0 && t2 == 1) return 2;
        if (t1 == 1 && t2 == 0) return 2;
        if (t1 == 2 && t2 == 1) return 0;
        if (t1 == 1 && t2 == 2) return 0;
        if (t1 == 2 && t2 == 0) return 1;
        if (t1 == 0 && t2 == 2) return 1;
        throw std::logic_error("Brahma ist zornig! "
                               "Du hast die heiligen Regeln verletzt, "
                               "als du totalen Unsinn programmiert hast."
                               );
      }
    public:
      Monk(unsigned rank): rank(rank) { }
      template <typename Temple> void solve_puzzle(Temple &temple, unsigned from, unsigned to)
      {
        if (rank > 1)
          {
            // Ask subordinate to move all the top disks:
            Monk minor_monk(rank - 1);
            unsigned other = other_tower(from, to);
            minor_monk.solve_puzzle(temple, from, other);
            // Move the last piece
            temple.move(from, to);
            // Proudly draw the result:
            std::cout << temple;
            // Ask subordinate to move all the rest:
            minor_monk.solve_puzzle(temple, other, to);        
          }
        else
          {
            // This task is simple, even for the lowliest monk:
            temple.move(from, to);
            // Proudly draw the result:
            std::cout << temple;
          }
      }
    };
    
    int main()
    {
      const unsigned num_disks = 5;
      Temple<num_disks> temple;
      try
        {
          std::cout << temple;
          Monk abbot(num_disks);
          abbot.solve_puzzle(temple, 0, 1);
          temple.show_to_brahma();
        }
      catch (EndOfTheWorldAsWeKnowIt)
        {
          std::cout << "Brahma ist zufrieden. Das Universum hat seinen Zweck "
            "erfüllt und kann nun neu erschaffen werden. Einen schönen Tag noch!\n";
        }
    }
    

    P.S.: Da manche Leute immer noch kein C++11 haben:
    https://ideone.com/bgcPZl



  • SeppJ schrieb:

    Mir war langweilig 🙂 . Das Programm erzählt die Geschichte der Türme von Hanoi. Ich habe natürlich peinlich genau darauf geachtet, dass die Scheiben wirklich verschoben werden, keine Tricksereien mit Kopien von Scheiben. Kopien von realen Gegenständen gibt es in einem wohl geformten Universum nicht. Über allem wacht der mächtige Gott Brahma, dass alle Regeln eingehalten werden:

    ...
    

    👍

    nicht schlecht SeppJ .. nur bei der Ausgabe der Türme sehe ich noch Verbesserungspotential.

    Dazu habe ich mir auch was einfallen lassen. Vom Algorithmus her ist das so eine Art einfacher-2D-Raytracer. Der Kern besteht aus dem Interface Shape und der Klasse Display . Display hat Zugriff auf alle Shape -Objekte, die gezeichnet werden sollen. Wird ein Display -Objekt ausgegeben, so berechnet Display für jeden Punkt (Zeichen) in der Ausgabe die jeweilige Position und fordert alle Shapes auf, ggf ihren 'Stempel' dort zu hinterlassen.

    Jedes Objekt, was 'gezeichnet' werden soll, muss von Shape abgeleitet sein, und die Methode void draw( const Pos& drawPos, char& out ) const überladen. Dort muss dann eine Implementierung dafür sorgen, dass das Zeichen out genau dann überschrieben wird, wenn die Position drawPos auf eine Punkt des Objekts zeigt. Natürlich sollte das Objekt dazu wissen, wo es sich befindet.
    Die Klasse Pos enthält schlicht x- und y-Wert einer Position. Man bräuchte Pos in dieser Programm-Skizze nicht zwingend, ich finde das aber durchgängiger und verständlicher. Für Erweiterungen, die Addition- und/oder Subtraktion von Positionen nötig machen ist Pos sicher von Vorteil.

    Die Methode ermöglicht es auch, Objekte übereinander zu zeichnen. Es gewinnt das Objekt, welches 'oben' liegt.

    Anbei die Demo:

    #include <iostream>
    #include <string>
    #include <vector>
    
    class Pos
    {
    public:
        Pos() : x_(0), y_(0) {}
        Pos( int x, int y ) : x_(x), y_(y) {}
    
        friend int X( const Pos& pos ) { return pos.x_; }
        friend int Y( const Pos& pos ) { return pos.y_; }
    private:
        int x_, y_;
    };
    
    class Shape
    {
    public:
        virtual ~Shape() =0;
        virtual void draw( const Pos& drawPos, char& out ) const =0;
    };
    Shape::~Shape() {}
    
    // --   die Scheibe ist ein schlichter int
    //      es folgen 5 überflüssige Zeilen; sie dienen nur als 'syntaktischer Zucker' (s. Tower::draw)
    typedef int Slice;
    int get_diameter( const Slice& slice )
    {
        return slice;
    }
    
    // --   der Turm aus 'die Türme von Hanoi'
    class Tower : public Shape
    {
    public:
        Tower( const Pos& turm_pos )
            : slices_()
            , pos_(turm_pos)
        {}
    
        void push( const Slice& slice )
        {
            slices_.push_back( slice );
        }
    
        // --   die Methode 'draw' schaut nach, ob der Punkt 'out' in der
        //      Position 'drawPos' auf einer der Scheiben des Turms
        //      liegt. 
        //      Falls ja, wird 'out' mit der 'FARBE' der Scheibe belegt
        virtual void draw( const Pos& drawPos, char& out ) const
        {
            const char FARBE = 'X';
            const int idx = Y(drawPos) - Y(pos_);
            if( idx >= 0 && idx < int(slices_.size()) )
            {
                const int xBegin = X(pos_) - get_diameter( slices_[idx] )/2;
                if( xBegin <= X(drawPos) && X(drawPos) < xBegin + get_diameter( slices_[idx] ) )
                    out = FARBE; // Punkt 'drawPos' liegt in einer Scheibe 'slices_[idx]' -> 'anmalen'
            }
        }
    private:
        std::vector< Slice > slices_; // die Scheiben
        Pos pos_; // Position des Turms (Mitte unten)
    };
    
    class Display
    {
    public:
        Display()
            : shapes_()
            , lower_left_()
            , upper_right_( 60, 12 )
        {}
        void add( const Shape& shape )
        {
            shapes_.push_back( &shape );
        }
    
        // --   Bildausschnitt setzen
        Display& operator()( const Pos& lower_left, const Pos& upper_right )
        {
            lower_left_ = lower_left;
            upper_right_ = upper_right;
            return *this;
        }
    
        // --   'zeichnen' des Display-Inhalts
        friend std::ostream& operator<<( std::ostream& out, const Display& d )
        {
            for( int y = Y(d.upper_right_); y >= Y(d.lower_left_); --y )
            {
                out.width(3);
                out << y << ": ";
                for( int x = X(d.lower_left_); x <= X(d.upper_right_); ++x )
                {
                    char c = out.fill(); // background
                    const Pos pos(x, y);
                    for( auto s = begin(d.shapes_); s != end(d.shapes_); ++s )
                        (*s)->draw( pos, c );
                    out << c;
                }
                out << '\n'; // EOL
            }
            return out << "--x->" << std::string("|-10 |-5  |    |5   |10  |    |20  |    |30  |    |40  |    |50  |    |60  |    |80")
                .substr(X(d.lower_left_) + 10, X(d.upper_right_) - X(d.lower_left_) + 1) << "\n";
        }
    private:
        std::vector< const Shape* > shapes_;
        Pos lower_left_, upper_right_;
    };
    
    // --   Demo-main-Programm für obige Konstruktion
    int main()
    {
        using namespace std;
        Tower t1( Pos(10,0) );
        for( int slice = 9; slice > 0; slice -= 2 )
            t1.push( slice );
    
        Tower t2( Pos(25,2) ); // der zweite Turm liegt um 2 höher
        t2.push( 7 );
        t2.push( 3 );
        t2.push( 1 );
    
        Display display;
        display.add( t1 );
        display.add( t2 );
    
        cout << "zwei Tuerme:\n" << display( Pos(0,0), Pos(60,8) ) << endl; // die Koordinaten bestimmen den Bildausschnitt
    
        // -- noch ein wenig Demo-Schischi
        t2.push( 0 );
        for( int i=1; i<36; i+=4 )
            t2.push( i > 13? 26 - i: i );
        cout << "zweiter Bildausschnitt:\n" << display( Pos(9,2), Pos(35,12) ) << endl;
        return 0;
    }
    

    Noch ein Hinweis: die Ausgabe der Skaliereung für x und y ist für die Demo hier. Wer das nicht haben will, lässt die Zeilen 94 und 95 weg und ersetzt die Zeilen 106 und 107 durch ein schlichtes return out; .

    Es ist übrigens ein Leichtes, SeppJs Programm und dieses zu verbinden. Man nehme die Klassen Display und Shape und die Methode Tower::draw , füge Letztere zu SeppJs Tower hinzu und ersetze die Ausgabe des Temple durch die Ausgabe von Display .

    SeppJ schrieb:

    Da manche Leute immer noch kein C++11 haben:
    https://ideone.com/bgcPZl

    .. manchmal schlägt eben der normative Zwang des Faktischen erbarmungslos zu 😞

    Gruß
    Werner



  • SeppJ schrieb:

    Mir war langweilig 🙂 . Das Programm erzählt die Geschichte der Türme von Hanoi. Ich habe natürlich peinlich genau darauf geachtet, dass die Scheiben wirklich verschoben werden, keine Tricksereien mit Kopien von Scheiben. Kopien von realen Gegenständen gibt es in einem wohl geformten Universum nicht. Über allem wacht der mächtige Gott Brahma, dass alle Regeln eingehalten werden:

    #include <iostream>
    #include <vector>
    #include <stdexcept>
    #include <utility>
    
    class EndOfTheWorldAsWeKnowIt { };
    

    P.S.: Da manche Leute immer noch kein C++11 haben:
    https://ideone.com/bgcPZl

    Das liest sich wie ein spannendes Adventure ... mit mystischen Unwirklichkeiten 🙂 👍



  • Werner Salomon schrieb:

    nicht schlecht SeppJ .. nur bei der Ausgabe der Türme sehe ich noch Verbesserungspotential.

    Dazu habe ich mir auch was einfallen lassen. Vom Algorithmus her ist das so eine Art einfacher-2D-Raytracer. Der Kern besteht aus dem Interface Shape und der Klasse Display . Display hat Zugriff auf alle Shape -Objekte, die gezeichnet werden sollen. Wird ein Display -Objekt ausgegeben, so berechnet Display für jeden Punkt (Zeichen) in der Ausgabe die jeweilige Position und fordert alle Shapes auf, ggf ihren 'Stempel' dort zu hinterlassen.

    Jedes Objekt, was 'gezeichnet' werden soll, muss von Shape abgeleitet sein, und die Methode void draw( const Pos& drawPos, char& out ) const überladen. Dort muss dann eine Implementierung dafür sorgen, dass das Zeichen out genau dann überschrieben wird, wenn die Position drawPos auf eine Punkt des Objekts zeigt. Natürlich sollte das Objekt dazu wissen, wo es sich befindet.
    Die Klasse Pos enthält schlicht x- und y-Wert einer Position. Man bräuchte Pos in dieser Programm-Skizze nicht zwingend, ich finde das aber durchgängiger und verständlicher. Für Erweiterungen, die Addition- und/oder Subtraktion von Positionen nötig machen ist Pos sicher von Vorteil.

    Die Methode ermöglicht es auch, Objekte übereinander zu zeichnen. Es gewinnt das Objekt, welches 'oben' liegt.
    Noch ein Hinweis: die Ausgabe der Skaliereung für x und y ist für die Demo hier. Wer das nicht haben will, lässt die Zeilen 94 und 95 weg und ersetzt die Zeilen 106 und 107 durch ein schlichtes return out; .

    Es ist übrigens ein Leichtes, SeppJs Programm und dieses zu verbinden. Man nehme die Klassen Display und Shape und die Methode Tower::draw , füge Letztere zu SeppJs Tower hinzu und ersetze die Ausgabe des Temple durch die Ausgabe von Display .

    SeppJ schrieb:

    Da manche Leute immer noch kein C++11 haben:
    https://ideone.com/bgcPZl

    .. manchmal schlägt eben der normative Zwang des Faktischen erbarmungslos zu 😞

    Gruß
    Werner

    Code lesen ... 👍
    Ich sehe's schon ... es wird bei mir noch lange dauern ... 💡


  • Mod

    Werner Salomon schrieb:

    .. nur bei der Ausgabe der Türme sehe ich noch Verbesserungspotential.

    Ja, das war auch meine Sorge beim Programmieren. Erst alles schön fertig gemacht, aber dann wusste ich nicht mehr weiter, wie ich das elegant dargestellt bekomme. Derzeit ist es eine Krücke, die Objekte zeichnen sich selbst, das sollte nicht so sein. Logik und Darstellung sollten sauber getrennt sein, aber ich kenne die üblichen Designs dafür nicht und so wirklich zufrieden war ich auch nicht mit dem, was ich mir auf die Schnelle ausgedacht habe. Dein Design ist da ähnlich, wenn auch eleganter. Ist das schon eine der üblichen Vorgehensweisen? Da müsste man jetzt wohl wirklich mal einen Spieleprogrammierer fragen, wie das gut gemacht wird.



  • SeppJ schrieb:

    ..Derzeit ist es eine Krücke, die Objekte zeichnen sich selbst, das sollte nicht so sein.

    Muss ich mein Anfänger-Klassendesign wohl umswitchen. Dass die Methoden den entsprechenden Klassen nicht richtig zugeordnet sind, hatte ich mir schon gedacht. Objekte wie Slice u. Tower sollte man vermutlich so einfach wie möglich halten?

    Was bedeuten die Striche hinter den Variablen?



  • Werner Salomon schrieb:

    class Pos
    {
    public:
        Pos() : x_(0), y_(0) {}
        Pos( int x, int y ) : x_(x), y_(y) {}
    
        friend int X( const Pos& pos ) { return pos.x_; }
        friend int Y( const Pos& pos ) { return pos.y_; }
    private:
        int x_, y_;
    };
    

    Bist Du so freundlich uns zu erklären weshalb Du X(...) und Y(...) als globale Funktionen implementierst? Ich hab das hier schonmal irgendwo entdeckt und nur mit dem Kopf geschüttelt 😮 Du weißt schon dass es dafür Klassenmethoden gibt?

    Daraus resultiert dann völlig unleserlicher Code wie:

    for( int x = X(d.lower_left_); x <= X(d.upper_right_); ++x )
    

    - osdt


  • Mod

    Cpp_Anfaeger schrieb:

    Muss ich mein Anfänger-Klassendesign wohl umswitchen. Dass die Methoden den entsprechenden Klassen nicht richtig zugeordnet sind, hatte ich mir schon gedacht. Objekte wie Slice u. Tower sollte man vermutlich so einfach wie möglich halten?

    Das ist nicht unbedingt eine Frage der Einfachheit. Klassen sollen das darstellen, was sie darstellen sollen, egal ob einfach oder kompliziert. Man kann auch sehr gut ein Design verteidigen, bei dem eine Klasse sich selber zeichnet, denn wer wäre besser dafür geeignet als sie selbst?

    Die Trennung von Logik und Darstellung ist eher ein technischer Aspekt. Man möchte die Darstellung abkoppeln, damit sie austauschbar wird. So kann die gleiche Programmlogik auf allerlei Art dargestellt werden. Dann wäre es ein leichtes, mein Progrämmchen mit einer anspruchsvollen 3D-Grafik auszurüsten, ohne an der Logik irgendetwas verändern zu müssen.

    Was bedeuten die Striche hinter den Variablen?

    Bei Werners Code? Die gehören mit zum Namen und haben keine syntaktische Bedeutung. Werner will dadurch unterscheidbar machen, ob eine Variable lokal ist oder ein Klassenmember ist.



  • SeppJ schrieb:

    Die Trennung von Logik und Darstellung ist eher ein technischer Aspekt. Man möchte die Darstellung abkoppeln, damit sie austauschbar wird. So kann die gleiche Programmlogik auf allerlei Art dargestellt werden. Dann wäre es ein leichtes, mein Progrämmchen mit einer anspruchsvollen 3D-Grafik auszurüsten, ohne an der Logik irgendetwas verändern zu müssen.
    ..
    Bei Werners Code? Die gehören mit zum Namen und haben keine syntaktische Bedeutung. Werner will dadurch unterscheidbar machen, ob eine Variable lokal ist oder ein Klassenmember ist.

    Achso, danke. Dann wäre man gut beraten, es so zu designern, dass die Windows-Programmierung später ohne Probleme anknüpfen kann 🙂 💡



  • osdt schrieb:

    Bist Du so freundlich uns zu erklären weshalb Du X(...) und Y(...) als globale Funktionen implementierst? Ich hab das hier schonmal irgendwo entdeckt und nur mit dem Kopf geschüttelt 😮 Du weißt schon dass es dafür Klassenmethoden gibt?

    Nun - sehe als persönlichen Stil meinerseits. Vom Standpunkt des Designs ist es IMHO völlig egal, ob Du in einer Klasse eine Methode oder eine (friend) Funktion, mit der Klasse als einer der Parameter implementierst. Technisch gibt es da Feinheiten - die kannst Du bei Scott Meyers nachlesen.
    In diesem speziellen Fall bei Positionen habe ich mir das so angewöhnt, und als ich es oben geschrieben habe, war das reine Gewohnheit ohne konkret darüber nach zu denken. Es hat nämlich den Vorteil, dass man sehr viele Strukturen als Positionen verwenden kann. Teilweise solche, wo man keine Methoden hinzufügen kann, weil es sich um sogenannten Legacy Code oder um eine Klasse aus dem Standard wie std::pair<> oder std::complex<> handelt.
    Man kann aber immer diese Funktionen X() und Y() implementieren. Das ist ein großer Vorteil, insbesondere bei der Template-Programmierung.

    osdt schrieb:

    Daraus resultiert dann völlig unleserlicher Code wie:

    for( int x = X(d.lower_left_); x <= X(d.upper_right_); ++x )
    

    .. erstaunlich! Denke Dir mal X(egal) heißt X-Wert-von-egal. Dann steht da:

    for( int x = X_Wert_von(d.lower_left_); x <= X_Wert_von(d.upper_right_); ++x )
    

    also: Schleife über alle x beginnend vom X-Wert der (unteren) linken Ecke bis zum X-Wert der (oberen) rechten Ecke.

    Gruß
    Werner



  • Hallo SeppJ,

    wenn dieses Forum eine Person wäre, müsste es wegen Schizophrenie sofort zum Psychiater. 😉

    SeppJ schrieb:

    .. die Objekte zeichnen sich selbst, das sollte nicht so sein. Logik und Darstellung sollten sauber getrennt sein, aber ich kenne die üblichen Designs dafür nicht und so wirklich zufrieden war ich auch nicht mit dem, was ich mir auf die Schnelle ausgedacht habe. Dein Design ist da ähnlich, wenn auch eleganter. Ist das schon eine der üblichen Vorgehensweisen? Da müsste man jetzt wohl wirklich mal einen Spieleprogrammierer fragen, wie das gut gemacht wird.

    ich hätte mir diese Diskussion - und ich begrüße sie sehr 👍 - eher in diesem Thread gewünscht, wo ich explizit danach fragte, wie man Code verständlich schreiben kann. Fünf von fünf die geantwortet haben, waren der Meinung, dass der Code des Springerproblems durch meinen (IMHO ojektorientierten) Ansatz nur komplexer, länger und weniger verständlich wird.
    Und dann poste ich hier ein Schnipsel im gleichen Stil und Du meinst:

    SeppJ schrieb:

    Logik und Darstellung sollten sauber getrennt sein

    😃 - also noch mehr Code und noch mehr Klassen und womöglich noch weniger verständlich?

    Ja - natürlich sollte man Logik und Darstellung sauber trennen - was denn sonst. In sogenannten 'professionellen' Programmen kenne ich das gar nicht mehr anders. Und das machen nicht nur Spieleprogrammierer so - Modell-View-Architekturen machen auch wo anders Sinn. Und sie machen auch Sinn, wenn man die View nicht austauschen will. Einfach um die Komplexität runter zu kriegen.

    Ob man das bei den Türmen von Hanoi macht oder nicht ist was anderes. Der Turm ist viel zu einfach, als dass das wirklich nenneswerte Verbesserungen des Programms nach sich zieht. Adererseits ist das 'ne nette Übung - und wenn man es mit dem Turm aus Hanoi nicht sauber hinbekommt, braucht man nicht zu hoffen, dass es bei großen/komplexen Objekten einfacher wird.

    ich melde mich zu dem Thema später noch mal.
    Gruß
    Werner



  • Ufff ... Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...

    http://s14.directupload.net/images/131208/5afa74r2.jpg

    Soweit sind die Klassen fertig designed u. programmiert. Es scheint auch zu funktionieren. Ob ich gravierende Verstöße gegen die objektorientierte Programmierung gemacht habe, weiß ich allerdings nicht:

    #include <windows.h>    // wegen Windows Handle für Konsolenposition
    #include <cstdlib>      // wegen system("cls")
    #include <sstream>      // wegen stringstream,  für int kombiniert mit char*
    #include <iostream>
    #include <deque>
    #include <string>
    using namespace std;
    
    class Slice {
        public:
            Slice();
            ~Slice();
    
            void setSlice(string slice);
            string getSlice();
            int getSliceCenter();
            int getSliceSize();        
    
        private:
            string m_slice_object;
            int m_slice_length;
            int m_slice_center;        
    };
    
    Slice::Slice()
    {    
    }
    
    Slice::~Slice()
    {    
    }
    
    //---------------------------------------------------------------------
    // gibt der Scheibe ein Aussehen
    //---------------------------------------------------------------------
    void Slice::setSlice(string slice) {
        m_slice_object = slice;
    }
    
    //---------------------------------------------------------------------
    // holt die Scheibe
    //---------------------------------------------------------------------
    string Slice::getSlice() {
        return m_slice_object;
    }
    
    //---------------------------------------------------------------------
    // bestimmt die Mitte der Scheibe
    //---------------------------------------------------------------------
    int Slice::getSliceCenter() {
        m_slice_center = (m_slice_object.length()+1)/2;
        return m_slice_center;
    }
    
    //---------------------------------------------------------------------
    // bestimmt die Größe der Scheibe
    //---------------------------------------------------------------------
    int Slice::getSliceSize() {
        m_slice_length = m_slice_object.size();
        return m_slice_length;
    }
    
    class Tower {        
        public:
            Tower();
            ~Tower();
    
            Slice getSlice(); //oberste Scheibe zurückgeben
            Slice getAt(const int &iter); // irgendwo eine Scheibe
            int getSize(); // Towergröße auslesen
            void addSlice(Slice slice);   // 1x aufnehmen    
            void removeSlice(); // 1x löschen      
    
        private:
            deque<Slice> m_tower_content;
    
    };
    Tower::Tower()
    {    
    }
    
    Tower::~Tower()
    {    
    }
    
    //---------------------------------------------------------------------
    // holt oberste Scheibe  
    //---------------------------------------------------------------------
    Slice Tower::getSlice() {
        return m_tower_content.at(0);
    }
    
    //---------------------------------------------------------------------
    // holt eine Scheibe an einer best. Stelle im Turm  
    //---------------------------------------------------------------------
    Slice Tower::getAt(const int &iter) {
        return m_tower_content.at(iter);
    }
    
    //---------------------------------------------------------------------
    // Scheibenzahl auslesen
    //---------------------------------------------------------------------
    int Tower::getSize() {
        return m_tower_content.size();
    }
    
    //---------------------------------------------------------------------
    // fügt eine Scheibe oben rein
    //---------------------------------------------------------------------
    void Tower::addSlice(Slice slice) {
        m_tower_content.push_front(slice);
    }
    
    //---------------------------------------------------------------------
    // löscht oberste Scheibe aus Turm
    //---------------------------------------------------------------------
    void Tower::removeSlice() {
        m_tower_content.pop_front();
    }
    
    class Display {
        public:
            Display();
            ~Display();
    
            void clr();
            void gotoXY(const int &line, const int &column);        
            void waitKeyboard() {getwchar();}          
            void print(const string &value);
            void print(const stringstream  &value);  
            void print(Tower &tower, int x_start, const int &y_start);
    };
    Display::Display()
    {
    }
    
    Display::~Display()
    {
    }
    
    //---------------------------------------------------------------------
    // // Bildschirm löschen
    //---------------------------------------------------------------------
    void Display::clr() {
        system("cls");
    }
    
    //---------------------------------------------------------------------
    // Positioniere Cursor auf Konsole
    //---------------------------------------------------------------------
    void Display::gotoXY(const int &line,
                         const int &column) {
    
        COORD p;
        p.Y = line;     // Zeile
        p.X = column;   // Spalte
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), p);
    }
    
    //---------------------------------------------------------------------
    // Überladung 1: String auf dem Bildschirm
    //---------------------------------------------------------------------
    void Display::print(const string &value){
        cout << value << endl;
    }
    
    //---------------------------------------------------------------------
    // Überladung 2: StringStreams auf dem Bildschirm
    //---------------------------------------------------------------------
    void Display::print(const stringstream &value){
        cout << value.str() << endl;
    }
    
    //---------------------------------------------------------------------
    // Überladung 3: Tower auf Konsole ausgeben
    //---------------------------------------------------------------------
    void Display::print(Tower &tower,
                        int x_start,
                        const int &y_start) {
    
        int start_column; // Startspalte    
        for (int i=tower.getSize()-1; i > -1; i--) {    
            start_column = y_start - tower.getAt(i).getSliceCenter();
            this->gotoXY(x_start--, start_column);
            cout << tower.getAt(i).getSlice();
        }
    }
    
    class Simulation {       
        public:
            Simulation();  
            ~Simulation();
    
            void moveSlice(Tower &start_tower,
                           Tower &destination_tower);
            void recurFunction(int start,
                               int destination,
                               int slice_count,
                               Tower &tower_1,
                               Tower &tower_2,
                               Tower &tower_3);  
    
        private:
            int m_counter;     // Schrittzähler  
    };
    Simulation::Simulation() {
        m_counter = 0;
    }
    
    Simulation::~Simulation()
    {
    }
    
    //---------------------------------------------------------------------
    // Scheibe von Turm zu Turm bewegen
    //---------------------------------------------------------------------
    void Simulation::moveSlice(Tower &start_tower,
                               Tower &destination_tower) {            
    
        destination_tower.addSlice(start_tower.getSlice());
        start_tower.removeSlice();
    }
    
    //-------------------------------------------------------------------------
    // Rekursiver Methodenaufruf zur Berechnung & Anzeige der Möglichkeiten
    //-------------------------------------------------------------------------
    void Simulation::recurFunction(int start,           // Startturm
                                   int destination,     // Zielturm
                                   int slice_count,     // Anzahl Scheiben
                                   Tower &tower_1,
                                   Tower &tower_2,
                                   Tower &tower_3) {
    
        Display console;        // Konsole erstellen    
        stringstream step_no;   // Schrittnummer
        int free;               // freier Turm
    
        if (slice_count != 0) {        
            free = 6-start-destination;        
            // rekursiver Aufruf
            recurFunction(start,
                          free,
                          slice_count-1,
                          tower_1,
                          tower_2,
                          tower_3);        
    
            // Schritte anzeigen  
            console.gotoXY(0,0);
            step_no << ++m_counter << ".)  Turm "
                    << start << " nach -> Turm "
                    << destination << endl;    
    
            console.print(step_no);  
            console.gotoXY(1,26);
    
            // Ausgangszustand des Turms 1malig anzeigen
            if (m_counter == 1) {
                console.print(tower_1, 9, 15);
                console.gotoXY(1,26);
            }
    
            console.waitKeyboard();
    
            // Simulationsteil:
            if (start == 1 && destination == 2)
                moveSlice(tower_1, tower_2);        // Tower1->Tower2
            if (start == 1 && destination == 3)
                moveSlice(tower_1, tower_3);        // Tower1->Tower3
            if (start == 2 && destination == 1)
                moveSlice(tower_2, tower_1);        // Tower2->Tower1
            if (start == 2 && destination == 3)
                moveSlice(tower_2, tower_3);        // Tower2->Tower3
            if (start == 3 && destination == 1)
                moveSlice(tower_3, tower_1);        // Tower3->Tower1
            if (start == 3 && destination == 2)
                moveSlice(tower_3, tower_2);        // Tower3->Tower2          
    
            // neu zeichnen
            console.clr();
            console.print(tower_1, 9, 15);
            console.print(tower_2, 9, 42);
            console.print(tower_3, 9, 68);
    
            // erneuter rekursiver Aufruf
            recurFunction(free,
                          destination,
                          slice_count-1,
                          tower_1,
                          tower_2,
                          tower_3);        
        }      
        console.gotoXY(15,0);
    }
    
    //---------------------------------------------------------------------
    // Programm-Hauptfunktion
    //---------------------------------------------------------------------
    int main() {
    
        Slice slice[6]; // 6 Scheiben mit folgendem Aussehen
    
        slice[0].setSlice(          "***");
        slice[1].setSlice(        "*******");
        slice[2].setSlice(      "***********");
        slice[3].setSlice(    "***************");
        slice[4].setSlice(  "*******************");
        slice[5].setSlice("***********************");
    
        // 3 Türme erstellen
        Tower tower_1, tower_2, tower_3;
    
        // Turm_1 mit Scheiben auffüllen
        for (int i = 5; i > -1; --i) {
            tower_1.addSlice(slice[i]);
        }
    
        // Simulation starten
        Simulation sim;
        sim.recurFunction(1, 3, 6, tower_1, tower_2, tower_3);
        return 0;
    }
    

  • Mod


  • Mod

    Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...

    Was!?



  • SeppJ schrieb:

    http://en.wikipedia.org/wiki/Special_member_functions

    Meinst du die syntaktische Deklaration der Methoden ... dass sie so ungefährt deklariert werden sollten?



  • Arcoth schrieb:

    Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...

    Was!?

    Ich kann richtig mit verfolgen, wie die IDE bei der Autovervollständigung nachschlägt bei 1 GB RAM 🙂 Muss mir zu Weihnachten mal was Neues göhnen.


Anmelden zum Antworten