Bermuda: ein Beispiel-Applet in Java
Willemers Informatik-Ecke

Die Aufgabenstellung

Bermuda ist ein kleines Spiel, das mit einer einfachen Textoberfläche in einem Applet realisiert werden kann und damit ideal zur Demonstration verwendbar ist.

In einem Feld von 9x7 Punkten sind Schiffe verschwunden, die durch Anticken der Positionen gesucht werden können. Nach Anklicken zeigt die Position ein X, wenn ein Schiff gefunden wurde oder eine Zahl, die Auskunft gibt, in wieviele Richtungen (!) Schiffe gesehen werden. Ein Schiff kann also durch ein davor liegendes verdeckt werden.

Das Auffinden aller Schiffe soll erkannt und dem Anwender angezeigt werden. Er soll die Möglichkeit haben, das Spiel neu zu starten.

Das Spiel als Applet

Spielen Sie: es sollte funktionieren, wenn Ihr Browser Java unterstützt.



Die Sourcen

Die Klasse Schiff simuliert ein eigenes Schiff, das neben einer Position nur die Information enthält, ob es bereits entdeckt wurde.

public class Schiff {
    private int x, y;
    private boolean gefunden;

    Schiff() {
        gefunden = false; x = 0; y = 0;
    }

    void setPos(int px, int py) {
        x = px; y = py;
        gefunden = false;
    }

    boolean istPos(int px, int py) {
        return (px==x && py==y);
    }

    void setGefunden(boolean parGefunden) {
        gefunden = parGefunden;
    }

    boolean istGefunden() {
        return gefunden;
    }
}

Die Klasse SpielFeld realisiert das Spielfeld von 9 x 7 Feldern in einem zweidimensionalen Array. In einem einfachen Array werden die vier Schiffe abgelegt. Das Spielfeld versteckt die Schiffe und sucht nach ihnen.

import java.util.Random;

class SpielFeld {

    final int MaxX = 9;
    final int MaxY = 7;
    private char feld[][] = null;
    final int MAXSCHIFF = 4;
    private Schiff schiff[] = null;
    final char SCHIFF = 'X';

    SpielFeld() {
        // Spielfeld vorbereiten
        feld = new char[MaxX][];
        for (int x=0; x<MaxX; x++) {
            feld[x] = new char [MaxY];
            for (int y=0; y<MaxY; y++) {
                feld[x][y] = '.';
            }
        }
        // Schiffe vorbereiten
        schiff = new Schiff[MAXSCHIFF];
        for (int i=0; i<MAXSCHIFF; i++) {
            schiff[i] = new Schiff();
        }
        // Schiffe verteilen
        Random zufall = new Random();
        for (int i=0; i<MAXSCHIFF; i++) {
            boolean kollidieren = false;
            do {
                int x = Math.abs(zufall.nextInt()) % MaxX;
                int y = Math.abs(zufall.nextInt()) % MaxY;
                schiff[i].setPos(x, y);
                kollidieren = false;
                for (int j=0; j<i; j++) {
                    if (schiff[j].istPos(x, y)) {
                        kollidieren = true;
                    }
                }
            } while (kollidieren);
        }
    }

    public char get(int x, int y) {
       return feld[x][y];
    }
    boolean alleGefunden() {
         boolean gefunden = true;
         for (int i=0; i<MAXSCHIFF; i++) {
             if (schiff[i].istGefunden()==false) {
                 gefunden = false;
             }
         }
         return gefunden;
    }

    // ----- Suche die Schiffe und melde die Anzahl der Richtungen -----
    // lokal zu suche: schau auf das Feld
    private int schauMal(int posX, int posY) {
        for (int i=0; i<MAXSCHIFF; i++) {
            if (schiff[i].istPos(posX, posY)) {
                return i+1;
            }
        }
        return 0;
    }
    // lokal zu suche: laufe eine Linie entlang
    private int linieLaufen(int posX, int posY, int diffX, int diffY) {
        // Verhindere Endlosschleife! Entweder diffX oder diffY muss !=0 sein.
        if (diffX==0 && diffY==0) return 0;
        // Vorgegebene Richtung bis zum Rand durchlaufen
        while (posX>=0 && posX<MaxX && posY>=0 && posY<MaxY) {
            posX += diffX;
            posY += diffY;
            // Gefunden? Suche in dieser Richtung abbrechen!
            if (schauMal(posX, posY)>0) return 1;
        }
        return 0;
    }
    // Aufruf zur Suche für posX, posY
    public char suche(int posX, int posY) {
        // haben wir bereits ein Schiff getroffen?
        int index = schauMal(posX, posY);
        if (index>0) {
            schiff[index-1].setGefunden(true);
            feld[posX][posY] = SCHIFF;
        } else {
            // suche in alle Richtungen
            int anzahl=0;
            for (int diffX=-1; diffX<=1; diffX++) {
                for (int diffY=-1; diffY<=1; diffY++) {
                    anzahl += linieLaufen(posX, posY, diffX, diffY);
                }
            }
            feld[posX][posY] = (char)('0'+anzahl);
        }
        return feld[posX][posY];
    }
}

BermudaApplet erweitert die Klasse JApplet und kümmert sich um die Darstellung und die Interaktion mit dem Benutzer.

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

public class BermudaApplet extends JApplet implements MouseListener {

    SpielFeld sf = null;
    boolean spielEnde = false;    // ist das Spiel beendet worden?
    String StatusStr = "                ";

    public void init() {
        this.addMouseListener(this); // fange die Mausereignisse
        this.setSize(320,200);
        sf = new SpielFeld();
    }

    // Anzeige des Spielfelds findet durch repaint statt
    public void paint(Graphics g) {

        for (int i=0; i<sf.MaxX; i++) {
            for (int j=0; j<sf.MaxY; j++) {
                String str = String.valueOf(sf.get(i,j));
                g.drawString(str, (i+1)*20, (j+1)*20);
            }
        }
        g.drawString(StatusStr, 10, (sf.MaxY+1) * 20);
    }

    // der Benutzer tut was relevantes, er klickt mit der Maus
    public void mouseClicked(MouseEvent me) {
        if (!spielEnde) {
            int x = (me.getX()-10)/20;
            int y = (me.getY()-10)/20;
            char c = sf.suche(x, y);
            if (c==sf.SCHIFF) {
                if (sf.alleGefunden()) {
                    // alle gefunden: GEWONNEN!
                    spielEnde = true;
                    StatusStr = "Gewonnen! Mausklick: neues Spiel";
                }
            }
        } else {
            // sonderfall: Mausklick, um Sieg zu bestaetigen
            spielEnde = false;
            StatusStr = "                  ";
            sf = new SpielFeld(); 
        }
        repaint();
    }
    // zur Realisierung des Interface, auch wenn wir die Methoden nicht brauchen...
    public void mouseReleased(MouseEvent me) { }
    public void mousePressed(MouseEvent me)  { }
    public void mouseEntered(MouseEvent me)  { }
    public void mouseExited(MouseEvent me)   { }
}