# Frage zu Programmarchitektur



## Ishildur (15. Jul 2008)

Hallo zusammen
Ich programmiere gerade ein Mühlespiel und habe ein Riesenproblem mit der Programmarchitektur. Ich habe eine Klasse "Game" welche das Spielbrett sowie die Steine repräsentiert und hauptsächlich zwei Methode "isMoveValid" und "move" bereitstellt. Schliesslich habe ich eine abstrakte Klasse Player, von welcher dann HumanPlayer, RandomPlayer sowie AIPlayer abgeleitet werden. Das Ziel ist, dass ich jeden Typ Player gegen jeden anderen Typ Player spielen lassen kann. Also HumanPlayer gegen AIPlayer, AIPlayer gegen AIPlayer usw. Ich hatte mir das ungefähr so vorgestellt:

while(true){
 p1.nextMove();
 p2.nextMove();
}

Wenn ich RandomPlayer gegen RandomPlayer, AIPlayer gegen RandomPlayer oder AIPlayer gegen AIPlayer spielen lasse, funktioniert dies auch ganz gut. Das Problem ist nun der Humanplayer, weil der HumanPlayer anders als die beiden anderen Spieltypen ja nicht einfach einen Algorithums abarbeitet und schliesslich zurückkehrt, sondern stattdessen auf Benutzereingaben wartet.

Vielleicht kennt jemand von euch das Problem und kann mir einen Tipp geben?

Mfg Ishildur


----------



## Marco13 (15. Jul 2008)

Beschreib' das Problem mal genauer. Läuft das ganze in einem eigenen Thread, mit GUI ... wie passiert das "Warten auf eine Eingabe"... etc....


----------



## Niki (15. Jul 2008)

in deiner nextMove Methode vom Player musst du halt so lange warten bis der Benutzer eine Eingabe gemacht hat. Das könnte so aussehen:

```
private Move move = null;

public synchronized void nextMove(){
  while(move == null){
    try{
      wait();
    }catch(InterruptedException ex){  
    }
  }

  //mach was mit dem move
  move = null;
}

public synchronized void setMove(Move move){
  this.move = move;
  notify();
}
```

In der Gui musst du dann bei einem Event dem Player den Move mit teilen:

```
public void mousePressed(MouseEvent e){
  Move move = ....;
  //Zuerst muss ermittelt werden welcher Player gerade dran ist...
  player.setMove(move);
}
```


----------



## Ishildur (15. Jul 2008)

Hmmm naja, aber gibt es da nicht auch eine Variante ohne Threads?
Ich habe mirüberlegt, ob ich so ein Master - Slave Verfahren anwenden soll:

Also einer der Spieler ist der Master und ruft dann einfach den Slave spieler auf, nachdem er (der Master) seinen Zug gemacht hat.

while(true){
 // ich bin der Masterspieler und mache meinen Zug
 this.getSlavePlayer().nextMove();
}

Wenn mehrere AIPlayer oder RandomPlayer beteiligt sind, spielt es keine Rolle, wer Master und wer Slave ist, sobald allerdings ein HumanPlayer im Spiel ist, muss dieser der Master sein. Diese Architektur löst aber nicht das Problem, wenn beide Spieler HumanPlayers sind... :-(


----------



## Marco13 (15. Jul 2008)

Ishildur hat gesagt.:
			
		

> Das Problem ist nun der Humanplayer, weil der HumanPlayer anders als die beiden anderen Spieltypen ja nicht einfach einen Algorithums abarbeitet und schliesslich zurückkehrt, sondern stattdessen auf Benutzereingaben wartet.



Das ist kein Problem, sondern das gewünschte Verhalten. Beschreib' genauer, was das Problem ist.


----------



## Michael... (15. Jul 2008)

ohne Threads brauchst Du für jede Spielvariante eine eigene Logik.
KI vs. KI: Schleife, wie gehabt
KI vs. Mensch: Mensch initiert nach eigenem Zug, Zug der KI
Mensch vs. Mensch: jeder macht seinen eigenen Zug und muss auf den Zug des Gegners warten.


----------



## Marco13 (15. Jul 2008)

Die Schleife sollte nur so aussehen, dass alle Spieler nacheinander ihre Züge machen. Wer da auf wen wartet (und was für Spieler-Arten das sind) sollte die Hauptschleife nicht interessieren....


----------



## Ishildur (15. Jul 2008)

@Marco13
Genau das habe ich mir auch gedacht, aber wie kann ich das realisieren? Ich meine ich kann ja nicht das Hauptprogramm so lange unterbrechen, bis der menschliche Spieler seinen Zug getätigt hat, oder? Ich möchte das Ganze ohne Threads realisieren, weil das Spiel zu einem späteren Zeitpunkt auf eine Plattform portieren möchte, die kein Multithreading kennt. Somit würde ich das Problem nur hinauszögern.


----------



## Marco13 (15. Jul 2008)

:roll: Was soll das "Hauptprogramm" (was auch immer das ist) machen, solange ein Benutzer noch nichts eingegeben hat? Einfach mal pauschal weiterspielen oder was...?


----------



## Ishildur (16. Jul 2008)

Hehe, nein natürlich nicht  :lol: 
Aber die Frage ist, wie kann der HumanPlayer der Applikation mitteilen, dass es nun seinen Zug getätigt hat? Bei den ComputerPlayern gibt es einfach eine Method "nextMove" welche zurückkehrt, sobald der entsprechende Algorithmus durchgearbeitet ist. Das Problem beim HumanPlayer jedoch ist, dass ein Zug erst dann "möglicherweise" getätigt wurde, nachdem der Event "mouseReleased" abgefeuert wurde. Irgendwie passt da alles überhaupt nicht zusammen!


----------



## Marco13 (16. Jul 2008)

Ja, die nextMove-Methode muss halt warten, bis die Eingabe gemacht wurde - das ist genau das, was Niki gepostet hatte!


----------



## parabool (17. Jul 2008)

vielleicht über einen "MoveListener"




```
interface MoveListener
{
  public  void nextMove(Move move)
}


class Game implements MoveListener
...
```

game registriert sich dann bei den Spielern als Listener:


```
player1.addMoveListener(game)
player2.addMoveListener(game)
...
usw.
```

egal ob AI-Gegner oder nicht, beide informieren die Game-Klasse mittels Callback-Methode über den Zug

Müsste natürlich noch kontrolliert werden, welcher Spieler am Zug ist (sperren der gegn. Spielers)


----------



## Ishildur (17. Jul 2008)

@parabool
OK, die Spieler informieren die Gameklasse über denn Move, aber wie gehts dann weiter? Wer initiiert schliesslich den nächsten Move? Die Gameklasse kann es ja wohl nicht sein, weil man sonst einen Stackoverflow riskiert, weil die Methoden nie mehr zurückkehren?

Mfg Ishildur


----------



## Marco13 (17. Jul 2008)

Manchen Leuten muss man echt alles vorkauen. Wenn du in folgendem Codestück irgendwas findest, was du noch NICHT selbst geschrieben hattest, und was noch NICHT hier gepostet wurde, sag' bescheid....

```
import javax.swing.*;
import java.awt.event.*;




class NineMensMorrisTest
{
    public static void main(String args[])
    {
        //new NineMensMorrisTest(new ComputerPlayer(), new ComputerPlayer()).startGame();
        new NineMensMorrisTest(new ComputerPlayer(), new HumanPlayer()).startGame();
    }

    private Player player0;
    private Player player1;

    public NineMensMorrisTest(Player player0, Player player1)
    {
        this.player0 = player0;
        this.player1 = player1;
    }

    public void startGame()
    {
        for (int i=0; i<5; i++)
        {
            System.out.println("--- Move of player0: "+player0.doMove());
            System.out.println("----Move of player1: "+player1.doMove());
        }
    }
}


interface Player
{
    String doMove();
}

class HumanPlayer extends JFrame implements Player
{
    private String currentMove = null;

    public HumanPlayer()
    {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JButton b = new JButton("doMove");
        b.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                setMove();
            }
        });
        getContentPane().add(b);
        pack();
        setVisible(true);
    }

    private synchronized void setMove()
    {
        currentMove = "HumanPlayerMove";
        notify();
        System.out.println("Got for input from HumanPlayer");
    }


    public synchronized String doMove()
    {
        while (currentMove == null)
        {
            System.out.println("Waiting for input of HumanPlayer");
            try
            {
                wait();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
        System.out.println("HumanPlayer returning move...");
        String theMove = currentMove;
        currentMove = null;
        return theMove;
    }
}


class ComputerPlayer implements Player
{
    public String doMove()
    {
        System.out.println("ComputerPlayer computing move...");
        try
        {
            Thread.sleep(2000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        System.out.println("ComputerPlayer returning move...");
        return "ComputerPlayerMove";
    }

}
```


----------



## xysawq (17. Jul 2008)

Ach du lieber Schreck...

hab mir die ganze Krankheit hier mal durchgelesen und verstehe nicht, was das Problem sein soll...

Wenn das Programm ohne Threads in einer Schleife läuft und es nur 2 Spieler gibt, wobei jeder entweder AI, Random oder Human sein kann, dann würde ich eine abstrakte Oberklasse Player machen und 3 verschiedene Arten, nämlich Ai, Random und Human die Player erweitern (extends), wobei Player die Methode nextMove() hat und diese abstrahiert.


```
public abstract class Player
{
	public abstract Move nextMove();
}
```


```
public class AI extends Player
{
	public Move nextMove()
	{
		//Was deine künstliche Intelligenz eben so macht.
	}
}
```


```
public class Random extends Player
{
	public Move nextMove()
	{
		//Die Generierung deiner zufälligen Moves.
	}
}
```


```
public class Human extends Player implements java.awt.event.MouseListener
{
	Move move = null;
	
	public Move nextMove()
	{		
		while(move == null)
		{
			try
			{
				wait();
			}
			catch(InterruptedException ex)
			{ 
			}
		}
		
		Move temp = move;
		move = null;
		
		return temp;
	}
	
	public void mouseReleased(MouseEvent e)
	{
		//Rumrechnen und
		move = ...;
		//etwas zuweisen;
	}
}
```


Jetzt kannst du je nach Sielerart die Spieler zuweisen und deine schöne Schleife abarbeiten, aber dein "Hauptprogramm" muss logischerweise immer auf den HumanPlayer warten... es wartet ja auch, bis AI oder Random ihre Berechnungen beendet haben.


```
Player player1 = null;
Player player2 = null;

//je nach Auswahl dann später z.B.:
player1 = Human();
player2 = AI();

//und nun kannst du ohne Probleme deine Schleife abarbeiten
while(true)
{
	player1.nextMove();
	//Auswertung des Moves von Player1//
	
	player2.nextMove();
	//Auswertung des Moves von Player2//
}
```

Ist teilweise aber auch nur das, was die Anderen schon gesagt haben.


----------



## xysawq (17. Jul 2008)

Kurz nachdem ich angefangen habe mir mal darüber Gedanken zu machen kommt wieder Marco und programmiert fast das ganze Spiel...

Vielleicht konnte ich ja trotzdem helfen .

(Und ich hab echt fast ne halbe Stunde (mit Klopause) für meine Antwort gebraucht... *schäm*)


----------



## parabool (17. Jul 2008)

EDIT: Ishildur wollte es ja ohne Threads...


Ja natürlich muss da noch eine Steuerung rein.

vielleicht so in etwa (nur prinzipiell):


```
class Steuerung implements MoveListener
{
  
   private int counter = 0;
   private Player player0;
   private Player player1; 
   private Game game; 
   private ArrayList<Player> players; 


   public Steuerung(Game game)
  {   
    this.game = game;
    players = ArrayList<Player>();
    players.add(player0);
    players.add(player1);
    player0.addMoveListener(game);
    player1.addMoveListener(game);
    player0.addMoveListener(this);  //auch die Steuerungsklasse wird benachrichtigt
    player1.addMoveListener(this);
  }

  
  //callback-Methode 
  public void nextMove(Move move)  
  {
   
     // player wird benachrichtigt das er am Zug ist // bei Humanplayer zB ev. Buttons  aktivieren ..oder so..
     players.get(counter).duBistDran(); 
     counter++
     if(counter>players.size()-1)counter=0; //erster Spieler wieder am Zug
  }

}
```


Wer stellt eigentlich fest wann das Spiel beendet/gewonnen ist?
Der Spieler selbst?
Dann die callback-Methode etwas modifizieren:


```
public void nextMove(Move move)  
  {
   
    if(!move.isLastMove())
    {
     // player wird benachrichtigt das er am Zug ist // bei Humanplayer zB ev. Buttons  aktivieren ..oder so..
     players.get(counter).duBistDran(); 
     counter++
     if(counter>players.size-1)counter=0; //erster Spieler wieder am Zug
    }
    else
    {
       //wenn Spielende irgendwas tun, Spieler benachrichtigen usw
    }
  }

}
```

Game und Steuerung sind als Listener registriert.
Game wird bei jeden Zug benachrichtigt.
Steuerung hält die Spieler als Member in einer Liste und informiert sie wenn sie am Zug sind.
Wird ein Zug getätigt, so wird die Game-Klasse und die Steuerungsklasse informiert wobei
dadurch zugleich der nächste Spieler aktiviert wird.


Ist wirklich nur die Grundidee und schnell mal skizziert...


----------



## Marco13 (17. Jul 2008)

Ich finde die Sache mit dem MoveListener ... hm  :? also, das wirkt so, als würden die Player unkoordiniert moves in die Welt schleudern, und das Spiel würde beiden Zuhören, und versuchen, was sinnvolles draus zu machen. Ich finde, der ablauf ist klar: Der Spielablauf gibt vor, dass ein Spieler einen Zug abliefern muss. Und das macht man dann: Das Spiel sagt dem Spieler: "GIB MIR DEINEN ZUG! (ich wart's so lang auf dich)". Hm.


----------



## xysawq (17. Jul 2008)

Greifen wir uns das doch mal aus dem richtigen Leben...

Marco und ich spielen Mühle...

Nach 3 Minuten schnellen Spielens habe ich eine schwerwiegende Entscheidung zu treffen und überlege... das ist das erste Mal, dass Marco wirklich warten muss.

Natürlich mag er nicht warten und macht seinen nächsten Zug ohne mir Zeit für meinen zu geben... das Spiel ist hin, keiner gewinnt, Error, imaginäre Bluescreen, Explosion, Weltuntergang... so siehts doch aus.

Was macht das Spiel für einen Sinn, wenn nicht gewartet wird???


----------



## parabool (17. Jul 2008)

> Das Spiel sagt dem Spieler: "GIB MIR DEINEN ZUG! (ich wart's so lang auf dich)



Find ich nicht gut.
Das Spiel(brett) ist nur für die Darstellung der Züge und Validierung der Züge zuständig (siehe Eingansposting).




> als würden die Player unkoordiniert moves in die Welt schleudern



quatsch... die Koordinierung erfolgt ja über die Steuerung.

ev. könnte die Benachrichtigung des Spiels über die Steuerung gehen.

es ist ein anderer Ansatz der nicht unbedingt besser oder schlechter ist...
und er wollte eben ohnr Threads...





> [...]Was macht das Spiel für einen Sinn, wenn nicht gewartet wird???


schaut ihr eigentlich richtig hin?
Die Steuerung benachrichtigt den Spieler das er am Zug ist.
wenn nicht ist z.B  die "Zugfunktion" nicht aktiviert.


----------



## Marco13 (17. Jul 2008)

"Ohne Threads" hast du jetzt schon zweimal erwähnt. Einer läuft mindestens, aber spätestens bei einem GUI läuft auch noch der Event-Dispatch-Thread, da kommt man nicht drumrum - und zwischen dem main Thread und dem EDT muss man irgendwie synchronisieren.

Trotzdem könntest du - wenn du so einen Vorschlag mit listenern machst - (mir) mal genauer beschreiben, wie der Ablauf dann sein sollte - also WAS genau die Methoden machen sollen.


```
class SomePlayer
{
    void duBistDran()
    {
        // >>>>>>>>>>>> was soll hier passieren? <<<<<<<<<<<<<<<
        
        // (Hint: Wenn diese Methode nicht blockiert, hat man ein Problem...)
    }

    void actionPerformedOderSo()
    {
        ...
        for (listener...) listener.nextMove(move); // Wird im EDT aufgerufen
        ...
    }

}


class Game
{
    public void nextMove(Move move)  
    {
        ...
        players.get(counter).duBistDran(); 
        ...
    }
}
```

 :!:  :?:  :!: 

Man könnte da irgendwas mit irgendwelchen flags rum-murksen, aber wie das deiner Meinung nach aussehen würde, würde mich jetzt schon interessieren.

FALLS(!) ich das richtig verstanden habe, wäre das ja sowas wie
game.sagEinemSpielerBescheidDassErMirBescheidSagenDarfDassErEinenZugGemachtHat();
und dieses gegenseitige "bescheid sagen" müßte man irgendwie in eine vernünftige Reihenfolge bringen - da wäre sowas wie
game.holeDirZugVonSpieler();
irgendwie einfacher...
Aber vielleicht habe ich das auch falsch interpretiert.

BTW: ... ob ein ZUG tatsächlich die Kompetenz haben sollte, zu entscheiden, ob er der "istLastMove" (d.h. ob das Spiel zuende ist) ist, ist IMHO aber SEHR fraglich....


----------



## parabool (17. Jul 2008)

:gaen: 

Ja natürlich läuft das mindestens 1 Thread.
Die eigentliche Spielsteuerung  wollte er jedoch ohne Threads machen oder ?



> Aber vielleicht habe ich das auch falsch interpretiert.


hast Du. bzw. da gibt es nichts falsch zu interpretieren.


```
players.get(counter).duBistDran();
```
wird in Steuerung aufgerufen in der implementierung der Methode nextMove() des
MoveListener-Interfaces

Game wird NUR über die Züge benachrichtigt.


```
game.sagEinemSpielerBescheidDassErMirBescheidSagenDarfDassErEinenZugGemachtHat();
```
kommt also überhaupt nicht vor



> uob ein ZUG tatsächlich die Kompetenz haben sollte, zu entscheiden, ob er der "istLastMove" (d.h. ob das Spiel zuende ist) ist, ist IMHO aber SEHR fraglich....


stimmt




> Trotzdem könntest du - wenn du so einen Vorschlag mit listenern machst - (mir) mal genauer beschreiben, wie der Ablauf dann sein sollte - also WAS genau die Methoden machen sollen



naja ist doch alles irgendwie klar. Aber am WE finde ich vllt etwas Zeit aber jetzt...


----------



## Marco13 (17. Jul 2008)

Da bin ich gespannt


----------



## xysawq (17. Jul 2008)

Also ich finde, man kann ein Spiel mit mehreren Spielern nur auf 2 Arten lösen:

1. ohne Threads... linearer Ablauf bei dem gerechnet wird bis der HumanPlayer einen Zug machen muss (zu diesem Zeitpunkt wartet das Programm auf den Spieler, bis der seine Entscheidung getroffen hat).

2. mit Threads... dort läuft das Spiel an sich ununterbrochen und es wird immer nur einem Spieler die Möglichkeit gegeben etwas zu tun... macht aber IMHO nur Sinn, wenn es entscheident ist, dass während der Spieler überlegt etwas passieren muss (z.B. ein Countdown der zeigt, wieviel Zeit der Spieler noch für seinen Zug hat bevor er verliert, oder eine witzige Spielbrett-Animation)... ansonsten ist das sehr sinnfrei.


Warum sollte das Programm, wenn keine Threads erlaubt sind, nicht auch warten?


----------



## Ishildur (17. Jul 2008)

Guten Abend allerseits
Als erstes möchte ich sagen, dass ich sehr überrascht und auch begeistert bin von den vielen Post und die grosse Mühe, die ihr euch gebt, um mir zu helfen.

Ich habe mir nun sämtliche Post einmal aufmerksam durchgelesen und muss einige Punkte richtigstellen beziehungsweise bekräftigen:

1. Wie man das Problem mit mehreren Threads lösen kann, ist mir völlig klar! Allerdings muss die Applikation zu einem späteren Zeitpunkt auf eine Plattform portiert werden, die KEIN Multithreading unterstützt!

2. Das eigentliche PROBLEM besteht darin, dass die "nextMove" Methode des Humanplayers nicht blockiert!


```
public abstract class Player{
 public Move nextMove(Game gam);
}

public class AI extends Player{
 public Move nextMove(Game gam){
  // Spielbaum mit Minimax Algorithmus und Alpha-Beta Pruning sowie dynamischer Suchtiefen optierung
  return new Move(6,5); // oder so, der springende Punkt ist, diese Methode blockiert, bis der entsprechende Move berechnet ist
 }
}

public class Human extends Player implements MouseListener,MouseMotionListener{
 public Move nextMove(Game gam){
  // was zum Teufel soll ich hier machen??? KEINE THREADS!!!!!!
 }

 public void mouseReleased(){
  // weil der Move wird ja in dieser Methode berechnet
  if(this.gam.isMoveValid(4,5)) this.mov = new Move(4,5); 
 }
}

public void GameLoop(){
 while(game.getWinner() == Game.PLAYER_NONE){
  game.setMove(p1.nextMove());
  game.setMove(p2.nextMove());
 }
}
```

Die Variante mit den Listeners habe ich mir natürlich ebenfalls durch den Kopf gehen lassen, aber hier gibt es meiner Meinung nach einen Stackoverflow infolge von Zirkulären Methodenaufrufen:


```
interface MoveListener{
 public void moveExecuted();
}

class Game{
 public MoveListener lstMov; 

 public void setMove(byte src,byte dst){
  // validiere und führe den gewünschten Spielzug aus  

 this.lstMov.moveExecuted(); // informiere wen auch immer darüber, dass ein Zug getan wurde
}

public abstract class Player{
 public void nextMove(Game gam);
}

public class AI extends Player{
 public Move nextMove(Game gam){
  // Spielbaum mit Minimax Algorithmus und Alpha-Beta Pruning sowie dynamischer Suchtiefen optierung
  this.gam.setMove(4,5); // der Move wird direkt auf der Game klasse ausgeführt ausgeführt
 }
}

public class Human extends Player implements MouseListener,MouseMotionListener{
 public Move nextMove(Game gam){
  // OK, hier aktiviere ich einfach die GUI: JComponent.setEntabled(true);
 }

 public void mouseReleased(){
  // weil der Move wird ja in dieser Methode berechnet
  if(this.gam.isMoveValid(4,5)) this.gam.setMove();
 }
}

public class GameLogic implement MoveListener{
 public void moveExecuted(){
  this.pa.nextMove();
  this.pa = this.pa == this.p1 ? this.p2 : this.p1;
 }
}
```
Wie gesagt, dies führt aber über kurz oder lang zu einem Stackoverflow, ist also auch keine Lösung!

Ich hoffe, ihr seht das Problem, die Probleme!  :? 

Mfg Ishildur


----------



## SebiB90 (17. Jul 2008)

also hab jetzt nicht alle Beiträge durchgelesen, nur überflogen.
Zu der letzten Variante hab ich eine Frage:
Wo wird GameLoop() aufgerufen? Wenn es im EDT aufgerufen wird, kannst du es nicht mit blockieren lösen, weil sonst die GUI einfriert. Beim blockieren brauchst du ein weiteren Thread neben dem EDT. Ich hab es bei meinem Spiel so gelöst, dass das Warten auf die User Eingabe in einem extra Thread läuft mit einer blockierung mithilfe des Concurrency package.

Was mir noch einfällt:
Du kannst einfach die GUI immer verfügbar machen für den Spieler und bei jedem Klick erst überprüfen, ob der Spieler dran ist und nur dann was machen. Wenn der Gegner ein menschlicher Spieler ist, dann einfach umstellen das nächster Klick für den anderen Spieler ist. Wenn der Gegner der Computer ist, dann nach dem Klick die Computer-Logik aufrufen.

Edit:


> Wie gesagt, dies führt aber über kurz oder lang zu einem Stackoverflow, ist also auch keine Lösung!


Das doch aber nur wenn beide Spieler Computer sind. 
Sonst seh ich da keinen Stackoverflow.

Edit2:
jetzt seh ich den Stackoverflow.
Du musst den Spieler erst ändern und dann nextMove() Aufrufen.
Weil wenn bei dir der Computer einmal dran ist, er immer dran ist.

```
public void moveExecuted(){ 
  this.pa = this.pa == this.p1 ? this.p2 : this.p1; 
  this.pa.nextMove();  
}
```
so muss das sein


----------



## Ishildur (17. Jul 2008)

> Das doch aber nur wenn beide Spieler Computer sind.


Richtig! Aber diese Variante muss eben auch möglich sein, OHNE Stackoverflow!


----------



## SebiB90 (17. Jul 2008)

```
public void moveExecuted(){  
  SwingUtilities.invokeLater(new Runnable() {
  public void run() {
   this.pa = this.pa == this.p1 ? this.p2 : this.p1; 
   this.pa.nextMove(); 
  }
 });
}
```
so...ich hoffe da is kein syntax fehler drinne, aber so sollte es möglich sein ohne stackoverflow.


----------



## Ishildur (17. Jul 2008)

OHNE THREADS  :noe:


----------



## SebiB90 (17. Jul 2008)

bevor du meckerst, ließ die API.
Das Game wird sicha über die GUI gestartet, also im EventDispatcherThread(EDT).
Die Methode verlagert die ausführung nur an Ende des EDT..also das die bischen später ausgeführt ist, sobald alle Events abgearbeitet sind. Es wird also KEIN Thread erzeugt, sondern die Methode in einem BEREITS EXISTIERENDEN Thread ausgeführt.


----------



## Ishildur (17. Jul 2008)

@SebiB90
"Meckern" ist ein bisschen ein hartes Wort!   
Ich bin dir doch dankbar, dass du versuchst mir zu helfen. Also ich lasse nun mal die Katze aus dem Sack. Ich muss dasselbe Spiel später zu lernzwecken noch in 16-Bit Assembler schreiben und ich mache mir halt jetzt schon Gedanken über eine Architektur, die ich dann auch in Assembler umsetzen kann. Und glaube mir, da gibts kein InvokeLater und ähnliche Dinge  :?  Da werde ich einen Main-Loop haben und fertig.


----------



## SebiB90 (17. Jul 2008)

naja...ich kann kein assembler und kenn mich daher damit nicht aus.
aber du kannst schlecht java mit einer maschienensprache vergleichen, denke ich.
da wirds wohl sprachen spezifische lösungen geben.


----------



## Ishildur (17. Jul 2008)

Ich glaube nicht in Bezug auf die Architekur einer Software


----------



## SebiB90 (17. Jul 2008)

wie willste es denn da machen?
auch mit ner gui? weil hab da ka man sowas in Assembler macht.
kenn da nur befehle wie "mov al, 61h" oder so.
daher wo werden da die Events von der GUI bearbeitet...da muss es doch sowas ähnliches geben wie den EDT oder so


----------



## diggaa1984 (17. Jul 2008)

ich glaube schon das man n grossen unterschied in der architektur zwischen einer OOP-sprache machen kann und Assembler .. einfach mal schon von der abstraktion her. warum sollte man sich in Java mit etwas quälen wofür es gar nicht gedacht ist. Die denkweise OOP zu programmieren is denk ich mal nicht mit Assembler vereinbar 

Ich hab auch ma minimalst was in Assembler programmiert, das war mir zu abstrakt um dort ein derartiges komplexes konzept aufbauen zu können. In OOP fällt einem das mit sicherheit viel leichter


----------



## BjörnBu (18. Jul 2008)

Sorry ich bin zu faul so viel Arbeti zu investieren wie die andern.

hier aber ein vorschlag:
schluss mit GUI.

So weit ch das mitbekommen habe ist es eh keine Verteile Applikation sondern läuft auf einem Rechner. Also Kommandozeile, das Spiel kennt zwei player (eventell ein Spielfeld und eventuell Methoden um zu überprüfen ob Züge valide sind, obs es einen gewinner gibt und was weiß ich nich alles. lass am besten erstmal alles weg)

Ein Spieler kann Human oder Computer Spieler sein. Das Spiel macht nichts anderes als awechselnd p1.makeMove() und p2.makeMove(). Dazwischen ggf überprüfen ausgaben usw.
Computer laufen direkt durch, auf menschen wartet meinen wegen einfach ein BufferedReader br.readln() oder was auch immer. 

Danach kannst du das Ganze in einem iterativen Prozess erweitern. Sobald du GUIs machst, brauchst du zwangsläufig Threads. Ich kenne mich mit modernem Assembler leider auch nicht aus (hab nur mal in ner vorlesung was mit 8086 machen müssen), aber da MUSS es sowas zwangsläufig auch geben. Und auf den alten Prozessoren gäbe es auch niemals Swing GUIs.


----------



## Marco13 (18. Jul 2008)

Hmgl - wenn du ein GUI hast, hast du Threads, und dann must du synchronisieren, weil sonst die getMove-Methode des HumanPlayers nicht blockiert. 
Wenn du in Assembler programmierst, wirst du sicher kein GUI haben, und dann brauchst du auch keine synchronisation, weil DANN die getMove-Methode des HumanPlayers blockieren wird. Da wirst du kaum was dagegen machen können.


----------



## Ishildur (18. Jul 2008)

Ja lasst uns nicht darüber streiten, was man in Assembler machen kann und was nicht, kommen wir zurück zum Kernproblem!  :lol: 

Also ich kann nun möglicherweise das Problem noch ein wenig anders definieren: Es ist klar, dass auf den HumanPlayer gewartet werden muss, die Frage ist jedoch, wie!

Möglichkeit 1: Eine Funktion mit einer Schleife überprüft in regelmässigen Abständen, ob denn nun der Move getan wurde und kehrt schliesslich zum Hauptprogramm zurück. Problem: Diese Variante benötigt Multithreading, weil ansonsten die komplette GUI einfriert!

Möglichkeit 2: Irgendwas mit Notifikationen: Bsp. Spieler 1 ruft Spieler 2 auf, nachdem er den Zug gemacht hat. Spieler 2 ruft schliesslich wieder Spieler 1 auf. Das Problem hierbei sind die zirkulären Methodenaufrufe, welche mit Sicherheit in einem Stackoverflow enden.

Möglichkeit 3: Ich habe noch an ein Master/Slave System gedacht, aber dies funktioniert nicht mehr, sobald man 2 HumanPlayers hat.

Es ist doch zum wahnsinnig werden!  :? 

Nehmen wir mal an, man würde dieses Spiel in einem C++ Programm ohne Multithreading programmieren, dass muss doch verdammt noch mal möglich sein:

Eingabeverarbeitung -> Spiellogik -> Rendering -> Eingabeverarbeitung

Wie würde man es da machen?


----------



## SebiB90 (18. Jul 2008)

Also ich würde dir raten dich damit an ein Assembler Forum zu wenden.
Die wenigstens werden hier wissen, wie Assembler richtig funktioniert und was seine besonderheiten sind, die beachtet werden müssen.

Wenn du eine GUI in Java nutzt wirkt es zwingend ein Multithreadprogramm (der Thread indem main() aufgerufen wird + EDT). Also nimms so hin, wenn du java nutzen willst. Du kannst es eben nicht 1:1 kopieren in eine andere Programmiersprache.


----------



## Marco13 (18. Jul 2008)

Irgendwie wird mir das jetzt zu albern - man würde auch nicht in einem Forum über Webdesign fragen, wie man seine Homepage gestalten könnte, und dann alle Verschläge, die mit HTML zusammenhängen, abschmettern, weil man die Homepage ja auch später noch als Ölgemälde erstellen will  :? Ich bin dann mal weg.


----------



## BjörnBu (18. Jul 2008)

Ishildur hat gesagt.:
			
		

> Also ich kann nun möglicherweise das Problem noch ein wenig anders definieren: Es ist klar, dass auf den HumanPlayer gewartet werden muss, die Frage ist jedoch, wie!



Meiner Meinung nach liegt genau hier das Problem. Explizit warten musst du überhaupt nur bei multi Threading. An sonsten nutzt du einfach 'ne Eingabe, die blockiert. Wie das lesen aus der Kommandozeile z.B., oder das warten auf 'ne Nachrichit über 'nen Socket, oder oder oder.


----------



## Ishildur (18. Jul 2008)

Hehe und genau hier liegt der Hund begraben: Eine Eingabe über die Gui blockiert nun mal nicht!


----------



## SebiB90 (18. Jul 2008)

Ishildur hat gesagt.:
			
		

> Hehe und genau hier liegt der Hund begraben: Eine Eingabe über die Gui blockiert nun mal nicht!


Doch, teilweise schon. Ein modaler Dialog wie z.b. auch JOptionPane blockiert bis das Fenster wieder geschlossen ist.



> Irgendwie wird mir das jetzt zu albern - man würde auch nicht in einem Forum über Webdesign fragen, wie man seine Homepage gestalten könnte, und dann alle Verschläge, die mit HTML zusammenhängen, abschmettern, weil man die Homepage ja auch später noch als Ölgemälde erstellen will  Ich bin dann mal weg.


Jo, ich glaub ich schließ mich dir an.


----------



## parabool (19. Jul 2008)

ok, jetzt bei genaueren hinsehen: bei meiner Idee tritt Rekursion auf.
Hätte es eigentlich sehen müssen.   ???:L


----------



## xysawq (23. Jul 2008)

Will auch noch mal meinen Senf dazu geben und sagen, dass der liebe Herr Ishildur mit seinem ständigen blockiert nicht und keine Threads mehr als verwirrt hat.

Gibt es keine Threads dann wird automatisch blockiert. Gibt es Threads braucht man ein spezielles vorgehen zum Blockieren wie eine while(boolean)-Schleife die erst verlassen wird, wenn der Zug getan und der boolean auf false gelegt wurde, jedoch soll es ja keine Threads geben.

Im Assembler habe ich schon viel Programmiert (deswegen tue ich mich gelegentlich in Java schwer, weil man in Assembler sein Ziel meist nur über Umwege erreicht und Java wesentlich komplexer ist), und ich muss zugeben, eine GUI in Assembler ist sauschwer... da gibts maximal Textausgabe.

Wenn du dein GUI-Java-Programm in Assembler haben willst, kompilier es in Assembler!


Aber führe hier nicht alle an der Nase herum nur weil du nicht wahr haben willst, dass es eine GUI mit HumanPlayer nur in Verbindung mit Threads gibt.


----------

