Attribut nur in Unterklasse ändern

Status
Nicht offen für weitere Antworten.

Hallo,
dies ist mein erster Eintrag hier, ich bin auf die Antworten gespannt.
Ich möchte einen Spielbau modellieren. Dazu übergebe ich von der Klasse Spielbaum einem Zugbewerter die momentane Spielsituation und einen Zug. Der Zugbewerter soll den Zug durchführen und am Ende eine Bewertung zurückgeben.
Dieser Vorgang soll für alle Züge wiederholt werden.
Mein Problem ist folgendes. Jeder neue Zug muss natürlich wieder von der ursprünglichen Spielsituation ausgehen.
Durch Durchführen eines Zuges wird diese ursprüngliche Spielsituation jedoch geändert und zwar leider nicht nur in der Klasse Zugbewerter, sondern eben auch in der Klasse Spielbaum. Das heißt ein neuer Zug geht dann von der geänderten Spielsituation aus, was er natürlich nicht sol.
Schuld darn ist natürlich, dass dem Zugbewerter nur die Referenz auf die Spielsituation übergeben wird.
Eine Lösung des Problems wäre, dass ich die Spielsituation vor jedem einzelnen Zug kopiere und den Zug auf dieser Kopie ausführen lasse. Bei einem zu erwartenden sehr großen Spielbaum erzeuge ich dabei aber eine enorme Menge an neuen Objekten, die schnell den Speicher auffressen. Gibt es eine Möglichkeit, die Variable spielsituation in der Klasse Spielbaum davor zu bewahren, verändert zu werden.
Im folgenden Code habe ich das Problem kurz dargestellt. Führt man ihn aus, sieht man, dass die Bewertung stets von einer neuen Spielsituation ausgeht.
Also, bin mal gespannt, was kommt
Viele Grüße Thomas

Code:
 import java.awt.Point;



    public class Spielbaum {
    
        Point spielsituation=new Point (5,3);
        int [] bewertung=new int[10];
        ZugBerechner zugBerechner;
    

        public static void main(String[] args) {
            Spielbaum sp = new Spielbaum();

         }

        public Spielbaum() {
            starteBerechnung();

        }

       public void starteBerechnung() {
           for (int i=0;i<=10;i++) {
               zugBerechner=new ZugBerechner(spielsituation,i);
               System.out.print("Bewertung von Zug" + i   );
               System.out.println(":  "+ zugBerechner.bewertung);
          }
       }


     public class ZugBerechner{
         Point spielsituation;
         int zug;
         int bewertung;

         public ZugBerechner(Point spielsituation, int zug) {
             this.spielsituation=spielsituation;
             this.zug=zug;
             bewerteZug(spielsituation,zug);
           }

         public void  bewerteZug(Point spielsituation, int zug) {
             spielsituation.x=spielsituation.x+zug;
             bewertung=spielsituation.x+10;
          }
       }
    }
 
Mach einen Zug Undoable(Interface) und nimm ihn zurück sobald der Spielbewerter seine Arbeit erledigt hat.
 
Hallo,
danke für die Antwort. Was du nicht wissen konntest ist, dass ein 'Zug' bei diesem Spiel eine ganze Reihe von Aktionen auslösen kan und es ziemlicher Auwand ist, diese alle zurückzusetzen.
 
winterwanderer hat gesagt.:
Code:
         public ZugBerechner(Point spielsituation, int zug) {
             this.spielsituation=spielsituation;
             this.zug=zug;
             bewerteZug(spielsituation,zug);
           }

Du solltest die Parameter nicht genauso nennen wie die Attribute der Klassen. Woher weisst du, dass bewerteZug die Attribute "this.spielsituation" und "this.zug" verwendet, statt der Übergabeparameter?

Besser:
Code:
public ZugBerechner(Point newSpielsituation, int newZug) {
...
}

In dem du die Attribute verwendest sollte sich dein Problem eigentlich schon lösen... 😎

BTW: Der Konstruktor ist eigentlich zum Konstruieren da und nicht zum Bewerten... :wink:
 
> Dieser Vorgang soll für alle Züge wiederholt werden.
> Mein Problem ist folgendes. Jeder neue Zug muss natürlich wieder von der ursprünglichen Spielsituation ausgehen.

Was heißt das eigentlich?
Wozu einzen Zug mehrmals von der gleichen Spielsituation ausführen,
wird da nicht das gleiche nochmal berechnet?

Wie viele Züge meinst du, redest du von einen vollständigen Baum der Form:

Code:
Anfang,  Zug      1 (Tiefe 1), Zug   2 (Tiefe 2) ...
                               Zug 354 (Tiefe 2) (wieder bei Spielsituation von nach Zug 1) ...
                               ....
         Zug 45.066 (Tiefe 1)  (wieder bei Anfangsspielsituation)  .... 
         ...
oder auch
Code:
Anfang,  Zug 1 (Tiefe 1), Zug 12 (Tiefe 2) (Spielsituation von nach Zug 1)...
                          Zug 13 (Tiefe 2) (Spielsituation von nach Zug 1) ...
                         ...
         Zug 2 (Tiefe 1)  (Anfang)  Zug 26 (Tiefe 2, noch vor allen Zügen der Tiefe 3).... 
         ...

?

Ich denke mal, was Tiefensuche und Breitensuche ist, wirst du wissen wenn du sowas angehst.

Bei der Tiefensuche sehe ich kein Speicherproblem.
Selbst wenn du auf das einfache Undoable verzichtest, musst du pro Tiefe nur ein Objekt speichern?!
Ein Undo kommt an Stelle der Kopie genau dann in Frage, wenn es schneller als der Kopiervorgang ist.
Speicher spielt da eine untergeordnete Rolle.

Erzeugte Objeke werden, wenn nicht mehr benötigt, auch wieder gelöscht,
was natürlich durchaus auch wieder Zeit dauert.

-----------------
Bei der Breitensuche ist ein Undo unmöglich.
Es stellt sich die Frage: Wie soll ein Zug ausgeführt werden, wenn die Spielsituation unbekannt ist? Die MUSS doch irgendwo gespeichert sein.

Einzige Alternative: sie wird jedes mal neuberechnet.
Wenn du also in der Tiefe 4 bist und Zug 1,7,3,8 ausführen möchtest,
dann fange noch mal ganz am Anfang an und berecht Zug 1, dann Zug 7, dann 3 und 8.
Etwas Berechnung wird eingespart, wenn man den vorhanden Speicher nutzt, um zumindest die ersten paar Tiefen vollständig zu speichern.

Nicht zu vergessen ist die Speicherung von Informationen über den Spielbaum,
z.B. dass Zug 1,5, 2, 2 gar nicht möglich ist, ein weitere Untersuchung des Teilbaums also wegfällt.
Vielleicht willst du gar noch die Bewertung jeder dieser Zugkombinationen merken.
Das allein kostet schon Unmengen Speicher, aber der Aufwand pro Zugfolge verkleinert sich von einer vollständigen Spielsituation zu einer einzelnen Zahl oder einem kleinen Objekt.

---------

Da du das Undo für möglich hälst, bist du in der Tiefensuche?


-----------

> Du solltest die Parameter nicht genauso nennen wie die Attribute der Klassen. Woher weisst du, dass bewerteZug die
> Attribute "this.spielsituation" und "this.zug" verwendet, statt der Übergabeparameter?

wozu überhaupt Parameter übergeben wenn die gleichen Objekte als Attribute vorhanden sind?

> In dem du die Attribute verwendest sollte sich dein Problem eigentlich schon lösen...

da besteht kein Unterschied, wie du schon erkanntest ist das Attrbibut und der Parameter dasselbe Objekt,
was sollte sich ändern?

zumal ja die Kopie der Objekte anscheinend nicht in Frage kommt, das ist ja das Problem!
 
HLX hat geschrieben:
Du solltest die Parameter nicht genauso nennen wie die Attribute der Klassen. Woher weisst du, dass bewerteZug die Attribute "this.spielsituation" und "this.zug" verwendet, statt der Übergabeparameter?

Besser:
Code:
: 
public ZugBerechner(Point newSpielsituation, int newZug) { 
... 
} 
/[Code] 
In dem du die Attribute verwendest sollte sich dein Problem eigentlich schon lösen...  
 
Hallo HLX,
soweit ich das richtig verstehe (wobei ich mir aber nicht ganz sicher bin) führt eine Änderung einer übergebenen Variablen sowohl zu einer Änderung dieser Variablen in der Klasse, in der die Methode ausgeführt wird als auch in der Klasse, die die Variable übergeben hat und zwar unabhängig davon, wie ich die varable benenne. Ein anderes Benenen des Übergabeparameters führt demnach nur dazu, dass ich ALIASE bekomme, die mir verschleiern, dass auch in der Ursprungsklasse eine Änderung aufgetreten ist. Daher habe ich die Variablen stets gleich benannt. Zur Sicherheit habe ich deine Idee jedoch ma ausprobiert und es ändert sich wirklich nicht. D.h. meine Variable spielsituation in class Spielbaum wird trotdem verändert.  :cry:  

Dein Hinweis auf meinen Missbrauch des Konstruktors ist ok. Werd ich in der Zukunft zu beachten suchen.
 :)
 
so siehts besser aus:
Code:
 zugBerechner=new ZugBerechner(spielsituation,i); 
zugBerechner.bewerteZug();

public class ZugBerechner{
  Point spielsituation;
  int zug;
  int bewertung;

public ZugBerechner(Point spielsituation, int zug) {
  this.spielsituation=spielsituation;
  this.zug=zug;
}

public void bewerteZug() {
  spielsituation.x=spielsituation.x+zug;
  bewertung=spielsituation.x+10;
}
}
wobei das Problem natürlich bleibt, da hast du recht

ich schreibe auch nur um an meinen längeren Post zwischendurch zu erinnern, könnte ja übersehen worden sein 😉
 
Hallo SlaterB
erst mal danke für deine ausführliche Antwort. 🙂

SlaterB schrieb
<wozu einzen Zug mehrmals von der gleichen Spielsituation ausführen, wird da nicht das gleiche nochmal berechnet?>

Es soll natürlich nicht der gleiche Zug nochmal ausgeführt werden, sondern ein anderer, aber ausgehend von der gleichen Spielsituation.

SlaterB schrieb
<wie viele Züge meinst du eigentlich, redest du von einen vollständigen Baum >

Ich nehme eine Tiefensuche vor. Ist aber eigentlich für meine grundsätzliche Frage erst mal egal. Auch wenn ich nur einen Zug in der Tiefe eins berechne, stelt sich das grundsätzliche Problem, das mein Zug meine ursprüngliche Spielsituation verändert. Wenn ich den nächsten Zug auf Grundlage der alten Spielsituation ausführen möchte, muss ich also ein UNDO des Zuges vornehmen. (Das ist hier schwierig, da auch ein einziger Zug die Spielsituation über kettenartige Reaktionen stark verändern kann) oder zuvor die alte Spielsituation speichern. Da ich beides verhindern möchte, war meine Frage, ob es möglich ist die alte Spielsituation in der Ursprungsklasse unverändert zu lassen.

SlaterB schrieb
<Bei der Tiefensuche sehe ich eigentlich kein Speicherproblem.
Selbst wenn du auf das einfache Undoable verzichtest musst du pro Tiefe nur ein Objekt speichern?! >

Wenn das so wäre, dann hätte ich wirklich kein Problem. :wink:
Leider muss ich aber für jeden Pfad, den ich abschreite zuvor ein neues Objekt der alten Spielsituation anlegen, da ja bei jedem Einzelpfad die alte Spielsituation geändert wird und ich jeweils aber wieder die alte Spielsituation benötige. Zumindest ist das bei meiner rekursiven Vorgehensweise so, vielleicht könnte ich es bei einer iterativen Vorgehensweise umgehen ? :?: Das muss ich mir nochmal in Ruhe überlegen.
 
natürlich musst du für jeden Spielzug ein Objekt erzeugen, aber nicht (bis Programmende) speichern!

wenn du bei der Zugfolge 3,6,7,8,3,4 bist,
dann hast du 5 Objekte im Speicher
(Spielsituation nach Zugfolge 3, nach 3,6, nach 3,6,7, nach 3,6,8 usw.)
das aktuelle Objekt 3,6,7,8,3,4 musst du nun auch im Speicher halten,
solange bis alle Unterzüge 3,6,7,8,3,4 [1.., 2..., 3... usw] berechnet sind,
danach kann es wieder gelöscht werden

wie gesagt, wenn ein Undo eine aufwendigere Operation ist als etwa auf einen Schachfeld mit 20 Figuren eine zu verschieben,
dann ist es normalerweise kein Problem, ein bisschen Zeit in die Erzeugung und Löschung von Objekten zu investieren
(ich rede natürlich auch nur so daher, meine Erfahrungen sind da eher theoretisch als praktisch 😉 )

ich verstehe also dein Problem im Moment nicht,
was stört dich?

es ist auf jeden Fall unmöglich im luftleeren Raum irgendwas zu errechnen und kurzfristig zu speichern, ohne Speicherplatz zu belegen 😉
selbst wenn du alles nur als primitive Datentypen rekursiv als Parameter übergibts sind sie eben irgendwo im Stack gespeichert
 
Du hast eh nur 2 Möglichkeiten:
Undo, oder ständiges Klonen. Da beim Klonen jede Menge Objekte erzeugt werden lege ich dir Undo nahe.
Wenn die Schritte zu aufwendig zum Rückgängig machen sind, lege ich dir das Memento Pattern nahe.
Damit kannst du mit wenig Aufwand einen Zug zurücknehmen, und minimierst die Objekterzeugung wenn du dein Memento wiederverwendest.
 
Hallo SlaterB und Wildcard,
ich denke, es ist schon ein Problem ständig neue Objekte durch Klonen zu erzuegen und die müssen zwar nicht bis Programende erhalten bleiben, aber zumindest bis zum Rekursionsende und dabei entstehen eben eine ganze Unmenge an Klonen.
Aber ich beginne einzusehen, dass ich wirklich keine andere Möglichkeit habe als dies oder eben doch ein UNDO.
Das Memento Pattern kannte ich noch nicht, muss ich mir mal ansehen. Vielleicht ist das ja die Lösung.
Also danke ihr beiden. 🙂
 
Status
Nicht offen für weitere Antworten.

Zurück
Oben