# Busy waiting



## WieselAc (16. Jan 2007)

Hallo,

ich hab mich auch mal in die Welt der Spieleprogrammierung gewagt. Und jetzt hab ich mein erstes GUI Spiel lauffähig. Es ist die 2Mio Variante von Snake. Hab mir dazu ein paar Tipps und Angrungen in den zahlreichen Threads dazu geholt und jetzt läuft es auch wunderbar (wie ich finde). 

Allerdings stört mich noch etwas und zwar kann soll man mit der "pause"taste das Spiel anhalten können. Dies klappt auch, aber ich glaube die Lösung ist nicht gerade elegant. Hat vielleicht jemand einen Tipp wie ich das anders lösen kann?

Danke schonmal!!


und hier die, wie ich finde, relevaten Stellen (bei bedarf poste ich auch gerne mehr):


```
private boolean run;

  private void initFrame() {
       
        ...

        this.addKeyListener(new KeyListener() {

            public void keyReleased(KeyEvent k) {
            }

            public void keyPressed(KeyEvent k) {
                // Wenn die Pause taste gedrückt wurde wird eine Flag aktiviert/deaktiviert
                if (k.getKeyCode() == 19) {
                    run = !run;
                }
                else if (run && snake.isAllowedDirection(k.getKeyCode())) {
                    keys.add(new Integer(k.getKeyCode()));
                }
            }

            public void keyTyped(KeyEvent k) {
            }
        });

      ...
    }

 public void start() {
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        try {
           
            while (!snake.isCollision()) {
                if (!run) {
                    continue;
                }

                // bewege/ prüfe/ male etc...

                Thread.yield();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // ... ende
    }
```


----------



## Marco13 (16. Jan 2007)

Ungefähr so ....


```
private boolean run;

  private void initFrame() {
       
        ...

        this.addKeyListener(new KeyListener() {

            public void keyReleased(KeyEvent k) {
            }

            public void keyPressed(KeyEvent k) {
                // Wenn die Pause taste gedrückt wurde wird eine Flag aktiviert/deaktiviert
                if (k.getKeyCode() == 19) 
                {
                    run = !run;
                    if (run) //------------------------------------- Ggf. das Spiel aufwecken
                    { 
                        synchronized(EinschließendeKlasse.this)
                        {
                            notify();
                        }
                    }
                }
                else if (run && snake.isAllowedDirection(k.getKeyCode())) {
                    keys.add(new Integer(k.getKeyCode()));
                }
            }

            public void keyTyped(KeyEvent k) {
            }
        });

      ...
    }

public void start() {
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        try {
           
            while (!snake.isCollision()) {

                while (!run) 
                {
                    synchronized(this)  //------------------------------------- Spiel schlafen legen
                    {
                        wait();
                    }
                }

                // bewege/ prüfe/ male etc...

                Thread.yield();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // ... ende
    }
```


----------



## WieselAc (17. Jan 2007)

Danke für den Tipp. Ha es jetzt mal eingebaut und erhalte eine java.lang.IllegalMonitorStateException. hier der genau StackTrace:


```
Exception in thread "AWT-EventQueue-0" java.lang.IllegalMonitorStateException: current thread not owner
	at java.lang.Object.notify(Native Method)
	at ekans.Emag$1.keyPressed(Emag.java:110)
	at java.awt.Component.processKeyEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Window.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source)
	at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(Unknown Source)
	at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(Unknown Source)
	at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source)
	at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
```

EDIT: Der Fehler Tritt auf, wenn ich den Thread wieder aufwachen lasse.Schlafen legen funktioniert.


----------



## Marco13 (17. Jan 2007)

Ich hatte geschrieben


```
if (run) //------------------------------------- Ggf. das Spiel aufwecken
                    {
                        synchronized(EinschließendeKlasse.this)
```

Dabei bin ich davon ausgegangen, dass die beiden Methoden (run und der KeyListener) in der gleichen Klasse liegen (nämlich EinschließendeKlasse). Entscheidend ist, dass an beiden stellen auf dasselbe Objekt synchronisiert wird. Ist das bei dir der Fall?


----------



## WieselAc (17. Jan 2007)

Ich denke mal schon, aber zur Sicherheit mal die ganze Klasse. Hoffe es ist einigermassen verständlich.


```
package ekans;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;

public class Game extends JFrame {
    /**
     * ID.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Innen Masse des Frames (Hängen von den insest des LOOK an Feel ab).
     */
    public static int FRAME_WIDTH = 200;
    public static int FRAME_HEIGHT = 200;

    /**
     * Die aktiven KOmponeneten auf dem Spielfeld.
     */
    private Ekans snake;
    private int[] stone;
    private int[] extraStone;

    /**
     * Zusafflsgenerator zum erzeugen beliegiger Positionen auf dem Feld.
     */
    private Random random;
    /**
     * Repaint Strtegie mit der ein optimiertes update möglich wird.
     */
    private BufferStrategy strategy;

    /**
     * Die Statistik zum auswerten des Spiels (Punkte, Steine etc.)
     */
    private Statistik statistik;
    private int counter;

    /**
     * Speicher zum vorhalten der gedrückten Tasten, bis die abgearbeitet sind.
     */
    private List keys;

    /**
     * Flag zum pausieren des Spiels (busy waiting)
     */
    private boolean pause;
    private boolean stop;
    /**
     * Flag die anzeigt ob für diese Anzahl an Steine noch ein Bonus generiert
     * werden soll. Ist wichtig um timeouts zu registrieren.
     */
    private boolean createExtraStone;

    // private int tfps = 0;

    /**
     * Konstruktor, der alle für das Spiel nötigen Komponeneten erzeugt und
     * initialsiert.
     */
    public Game() {
        random = new Random();
        initData();
        initFrame();
        this.createBufferStrategy(2);
        strategy = this.getBufferStrategy();
    }

    /**
     * Initialisert die lokalen Variablen für die Spieldatenenthalten.
     */
    public void initData() {
        statistik = new Statistik();
        stone = new int[2];
        snake = new Ekans(statistik);
        pause = true;
        stop = false;
        stone = createStone();
        extraStone = new int[] { -1, -1, 0 };
        counter = 0;
        keys = new LinkedList();
    }

    /**
     * Initialisiert den Frame. Es wird die Größe, der Title und ein KeyListener
     * geadded.Zusätzlich wird der Frame noch mit einer EXIT_ON_CLOSE Option
     * versehen und sichtbar gemacht. Der Frame muss hier schon sichtbargemacht
     * werden, da später seine genauen Masse und die BufferStartegie benötigt
     * werden, die erst nach dem Sichtbarmachen des Frames gesetzt sind.
     */
    private void initFrame() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setTitle("Testspiel");
        this.setSize(new Dimension(FRAME_WIDTH + 8, FRAME_HEIGHT + 46));
        this.setMenuBar(new MenuGame(this)); 
        this.addKeyListener(new KeyListener() {

            public void keyReleased(KeyEvent k) {
            }

            public void keyPressed(KeyEvent k) {
                if (k.getKeyCode() == 19) {
                    pause = !pause;
                    // if (run) {
                    // synchronized (Game.this) {
                    // notify();
                    // }
                    // }
                } else if (pause && snake.isAllowedDirection(k.getKeyCode())) {
                    keys.add(new Integer(k.getKeyCode()));
                }
            }

            public void keyTyped(KeyEvent k) {
            }
        });
        setVisible(true);
    }

    /**
     * Erzeugt einen Stein, der auf dem Spielfeld, aber nicht auf der Schlange
     * liegt.
     * 
     * @return eine Stein, der frei im Spielfeld liegt
     */
    private int[] createStone() {
        int[] stone = {
                random.nextInt(FRAME_WIDTH / Ekans.SNAKE_WIDTH)
                        * Ekans.SNAKE_WIDTH,
                random.nextInt(FRAME_HEIGHT / Ekans.SNAKE_HEIGHT)
                        * Ekans.SNAKE_HEIGHT };
        if (snake.isCollision(stone)) {
            return createStone();
        }
        return stone;
    }

    /**
     * Erzeugt einen extra Stein, der Bonuspunkte bringt und eine beschränkte
     * Lebensdauer hat.
     * 
     * @return einen Stein mit einer begrenzten Lebensdauer
     */
    private int[] createExtraStone() {
        int[] point = createStone();
        return new int[] { point[0], point[1], 10 };
    }

    public void start() {
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        try {
            long s = System.currentTimeMillis();
            // int fps = 0;
            while (!snake.isCollision() && !stop) {
                // while (!run) {
                // synchronized (this) {
                // wait();
                // }
                // }
                if (!pause) {
                    continue;
                }
                if (keys.size() != 0) {
                    statistik.removePoints();
                    snake.keyResolver(((Integer)keys.get(0)).intValue());
                    keys.remove(0);
                }
                if (counter % 4 == 0 && createExtraStone) {
                    extraStone = createExtraStone();
                    createExtraStone = false;
                }
                snake.move(false);
                if (snake.addPoint(stone, false)) {
                    stone = createStone();
                    createExtraStone = true;
                    counter++;
                } else if (snake.addPoint(extraStone, true)) {
                    removeExtraStone();
                    createExtraStone = false;
                }
                Thread.sleep(200);
                myUpdate(getBackground());

                // fps++;
                if (System.currentTimeMillis() - s > 1000) {
                    // tfps = fps;
                    // fps = 0;
                    if (extraStone[2] == 0) {
                        removeExtraStone();
                        createExtraStone = false;
                    } else if (extraStone[2] > 0) {
                        extraStone[2] = extraStone[2] - 1;
                    }
                    s = System.currentTimeMillis();
                }
                Thread.yield();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Führt alle beim beenden nötigen Schritte aus. Im wesentlichen wird bei
     * einem Crash dieser durch blinken der Applikation scichtbar gemacht und
     * die aktuelle Statistik gespeichert.
     */
    public void finish() {
        stop = true;
        if (snake.isCollision()) {
            for (int i = 0; i < 3; i++) {

                try {
                    myUpdate(Color.WHITE);
                    Thread.sleep(200);
                    myUpdate(getBackground());
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                }
            }
            statistik.save("src\\ekans\\score.txt");
        }
    }

    /**
     * Setzt den extra Stein auf den default Wert, nachdem er eingesammelt wurde
     * oder die Zeit abgelaufen ist.
     */
    private void removeExtraStone() {
        extraStone = new int[] { -1, -1, -1 };

    }

    /**
     * Zeichnet den aktuellen Spielstand mit der Standardhintergrundfarbe des
     * look and feel.
     */
    public void myUpdate() {
        myUpdate(getBackground());
    }

    /**
     * Zeichnet den aktuellen Spielstand mit der gewünschten Hintergrundfarbe.
     * 
     * @param c
     *            die gewünschte Hintergrundfarbe
     */
    public void myUpdate(Color c) {
        Insets insets = getInsets();
        
        Graphics g = strategy.getDrawGraphics();
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setFont(new Font("Arial", Font.ITALIC | Font.PLAIN, 10));

        g2.setColor(c);
        g2.fillRect(insets.left, insets.top, FRAME_WIDTH, FRAME_WIDTH);
        g2.setColor(Color.BLACK);

        // g.drawString("fps: " + tfps, FRAME_WIDTH - 100, 50);

        snake.draw(g2, insets);

        statistik.drawPoints(g2, insets);

        g2.setColor(Color.RED);

        g2.fillOval(stone[0] + insets.left, stone[1] + insets.top,
                Ekans.SNAKE_WIDTH, Ekans.SNAKE_HEIGHT);

        if (extraStone[0] != -1 && extraStone[1] != -1) {
            g2.setColor(Color.GREEN);
            g2.fillOval(extraStone[0] + insets.left,
                    extraStone[1] + insets.top, Ekans.SNAKE_WIDTH,
                    Ekans.SNAKE_HEIGHT);
            g2.drawString(timeToString(), FRAME_WIDTH - insets.left - 5,
                    insets.top + g2.getFont().getSize() + 2);
        }
        strategy.show();
    }

    /**
     * Wandelt die verbleibende Zeit des Extrasteins in einen zweistelligen
     * String um.
     * 
     * @return die formatierte Restzeit
     */
    private String timeToString() {
        if (extraStone[2] < 10) {
            return "0" + extraStone[2];
        } else {
            return "" + extraStone[2];
        }
    }

    /**
     * {@inheritDoc}.
     */
    public void paint(Graphics arg0) {
    }

    /**
     * {@inheritDoc}.
     */
    public void update(Graphics arg0) {
    }

    /**
     * Startet das Spiel.
     * 
     * @param args
     *            default Eingabeparameter
     */
    public static void main(String[] args) {
        Game game = new Game();
        game.start();
        game.finish();
    }
}
```


----------



## Marco13 (18. Jan 2007)

Sorry. Da hab ich jetzt selbst wieder was gelernt. Normalerweise kann man in einer Inneren Klasse mit
EinschließendeKlasse.this
auf das "übergeordnete this" zugreifen. In deinem Fall greift man mit
Game.this
auf das Game-Objekt zu, in dem der (innere) KeyListener liegt. Aber offenbar funktioniert das NICHT für synchronisation (OBWOHL es scheinbar wirklich das richtige Objekt ist). Dass es ein Bug ist, kann ich mir kaum vorstellen - aber vielleicht wird das in der Java-Sprachspezifikation irgendwo im Kleingedruckten erwähnt   

Umgehen kann man dieses Problem, indem man das "notify()" in eine private Methode von "Game" packt. Habe das Beispiel von dir mal "pseudo-Lauffähig" gemacht. (Welche Taste hat KeyCode 19?) However. Man kann dort jetzt mit "p" zwischen Pause und NichtPause hin-und herschalten.

Entscheidend is eigentlich nur die doNotify-Methode, aber es ist immer schön, was zu haben, was man einfach Rauskopieren, Compilieren und Starten kann  :wink: 


```
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;

public class Game extends JFrame {
    /**
     * ID.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Innen Masse des Frames (Hängen von den insest des LOOK an Feel ab).
     */
    public static int FRAME_WIDTH = 200;
    public static int FRAME_HEIGHT = 200;

    /**
     * Die aktiven KOmponeneten auf dem Spielfeld.
     */
    private Object snake = null; //Ekans snake;
    private int[] stone;
    private int[] extraStone;

    /**
     * Zusafflsgenerator zum erzeugen beliegiger Positionen auf dem Feld.
     */
    private Random random;
    /**
     * Repaint Strtegie mit der ein optimiertes update möglich wird.
     */
    private BufferStrategy strategy;

    /**
     * Die Statistik zum auswerten des Spiels (Punkte, Steine etc.)
     */
    private Object statistik = null; //Statistik statistik;
    private int counter;

    /**
     * Speicher zum vorhalten der gedrückten Tasten, bis die abgearbeitet sind.
     */
    private List keys;

    /**
     * Flag zum pausieren des Spiels (busy waiting)
     */
    private boolean pause;
    private boolean stop;
    /**
     * Flag die anzeigt ob für diese Anzahl an Steine noch ein Bonus generiert
     * werden soll. Ist wichtig um timeouts zu registrieren.
     */
    private boolean createExtraStone;

    // private int tfps = 0;

    /**
     * Konstruktor, der alle für das Spiel nötigen Komponeneten erzeugt und
     * initialsiert.
     */
    public Game() {
        random = new Random();
        initData();
        initFrame();
        this.createBufferStrategy(2);
        strategy = this.getBufferStrategy();
    }

    /**
     * Initialisert die lokalen Variablen für die Spieldatenenthalten.
     */
    public void initData() {
        statistik = null;// new Statistik();
        stone = new int[2];
        snake = null;//new Ekans(statistik);
        pause = true;
        stop = false;
        stone = createStone();
        extraStone = new int[] { -1, -1, 0 };
        counter = 0;
        keys = new LinkedList();
    }

    /**
     * Initialisiert den Frame. Es wird die Größe, der Title und ein KeyListener
     * geadded.Zusätzlich wird der Frame noch mit einer EXIT_ON_CLOSE Option
     * versehen und sichtbar gemacht. Der Frame muss hier schon sichtbargemacht
     * werden, da später seine genauen Masse und die BufferStartegie benötigt
     * werden, die erst nach dem Sichtbarmachen des Frames gesetzt sind.
     */
    private void initFrame() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setTitle("Testspiel");
        this.setSize(new Dimension(FRAME_WIDTH + 8, FRAME_HEIGHT + 46));
        //this.setMenuBar(new MenuGame(this));
        this.addKeyListener(new KeyListener() {

            public void keyReleased(KeyEvent k) {
            }

            public void keyPressed(KeyEvent k) {
                if (k.getKeyChar() == 'p') {
                    pause = !pause;
                    if (!pause) {
                        doNotify();
                    }
                } else { //if (pause && snake.isAllowedDirection(k.getKeyCode())) {
                    keys.add(new Integer(k.getKeyCode()));
                }
            }

            public void keyTyped(KeyEvent k) {
            }
        });
        setVisible(true);
    }

    private void doNotify()
    {
        synchronized (this)
        {
            System.out.println("notify on "+this);
            notify();
        }
    }


    /**
     * Erzeugt einen Stein, der auf dem Spielfeld, aber nicht auf der Schlange
     * liegt.
     *
     * @return eine Stein, der frei im Spielfeld liegt
     */
    private int[] createStone() {
        return null;
        /*
        int[] stone = {
                random.nextInt(FRAME_WIDTH / Ekans.SNAKE_WIDTH)
                        * Ekans.SNAKE_WIDTH,
                random.nextInt(FRAME_HEIGHT / Ekans.SNAKE_HEIGHT)
                        * Ekans.SNAKE_HEIGHT };
        if (snake.isCollision(stone)) {
            return createStone();
        }
        */
        //return stone;
    }

    /**
     * Erzeugt einen extra Stein, der Bonuspunkte bringt und eine beschränkte
     * Lebensdauer hat.
     *
     * @return einen Stein mit einer begrenzten Lebensdauer
     */
    private int[] createExtraStone() {
        int[] point = createStone();
        return new int[] { point[0], point[1], 10 };
    }

    public void start() {
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        try {
            long s = System.currentTimeMillis();
            // int fps = 0;
            while (!stop) { //!snake.isCollision() && !stop) {
                while (pause) {
                    synchronized (this)
                    {
                        System.out.println("wait on "+this);
                        wait();
                        System.out.println("wait DONE");
                    }
                }
                System.out.println("Snake is moving...");
                Thread.sleep(200);

                /*
                if (keys.size() != 0) {
                    statistik.removePoints();
                    snake.keyResolver(((Integer)keys.get(0)).intValue());
                    keys.remove(0);
                }
                if (counter % 4 == 0 && createExtraStone) {
                    extraStone = createExtraStone();
                    createExtraStone = false;
                }
                snake.move(false);
                if (snake.addPoint(stone, false)) {
                    stone = createStone();
                    createExtraStone = true;
                    counter++;
                } else if (snake.addPoint(extraStone, true)) {
                    removeExtraStone();
                    createExtraStone = false;
                }
                Thread.sleep(200);
                myUpdate(getBackground());

                // fps++;
                if (System.currentTimeMillis() - s > 1000) {
                    // tfps = fps;
                    // fps = 0;
                    if (extraStone[2] == 0) {
                        removeExtraStone();
                        createExtraStone = false;
                    } else if (extraStone[2] > 0) {
                        extraStone[2] = extraStone[2] - 1;
                    }
                    s = System.currentTimeMillis();
                }
                Thread.yield();
                */
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Führt alle beim beenden nötigen Schritte aus. Im wesentlichen wird bei
     * einem Crash dieser durch blinken der Applikation scichtbar gemacht und
     * die aktuelle Statistik gespeichert.
     */
    public void finish() {
        stop = true;
        /*
        if (snake.isCollision()) {
            for (int i = 0; i < 3; i++) {

                try {
                    myUpdate(Color.WHITE);
                    Thread.sleep(200);
                    myUpdate(getBackground());
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                }
            }
            statistik.save("src\\ekans\\score.txt");
        }
        */
    }

    /**
     * Setzt den extra Stein auf den default Wert, nachdem er eingesammelt wurde
     * oder die Zeit abgelaufen ist.
     */
    private void removeExtraStone() {
        extraStone = new int[] { -1, -1, -1 };

    }

    /**
     * Zeichnet den aktuellen Spielstand mit der Standardhintergrundfarbe des
     * look and feel.
     */
    public void myUpdate() {
        myUpdate(getBackground());
    }

    /**
     * Zeichnet den aktuellen Spielstand mit der gewünschten Hintergrundfarbe.
     *
     * @param c
     *            die gewünschte Hintergrundfarbe
     */
    public void myUpdate(Color c) {
        Insets insets = getInsets();

        Graphics g = strategy.getDrawGraphics();
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setFont(new Font("Arial", Font.ITALIC | Font.PLAIN, 10));

        g2.setColor(c);
        g2.fillRect(insets.left, insets.top, FRAME_WIDTH, FRAME_WIDTH);
        g2.setColor(Color.BLACK);

        // g.drawString("fps: " + tfps, FRAME_WIDTH - 100, 50);

        //snake.draw(g2, insets);

        //statistik.drawPoints(g2, insets);

        g2.setColor(Color.RED);

        /*
        g2.fillOval(stone[0] + insets.left, stone[1] + insets.top,
                Ekans.SNAKE_WIDTH, Ekans.SNAKE_HEIGHT);

        if (extraStone[0] != -1 && extraStone[1] != -1) {
            g2.setColor(Color.GREEN);
            g2.fillOval(extraStone[0] + insets.left,
                    extraStone[1] + insets.top, Ekans.SNAKE_WIDTH,
                    Ekans.SNAKE_HEIGHT);
            g2.drawString(timeToString(), FRAME_WIDTH - insets.left - 5,
                    insets.top + g2.getFont().getSize() + 2);
        }
        */
        strategy.show();
    }

    /**
     * Wandelt die verbleibende Zeit des Extrasteins in einen zweistelligen
     * String um.
     *
     * @return die formatierte Restzeit
     */
    private String timeToString() {
        if (extraStone[2] < 10) {
            return "0" + extraStone[2];
        } else {
            return "" + extraStone[2];
        }
    }

    /**
     * {@inheritDoc}.
     */
    public void paint(Graphics arg0) {
    }

    /**
     * {@inheritDoc}.
     */
    public void update(Graphics arg0) {
    }

    /**
     * Startet das Spiel.
     *
     * @param args
     *            default Eingabeparameter
     */
    public static void main(String[] args) {
        Game game = new Game();
        game.start();
        game.finish();
    }
}
```


----------



## WieselAc (19. Jan 2007)

Ah super danke!! Genau so hab ich mir das gedacht. Die taste 19 ist die wo pause drauf steht.

Was lauffähiges zu haben wo man gucken kann was passiert, da hast du recht, so etwas ist immer sehr hilfreich. Hätt ich mal vorher dran denken sollen. Naja mit dem Menü und so ist es etwas "viel" geworden. Außerdem bastel ich gerade noch an einer High Score liste, aber so wirklich komm ich nitt dazu. Auf jeden fall hier mal eine laufföhige Version. 

Besser spät als nie!


Klasse Game:


```
package snake;

import Statistik;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;

public class Game extends JFrame {
    /**
     * ID.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Innen Masse des Frames (Hängen von den insest des LOOK an Feel ab).
     */
    public static int FRAME_WIDTH = 200;
    public static int FRAME_HEIGHT = 200;

    /**
     * Die aktiven KOmponeneten auf dem Spielfeld.
     */
    private Snake snake;
    private int[] stone;
    private int[] extraStone;

    /**
     * Zusafflsgenerator zum erzeugen beliegiger Positionen auf dem Feld.
     */
    private Random random;
    /**
     * Repaint Strtegie mit der ein optimiertes update möglich wird.
     */
    private BufferStrategy strategy;

    /**
     * Die Statistik zum auswerten des Spiels (Punkte, Steine etc.)
     */
    private Statistik statistik;
    private int counter;

    /**
     * Speicher zum vorhalten der gedrückten Tasten, bis die abgearbeitet sind.
     */
    private List keys;

    /**
     * Flag zum pausieren des Spiels (busy waiting)
     */
    private boolean pause;
    private boolean stop;
    /**
     * Flag die anzeigt ob für diese Anzahl an Steine noch ein Bonus generiert
     * werden soll. Ist wichtig um timeouts zu registrieren.
     */
    private boolean createExtraStone;

    // private int tfps = 0;

    /**
     * Konstruktor, der alle für das Spiel nötigen Komponeneten erzeugt und
     * initialsiert.
     */
    public Game() {
        random = new Random();
        initData();
        initFrame();
        this.createBufferStrategy(2);
        strategy = this.getBufferStrategy();
    }

    /**
     * Initialisert die lokalen Variablen für die Spieldatenenthalten.
     */
    public void initData() {
        statistik = new Statistik();
        stone = new int[2];
        snake = new Snake(statistik);
        pause = false;
        stop = false;
        stone = new int[] { -1, -1 };
        extraStone = new int[] { -1, -1, 0 };
        stone = createStone();
        counter = 0;
        keys = new LinkedList();
    }

    /**
     * Initialisiert den Frame. Es wird die Größe, der Title und ein KeyListener
     * geadded.Zusätzlich wird der Frame noch mit einer EXIT_ON_CLOSE Option
     * versehen und sichtbar gemacht. Der Frame muss hier schon sichtbargemacht
     * werden, da später seine genauen Masse und die BufferStartegie benötigt
     * werden, die erst nach dem Sichtbarmachen des Frames gesetzt sind.
     */
    private void initFrame() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setTitle("Testspiel");
        this.setSize(new Dimension(FRAME_WIDTH + 8, FRAME_HEIGHT + 46));
        this.setMenuBar(new GameMenu(this));
        this.addKeyListener(new KeyListener() {

            public void keyReleased(KeyEvent k) {
            }

            public void keyPressed(KeyEvent k) {
                if (k.getKeyChar() == 'p' || k.getKeyCode() == 19) {
                    pause = !pause;
                    if (!pause) {
                        doNotify();
                    }

                } else if (!pause && snake.isAllowedDirection(k.getKeyCode())) {
                    keys.add(new Integer(k.getKeyCode()));
                }
            }

            public void keyTyped(KeyEvent k) {
            }
        });
        setVisible(true);
    }

    private void doNotify() {
        synchronized (this) {
            System.out.println("notify on " + this);
            notify();
        }
    }

    /**
     * Erzeugt einen Stein, der auf dem Spielfeld, aber nicht auf der Schlange
     * liegt.
     * 
     * @return eine Stein, der frei im Spielfeld liegt
     */
    private int[] createStone() {
        int[] newStone = {
                random.nextInt(FRAME_WIDTH / Snake.SNAKE_WIDTH)
                        * Snake.SNAKE_WIDTH,
                random.nextInt(FRAME_HEIGHT / Snake.SNAKE_HEIGHT)
                        * Snake.SNAKE_HEIGHT };
        if (snake.isCollision(newStone) || newStone[0] == stone[0]
                && newStone[1] == stone[1] || newStone[0] == extraStone[0]
                && newStone[1] == extraStone[1]) {
            return createStone();
        }
        return newStone;
    }

    /**
     * Erzeugt einen extra Stein, der Bonuspunkte bringt und eine beschränkte
     * Lebensdauer hat.
     * 
     * @return einen Stein mit einer begrenzten Lebensdauer
     */
    private int[] createExtraStone() {
        int[] point = createStone();
        return new int[] { point[0], point[1], 10 };
    }

    public void start() {
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        try {
            long s = System.currentTimeMillis();
            // int fps = 0;
            while (!snake.isCollision() && !stop) {
                while (pause) {
                    synchronized (this) {
                        wait();
                    }
                }
                if (keys.size() != 0) {
                    statistik.removePoints();
                    snake.keyResolver(((Integer)keys.get(0)).intValue());
                    keys.remove(0);
                }
                if (counter % 4 == 0 && createExtraStone) {
                    extraStone = createExtraStone();
                    createExtraStone = false;
                }
                snake.move(false);
                if (snake.addPoint(stone, false)) {
                    stone = createStone();
                    createExtraStone = true;
                    counter++;
                } else if (snake.addPoint(extraStone, true)) {
                    removeExtraStone();
                    createExtraStone = false;
                }
                Thread.sleep(200);
                myUpdate(getBackground());

                // fps++;
                if (System.currentTimeMillis() - s > 1000) {
                    // tfps = fps;
                    // fps = 0;
                    if (extraStone[2] == 0) {
                        removeExtraStone();
                        createExtraStone = false;
                    } else if (extraStone[2] > 0) {
                        extraStone[2] = extraStone[2] - 1;
                    }
                    s = System.currentTimeMillis();
                }
                Thread.yield();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Führt alle beim beenden nötigen Schritte aus. Im wesentlichen wird bei
     * einem Crash dieser durch blinken der Applikation scichtbar gemacht und
     * die aktuelle Statistik gespeichert.
     */
    public void finish() {
        stop = true;
        if (snake.isCollision()) {
            for (int i = 0; i < 3; i++) {

                try {
                    myUpdate(Color.WHITE);
                    Thread.sleep(200);
                    myUpdate(getBackground());
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                }
            }
            statistik.save("src\\score.txt");
        }
    }

    /**
     * Setzt den extra Stein auf den default Wert, nachdem er eingesammelt wurde
     * oder die Zeit abgelaufen ist.
     */
    private void removeExtraStone() {
        extraStone = new int[] { -1, -1, -1 };

    }

    /**
     * Zeichnet den aktuellen Spielstand mit der Standardhintergrundfarbe des
     * look and feel.
     */
    public void myUpdate() {
        myUpdate(getBackground());
    }

    /**
     * Zeichnet den aktuellen Spielstand mit der gewünschten Hintergrundfarbe.
     * 
     * @param c
     *            die gewünschte Hintergrundfarbe
     */
    public void myUpdate(Color c) {
        Insets insets = getInsets();

        Graphics g = strategy.getDrawGraphics();
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setFont(new Font("Arial", Font.ITALIC | Font.PLAIN, 10));

        g2.setColor(c);
        g2.fillRect(insets.left, insets.top, FRAME_WIDTH, FRAME_WIDTH);
        g2.setColor(Color.BLACK);

        // g.drawString("fps: " + tfps, FRAME_WIDTH - 100, 50);

        snake.draw(g2, insets);

        statistik.drawPoints(g2, insets);

        g2.setColor(Color.RED);

        g2.fillOval(stone[0] + insets.left, stone[1] + insets.top,
                Snake.SNAKE_WIDTH, Snake.SNAKE_HEIGHT);

        if (extraStone[0] != -1 && extraStone[1] != -1) {
            g2.setColor(Color.GREEN);
            g2.fillOval(extraStone[0] + insets.left,
                    extraStone[1] + insets.top, Snake.SNAKE_WIDTH,
                    Snake.SNAKE_HEIGHT);
            g2.drawString(timeToString(), FRAME_WIDTH - insets.left - 5,
                    insets.top + g2.getFont().getSize() + 2);
        }
        strategy.show();
    }

    /**
     * Wandelt die verbleibende Zeit des Extrasteins in einen zweistelligen
     * String um.
     * 
     * @return die formatierte Restzeit
     */
    private String timeToString() {
        if (extraStone[2] < 10) {
            return "0" + extraStone[2];
        } else {
            return "" + extraStone[2];
        }
    }

    /**
     * {@inheritDoc}.
     */
    public void paint(Graphics arg0) {
        super.paint(arg0);
    }

    /**
     * {@inheritDoc}.
     */
    public void update(Graphics arg0) {
        super.update(arg0);
    }

    public void showHighscore() {
        statistik.showHighscore();

    };

    /**
     * Startet das Spiel.
     * 
     * @param args
     *            default Eingabeparameter
     */
    public static void main(String[] args) {
        Game game = new Game();
        game.start();
        game.finish();

    }
}
```


Klasse Snake


```
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.List;

public class Snake {

    private static final int MOVE_LEFT = 0;
    private static final int MOVE_UP = 1;
    private static final int MOVE_RIGHT = 2;
    private static final int MOVE_DOWN = 3;

    private static final int CURVE_RIGHT_UP = 0;
    private static final int CURVE_RIGHT_DOWN = 1;
    private static final int CURVE_LEFT_DOWN = 2;
    private static final int CURVE_LEFT_UP = 3;

    private static final int KEY_LEFT = 37;
    private static final int KEY_UP = 38;
    private static final int KEY_RIGHT = 39;
    private static final int KEY_DOWN = 40;

    public static int SNAKE_WIDTH = 10;
    public static int SNAKE_HEIGHT = 10;

    private int direction;

    private int posX;
    private int posY;
    private List positions;

    private Statistik statistik;

    public Snake(Statistik statistik) {
        this.statistik = statistik;
        direction = MOVE_RIGHT;
        posX = Game.FRAME_WIDTH / 2;
        posY = Game.FRAME_HEIGHT / 2;
        positions = new ArrayList();
        addPoint(new int[] { posX, posY }, false);
        addPoint(new int[] { posX, posY }, false);
        addPoint(new int[] { posX, posY }, false);
        addPoint(new int[] { posX, posY }, false);
        addPoint(new int[] { posX, posY }, false);
        addPoint(new int[] { posX, posY }, false);
    }

    public void keyResolver(int newDirection) {

        switch (newDirection) {
            case KEY_LEFT:
                // case left
                if (this.direction != MOVE_RIGHT) {
                    this.direction = MOVE_LEFT;
                }
                break;

            case KEY_UP:
                // case up
                if (this.direction != MOVE_DOWN) {
                    this.direction = MOVE_UP;
                }
                break;
            case KEY_RIGHT:
                // case right
                if (this.direction != MOVE_LEFT) {
                    this.direction = MOVE_RIGHT;
                }
                break;

            case KEY_DOWN:
                // case down
                if (this.direction != MOVE_UP) {
                    this.direction = MOVE_DOWN;
                }
                break;
        }
    }

    public void move(boolean add) {
        if (direction == MOVE_LEFT) {
            posX -= SNAKE_WIDTH;
        } else if (direction == MOVE_UP) {
            posY -= SNAKE_HEIGHT;
        } else if (direction == MOVE_RIGHT) {
            posX += SNAKE_WIDTH;
        } else if (direction == MOVE_DOWN) {
            posY += SNAKE_HEIGHT;
        }
        jump();
        if (!add && positions.size() != 0) {
            positions.remove(0);
        }
        positions.add(new int[] { posX, posY, direction });
    }

    private void jump() {
        if (posX < 0) {
            // left out
            posX = Game.FRAME_WIDTH - SNAKE_WIDTH;
        } else if (posX >= Game.FRAME_WIDTH) {
            // right out
            posX = 0;
        } else if (posY < 0) {
            // top out
            posY = Game.FRAME_HEIGHT - SNAKE_HEIGHT;
        } else if (posY >= Game.FRAME_HEIGHT) {
            // bottom out
            posY = 0;
        }
    }

    public boolean isCollision() {
        int[][] positionHelper = (int[][])positions.toArray(new int[][] {});
        for (int i = 0; i < positions.size(); i++) {
            for (int j = i + 1; j < positions.size(); j++) {
                if (isCollision(positionHelper[i], positionHelper[j])) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean isCollision(int[] point) {
        int[][] positionHelper = (int[][])positions.toArray(new int[][] {});
        for (int i = 0; i < positions.size(); i++) {
            if (isCollision(positionHelper[i], point)) {
                return true;
            }
        }
        return false;
    }

    private boolean isCollision(int[] point1, int[] point2) {
        return point1[0] != -1 && point1[0] == point2[0] && point1[1] != -1
                && point1[1] == point2[1];
    }

    public void draw(Graphics2D g2, Insets insets) {
        int[] tmp;
        for (int i = 1; i < positions.size() - 1; i++) {
            tmp = (int[])positions.get(i);
            if (!isCurve(i)) {
                g2.fillRect(tmp[0] + insets.left, tmp[1] + insets.top,
                        SNAKE_WIDTH, SNAKE_HEIGHT);
            } else {
                drawCurve(i, g2, insets);

            }
        }
        drawFirst(g2, insets);
        drawLast(g2, insets);
    }

    private void drawCurve(int i, Graphics2D g2, Insets insets) {
        int[] tmp = (int[])positions.get(i);
        g2.fillOval(tmp[0] + insets.left, tmp[1] + insets.top, SNAKE_WIDTH,
                SNAKE_HEIGHT);
        int curve = curveType(i);
        if (curve == CURVE_RIGHT_UP) {
            g2.fillRect(tmp[0] + insets.left, tmp[1] + insets.top
                    + SNAKE_HEIGHT / 2, SNAKE_WIDTH, SNAKE_HEIGHT / 2);
            g2.fillRect(tmp[0] + insets.left, tmp[1] + insets.top,
                    SNAKE_WIDTH / 2, SNAKE_HEIGHT / 2);
        } else if (curve == CURVE_RIGHT_DOWN) {
            g2.fillRect(tmp[0] + insets.left, tmp[1] + insets.top, SNAKE_WIDTH,
                    SNAKE_HEIGHT / 2);
            g2.fillRect(tmp[0] + insets.left, tmp[1] + insets.top
                    + SNAKE_HEIGHT / 2, SNAKE_WIDTH / 2, SNAKE_HEIGHT / 2);
        } else if (curve == CURVE_LEFT_DOWN) {
            g2.fillRect(tmp[0] + insets.left, tmp[1] + insets.top, SNAKE_WIDTH,
                    SNAKE_HEIGHT / 2);
            g2.fillRect(tmp[0] + insets.left + SNAKE_WIDTH / 2 + 1, tmp[1]
                    + insets.top + SNAKE_WIDTH / 2, SNAKE_WIDTH / 2,
                    SNAKE_HEIGHT / 2);
        } else if (curve == CURVE_LEFT_UP) {
            g2.fillRect(tmp[0] + insets.left, tmp[1] + insets.top
                    + SNAKE_HEIGHT / 2, SNAKE_WIDTH, SNAKE_HEIGHT / 2);
            g2.fillRect(tmp[0] + insets.left + SNAKE_WIDTH / 2 + 1, tmp[1]
                    + insets.top, SNAKE_WIDTH / 2, SNAKE_HEIGHT / 2);

        }
    }

    private int curveType(int i) {
        int[] cur = (int[])positions.get(i);
        int[] post = (int[])positions.get(i + 1);

        if (cur[2] == post[2]) {
            return -1;
        } else if ((cur[2] == MOVE_RIGHT && post[2] == MOVE_UP)
                || (cur[2] == MOVE_DOWN && post[2] == MOVE_LEFT)) {
            return CURVE_RIGHT_DOWN;
        } else if ((cur[2] == MOVE_RIGHT && post[2] == MOVE_DOWN)
                || (cur[2] == MOVE_UP && post[2] == MOVE_LEFT)) {
            return CURVE_RIGHT_UP;
        } else if ((cur[2] == MOVE_LEFT && post[2] == MOVE_UP)
                || (cur[2] == MOVE_DOWN && post[2] == MOVE_RIGHT)) {
            return CURVE_LEFT_DOWN;
        } else if ((cur[2] == MOVE_LEFT && post[2] == MOVE_DOWN)
                || (cur[2] == MOVE_UP && post[2] == MOVE_RIGHT)) {
            return CURVE_LEFT_UP;
        }
        return -1;

    }

    private boolean isCurve(int i) {
        return curveType(i) != -1;
    }

    private void drawFirst(Graphics2D g2, Insets insets) {
        int[] first = (int[])positions.get(positions.size() - 1);
        g2.fillOval(first[0] + insets.left, first[1] + insets.top, SNAKE_WIDTH,
                SNAKE_HEIGHT);
        if (first[2] == MOVE_UP) {
            g2.fillRect(first[0] + insets.left, first[1] + insets.top
                    + SNAKE_HEIGHT / 2 + 1, SNAKE_WIDTH, SNAKE_HEIGHT / 2);
        } else if (first[2] == MOVE_DOWN) {
            g2.fillRect(first[0] + insets.left, first[1] + insets.top,
                    SNAKE_WIDTH, SNAKE_HEIGHT / 2);
        } else if (first[2] == MOVE_LEFT) {
            g2.fillRect(first[0] + insets.left + SNAKE_WIDTH / 2 + 1, first[1]
                    + insets.top, SNAKE_WIDTH / 2, SNAKE_HEIGHT);
        } else if (first[2] == MOVE_RIGHT) {
            g2.fillRect(first[0] + insets.left, first[1] + insets.top,
                    SNAKE_WIDTH / 2, SNAKE_HEIGHT);
        }
    }

    private void drawLast(Graphics2D g2, Insets insets) {
        int[] last = (int[])positions.get(0);
        int[] pre = (int[])positions.get(1);
        Color c = g2.getColor();

        if (pre[2] == MOVE_UP) {
            for (int i = 0; i < SNAKE_HEIGHT; i++) {
                g2.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), c
                        .getAlpha()
                        / (i + 1)));
                g2.fillRect(last[0] + insets.left, last[1] + insets.top + i,
                        SNAKE_WIDTH, 1);
            }
        } else if (pre[2] == MOVE_DOWN) {

            for (int i = 0; i < SNAKE_HEIGHT; i++) {
                g2.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), c
                        .getAlpha()
                        / (SNAKE_HEIGHT - i)));
                g2.fillRect(last[0] + insets.left, last[1] + insets.top + i,
                        SNAKE_WIDTH, 1);
            }
        } else if (pre[2] == MOVE_LEFT) {
            for (int i = 0; i < SNAKE_HEIGHT; i++) {
                g2.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), c
                        .getAlpha()
                        / (i + 1)));
                g2.fillRect(last[0] + insets.left + i, last[1] + insets.top, 1,
                        SNAKE_WIDTH);
            }
        } else if (pre[2] == MOVE_RIGHT) {
            for (int i = 0; i < SNAKE_HEIGHT; i++) {
                g2.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), c
                        .getAlpha()
                        / (SNAKE_HEIGHT - i)));
                g2.fillRect(last[0] + insets.left + i, last[1] + insets.top, 1,
                        SNAKE_WIDTH);
            }
        }
        g2.setColor(Color.BLACK);
    }

    public boolean addPoint(int[] point, boolean extra) {
        if (!isCollision(point, new int[] { posX, posY })) {
            return false;
        }
        if (!extra) {
            move(true);
            statistik.addStone();
        } else {
            statistik.addExtraStone();
        }
        return true;
    }

    public boolean isAllowedDirection(int keyCode) {
        if (keyCode == KEY_RIGHT || keyCode == KEY_LEFT) {
            return !(this.direction == MOVE_RIGHT || this.direction == MOVE_LEFT);
        }
        if (keyCode == KEY_UP || keyCode == KEY_DOWN) {
            return !(this.direction == MOVE_DOWN || this.direction == MOVE_UP);
        }
        return false;
    }
}
```

Klasse Statistik 


```
import java.awt.Graphics2D;
import java.awt.Insets;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

public class Statistik implements Comparable, Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    /**
     * Die Punktzahl für das sammeln von Steinen.
     */
    private static final int POINTS_ADD_STONE = 5;
    private static final int POINTS_ADD_EXTRA_STONE = 10;

    /**
     * Die Anzahl der gesammelten Steine.
     */
    private int numberStones;
    private int numberExtraStones;

    /**
     * Die aktuelle Punktezahl.
     */
    private int points;

    /**
     * Das aktuelle Dautm
     */
    private Date date;

    /**
     * Erzeugt und initialsiert die Statistk für dieses Spiel.
     */
    public Statistik() {
        numberStones = 0;
        numberExtraStones = 0;
        points = 0;
        date = new Date();
    }

    /**
     * Zieht einen Punkt bei aktuellen Punktestand ab.
     */
    public void removePoints() {
        points--;
    }

    /**
     * Fügt der Statistik einen eingesammelten extra Stein hinzu.
     */
    public void addExtraStone() {
        points += POINTS_ADD_EXTRA_STONE;
        numberExtraStones++;
    }

    /**
     * Fügt der Statistik einen eingesammelten Stein hinzu.
     */
    public void addStone() {
        points += POINTS_ADD_STONE;
        numberStones++;
    }

    /**
     * Formatiert die akteulle Punktezahl in einen drei stelligen String
     * fehlende Stellen werden dabei von vorne mir 0 aufegüllt.
     * 
     * @return die formatierte Punktezahl
     */
    public String pointsToString() {
        int tmpPoints = points;
        String pref = "";
        if (tmpPoints < 0) {
            pref = "-";
            tmpPoints = -tmpPoints;
        }
        if (tmpPoints < 10) {
            return pref + "000" + tmpPoints;
        } else if (tmpPoints < 100) {
            return pref + "00" + tmpPoints;
        } else if (tmpPoints < 1000) {
            return pref + "0" + tmpPoints;
        } else {
            return pref + points + "";
        }

    }

    /**
     * Speichert das aktuelle Ergebinss in die Highscore Datei. Dabei werden
     * immer nur die 10 besten Resultate in sortierter Reihenfolge gespeichert.
     * 
     * @param filePath
     *            der Dateipfad der Highscoredatei
     */
    public void save(String filePath) {
        List read = createHighscore(filePath);
        try {
            FileOutputStream fos = new FileOutputStream(filePath);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(read);
            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private List createHighscore(String filePath){
        List read = new ArrayList();
        if (!new File(filePath).exists()) {
            File file = new File(filePath);
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            read = readFile(filePath);
        }
        read.add(this);
        Collections.sort(read);
        for (int i = 10; i < read.size();) {
            read.remove(10);
        }
        return read;
    }

    /**
     * Liest aus einer Datei die Statistikliste aus. Ist die datei leer oder
     * nicht verhanden, wird eine leere Liste zurück gegebn.
     * 
     * @param filePath
     *            der Dateipfad
     * @return die Liste der gespeicherten Statistiken
     */
    private List readFile(String filePath) {
        List lines = new ArrayList();
        FileInputStream fin;
        try {
            fin = new FileInputStream(filePath);
            if (fin.available() != 0) {

                ObjectInputStream ois;
                try {
                    ois = new ObjectInputStream(fin);
                    try {
                        lines = (List)ois.readObject();
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            fin.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return lines;
    }

    /**
     * Gibt das aktuelle Datum als String urück (dd.MM.yyyy).
     * 
     * @return das aktuelle Datum
     */
    public String getDateAsString() {
        DateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
        return formatter.format(date);
    }

    /**
     * Gibt die aktuelle zeit als String zurück (HH:mm:ss).
     * 
     * @return die aktuelle Zeit
     */
    public String getTimeAsString() {
        DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
        return formatter.format(date);
    }

    /**
     * Gibt die aktuellen Werte der Klassenvariablen aus.
     */
    public String toString() {
        return "\t" + points + "\t|\t" + numberStones + "\t|\t"
                + numberExtraStones + "\t|\t" + getDateAsString() + "\t|\t"
                + getTimeAsString();
    }

    /**
     * Gibt die aktuelle Punktzahl auf dem Spielbrett in der linken oberen Ecke
     * aus.
     * 
     * @param g
     *            das Zeichenobjekt
     * @param insets
     *            der Abstand auf dem Frame zum Rand
     */
    public void drawPoints(Graphics2D g, Insets insets) {
        g.drawString(pointsToString(), insets.left + 2, insets.top
                + g.getFont().getSize() + 2);
    }

    /**
     * Vergleicht eine übergebenen Statistik mit der aktuellen. Und Gibt 1
     * zurück wenn die aktuelle größer ist. Ist die aktuelle kleine, wird -1
     * zurück gegen. Sind beide gleich, so wird 0 zurück gegebn. Rehenfolge:
     * Punkte -> Steine -> ExtraSteine -> Datum
     * 
     * @param arg0
     *            das zu vergleichende Objekt
     * @return Vergleichswert fr das aktuelle und das übergeben Objekt
     */
    public int compareTo(Object arg0) {
        if (arg0 instanceof Statistik) {
            Statistik comp = (Statistik)arg0;
            // PunkteZahl unterschiedlich
            if (getPoints() < comp.getPoints()) {
                return 1;
            }
            if (getPoints() > comp.getPoints()) {
                return -1;
            }
            // Steine unterschiedlich
            if (getNumberStones() < comp.getNumberStones()) {
                return 1;
            }
            if (getNumberStones() > comp.getNumberStones()) {
                return -1;
            }
            // Extra Steine unterschiedlich
            if (getNumberExtraStones() < comp.getNumberExtraStones()) {
                return 1;
            }
            if (getNumberExtraStones() > comp.getNumberExtraStones()) {
                return -1;
            }
            // Zeit vergleich
            return (int)(getDate().getTime() - comp.getDate().getTime());
        }
        return 1;
    }

    /**
     * Gibt die Anzahl an extra Steienn zurück.
     * 
     * @return die Anzahl an extra Steinen
     */
    public int getNumberExtraStones() {
        return numberExtraStones;
    }

    /**
     * Gibt die Anzahl an Steien zurück.
     * 
     * @return die Anzahl an Steinen
     */
    public int getNumberStones() {
        return numberStones;
    }

    /**
     * Gibt die aktuelel Punktezahl zurück.
     * 
     * @return die aktuelle Punktezahl
     */
    public int getPoints() {
        return points;
    }

    /**
     * Gibt das Datum der Statsistik zurück.
     * 
     * @return das Datum
     */
    public Date getDate() {
        return date;
    }

    public void showHighscore() {
        List l = createHighscore("src\\score.txt");
        for (Iterator iter = l.iterator(); iter.hasNext();) {
            System.out.println((Statistik)iter.next());
            
        }
        
    }

}
```


----------



## Marco13 (19. Jan 2007)

Noch als Nachtrag, falls es dich interessiert: In meiner ersten Antwort war der Fehler, dass es nicht

```
synchronized (Game.this) {
                       notify();
                   }
```
heißen müßte, sondern

```
synchronized (Game.this) {
                       Game.this.notify();
                   }
```
dann hätte man das "doNotify" nicht gebraucht. Aber es ist beides OK.


----------



## WieselAc (19. Jan 2007)

super danke, solangsam lern ich sogar noch wie man mit thread arbeitet )


----------

