semesterprojekt: implement full text adventure (phases 1-7)
Walking skeleton through Swing GUI: YAML-driven world (4 rooms, 4 items, 1 NPC), HashMap command dispatch with parser, three-tier item hierarchy (readable / switchable / plain), and end-to-end NPC give/receive flow. 67 tests green.
This commit is contained in:
127
Semesterprojekt/docs/commands.md
Normal file
127
Semesterprojekt/docs/commands.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Befehle
|
||||
|
||||
Command-Pattern mit Registry — passt zum Aufgaben-Tipp „switch, if-else, HashMap" und macht die HashMap-Variante explizit.
|
||||
|
||||
## Interface
|
||||
|
||||
```java
|
||||
public interface Command {
|
||||
/** Wird vom Parser nach Tokenisierung aufgerufen. */
|
||||
void execute(GameContext ctx, List<String> args);
|
||||
|
||||
/** Short help text for the 'help' command. */
|
||||
String help();
|
||||
}
|
||||
```
|
||||
|
||||
## Registry
|
||||
|
||||
```java
|
||||
public class CommandRegistry {
|
||||
private final Map<String, Command> commands = new HashMap<>();
|
||||
|
||||
public void register(Command cmd, String... names) {
|
||||
for (String n : names) {
|
||||
commands.put(n.toLowerCase(), cmd);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<Command> find(String verb) {
|
||||
return Optional.ofNullable(commands.get(verb.toLowerCase()));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Aliase per Mehrfach-Registrierung:**
|
||||
|
||||
```java
|
||||
registry.register(new GoCommand(), "go", "move");
|
||||
registry.register(new TakeCommand(), "take", "pick");
|
||||
```
|
||||
|
||||
## Parser
|
||||
|
||||
Einfacher tokenisierender Parser. Erst-Token = Verb, Rest = Argumente.
|
||||
|
||||
Spezialfall: Präpositionen / Artikel wegfiltern, damit `go to north` und `go north` beide funktionieren.
|
||||
|
||||
```java
|
||||
public record ParsedCommand(String verb, List<String> args) {}
|
||||
|
||||
public class CommandParser {
|
||||
private static final Set<String> FILLERS = Set.of("to", "with", "at", "the", "a", "an");
|
||||
|
||||
public ParsedCommand parse(String input) {
|
||||
String[] tokens = input.trim().toLowerCase().split("\\s+");
|
||||
if (tokens.length == 0 || tokens[0].isEmpty()) {
|
||||
return new ParsedCommand("", List.of());
|
||||
}
|
||||
String verb = tokens[0];
|
||||
List<String> args = Arrays.stream(tokens, 1, tokens.length)
|
||||
.filter(t -> !FILLERS.contains(t))
|
||||
.toList();
|
||||
return new ParsedCommand(verb, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Befehlsliste (Pflicht + Optional)
|
||||
|
||||
| Verb | Aliase | Wirkung | Quelle |
|
||||
|---|---|---|---|
|
||||
| `go <direction>` | `move`, `walk` | Spieler wechselt Raum | Pflicht |
|
||||
| `take <item>` | `pick`, `get` | Item aus Raum ins Inventar | Pflicht |
|
||||
| `drop <item>` | `put` | Item aus Inventar in Raum | sinnvoll |
|
||||
| `use <item>` | — | Item-spezifische Aktion | Pflicht |
|
||||
| `read <item>` | — | Spezialfall von use für `readable` | Pflicht |
|
||||
| `inventory` | `inv`, `i` | Inventar anzeigen | sinnvoll |
|
||||
| `look` | `l` | Raumbeschreibung wiederholen | sinnvoll |
|
||||
| `examine <item>` | `x` | Item-Beschreibung anzeigen | sinnvoll |
|
||||
| `talk <npc>` | `speak` | NPC-Greeting ausgeben | NPC-Bonus |
|
||||
| `give <item> <npc>` | — | Item übergeben, Reaktion auslösen | NPC-Bonus |
|
||||
| `help` | `?` | Befehlsübersicht | sinnvoll |
|
||||
| `quit` | `exit` | Spiel beenden | sinnvoll |
|
||||
|
||||
## Behandlung unbekannter Befehle
|
||||
|
||||
```java
|
||||
parser.parse(input);
|
||||
registry.find(parsed.verb())
|
||||
.ifPresentOrElse(
|
||||
cmd -> cmd.execute(ctx, parsed.args()),
|
||||
() -> ctx.io().write("I don't understand '" + parsed.verb() + "'. Type 'help'.")
|
||||
);
|
||||
```
|
||||
|
||||
## GameContext
|
||||
|
||||
Wird allen Commands gereicht, kapselt was sie ändern dürfen:
|
||||
|
||||
```java
|
||||
public class GameContext {
|
||||
private final World world;
|
||||
private final Player player;
|
||||
private final GameIO io;
|
||||
// Lombok @Getter, kein Setter
|
||||
}
|
||||
```
|
||||
|
||||
So vermeidest du, dass jeder Command 5 Konstruktor-Parameter braucht.
|
||||
|
||||
## Tests
|
||||
|
||||
Pro Command ein Testfall, der `GameContext` mit Mockito mockt (oder als Fake-IO baut):
|
||||
|
||||
```java
|
||||
@Test
|
||||
void goCommand_movesPlayerToConnectedRoom() {
|
||||
Room kitchen = ...; Room hallway = ...;
|
||||
kitchen.getExits().put(Direction.NORTH, hallway);
|
||||
Player p = new Player(kitchen, 0);
|
||||
GameContext ctx = new GameContext(world, p, new TestIO());
|
||||
|
||||
new GoCommand().execute(ctx, List.of("north"));
|
||||
|
||||
assertThat(p.getCurrentRoom()).isEqualTo(hallway);
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user