From 44babda11450ba5f7b4406137a03411ed9e53cc8 Mon Sep 17 00:00:00 2001 From: Bernhard Tellenbach Date: Sun, 17 Sep 2017 17:06:55 +0200 Subject: [PATCH] Initial commit of the source files. --- Befehl.java | 74 +++++++++++++++ Befehlswoerter.java | 41 +++++++++ Parser.java | 66 ++++++++++++++ README.TXT | 29 ++++++ Raum.java | 90 ++++++++++++++++++ Spiel.java | 217 ++++++++++++++++++++++++++++++++++++++++++++ package.bluej | 103 +++++++++++++++++++++ 7 files changed, 620 insertions(+) create mode 100644 Befehl.java create mode 100644 Befehlswoerter.java create mode 100644 Parser.java create mode 100644 README.TXT create mode 100644 Raum.java create mode 100644 Spiel.java create mode 100644 package.bluej diff --git a/Befehl.java b/Befehl.java new file mode 100644 index 0000000..1fe5f7e --- /dev/null +++ b/Befehl.java @@ -0,0 +1,74 @@ +/** + * Objekte dieser Klasse halten Informationen über Befehle, + * die der Benutzer eingegeben hat. Ein Befehl besteht momentan + * aus zwei Zeichenketten: einem Befehlswort und einem zweiten + * Wort. Beim Befehl "nimm karte" beispielsweise sind die beiden + * Zeichenketten "nimm" und "karte". + * + * Befehle werden von Benutzern dieser Klasse auf Gültigkeit + * überprüft. Wenn ein Spieler einen ungültigen Befehl eingegeben + * hat (ein unbekanntes Befehlswort), dann ist das Befehlswort . + * + * Wenn der Befehl nur aus einem Wort bestand, dann ist das + * zweite Wort . + * + * @author Michael Kölling und David J. Barnes + * @version 31.07.2011 + */ + +class Befehl +{ + private String befehlswort; + private String zweitesWort; + + /** + * Erzeuge ein Befehlsobjekt. Beide Wörter müssen angegeben werden, + * aber jedes oder beide dürfen 'null' sein. + * @param erstesWort Das erste Wort des Befehls. Sollte + * 'null' sein, wenn dieser Befehl als nicht + * vom Spiel erkannt gekennzeichnet werden soll. + * @param zweitesWort Das zweite Wort des Befehls. + */ + public Befehl(String erstesWort, String zweitesWort) + { + befehlswort = erstesWort; + this.zweitesWort = zweitesWort; + } + + /** + * Liefere das Befehlswort (das erste Wort) dieses Befehls. + * Wenn der Befehl nicht verstanden wurde, ist das Ergebnis + * 'null'. + * @return Das Befehlswort. + */ + public String gibBefehlswort() + { + return befehlswort; + } + + /** + * @return Das zweite Wort dieses Befehls. Liefere 'null', wenn + * es kein zweites Wort gab. + */ + public String gibZweitesWort() + { + return zweitesWort; + } + + /** + * @return 'true', wenn dieser Befehl nicht verstanden wurde. + */ + public boolean istUnbekannt() + { + return (befehlswort == null); + } + + /** + * @return 'true', wenn dieser Befehl ein zweites Wort hat. + */ + public boolean hatZweitesWort() + { + return (zweitesWort != null); + } +} + diff --git a/Befehlswoerter.java b/Befehlswoerter.java new file mode 100644 index 0000000..0fd538e --- /dev/null +++ b/Befehlswoerter.java @@ -0,0 +1,41 @@ +/* + * Diese Klasse hält eine Aufzählung aller Befehlswörter, die dem + * Spiel bekannt sind. Mit ihrer Hilfe werden eingetippte Befehle + * erkannt. + * + * @author Michael Kölling und David J. Barnes + * @version 31.07.2011 + */ + +class Befehlswoerter +{ + // ein konstantes Array mit den gültigen Befehlswörtern + private static final String gueltigeBefehle[] = { + "go", "quit", "help", "look" + }; + + /** + * Konstruktor - initialisiere die Befehlswörter. + */ + public Befehlswoerter() + { + // nichts zu tun momentan... + } + + /** + * Prüfe, ob eine gegebene Zeichenkette ein gültiger + * Befehl ist. + * @return 'true', wenn die gegebene Zeichenkette ein gültiger + * Befehl ist, 'false' sonst. + */ + public boolean istBefehl(String eingabe) + { + for(int i = 0; i < gueltigeBefehle.length; i++) { + if(gueltigeBefehle[i].equals(eingabe)) + return true; + } + // Wenn wir hierher gelangen, wurde die Eingabe nicht + // in den Befehlswörter gefunden. + return false; + } +} diff --git a/Parser.java b/Parser.java new file mode 100644 index 0000000..2e7bb61 --- /dev/null +++ b/Parser.java @@ -0,0 +1,66 @@ +import java.util.Scanner; +import java.util.StringTokenizer; + +/** + * Dieser Parser liest Benutzereingaben und wandelt sie in + * Befehle für das Adventure-Game um. Bei jedem Aufruf + * liest er eine Zeile von der Konsole und versucht, diese als + * einen Befehl aus bis zu zwei Wörtern zu interpretieren. Er + * liefert den Befehl als ein Objekt der Klasse Befehl zurück. + * + * Der Parser verfügt über einen Satz an bekannten Befehlen. Er + * vergleicht die Eingabe mit diesen Befehlen. Wenn die Eingabe + * keinen bekannten Befehl enthält, dann liefert der Parser ein als + * unbekannter Befehl gekennzeichnetes Objekt zurück. + * + * @author Michael Kölling und David J. Barnes + * @version 31.07.2011 + */ +class Parser +{ + private Befehlswoerter befehle; // hält die gültigen Befehlswörter + private Scanner leser; // Lieferant für eingegebene Befehle + + /** + * Erzeuge einen Parser, der Befehle von der Konsole einliest. + */ + public Parser() + { + befehle = new Befehlswoerter(); + leser = new Scanner(System.in); + } + + /** + * @return Den nächsten Befehl des Benutzers. + */ + public Befehl liefereBefehl() + { + String eingabezeile; // für die gesamte Eingabezeile + String wort1 = null; + String wort2 = null; + + System.out.print("> "); // Eingabeaufforderung + + eingabezeile = leser.nextLine(); + + // Finde bis zu zwei Wörter in der Zeile + Scanner zerleger = new Scanner(eingabezeile); + if(zerleger.hasNext()) { + wort1 = zerleger.next(); // erstes Wort lesen + if (zerleger.hasNext()) { + wort2 = zerleger.next(); // zweites Wort lesen + // Hinweis: Wir ignorieren den Rest der Eingabezeile. + } + } + + // Jetzt prüfen, ob der Befehl bekannt ist. Wenn ja, erzeugen + // wir das passende Befehl-Objekt. Wenn nicht, erzeugen wir + // einen unbekannten Befehl mit 'null'. + if(befehle.istBefehl(wort1)) { + return new Befehl(wort1, wort2); + } + else { + return new Befehl(null, wort2); + } + } +} diff --git a/README.TXT b/README.TXT new file mode 100644 index 0000000..6c12bff --- /dev/null +++ b/README.TXT @@ -0,0 +1,29 @@ +Projekt: Zuul-schlecht +Autoren: Michael Kölling und David J. Barnes + +Dieses Projekt ist Teil des Zusatzmaterials zum Buch + + Java lernen mit BlueJ - eine Einführung in die + objektorientierte Programmierung, 5. Auflage + David J. Barnes und Michael Kölling + Pearson Education Deutschland, 2012 + +Dieses Projekt ist das Grundgerüst für ein Adventure-Game. +In dieser Version hat es ein paar Räume und bietet dem Spieler die +Möglichkeit, sich zwischen diesen Räumen zu bewegen. Das ist alles. + +Um ein Spiel zu starten, erzeugen Sie eine Instanz von 'Spiel' und rufen +die Methode 'spielen' auf. + +Diese Version des Spiels enthält einige schlechte Entwurfsentscheidungen. +Sie sollte nicht als Basis für Erweiterungen am Projekt benutzt werden, +bevor diese problematischen Entwurfsentscheidungen korrigiert wurden. +Diese Version dient als ein Beispiel in der Diskussion über gute +und schlechte Entwürfe (Kapitel 6 im Buch). + +In Kapitel 6 des Buches ist ausführlich beschrieben, welche Probleme +bestehen und wie sie behoben werden können. + +Das Projekt 'Zuul-besser' enthält eine Version dieses Projektes mit +einer besser entworfenen Klassenstruktur, in der die im Buch +beschriebenen Schwächen behoben sind. diff --git a/Raum.java b/Raum.java new file mode 100644 index 0000000..8801b2e --- /dev/null +++ b/Raum.java @@ -0,0 +1,90 @@ + + +import java.util.HashMap; +import java.util.Set; + +/** + * Diese Klasse modelliert Räume in der Welt von Zuul. + * + * Ein "Raum" repräsentiert einen Ort in der virtuellen Landschaft des + * Spiels. Ein Raum ist mit anderen Räumen über Ausgänge verbunden. + * Mögliche Ausgänge liegen im Norden, Osten, Süden und Westen. + * Für jede Richtung hält ein Raum eine Referenz auf den + * benachbarten Raum. + * + * @author Michael Kölling und David J. Barnes + * @version 31.07.2011 + */ +public class Raum { + private String beschreibung; + private HashMap ausgaenge; + + /** + * Erzeuge einen Raum mit einer Beschreibung. Ein Raum hat anfangs keine + * Ausgänge. + * + * @param beschreibung + * enthält eine Beschreibung in der Form "in einer Küche" oder + * "auf einem Sportplatz". + */ + public Raum(String beschreibung) { + this.beschreibung = beschreibung; + this.ausgaenge = new HashMap<>(); + } + + /** + * Definiert die Ausgänge dieses Raums. + * + * @param richtung + * Die Richtung (north, east, south, west) + * @param raum + * Der nächste Raum + */ + public void setzeAusgaenge(String richtung, Raum raum) { + ausgaenge.put(richtung, raum); + } + + /** + * Gibt den Raum für eine Richtung zurück + * + * @param richtung + * Die Richtung (north, east, south, west) + * @return Der nächste Raum + */ + public Raum gibAusgang(String richtung) { + return ausgaenge.get(richtung); + } + + /** + * @return die Beschreibung dieses Raums. + */ + public String gibBeschreibung() { + return beschreibung; + } + + /** + * Liefert eine Beschreibung der Ausgänge dieses Raumes, bespielsweise + * "Ausgänge: north west". + * + * @return eine Beschreibung der verfügbaren Ausgänge + */ + public String gibAusgaengeAlsString() { + String resultat = "Ausgänge:"; + Set ausgaengeSet = ausgaenge.keySet(); + for (String ausgang : ausgaengeSet) { + resultat += " " + ausgang; + } + return resultat; + } + + /** + * Liefert eine lange Beschreibung dieses Raumes, in der Form + * Sie sind in der Küche + * Ausgänge: north west + * + * @return eine lange Beschreibung dieses Raumes. + */ + public String gibLangeBeschreibung() { + return "Sie sind " + beschreibung + ".\n" + gibAusgaengeAlsString(); + } +} diff --git a/Spiel.java b/Spiel.java new file mode 100644 index 0000000..3eec68b --- /dev/null +++ b/Spiel.java @@ -0,0 +1,217 @@ + + +/** + * Dies ist die Hauptklasse der Anwendung "Die Welt von Zuul". + * "Die Welt von Zuul" ist ein sehr einfaches, textbasiertes Adventure-Game. Ein + * Spieler kann sich in einer Umgebung bewegen, mehr nicht. Das Spiel sollte auf + * jeden Fall ausgebaut werden, damit es interessanter wird! + * + * Zum Spielen muss eine Instanz dieser Klasse erzeugt werden und an ihr die + * Methode "spielen" aufgerufen werden. + * + * Diese Instanz erzeugt und initialisiert alle anderen Objekte der Anwendung: + * Sie legt alle Räume und einen Parser an und startet das Spiel. Sie wertet + * auch die Befehle aus, die der Parser liefert, und sorgt für ihre Ausführung. + * + * @author Michael Kölling und David J. Barnes + * @version 31.07.2011 + */ + +public class Spiel { + private final static String NORTH = "north"; + private final static String EAST = "east"; + private final static String SOUTH = "south"; + private final static String WEST = "west"; + + private Parser parser; + private Raum aktuellerRaum; + + /** + * Erzeuge ein Spiel und initialisiere die interne Raumkarte. + */ + public Spiel() { + raeumeAnlegen(); + parser = new Parser(); + } + + /** + * Erzeuge alle Räume und verbinde ihre Ausgänge miteinander. + */ + private void raeumeAnlegen() { + Raum draussen, hoersaal, cafeteria, labor, buero; + + draussen = new Raum("vor dem Haupteingang der Universität"); + hoersaal = new Raum("in einem Vorlesungssaal"); + cafeteria = new Raum("in der Cafeteria der Uni"); + labor = new Raum("in einem Rechnerraum"); + buero = new Raum("im Verwaltungsbüro der Informatik"); + + draussen.setzeAusgaenge(EAST, hoersaal); + draussen.setzeAusgaenge(SOUTH, labor); + draussen.setzeAusgaenge(WEST, cafeteria); + + hoersaal.setzeAusgaenge(WEST, draussen); + + cafeteria.setzeAusgaenge(EAST, draussen); + + labor.setzeAusgaenge(NORTH, draussen); + labor.setzeAusgaenge(EAST, buero); + + buero.setzeAusgaenge(WEST, labor); + + aktuellerRaum = draussen; + } + + /** + * Die Hauptmethode zum Spielen. Läuft bis zum Ende des Spiels in einer + * Schleife. + */ + public void spielen() { + willkommenstextAusgeben(); + + // Die Hauptschleife. Hier lesen wir wiederholt Befehle ein + // und führen sie aus, bis das Spiel beendet wird. + + boolean beendet = false; + while (!beendet) { + Befehl befehl = parser.liefereBefehl(); + beendet = verarbeiteBefehl(befehl); + } + System.out.println("Danke für dieses Spiel. Auf Wiedersehen."); + } + + /** + * Einen Begrüssungstext für den Spieler ausgeben. + */ + private void willkommenstextAusgeben() { + System.out.println(); + System.out.println("Willkommen zu Zuul!"); + System.out + .println("Zuul ist ein neues, unglaublich langweiliges Spiel."); + System.out.println("Tippen sie 'help', wenn Sie Hilfe brauchen."); + System.out.println(); + rauminfoAusgeben(); + } + + /** + * Verarbeite einen gegebenen Befehl (führe ihn aus). + * + * @param befehl + * Der zu verarbeitende Befehl. + * @return 'true', wenn der Befehl das Spiel beendet, 'false' sonst. + */ + private boolean verarbeiteBefehl(Befehl befehl) { + boolean moechteBeenden = false; + + if (befehl.istUnbekannt()) { + System.out.println("Ich weiss nicht, was Sie meinen..."); + return false; + } + String befehlswort = befehl.gibBefehlswort(); + if (befehlswort.equals("help")) { + hilfstextAusgeben(); + } else if (befehlswort.equals("go")) { + wechsleRaum(befehl); + } else if (befehlswort.equals("quit")) { + moechteBeenden = beenden(befehl); + } + + return moechteBeenden; + } + + // Implementierung der Benutzerbefehle: + + /** + * Gib Hilfsinformationen aus. Hier geben wir eine etwas alberne und unklare + * Beschreibung aus, sowie eine Liste der Befehlswörter. + */ + private void hilfstextAusgeben() { + System.out.println("Sie haben sich verlaufen. Sie sind allein."); + System.out.println("Sie irren auf dem Unigelände herum."); + System.out.println(); + System.out.println("Ihnen stehen folgende Befehle zur Verfügung:"); + System.out.println(" go quit help"); + } + + /** + * Versuche, in eine Richtung zu gehen. Wenn es einen Ausgang gibt, wechsele + * in den neuen Raum, ansonsten gib eine Fehlermeldung aus. + */ + private void wechsleRaum(Befehl befehl) { + if (!befehl.hatZweitesWort()) { + // Gibt es kein zweites Wort, wissen wir nicht, wohin... + System.out.println("Wohin möchten Sie gehen?"); + return; + } + + String richtung = befehl.gibZweitesWort(); + + // Wir versuchen, den Raum zu verlassen. + Raum naechsterRaum = aktuellerRaum.gibAusgang(richtung); + if (naechsterRaum == null) { + System.out.println("Dort ist keine Tür!"); + } else { + aktuellerRaum = naechsterRaum; + rauminfoAusgeben(); + } + } + + /** + * "quit" wurde eingegeben. Ãœberprüfe den Rest des Befehls, ob das Spiel + * wirklich beendet werden soll. + * + * @return 'true', wenn der Befehl das Spiel beendet, 'false' sonst. + */ + private boolean beenden(Befehl befehl) { + if (befehl.hatZweitesWort()) { + System.out.println("Was soll beendet werden?"); + return false; + } else { + return true; // Das Spiel soll beendet werden. + } + } + + private void rauminfoAusgeben() { + + // Lösung A6 + System.out.println(aktuellerRaum.gibLangeBeschreibung()); + + // Lösung A3 + // System.out.println("Sie sind " + aktuellerRaum.gibBeschreibung()); + + // Lösung A5 + // System.out.println(aktuellerRaum.gibAusgaengeAlsString()); + + // Lösung A3 + // System.out.print("Ausgänge: "); + // if (aktuellerRaum.nordausgang != null) { + // System.out.print("north "); + // } + // if (aktuellerRaum.ostausgang != null) { + // System.out.print("east "); + // } + // if (aktuellerRaum.suedausgang != null) { + // System.out.print("south "); + // } + // if (aktuellerRaum.westausgang != null) { + // System.out.print("west "); + // } + // System.out.println(); + + // Lösung A4 + // System.out.print("Ausgänge: "); + // if (aktuellerRaum.gibAusgang(NORTH) != null) { + // System.out.print("north "); + // } + // if (aktuellerRaum.gibAusgang(EAST) != null) { + // System.out.print("east "); + // } + // if (aktuellerRaum.gibAusgang(SOUTH) != null) { + // System.out.print("south "); + // } + // if (aktuellerRaum.gibAusgang(WEST) != null) { + // System.out.print("west "); + // } + // System.out.println(); + } +} diff --git a/package.bluej b/package.bluej new file mode 100644 index 0000000..a63c36c --- /dev/null +++ b/package.bluej @@ -0,0 +1,103 @@ +#BlueJ package file +dependency1.from=Parser +dependency1.to=Befehlswoerter +dependency1.type=UsesDependency +dependency2.from=Parser +dependency2.to=Befehl +dependency2.type=UsesDependency +dependency3.from=Spiel +dependency3.to=Parser +dependency3.type=UsesDependency +dependency4.from=Spiel +dependency4.to=Raum +dependency4.type=UsesDependency +dependency5.from=Spiel +dependency5.to=Befehl +dependency5.type=UsesDependency +objectbench.height=100 +objectbench.width=552 +package.editor.height=497 +package.editor.width=759 +package.editor.x=662 +package.editor.y=297 +package.numDependencies=5 +package.numTargets=6 +package.showExtends=true +package.showUses=true +project.charset=windows-1252 +readme.editor.height=526 +readme.editor.width=748 +readme.editor.x=991 +readme.editor.y=186 +target1.editor.height=526 +target1.editor.width=748 +target1.editor.x=574 +target1.editor.y=47 +target1.height=50 +target1.name=Raum +target1.naviview.expanded=false +target1.showInterface=false +target1.type=ClassTarget +target1.typeParameters= +target1.width=80 +target1.x=130 +target1.y=220 +target2.editor.height=984 +target2.editor.width=1069 +target2.editor.x=-1887 +target2.editor.y=64 +target2.height=50 +target2.name=Spiel +target2.naviview.expanded=false +target2.showInterface=false +target2.type=ClassTarget +target2.typeParameters= +target2.width=80 +target2.x=40 +target2.y=70 +target3.height=50 +target3.name=Note +target3.showInterface=false +target3.type=EnumTarget +target3.typeParameters= +target3.width=80 +target3.x=70 +target3.y=10 +target4.editor.height=526 +target4.editor.width=748 +target4.editor.x=0 +target4.editor.y=0 +target4.height=50 +target4.name=Befehl +target4.showInterface=false +target4.type=ClassTarget +target4.typeParameters= +target4.width=80 +target4.x=130 +target4.y=150 +target5.editor.height=526 +target5.editor.width=748 +target5.editor.x=3 +target5.editor.y=81 +target5.height=50 +target5.name=Befehlswoerter +target5.naviview.expanded=false +target5.showInterface=false +target5.type=ClassTarget +target5.typeParameters= +target5.width=110 +target5.x=320 +target5.y=150 +target6.editor.height=526 +target6.editor.width=748 +target6.editor.x=0 +target6.editor.y=0 +target6.height=50 +target6.name=Parser +target6.naviview.expanded=false +target6.showInterface=false +target6.type=ClassTarget +target6.typeParameters= +target6.width=80 +target6.x=230 +target6.y=20