Als ziemlich grüner Java-Codierer habe ich mich der großen Herausforderung gestellt, ein einfaches Textabenteuer zu schreiben. Es überrascht nicht, dass ich bereits auf Schwierigkeiten gestoßen bin!
Ich versuche, meiner Location-Klasse eine Eigenschaft zum Speichern zu geben, die die darin enthaltenen Exits enthält. Ich habe dafür ein boolesches Array verwendet, um im Wesentlichen True / False-Werte für jeden Exit zu speichern. Das bin ich nicht ganz überzeugt
a) Dies ist der effizienteste Weg, dies zu tun und
b) dass ich den richtigen Code verwende, um das Array zu füllen.
Ich würde mich über jegliches Feedback freuen, auch wenn es sich um eine vollständige Codeüberholung handelt!
Gegenwärtig generiere ich beim Instanziieren eines Speicherorts einen String, den ich an die setExits-Methode sende:
String e = "N S U";
secretRoom.setExits(e);
In der Location-Klasse sieht setExits folgendermaßen aus:
public void setExits(String e) {
if (e.contains("N"))
bexits[0] = true;
else if (e.contains("W"))
bexits[1] = true;
else if (e.contains("S"))
bexits[2] = true;
else if (e.contains("E"))
bexits[3] = true;
else if (e.contains("U"))
bexits[4] = true;
else if (e.contains("D"))
bexits[5] = true;
}
Ich bin ehrlich, ich denke, das sieht besonders klobig aus, aber ich konnte mir keinen anderen Weg vorstellen, es zu tun. Ich bin mir jetzt auch nicht ganz sicher, wie ich die getExits-Methode schreiben soll ...
Jede Hilfe wäre willkommen!
Antworten:
Gibt es einen Grund, warum Sie dies mit
String
s tun und nicht vorbeikommenbooleans
, dhpublic void setExits(boolean N, boolean E, boolean S, boolean W, boolean U, boolean D)
Oder Setter haben ?
public void setNorthOpen(boolean open) { bexits[4] = open; }
Zweitens, warum speichern Sie die Ausgänge als Array von Booleschen Werten? Es ist eine kleine endliche Menge, warum nicht einfach
boolean N,S,E,W,U,D;
Bis dahin müssen Sie nicht verfolgen, welche Nummer im Array jede Richtung ist.
Ebenfalls
Dies ist eine korrekte Antwort (wenn auch nicht ganz optimal wie die von @gexicide), aber ich ermutige jeden, sich die anderen Antworten hier anzusehen, um einen interessanten Blick darauf zu werfen, wie Dinge in Java auf verschiedene Arten getan werden können.
Zum späteren Nachschlagen
Code, der funktioniert, gehört zur Codeüberprüfung , nicht zum Stapelüberlauf. Obwohl, wie @kajacx betonte, dieser Code eigentlich nicht funktionieren sollte.
quelle
String
s halten möchten, aber wenn Sie keinen besonderen Grund dafür haben, würde ich vorschlagen, zu allen zu wechseln,booleans
wie in meinem Beispiel.EnumSet
schönste Option ist, aber ich hatte gelesen, dass das OP "grün" ist, und dachte, etwas Einfaches als Sprungbrett vom offensichtlich schmerzhaftenString
Passieren sei eine bessere Option. Normalerweise hätte ich meine Antwort erweitert, um fortgeschrittener zu sein, aber @gexicide hat in der Zwischenzeit geantwortet und ich möchte nicht auf stehlen antworten und beide sind jetzt für jeden da, der es will. Down Voting, weil es nicht die absolut optimale Antwort ist, ist nicht wirklich richtig, oder?Der effizienteste und ausdrucksstärkste Weg ist der folgende:
Verwenden Sie
enum
s als Exits und verwenden Sie aEnumSet
, um sie zu speichern.EnumSet
ist eine effizienteSet
Implementierung, die ein Bitfeld verwendet, um die Enum-Konstanten darzustellen.So können Sie es machen:
public enum Exit { North, West, South, East, Up, Down; } EnumSet<Exit> set = EnumSet.noneOf(Exit.class); // An empty set. // Now you can simply add or remove exits, everything will be stored compactly set.add(Exit.North); // Add exit set.contains(Exit.West); // Test if an exit is present set.remove(Exit.South); //Remove an exit
Enum set speichert alle Exits
long
intern in einem einzigen , sodass Ihr Code ausdrucksstark und schnell ist und viel Speicherplatz spart.quelle
public enum Exit { North, West, South, East, Up, Down; }
EnumSet
, aber es sieht nach einer großartigen Redewendung aus, an die man sich gewöhnen muss.Exit
immer dieselbe Klasse sein wird (RegularEnumSet), kann die JIT möglicherweise alle virtuellen Aufrufe eliminieren und alles inline schalten. Die Verwendungint
, um einige virtuelle Anrufe loszuwerden, die sehr wahrscheinlich optimiert werden, riecht sehr nach vorzeitiger Optimierung.EnumSet
hier benutze .OK, zunächst einmal
setExits()
funktioniert Ihre Methode nicht wie beabsichtigt, verkettet, wenn-elseif maximal 1 Codezweig ausführt, zum Beispiel:if (e.contains("N")) bexits[0] = true; else if (e.contains("W")) bexits[1] = true;
Auch wenn
e
beideN
und enthaltenW
,bexits[0]
wird nur gesetzt. Außerdem werden mit dieser Methode nur Exits hinzugefügt (z. B. werden durch AufrufensetExits("")
keine vorhandenen Exits gelöscht.Ich würde diese Methode ändern in:
bexits[0] = e.contains("N"); bexits[1] = e.contains("W"); ...
Außerdem würde ich mich definitiv nicht daran erinnern, dass Nord auf Index 0 ist, West auf 1, ... daher ist es üblich, Ihre Indizes mit endgültigen statischen Konstanten zu benennen:
public static final int NORTH = 0; public static final int WEST = 1; ...
Dann können Sie in Ihrer
setExits
Methode schreiben :bexits[NORTH] = e.contains("N"); bexits[WEST] = e.contains("W"); ...
(viel besser lesbar)
Wenn Sie Ihren Code noch übersichtlicher gestalten möchten, können Sie eine
Exits
Klasse erstellen, die verfügbare Exits darstellt und von einem booleschen Array unterstützt wird. An der Stelle, an der Sie Ihren String erstellen, können Sie stattdessen diese Klasse erstellen und sich die Arbeit beim Generieren und anschließenden Parsen eines Strings ersparen.BEARBEITEN:
Wie @gexicide antwortet, gibt es eine wirklich praktische Klasse,
EnumSet
die wahrscheinlich besser für die Darstellung der Exits geeignet wäre als ein Bollean-Array.quelle
public static final int
fühlt sich an wie Java 1.4 und wurde stattdessen durch Aufzählungen ersetztDirections.NORTH.ordinal()
für den Index einer bestimmten Richtung verwenden müssen ...EnumSet
sieht das wirklich cool aus, kannte diese Klasse nicht, ich werde es der Antwort hinzufügen.Die
EnumSet
in der anderen Antwort ist der beste Weg, dies zu tun. Ich wollte jedoch nur noch eine Sache für die Zukunft hinzufügen, wenn Sie anfangen, nicht nur zu prüfen, ob Sie sich bewegen können, sondern wohin Sie ziehen.Sowie
EnumSet
du auch hastEnumMap
.Wenn Sie eine Raumklasse / Schnittstelle definieren, können Sie diese innerhalb der Raumklasse haben
Map<Direction, Room> exits = new EnumMap<>(Direction.class);
Sie können Ihre Links jetzt wie folgt zur Karte hinzufügen:
Dann kann Ihr Code zum Wechseln zwischen Räumen ein sehr allgemeiner Zweck sein:
Room destination=currentRoom.getExit(directionMoved); if (destination == null) { // Cannot move that way } else { // Handle move to destination }
quelle
Ich würde eine Exit-Aufzählung erstellen und für die Standortklasse einfach eine Liste von Exit-Objekten festlegen.
so wäre es so etwas wie:
public enum Exit { N, S, E, W, U, D } List<Exit> exits = parseExits(String exitString); location.setExits(exits);
quelle
parseExits
Angesichts dessen, wie Ihr Code aussieht, ist dies die am besten lesbare Implementierung, die ich mir vorstellen kann:
public class Exits { private static final char[] DIRECTIONS = "NSEWUD".toCharArray(); public static void main(String... args) { String input = "N S E"; boolean[] exits = new boolean[DIRECTIONS.length]; for(int i = 0; i< exits.length; i++) { if (input.indexOf(DIRECTIONS[i]) >= 0) { exits[i] = true; } } } }
Davon abgesehen sind eine Reihe sauberer Lösungen möglich. Persönlich würde ich mit Aufzählungen und einem gehen
EnumSet
.Ihr ursprünglicher Code ist übrigens falsch, da er als höchstens ein Wert im Array auf true gesetzt wird.
quelle
Wenn Sie Exits als Zeichenfolge definieren, sollten Sie diese verwenden. Ich würde es gerne machen wie:
public class LocationWithExits { public static final String NORTH_EXIT="[N]"; public static final String SOUTH_EXIT="[S]"; public static final String EAST_EXIT="[E]"; public static final String WEST_EXIT="[W]"; private final String exitLocations; public LocationWithExits(String exitLocations) { this.exitLocations = exitLocations; } public boolean hasNorthExit(){ return exitLocations.contains(NORTH_EXIT); } public static void main(String[] args) { LocationWithExits testLocation=new LocationWithExits(NORTH_EXIT+SOUTH_EXIT); System.out.println("Has exit on north?: "+testLocation.hasNorthExit()); } }
Die Verwendung eines Arrays von Booleschen Werten kann viele Probleme verursachen, wenn Sie vergessen, was genau bexits bedeutet [0]. Os es für Norden oder Süden? etc.
oder Sie können einfach Aufzählungen und eine Liste der verfügbaren Exits verwenden. Dann im Methid-Test, wenn die Liste einen bestimmten Aufzählungswert enthält
quelle
Persönlich denke ich, dass Sie es mit einer Aufzählung ein wenig hacken und Folgendes drehen können:
public void setExits(String e) { if (e.contains("N")) bexits[0] = true; else if (e.contains("W")) bexits[1] = true; else if (e.contains("S")) bexits[2] = true; else if (e.contains("E")) bexits[3] = true; else if (e.contains("U")) bexits[4] = true; else if (e.contains("D")) bexits[5] = true; }
in
public enum Directions { NORTH("N"), WEST("W"), SOUTH("S"), EAST("E"), UP("U"), DOWN("D"); private String identifier; private Directions(String identifier) { this.identifier = identifier; } public String getIdentifier() { return identifier; } }
und dann mache:
public void setExits(String e) { String[] exits = e.split(" "); for(String exit : exits) { for(Directions direction : Directions.values()) { if(direction.getIdentifier().equals(exit)) { bexits[direction.ordinal()] = true; break; } } } }
Obwohl ich Ihnen nach dem Aufschreiben nicht wirklich sagen kann, ob es so viel besser ist. Es ist einfacher, neue Richtungen hinzuzufügen, das ist sicher.
quelle
boolean bexits = new boolean[Directions.values().length];
Map<Directions, Boolean> map = new HashMap<>();
und benutzte sie entwedermap.put(Directions.NORTH, false);
odermap.get(Directions.NORTH);
. Es ist etwas schöner als der einen hoch oben, aber es ist noch nicht so schön wie dieEnumSet
vongexicide
oben.Alle in den Antworten aufgeführten Ansätze sind gut. Aber ich denke, der Ansatz, den Sie wählen müssen, hängt davon ab, wie Sie das Exit-Feld verwenden. Wenn Sie beispielsweise exit als Zeichenfolgen behandeln möchten, erfordert der Ross Drews-Ansatz viele if-else-Bedingungen und -Variablen.
String exit = "N E"; String[] exits = exit.split(" "); boolean N = false, E = false, S = false, W = false, U = false, D = false; for(String e : exits){ if(e.equalsIgnoreCase("N")){ N = true; } else if(e.equalsIgnoreCase("E")){ E = true; } else if(e.equalsIgnoreCase("W")){ W= true; } else if(e.equalsIgnoreCase("U")){ U = true; } else if(e.equalsIgnoreCase("D")){ D = true; } else if(e.equalsIgnoreCase("S")){ S = true; } } setExits(N, E, S, W, U, D);
Auch wenn Sie einen Ausgang haben und prüfen möchten, ob ein Standort diesen bestimmten Ausgang hat, müssen Sie dasselbe erneut tun
public boolean hasExit(String exit){ if(e.equalsIgnoreCase("N")){ return this.N; // Or the corresponding getter method } else if(e.equalsIgnoreCase("E")){ return this.E; } else if(e.equalsIgnoreCase("W")){ return this.W; } else if(e.equalsIgnoreCase("U")){ return this.U; } else if(e.equalsIgnoreCase("D")){ return this.D; } else if(e.equalsIgnoreCase("S")){ return this.S; } }
Wenn Sie es also als Zeichenfolge manipulieren möchten, ist es meiner Meinung nach am besten, sich für Liste und Aufzählung zu entscheiden. Auf diese Weise können Sie Methoden wie hasExit, hasAnyExit, hasAllExits, hasNorthExit, hasSouthExit, getAvailableExits usw. usw. sehr einfach ausführen. Und wenn man die Anzahl der Exits (6) berücksichtigt, die eine Liste (oder einen Satz) verwenden, ist dies kein Overhead. Zum Beispiel
Aufzählung
public enum EXIT { EAST("E"), WEST("W"), NORTH("N"), SOUTH("S"), UP("U"), DOWN("D"); private String exitCode; private EXIT(String exitCode) { this.exitCode = exitCode; } public String getExitCode() { return exitCode; } public static EXIT fromValue(String exitCode) { for (EXIT exit : values()) { if (exit.exitCode.equalsIgnoreCase(exitCode)) { return exit; } } return null; } public static EXIT fromValue(char exitCode) { for (EXIT exit : values()) { if (exit.exitCode.equalsIgnoreCase(String.valueOf(exitCode))) { return exit; } } return null; } }
Location.java
import java.util.ArrayList; import java.util.List; public class Location { private List<EXIT> exits; public Location(){ exits = new ArrayList<EXIT>(); } public void setExits(String exits) { for(char exitCode : exits.toCharArray()){ EXIT exit = EXIT.fromValue(exitCode); if(exit != null){ this.exits.add(exit); } } } public boolean hasExit(String exitCode){ return exits.contains(EXIT.fromValue(exitCode)); } public boolean hasAnyExit(String exits){ for(char exitCode : exits.toCharArray()){ if(this.exits.contains(EXIT.fromValue(exitCode))){ return true; } } return false; } public boolean hasAllExit(String exits){ for(char exitCode : exits.toCharArray()){ EXIT exit = EXIT.fromValue(exitCode); if(exit != null && !this.exits.contains(exit)){ return false; } } return true; } public boolean hasExit(char exitCode){ return exits.contains(EXIT.fromValue(exitCode)); } public boolean hasNorthExit(){ return exits.contains(EXIT.NORTH); } public boolean hasSouthExit(){ return exits.contains(EXIT.SOUTH); } public List<EXIT> getExits() { return exits; } public static void main(String args[]) { String exits = "N E W"; Location location = new Location(); location.setExits(exits); System.out.println(location.getExits()); System.out.println(location.hasExit('W')); System.out.println(location.hasAllExit("N W")); System.out.println(location.hasAnyExit("U D")); System.out.println(location.hasNorthExit()); } }
quelle
List<EXIT>
. Warum nichtSet<EXIT>
?Warum nicht, wenn Sie einen kürzeren Code wünschen:
String symbols = "NWSEUD"; public void setExits(String e) { for (int i = 0; i < 6; i++) { bexits[i] = e.contains(symbols.charAt(i)); } }
quelle
Wenn Sie eine generische Lösung wünschen, können Sie eine Karte verwenden , die von einem Schlüssel (in Ihrem Fall W, S, E ..) einem entsprechenden Wert (in Ihrem Fall einem Booleschen Wert) zugeordnet wird.
Wenn Sie a ausführen
set
, aktualisieren Sie den Wert, dem der Schlüssel zugeordnet ist. Wenn Sie a ausführenget
, können Sie einen Argumentschlüssel nehmen und einfach den Wert des Schlüssels abrufen. Diese Funktionalität ist in map bereits vorhanden und heißt put and get .quelle
contains
Methode. Wenn sich das Objekt in der Menge befindet, hätte die entsprechende Karte WAHR gehabt, wäre das Objekt nicht in der Menge, dann hätte die entsprechende Karte FALSCH gehabt. Nicht zu sagen, dass Ihre Antwort falsch ist, nur eine andere Art, darüber nachzudenken, die etwas weniger Platz benötigt :)Ich mag die Idee, die Exits eines Strings zuzuweisen, sehr, weil sie einen kurzen und lesbaren Code ergibt. Sobald dies erledigt ist, verstehe ich nicht, warum Sie ein boolesches Array erstellen möchten. Wenn Sie eine Zeichenfolge haben, verwenden Sie diese einfach, obwohl Sie möglicherweise eine Validierung hinzufügen möchten, um eine versehentliche Zuweisung von Zeichenfolgen mit unerwünschten Zeichen zu verhindern:
private String exits; public void setExits(String e) { if (!e.matches("[NSEWUD ]*")) throw new IllegalArgumentException(); exits = e; }
Das einzige andere, was ich hinzufügen möchte, ist eine Methode
canExit
, die Sie mit einem Richtungsparameter aufrufen können. zBif (location.canExit('N')) ...
:public boolean canExit(char direction) { return exits.indexOf(direction) >= 0; }
Ich mag Aufzählungen, aber die Verwendung hier scheint mir eine Überentwicklung zu sein, die schnell nervig wird.
** Bearbeiten **: Eigentlich mach das nicht. Es beantwortet die falsche Frage und tut etwas, was nicht getan werden muss. Ich habe gerade die Antwort von @ TimB bemerkt , eine Karte (eine EnumMap) zu verwenden, um Richtungen mit Räumen zu verknüpfen. Es ergibt Sinn.
Ich habe immer noch das Gefühl, dass ein String einfach und effektiv ist , wenn Sie nur die Exit- Existenz verfolgen müssen , und alles andere macht ihn zu kompliziert. Es ist jedoch nicht sinnvoll zu wissen, welche Ausgänge verfügbar sind. Sie werden diese Ausgänge durchlaufen wollen, und wenn Ihr Spiel kein sehr einfaches Layout hat, kann der Code nicht für jede Richtung den richtigen Raum ableiten. Daher müssen Sie jede Richtung explizit einem anderen Raum zuordnen. Es scheint also keine tatsächliche Verwendung für eine Methode "setExits" zu geben, die eine Liste von Anweisungen akzeptiert (unabhängig davon, wie sie intern implementiert ist).
quelle
public void setExits(String e) { String directions="NwSEUD"; for(int i=0;i<directions.length();i++) { if(e.contains(""+directions.charAt(i))) { bexits[i]=true; break; } } }
die iterative Art, das Gleiche zu tun ..
quelle
Lange
else if
Anweisungsketten sollten durchswitch
Anweisungen ersetzt werden.Enum
s sind die aussagekräftigste Art, solche Werte zu speichern, solange die Effizienz keine Rolle spielt. Beachten Sie, dass diesenum
eine Klasse ist, sodass die Erstellung einer neuen Aufzählung mit dem entsprechenden Overhead verbunden ist.quelle