api:projects:minesweeper:start
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
api:projects:minesweeper:start [2020/12/28 15:40] – Martin Pabst | api:projects:minesweeper:start [2021/12/29 11:29] (aktuell) – Externe Bearbeitung 127.0.0.1 | ||
---|---|---|---|
Zeile 4: | Zeile 4: | ||
Hat der Spieler alle Felder des Spielfeldes entweder aufgedeckt oder mit Fahnen markiert, so hat ist das Spiel gewonnen. \\ \\ | Hat der Spieler alle Felder des Spielfeldes entweder aufgedeckt oder mit Fahnen markiert, so hat ist das Spiel gewonnen. \\ \\ | ||
Etwas weiter unten findest Du das gesamte Spiel zum Ausprobieren und verändern. Ich habe die einzelnen Entwicklungsschritte dokumentiert und mit ausführlichen Erklärungen versehen: | Etwas weiter unten findest Du das gesamte Spiel zum Ausprobieren und verändern. Ich habe die einzelnen Entwicklungsschritte dokumentiert und mit ausführlichen Erklärungen versehen: | ||
- | * Die Klasse Zelle | + | * [[.zelle: |
- | * Die Klasse Spielfeld | + | * [[.spielfeld: |
* Die Klasse Minesweeper | * Die Klasse Minesweeper | ||
< | < | ||
- | <div class=" | + | <div class=" |
<script type=" | <script type=" | ||
- | // Hier programmieren! | + | // Hauptprogramm |
+ | new World(645, 800); | ||
+ | |||
+ | // Der Konstruktor der Klasse Minesweeper initialisiert das Spiel, | ||
+ | // danach wird die Anwendung durch Ereignisse (click, mouseup) und die | ||
+ | // act-Methode von Minesweeper weiter vorangebracht. | ||
+ | new Minesweeper(); | ||
+ | |||
+ | // Mit dem Aufruf der Methode setZustand der Klasse Minesweeper wird der | ||
+ | // Übergang von einem Zustand zum nächsten herbeigeführt. | ||
+ | enum Zustand { | ||
+ | | ||
+ | } | ||
+ | |||
+ | // Hauptklasse der Anwendung | ||
+ | class Minesweeper extends Actor { | ||
+ | |||
+ | | ||
+ | |||
+ | // Der linke rote Zähler zeigt die Differenz aus der Anzahl der Minen und | ||
+ | // der Flaggen. | ||
+ | | ||
+ | |||
+ | // Der rechte rote Zähler zählt die verstrichenen Sekunden. | ||
+ | | ||
+ | |||
+ | // Die Smiley-Grafik in der Mitte | ||
+ | | ||
+ | |||
+ | // Zustand, in dem sich das Spiel gerade befindet | ||
+ | | ||
+ | |||
+ | // Text am unteren Rand des Grafikbereichs zur Ausgabe von Status- und Hilfstexten | ||
+ | | ||
+ | |||
+ | // Zähler, mit dem jeweils 30 verstrichene Frames (d.h. 1 Sekunde) gezählt werden. | ||
+ | // Je 30 Frames wird eine Sekunde hochgezählt. | ||
+ | | ||
+ | |||
+ | | ||
+ | super(); | ||
+ | |||
+ | spielfeld = new Spielfeld(20, | ||
+ | spielfeld.init(); | ||
+ | |||
+ | text = new Text(322, 750, 32, "" | ||
+ | text.setAlignment(Alignment.center); | ||
+ | text.setFillColor(Color.white); | ||
+ | |||
+ | this.smiley = new Smiley(305, 50, this); | ||
+ | setZustand(Zustand.spiel_läuft); | ||
+ | } | ||
+ | |||
+ | | ||
+ | // Zähle die Frames: | ||
+ | if(--frameCounter <= 0) { | ||
+ | // 30 Frames (d.h. 1 Sekunde) sind verstrichen | ||
+ | | ||
+ | | ||
+ | spielfeld.zufälligeZelleKlicken(); | ||
+ | } else if(zustand == Zustand.spiel_läuft) { | ||
+ | sekundenCounter.add(1); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | | ||
+ | return minenCounter; | ||
+ | } | ||
+ | |||
+ | | ||
+ | return zustand; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Alle Zustandsübergänge werden durch Aufrufen dieser Methode herbeigeführt. | ||
+ | */ | ||
+ | | ||
+ | |||
+ | zustand = neuerZustand; | ||
+ | |||
+ | switch(neuerZustand) { | ||
+ | case Zustand.intro : // intro | ||
+ | text.setText(" | ||
+ | break; | ||
+ | case Zustand.spiel_läuft : // Spiel läuft | ||
+ | text.setText("" | ||
+ | spielfeld.löschen(); | ||
+ | spielfeld.init(); | ||
+ | sekundenCounter.set(0); | ||
+ | minenCounter.set(spielfeld.getMinenAnzahl()); | ||
+ | smiley.showSmile(); | ||
+ | break; | ||
+ | case Zustand.spiel_gewonnen : // Spiel gewonnen | ||
+ | smiley.showFaceWithSunglasses(); | ||
+ | text.setText(" | ||
+ | break; | ||
+ | case Zustand.spiel_verloren : // Spiel verloren | ||
+ | smiley.showCryingFace(); | ||
+ | spielfeld.allesAufdecken(); | ||
+ | text.setText(" | ||
+ | break; | ||
+ | default : | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Durch Klick auf den Smiley kann ein neues Spiel gestartet werden. Diese | ||
+ | * Methode wird durch die Methode Smiley.onMouseDown aufgerufen. | ||
+ | */ | ||
+ | | ||
+ | if(zustand != Zustand.spiel_läuft) { | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | <script type=" | ||
+ | /** | ||
+ | * Ein Objekt der Klasse Spielfeld enthält und verwaltet alle Zellen. | ||
+ | */ | ||
+ | class Spielfeld { | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | this.spalten = spalten; | ||
+ | this.zeilen = zeilen; | ||
+ | this.minesweeper = minesweeper; | ||
+ | this.minenAnzahl = minenAnzahl; | ||
+ | zellen = new Zelle[spalten][zeilen]; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Instanziere alle Zellen und " | ||
+ | */ | ||
+ | | ||
+ | // Erzeuge zunächst Zellen ohne Minen. | ||
+ | for(int spalte = 0; spalte < spalten; spalte++) { | ||
+ | | ||
+ | |||
+ | zellen[spalte][zeile] = new Zelle(this, ZellBild.hintergrund_leer, | ||
+ | |||
+ | } | ||
+ | } | ||
+ | |||
+ | // " | ||
+ | for(int i = 0; i < minenAnzahl; | ||
+ | | ||
+ | | ||
+ | int spalte = Math.floor(Math.random() * spalten); | ||
+ | int zeile = Math.floor(Math.random() * zeilen); | ||
+ | Zelle zelle = zellen[spalte][zeile]; | ||
+ | if(zelle.getInhalt() != ZellBild.mine) { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | // Zähle für jede nicht-Minen-Zelle die Nachbarminen und stelle die | ||
+ | // Anzahl als Zahlengrafik dar. | ||
+ | for(int spalte = 0; spalte < spalten; spalte++) { | ||
+ | | ||
+ | |||
+ | Zelle zelle = zellen[spalte][zeile]; | ||
+ | if(zelle.getInhalt() == ZellBild.hintergrund_leer) { | ||
+ | int nachbarbomben = zähleNachbarMinen(zelle); | ||
+ | | ||
+ | zelle.setZustand(ZellBild.zahl_0 + nachbarbomben, | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | | ||
+ | return minesweeper; | ||
+ | } | ||
+ | |||
+ | | ||
+ | return minenAnzahl; | ||
+ | } | ||
+ | |||
+ | | ||
+ | int nachbarMinen = 0; | ||
+ | for(int dx = -1; dx <= 1; dx++) { | ||
+ | | ||
+ | if(dx != 0 || dy != 0) { | ||
+ | int spalte = zelle.getSpalte() + dx; | ||
+ | int zeile = zelle.getZeile() + dy; | ||
+ | | ||
+ | if(zellen[spalte][zeile].getInhalt() == ZellBild.mine) { | ||
+ | | ||
+ | }; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | return nachbarMinen; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Zerstört alle Zellen des Spielfelds | ||
+ | */ | ||
+ | | ||
+ | for(int spalte = 0; spalte < spalten; spalte++) { | ||
+ | | ||
+ | |||
+ | zellen[spalte][zeile].destroy(); | ||
+ | zellen[spalte][zeile] = null; | ||
+ | |||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Berechnet die x-Koordinate des Pixels am linken Rand der übergebenen Spalte. | ||
+ | */ | ||
+ | | ||
+ | return 21 + 32 * spalte; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Berechnet die y-Koordinate des Pixels am oberen Rand der übergebenen Zeile. | ||
+ | */ | ||
+ | | ||
+ | return 116 + 32 * zeile; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Wird von Zelle.onMouseUp aufgerufen und deckt die Zelle auf. | ||
+ | * Enthält sie keine Mine und enthalten auch die Nachbarzellen keine Minen, so werden | ||
+ | * rekursiv alle benachbarten Zellen aufgedeckt, die ebenfalls keine Minen enthalten | ||
+ | * und deren Nachbarzellen ebenfalls keine Minen enthalten. | ||
+ | */ | ||
+ | | ||
+ | zelle.aufdecken(); | ||
+ | if(zelle.getInhalt() == ZellBild.hintergrund_leer) { | ||
+ | // Falls weder die Zelle noch die Nachbarzellen Minen enthalten, | ||
+ | // dann untersuche die acht benachbarten Zellen: | ||
+ | | ||
+ | for(int dy = -1; dy <= 1; dy++) { | ||
+ | if(dx != 0 || dy != 0) { | ||
+ | int spalte = zelle.getSpalte() + dx; | ||
+ | int zeile = zelle.getZeile() + dy; | ||
+ | if(spalte >= 0 && spalte < spalten && zeile >= 0 && zeile < zeilen) { | ||
+ | // Hole das benachbarte Zellen-Objekt | ||
+ | Zelle zelle = zellen[spalte][zeile]; | ||
+ | | ||
+ | klickAufZelle(zelle); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Fürs Intro: Zufällige Zelle öffnen | ||
+ | */ | ||
+ | | ||
+ | int spalte = Math.floor(Math.random() * spalten); | ||
+ | int zeile = Math.floor(Math.random() * zeilen); | ||
+ | klickAufZelle(zellen[spalte][zeile]); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Markiere die Zelle mit einer Fahne | ||
+ | */ | ||
+ | | ||
+ | if(minesweeper.getMinenCounter().getNumber() > 0) { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Entferne die Fahnen-Markierung der Zelle | ||
+ | */ | ||
+ | | ||
+ | zelle.setZustand(zelle.getInhalt(), | ||
+ | minesweeper.getMinenCounter().add(1); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Nachdem das Spiel verloren ist, werden alle Zellen aufgedeckt. | ||
+ | */ | ||
+ | | ||
+ | for(int spalte = 0; spalte < spalten; spalte++) { | ||
+ | | ||
+ | |||
+ | Zelle zelle = zellen[spalte][zeile]; | ||
+ | if(zelle.istZugedeckt()) { | ||
+ | | ||
+ | if(zelle.getInhalt() == ZellBild.mine) { | ||
+ | | ||
+ | } else { | ||
+ | | ||
+ | } | ||
+ | } else { | ||
+ | zelle.aufdecken(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Gibt genau dann true zurück, wenn alle Minen markiert und alle anderen | ||
+ | * Zellen aufgedeckt sind. | ||
+ | */ | ||
+ | | ||
+ | for(int spalte = 0; spalte < spalten; spalte++) { | ||
+ | | ||
+ | Zelle zelle = zellen[spalte][zeile]; | ||
+ | if(zelle.istZugedeckt() && !zelle.hatFahne()) return false; | ||
+ | } | ||
+ | } | ||
+ | return true; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | <script type=" | ||
+ | // Indizes der Sprites für die verschiedenen Zellbilder | ||
+ | class ZellBild { | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Die Klasse Zelle repräsentiert eine quadratische Zelle des Spielfeldes. Sie wird mithilfe von | ||
+ | * zwei Sprites gezeichnet: dem Hintergrund (Zell-Objekt ist selbst ein Sprite) und dem Vordergrund | ||
+ | * (gleichnamiges Attribut). | ||
+ | */ | ||
+ | class Zelle extends Sprite { | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | // Jede Zelle " | ||
+ | // öffentliche Methoden aufrufen. | ||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | super(spielfeld.spalteToX(spalte), | ||
+ | vordergrund = new Sprite(spielfeld.spalteToX(spalte), | ||
+ | this.spielfeld = spielfeld; | ||
+ | this.spalte = spalte; | ||
+ | this.zeile = zeile; | ||
+ | setZustand(zustand, | ||
+ | } | ||
+ | |||
+ | | ||
+ | return inhalt; | ||
+ | } | ||
+ | |||
+ | | ||
+ | return zeile; | ||
+ | } | ||
+ | |||
+ | | ||
+ | return spalte; | ||
+ | } | ||
+ | |||
+ | | ||
+ | return zugedeckt; | ||
+ | } | ||
+ | |||
+ | | ||
+ | return fahne; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Ändert sich der Zustand der Zelle, so sorgt die Methode setZustand dafür, | ||
+ | * dass die Bilder der Sprites entsprechend gesetzt werden. | ||
+ | */ | ||
+ | | ||
+ | if(zugedeckt) { | ||
+ | | ||
+ | vordergrund.setImageIndex(ZellBild.fahne); | ||
+ | } else { | ||
+ | vordergrund.setImageIndex(ZellBild.zugedeckt); | ||
+ | } | ||
+ | } else { | ||
+ | | ||
+ | } | ||
+ | this.zugedeckt = zugedeckt; | ||
+ | this.fahne = fahne; | ||
+ | this.inhalt = inhalt; | ||
+ | } | ||
+ | |||
+ | | ||
+ | super.destroy(); | ||
+ | vordergrund.destroy(); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Wird vom Browser aufgerufen, wenn der Mauszeiger sich von außen in die Zelle hineinbewegt | ||
+ | */ | ||
+ | | ||
+ | if(zugedeckt && getSpielZustand() == Zustand.spiel_läuft) { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Wird vom Browser aufgerufen, wenn der Mauszeiger sich aus der Zelle | ||
+ | * hinausbewegt. | ||
+ | */ | ||
+ | | ||
+ | vordergrund.tint("# | ||
+ | getWorld().setCursor(" | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Wird vom Browser aufgerufen, wenn eine der Maustasten nach unten gedrückt wird. | ||
+ | */ | ||
+ | | ||
+ | vordergrund.tint("# | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Wird vom Browser aufgerufen, wenn eine der Maustasten losgelassen wird. | ||
+ | */ | ||
+ | | ||
+ | if(getSpielZustand() != Zustand.spiel_läuft) return; | ||
+ | |||
+ | if(key == 0) { // linke Maustaste, also aufdecken | ||
+ | | ||
+ | // Mine erwischt? | ||
+ | | ||
+ | inhalt = ZellBild.mine_explodiert; | ||
+ | setZustand(inhalt, | ||
+ | spielfeld.getMinesweeper().setZustand(Zustand.spiel_verloren); | ||
+ | return; | ||
+ | } | ||
+ | | ||
+ | } else { | ||
+ | // Rechte Maustaste => Fahne setzen oder löschen | ||
+ | | ||
+ | if(!fahne) { | ||
+ | | ||
+ | } else { | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Spiel gewonnen? | ||
+ | if(spielfeld.alleAufgedeckt()) { | ||
+ | | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | | ||
+ | setZustand(inhalt, | ||
+ | } | ||
+ | |||
+ | | ||
+ | return spielfeld.getMinesweeper().getZustand(); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <script type=" | ||
+ | /** | ||
+ | * Das Smiley-Objekt stellt den Smiley über dem Spielfeld dar. | ||
+ | */ | ||
+ | class Smiley extends Sprite { | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | super(x, y, SpriteLibrary.Minesweeper, | ||
+ | this.minesweeper = minesweeper; | ||
+ | scale(2); | ||
+ | } | ||
+ | |||
+ | | ||
+ | minesweeper.onSmileyKlicked(); | ||
+ | } | ||
+ | |||
+ | | ||
+ | this.setImageIndex(18); | ||
+ | } | ||
+ | |||
+ | | ||
+ | this.setImageIndex(17); | ||
+ | } | ||
+ | |||
+ | | ||
+ | this.setImageIndex(19); | ||
+ | } | ||
+ | |||
+ | | ||
+ | if(minesweeper.getZustand() != Zustand.spiel_läuft) { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | | ||
+ | getWorld().setCursor(" | ||
+ | tint("# | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <script type=" | ||
+ | /** | ||
+ | * Ein Objekt dieser Klasse stellt einen roten dreistelligen Zähler dar. | ||
+ | */ | ||
+ | class Counter extends Group { | ||
+ | |||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | super(); | ||
+ | for(int i = 0; i < 3; i++) { | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Stellt die übergebene Zahl auf dem Bildschirm dar. | ||
+ | */ | ||
+ | | ||
+ | this.number = number; | ||
+ | for(int i = 0; i < 3; i++) { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | | ||
+ | return number; | ||
+ | } | ||
+ | |||
+ | | ||
+ | set(number + n); | ||
+ | } | ||
+ | |||
+ | } | ||
</ | </ | ||
api/projects/minesweeper/start.1609166411.txt.gz · Zuletzt geändert: 2021/12/29 11:29 (Externe Bearbeitung)