The docs had drifted from the code and described things that never existed. - architecture.md documented a QuitCommand class (quit/exit are aliases on MenuCommand) and a GameIO interface of read()/write(String); the real one is readLine() plus print(StyledText) with presentation hooks. Both corrected, along with the package diagram and the game-loop sketch. - conventions.md listed a package tree missing save, menu, io.text and command.impl. Updated, including why MapLayout lives in game. - data-structures.md claimed LinkedList was deliberately avoided (Pathfinder uses one for O(1) addFirst during path reconstruction), named the wrong queue for the Swing bridge, and listed an input history that was never built. - Removed enhancement-ideas.md and implementation-status.md: a backlog and a phase tracker, neither of which documents the delivered project. Also stop tracking the built jars. They still ship in the submission, but a 22 MB binary that changes on every build does not belong in git history.
4.0 KiB
Datenstrukturen
Bewusste Wahl jeder Collection — der Dozent bewertet das laut Aufgabenstellung explizit.
Übersicht
| Verwendung | Struktur | Komplexität Lookup |
|---|---|---|
| Ausgänge eines Raums | EnumMap<Direction, Room> |
O(1) |
| Alle Räume der Welt | HashMap<String, Room> |
O(1) |
| Item-Registry (global) | HashMap<String, Item> |
O(1) |
| NPC-Registry (global) | HashMap<String, Npc> |
O(1) |
| Items in einem Raum | LinkedHashMap<String, Item> |
O(1) |
| NPCs in einem Raum | LinkedHashMap<String, Npc> |
O(1) |
| Spieler-Inventar | LinkedHashMap<String, Item> |
O(1) |
| Befehlsregistry | HashMap<String, Command> |
O(1) |
| NPC-Reaktionen | HashMap<String, NpcReaction> |
O(1) |
| Besuchte Räume (für die Karte) | LinkedHashSet<String> |
O(1) + Besuchsreihenfolge |
| BFS-Queue (Pathfinder, MapLayout) | ArrayDeque<Room> |
O(1) FIFO |
| BFS-Pfadrekonstruktion | LinkedList<Room> |
O(1) addFirst |
| GUI-Eingabebrücke (EDT → Worker) | LinkedBlockingQueue<String> |
blockierend, unbegrenzt |
Begründungen im Detail
EnumMap<Direction, Room> für Raum-Ausgänge
- Direction ist Enum (
NORTH,SOUTH,EAST,WEST) EnumMapist array-backed, kein Hashing nötig → schneller und kompakter alsHashMap- Iteration in Enum-Deklarationsreihenfolge (stabil)
HashMap<String, Room> für die Welt
- Lookup über
idbeim Auflösen von Exits - Reihenfolge irrelevant (keine Anzeige der gesamten Welt)
- Standardwahl wenn nur Lookup gebraucht wird
LinkedHashMap<String, Item> für Inventar & Raum-Items
Zwei Anforderungen gleichzeitig:
- O(1) Lookup beim
take letter/read letter - Stabile Anzeigereihenfolge beim
inventory
Eine plain HashMap würde Punkt 2 verletzen (Items springen scheinbar zufällig zwischen Ausgaben). Eine ArrayList<Item> würde Punkt 1 auf O(n) drücken und Duplikat-Prüfung verlangen.
Entscheidung "keine Stapel" (1 Item pro id) macht das Map-basierte Modell sauber.
HashMap<String, Command> für Befehle
- O(1)-Dispatch
- Aliase durch Mehrfach-Registrierung (
put("go", goCmd); put("move", goCmd);) - Entspricht dem expliziten Tipp aus der Aufgabenstellung
- Vermeidet wachsendes
switch-Statement
ArrayDeque<Room> als BFS-Queue
Pathfinder(Auto-Travelgo to <raum>) undMapLayout(Karten-Layout) sind beides Breitensuchen → brauchen eine FIFO-QueueArrayDequeist die Standardwahl dafür: O(1) an beiden Enden, keine Node-Allokation wie beiLinkedList, bessere Cache-Lokalität
LinkedList<Room> für die Pfadrekonstruktion
Die eine Stelle, an der LinkedList die richtige Wahl ist (Pathfinder):
Nach der BFS läuft man über cameFrom rückwärts vom Ziel zum Start und will den
Pfad in Vorwärtsreihenfolge. addFirst() ist bei LinkedList O(1) — bei ArrayList
wäre add(0, x) O(n), weil alles verschoben werden müsste.
Bewusst NICHT gewählt
| Struktur | Warum nicht |
|---|---|
ArrayList<Item> für Inventar |
O(n)-Lookup, Duplikat-Handling nötig |
HashMap<String, Item> für Inventar |
Anzeige-Reihenfolge instabil |
TreeMap irgendwo |
Keine sortierte Iteration nötig, O(log n) ohne Nutzen |
LinkedList als Queue |
dafür ist ArrayDeque besser (s.o.) — als addFirst-Liste in der Pfadrekonstruktion aber sehr wohl genutzt |
Vector / Hashtable |
Legacy, synchronisiert (nicht gebraucht), langsamer |
Map<String, String> für Exits in Domain |
Direction sollte Enum sein, nicht String |
Threading
Single-threaded: Game-Loop liest, dispatcht, schreibt — keine parallelen Mutationen.
Ausnahme: Bei Swing-GUI läuft Input über den Event-Dispatch-Thread, der Game-Loop in
einem Worker-Thread. Als Brücke dient eine LinkedBlockingQueue<String> (SwingIO):
Der JTextField-ActionListener (EDT) legt die Zeile ab, der Worker blockiert in
take(). Unbegrenzt statt ArrayBlockingQueue, weil es keine sinnvolle Obergrenze für
getippte Zeilen gibt und ein volles offer() Eingaben verlieren würde — siehe
architecture.md.