aboutsummaryrefslogtreecommitdiff
path: root/Snake.java
diff options
context:
space:
mode:
Diffstat (limited to 'Snake.java')
-rw-r--r--Snake.java257
1 files changed, 257 insertions, 0 deletions
diff --git a/Snake.java b/Snake.java
new file mode 100644
index 0000000..9f31425
--- /dev/null
+++ b/Snake.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2024 Florian Fischer. All rights reserved.
+ * This file is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * It is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import java.awt.event.*;
+import java.util.Random;
+
+/**
+ * Die Klasse Snake verwaltet ein einfaches Snake-Spiel.
+ */
+class Snake implements Zeichenfenster.AktionsEmpfaenger {
+ Random zufall;
+ Schlange schlange;
+ Rechteck essen;
+ long tick;
+
+ /**
+ * Konstruktor der Klasse Snake.
+ * Erzeugt eine Umrandung der Spielflaeche sowie die Schlange.
+ */
+ public Snake() {
+ Zeichenfenster.AktionsEmpfängerEintragen(this);
+
+ // Setze die Taktdauer auf 100ms ~ 10 Bilder/Sekunde
+ Zeichenfenster.TaktdauerSetzen(100);
+
+ // Initalisiere den Zufallsgenerator
+ zufall = new Random();
+ // Initialisiere die Schlange
+ schlange = new Schlange(380, 280);
+
+ // Erzeuge den Rand der Welt
+ Rechteck rand = new Rechteck();
+ rand.FarbeSetzen("schwarz");
+ rand.GrößeSetzen(800, 600);
+ rand.PositionSetzen(0, 0);
+
+ // Erzeuge den Hintergrund der Welt
+ Rechteck hintergrund = new Rechteck();
+ hintergrund.FarbeSetzen("weiss");
+ hintergrund.GrößeSetzen(800 - 4, 600 - 4);
+ hintergrund.PositionSetzen(2, 2);
+
+ // Zeichne den Hintergrund ueber dem Rand
+ hintergrund.GanzNachHintenBringen();
+ rand.GanzNachHintenBringen();
+ }
+
+ /**
+ * Haelt das Spiel an.
+ */
+ void gameOver() {
+ Zeichenfenster.TaktgeberStoppen();
+ Text text = new Text();
+ text.TextSetzen("Game Over!");
+ }
+
+ /**
+ * Wird bei jedem Tick ausgeführt.
+ * Sorgt für die Bewegung der Schlange und erzeugt alle
+ * 30 Takte neues Essen.
+ */
+ public void Ausführen() {
+ tick++;
+ schlange.bewegen();
+
+ // Erzeuge alle dreißig Takte neues Essen
+ if (tick % 30 == 0) {
+ erzeugeEssen();
+ }
+
+ // Hat die Schlage das Essen gefressen?
+ if (essen != null && schlange.beruehrt(essen)) {
+ schlange.wachse(essen.x, essen.y);
+ essen.Entfernen();
+ essen = null;
+ }
+ }
+
+ /**
+ * Methode die aufgerufen wird wenn eine Taste gedrueckt wurde
+ * @param taste die gedrueckte Taste
+ */
+ public void Taste (char taste) {
+ switch (taste) {
+ case 's': schlange.setzeRichtung("runter"); break;
+ case 'w': schlange.setzeRichtung("rauf"); break;
+ case 'a': schlange.setzeRichtung("links"); break;
+ case 'd': schlange.setzeRichtung("rechts"); break;
+ }
+ }
+
+ /**
+ * Methode die aufgerufen wird wenn eine Sondertaste gedrueckt wurde
+ * @param taste das java.awt.event.KeyEvent der gedrueckten Sondertaste
+ */
+ public void SonderTaste (int taste) {
+ switch (taste) {
+ case KeyEvent.VK_DOWN: schlange.setzeRichtung("runter"); break;
+ case KeyEvent.VK_UP: schlange.setzeRichtung("rauf"); break;
+ case KeyEvent.VK_LEFT: schlange.setzeRichtung("links"); break;
+ case KeyEvent.VK_RIGHT: schlange.setzeRichtung("rechts"); break;
+ }
+ }
+
+ public void Geklickt (int x, int y, int anzahl) { }
+
+ /**
+ * Erzeugt ein neues Essens-Rechteck an einer zufaelligen Position.
+ */
+ void erzeugeEssen() {
+ // Entferne das alte Essen
+ if (essen != null) {
+ essen.Entfernen();
+ }
+
+ essen = new Rechteck();
+ essen.FarbeSetzen("rot");
+ essen.GrößeSetzen(20, 20);
+ // Die neue X-Koordinate liegt im Intervall [20; 780]
+ int x = zufall.nextInt((800 - 40) / 20) * 20 + 20;
+ // Die neue Y-Koordinate liegt im Intervall [20; 580]
+ int y = zufall.nextInt((600 - 40) / 20) * 20 + 20;
+ essen.PositionSetzen(x, y);
+ }
+
+ /**
+ * Die Klasse Schlange verwaltet die rechteckigen Koerperteile.
+ */
+ class Schlange {
+ /** Die Richtung in die sich die Schlange bewegt. */
+ String richtung;
+ /** Koerperteile der Schlange. */
+ Rechteck koerper[];
+
+ /**
+ * Konstruktor für Objekte der Klasse Schlange
+ * @param x die X-Koordinate des Kopfes
+ * @param y die Y-Koordinate des Kopfes
+ */
+ public Schlange(int x, int y) {
+ // Instanzvariablen initialisieren
+ richtung = "runter";
+ koerper = new Rechteck[0];
+
+ // Laesst den Kopf der Schlange wachsen
+ wachse(x, y);
+ }
+
+ /**
+ * Laesst ein neues Koerperteil wachsen
+ * @param x die X-Koordinate des neuen Koerperteils
+ * @param y die Y-Koordinate des neuen Koerperteils
+ */
+ void wachse(int x, int y) {
+ Rechteck[] neuerKoerper = new Rechteck[koerper.length + 1];
+
+ Rechteck kopf = new Rechteck();
+ kopf.PositionSetzen(x, y);
+ kopf.GrößeSetzen(20, 20);
+ kopf.FarbeSetzen("gruen");
+
+ neuerKoerper[0] = kopf;
+
+ // Kopiere die alten Koerperteile
+ for (int i = 0; i < koerper.length; i++) {
+ neuerKoerper[i + 1] = koerper[i];
+ }
+
+ koerper = neuerKoerper;
+ }
+
+ /**
+ * Setzt die Bewegungsrichtung der Schlange
+ * Abrupte Richtungswechsel sind nicht erlaubt.
+ * @param neueRichtung die neue Laufrichtung der Schlange
+ */
+ void setzeRichtung(String neueRichtung) {
+ // Erlaube keine abrupten Richtungswechsel bei denen
+ // die Schlange sich selbst fressen wuerde.
+ if ((richtung == "links" && neueRichtung != "rechts") ||
+ (richtung == "rechts" && neueRichtung != "links") ||
+ (richtung == "rauf" && neueRichtung != "runter") ||
+ (richtung == "runter" && neueRichtung != "rauf")
+ ) {
+ richtung = neueRichtung;
+ }
+ }
+
+ /**
+ * Testet ob der Kopf der Schlange ein Rechteck beruehrt.
+ * @param rechteck das Rechteck, das getestet werden soll.
+ * @return true, wenn der Kopf der Schlange das Rechteck schneidet.
+ */
+ boolean beruehrt(Rechteck rechteck) {
+ Rechteck kopf = koerper[0];
+ return kopf.symbol.Schneidet(rechteck.symbol.form);
+ }
+
+ /**
+ * Bewege die Schlange um einen Schritt und behandle Kollisionen
+ */
+ void bewegen() {
+ int deltaX = 0;
+ int deltaY = 0;
+ switch (richtung) {
+ case "rauf": deltaY = -20; break;
+ case "runter": deltaY = 20; break;
+ case "links": deltaX = -20; break;
+ case "rechts": deltaX = 20; break;
+ default:
+ }
+
+ Rechteck kopf = koerper[0];
+
+ // Ziehe alle Koerperteile auf die Position ihres Vorgaengers
+ for (int i = koerper.length - 1; i > 0; i--) {
+ Rechteck vorgaenger = koerper[i-1];
+ koerper[i].PositionSetzen(vorgaenger.x, vorgaenger.y);
+ }
+
+ // Bewege den Kopf der Schlange
+ kopf.Verschieben(deltaX, deltaY);
+
+ // Ist die Schlange gegen den Rand gelaufen?
+ if (kopf.x <= 0 || kopf.x >= 800 - 20 || kopf.y <= 0 || kopf.y >= 600 - 20) {
+ gameOver();
+ }
+
+ // Beruehrt der Kopf ein anderes Koerperteil der Schlange?
+ for (int i = 1; i < koerper.length; i++) {
+ if (beruehrt(koerper[i])) {
+ gameOver();
+ }
+ }
+ }
+ }
+
+ /**
+ * Erzeuge und starte ein neues Spiel.
+ */
+ public static void main(String[] argv) {
+ Snake s = new Snake();
+ Zeichenfenster.TaktgeberStarten();
+ }
+}