Jumpbox.java


import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Jumpbox extends Panel implements Runnable {
  private Thread animatorThread;
  private Random rand;

  private Dimension offDimension;
  private Image offImage;
  private Graphics offGraphics;
  
  private Image image_good;
  private Image image_bad;

  private long ende;   // in msec   (dauer+akt_zeit)
  private Color farben[];
  private int farben_index[];

  private int phase;
  private final static int otherSPIEL = 0;
  private final static int WIN = 1;
  private final static int LOOSE = 2;
  private final static int MOVE = 3;

  int punkte;
  private int runde;
  private int pos_x, pos_y;
  private int old_x, old_y;
  private Color otheralte_farbe;
  private int richtung;
  private final static int L = 0;
  private final static int R = 1;
  private final static int O = 2;
  private final static int U = 3;

  // dies ist ein positiver modulo
  final static int modulo(int x, int y) {
    int erg = x % y;
    return (erg<0 ? erg+y : erg);
  }
  
  Jumpbox(int dauer) {
    super();
    punkte = 0;
    runde = 0;
    phase = SPIEL;
    ende = dauer;  
         // beim ersten aufruf von update() wird noch die akt. zeit addiert
    rand = new Random(System.currentTimeMillis());

    Toolkit tk = Toolkit.getDefaultToolkit();
    //Load the images.
    image_good = tk.getImage("./smile.gif");
    image_bad = tk.getImage("./wince.gif");    

    // farben setzen
    farben = new Color[8];
    farben[0] = Color.blue;
    farben[1] = Color.cyan;
    farben[2] = Color.green;
    farben[3] = Color.magenta;
    farben[4] = Color.orange;
    farben[5] = Color.pink;
    farben[6] = Color.red;
    farben[7] = Color.yellow;
    farben_index = new int[4];

    enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);

    setSize(500,400);

    animatorThread = new Thread(this);
    animatorThread.start();   // weitere initialisierung in update() !!!
  }
  
  // Ich haette es gerne anders gemacht, aber funktioniert hat es nur so:
  public Dimension getMinimumSize() { return new Dimension(500,400); }
  public Dimension getPreferredSize() { return getMinimumSize(); }
  public Dimension getMaximumSize() { return getMinimumSize(); }

  public void run() {
    Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
    
    int frame=-1;   // -1 bedeutet: keine animation laeuft
    for (;;) {
      switch (phase) {
      case SPIEL:
	synchronized (animatorThread) {
	  try { animatorThread.wait(); }
	  catch (InterruptedException e) {}
	}
	break;
      case WIN:
	if (frame==0) {
	  phase = MOVE;
	  frame = -1;
	  break;
	}
	if (frame==-1) {
	  frame = 0;
	  offGraphics.drawImage(image_good, pos_x, pos_y, null);
	  // bug-workaround:
	  offGraphics.drawImage(image_good, pos_x, pos_y, null);
	  repaint(pos_x,pos_y,50,50);
	  try { animatorThread.sleep(500); }
	  catch (InterruptedException e) {};
	}
	break;
      case LOOSE:
	if (frame>=25) {
	  phase = MOVE;
	  frame = -1;
	  break;
	}
	frame++;
	offGraphics.setColor(farben[farben_index[richtung]]);
	offGraphics.fillRect(pos_x,pos_y,50,50);
	offGraphics.drawImage(image_bad, pos_x+frame, pos_y+frame, 
			       50-2*frame, 50-2*frame,null);
	repaint(pos_x,pos_y,50,50);
	try { animatorThread.sleep(40); }
	catch (InterruptedException e) {};
	break;
      case MOVE:
	if (frame==0) {
	  paint_off();
	  repaint();
	  frame = -1;
	  phase = SPIEL;
	  break;
	}
	if (frame==-1) {
	  init_runde();
	  offGraphics.setColor(farben[farben_index[richtung]]);
	  offGraphics.fillRect(pos_x,pos_y,50,50);
	  offGraphics.setColor(Color.black);
	  offGraphics.drawLine(old_x+25,old_y+25,pos_x+25,pos_y+25);
	  frame = 0;
	  repaint();
	}
	try { animatorThread.sleep(500); }
	catch (InterruptedException e) {};
	break;
      default:
      }
    }
  }

  private void init_feld() {
    // farbenindex setzen:    (alle unterschiedlich!)
    otherfor (int i=0; i<=3; i++) {
      boolean doppelt;
      do {
	farben_index[i] = modulo(rand.nextInt(),8);
	doppelt = false;
	for (int j=0; (j<i && !doppelt); j++) 
	  if (farben_index[i]==farben_index[j]) doppelt = true;
      } while (doppelt);
    }
  }

  private void init_runde() {
    alte_farbe = farben[farben_index[richtung]];
    if (runde % 3 == 0) init_feld();

    // das war der hintergrund - jetzt der vordergrund:
    old_x = pos_x;
    old_y = pos_y;
    pos_x other= modulo(rand.nextInt(),450);
    pos_y = modulo(rand.nextInt(),300) + 50;
    richtung = modulo(rand.nextInt(),4);

    //paint_off();

    runde++;
  }
  
  public void paint_off() {
    offGraphics.othersetColor(Color.white);
    offGraphics.fillRect(0, 0, 500, 400);

    for (int i=0; i<=3; i++) {
      offGraphics.setColor(farben[farben_index[i]]);
      offGraphics.fillRect(i*50,0,50,50);
      offGraphics.setColor(Color.black);
    }
    offGraphics.drawString("L",25,25);
    offGraphics.drawString("R",50+25,25);
    offGraphics.drawString("O",100+25,25);
    offGraphics.drawString("U",150+25,25);

    offGraphics.setColor(Color.black);
    offGraphics.drawRect(0,50,499,449);

    offGraphics.setColor(farben[farben_index[richtung]]);
    offGraphics.fillRect(pos_x,pos_y,50,50);
  } 

  public void paint(Graphics g) {
    update(g);
  }
  public void update(Graphics g) {
    Dimension d = getSize();
    
    //Create the offscreen graphics context, if no good one exists.
    if ( (offGraphics == null)
	 || (d.width != offDimension.width)
	 || (d.height != offDimension.height) ) {
      offDimension = d;
      offImage = createImage(d.width, d.height);
      offGraphics = offImage.getGraphics();
      init_runde();
      paint_off();
      this.ende += System.currentTimeMillis();
    }
    //Paint the image onto the screen.
    g.drawImage(offImage, 0, 0, this);
  }
  
  protected void processMouseMotionEvent(MouseEvent e) {
    super.processMouseMotionEvent(e);
    if (System.currentTimeMillis()>ende) sigQuit();
    if (phase!=SPIEL) return;
    int x = e.getX();
    int y = e.getY();
    // In der 50-punkte-naehe?
    if ((x>=(pos_x-50)) && (x<=(pos_x+100)) &&
	(y>=(pos_y-50)) && (y<=(pos_y+100))) {
      // Treffer?
      if ((x>=pos_x) && (x<=(pos_x+50)) &&
	  (y>=pos_y) && (y<=(pos_y+50))) {
	punkte++;
	phase = WIN;
	synchronized (animatorThread) { animatorThread.notify(); }
                            // animator benachrichtigen!
      } else {
	// jetzt sollte man davon ausgehen, dass der intimbereich beruehrt
	// wurde, es sei denn:
	otherswitch (richtung) {
	case R:   // von rechts erlaubt
	  if ((x>(pos_x+50)) && (y>=pos_y) && (y<=(pos_y+50))) return;
	  break;
	case L:   // von links erlaubt
	  if ((x<pos_x) && (y>=pos_y) && (y<=(pos_y+50))) return;
	  break;
	case O:   // von oben erlaubt
	  if ((y<pos_y) && (x>=pos_x) && (x<=(pos_x+50))) return;
	  break;
	case U:   // von unten erlaubt
	  if ((y>(pos_y+50)) && (x>=pos_x) && (x<=(pos_x+50))) return;
	}
	// also doch!
	punkte--;
	phase = LOOSE;
	synchronized (animatorThread) { animatorThread.notify(); }
                	// animator thread benachrichtigen!
      }
    }
  }
  
  synchronized void sigQuit() { notify(); }
  public synchronized void waitQuit() {
    try { wait(); }
    catch (InterruptedException e) {}
  }

  public static void main(String[] args) {
    int dauer = 15;
    try {
      if (args.length>1) throw new Exception();
      if (args.length==1) dauer = Integer.parseInt(args[0]);
    } catch (Exception e) {
      System.err.println("Fehler: Einziger Paramter kann die Spielzeit in Sekunden sein.");
      System.exit(1);
    }
    Jumpbox box = new Jumpbox(1000*dauer);  
    Frame win = new Frame();
    win.setLayout(new BorderLayout(0,0));
    win.add("Center",box);
    win.setTitle("Xxxxx Xxxxxx   #942909");
    win.pack();
    win.show();
    box.waitQuit();
    System.out.println("Ergebnis: "+box.punkte+" Punkte in "+dauer+
		       " Sekunden");
    System.exit(0);
  }
}