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.
90 lines
4.0 KiB
Markdown
90 lines
4.0 KiB
Markdown
# 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`)
|
|
- `EnumMap` ist array-backed, kein Hashing nötig → schneller und kompakter als `HashMap`
|
|
- Iteration in Enum-Deklarationsreihenfolge (stabil)
|
|
|
|
### `HashMap<String, Room>` für die Welt
|
|
|
|
- Lookup über `id` beim 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:
|
|
|
|
1. **O(1) Lookup** beim `take letter` / `read letter`
|
|
2. **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-Travel `go to <raum>`) und `MapLayout` (Karten-Layout) sind beides
|
|
Breitensuchen → brauchen eine **FIFO-Queue**
|
|
- `ArrayDeque` ist die Standardwahl dafür: O(1) an beiden Enden, keine Node-Allokation
|
|
wie bei `LinkedList`, 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](architecture.md).
|