feat(command): accept German command and direction aliases
The assignment's examples are German ("Gehe nach Norden", "Nimm Brief",
"Lies Brief", "Benutze Schaufel") but every verb was English-only, so none of
them parsed.
- Register German aliases alongside the English ones (gehe, nimm, lies, benutze,
lege, gib, rede, schau, karte, inventar, hilfe, beenden)
- Direction.fromString accepts norden/sueden/süden/osten/westen and the n/s/e/w
shorthands; YAML exits keep the canonical English names
- Treat "nach", "den", "die", "das" as filler words so "Gehe nach Norden" parses
This commit is contained in:
@@ -47,10 +47,15 @@ import java.util.List;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public final class App {
|
public final class App {
|
||||||
|
|
||||||
|
/** Utility class; not instantiable. */
|
||||||
private App() {
|
private App() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Standard JVM entry point. @param args ignored */
|
/**
|
||||||
|
* Standard JVM entry point.
|
||||||
|
*
|
||||||
|
* @param args ignored
|
||||||
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
GameIO io = new ConsoleIO();
|
GameIO io = new ConsoleIO();
|
||||||
try {
|
try {
|
||||||
@@ -62,12 +67,22 @@ public final class App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Runs the menu shell on the given IO until the player quits. */
|
/**
|
||||||
|
* Runs the menu shell on the given IO until the player quits.
|
||||||
|
*
|
||||||
|
* @param io frontend the menu and the game are rendered on
|
||||||
|
*/
|
||||||
public static void run(GameIO io) {
|
public static void run(GameIO io) {
|
||||||
run(io, new SaveService(), new SettingsStore());
|
run(io, new SaveService(), new SettingsStore());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Runs the menu shell with injectable persistence (for testing). */
|
/**
|
||||||
|
* Runs the menu shell with injectable persistence (for testing).
|
||||||
|
*
|
||||||
|
* @param io frontend the menu and the game are rendered on
|
||||||
|
* @param saves save-slot backend
|
||||||
|
* @param settingsStore settings backend
|
||||||
|
*/
|
||||||
static void run(GameIO io, SaveService saves, SettingsStore settingsStore) {
|
static void run(GameIO io, SaveService saves, SettingsStore settingsStore) {
|
||||||
Settings settings = settingsStore.load();
|
Settings settings = settingsStore.load();
|
||||||
SettingsMenu.apply(settings, io);
|
SettingsMenu.apply(settings, io);
|
||||||
@@ -112,27 +127,29 @@ public final class App {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// German aliases mirror the examples in the assignment ("Gehe nach
|
||||||
|
// Norden", "Nimm Brief", "Lies Brief", "Benutze Schaufel").
|
||||||
CommandRegistry registry = new CommandRegistry();
|
CommandRegistry registry = new CommandRegistry();
|
||||||
registry.register(new GoCommand(), "go", "move", "walk");
|
registry.register(new GoCommand(), "go", "move", "walk", "gehe", "geh");
|
||||||
registry.register(new LookCommand(), "look", "l");
|
registry.register(new LookCommand(), "look", "l", "umsehen", "schau");
|
||||||
registry.register(new InventoryCommand(), "inventory", "inv", "i");
|
registry.register(new InventoryCommand(), "inventory", "inv", "i", "inventar");
|
||||||
registry.register(new TakeCommand(), "take", "pick", "get");
|
registry.register(new TakeCommand(), "take", "pick", "get", "nimm", "nehme");
|
||||||
registry.register(new DropCommand(), "drop", "put");
|
registry.register(new DropCommand(), "drop", "put", "lege", "ablegen");
|
||||||
registry.register(new UseCommand(), "use");
|
registry.register(new UseCommand(), "use", "benutze", "benutz");
|
||||||
registry.register(new ReadCommand(), "read");
|
registry.register(new ReadCommand(), "read", "lies", "lese");
|
||||||
registry.register(new ExamineCommand(), "examine", "x", "inspect");
|
registry.register(new ExamineCommand(), "examine", "x", "inspect", "untersuche");
|
||||||
registry.register(new MapCommand(), "map", "m");
|
registry.register(new MapCommand(), "map", "m", "karte");
|
||||||
registry.register(new QuestsCommand(), "quests", "log", "journal");
|
registry.register(new QuestsCommand(), "quests", "log", "journal", "aufgaben");
|
||||||
registry.register(new TalkCommand(), "talk", "speak");
|
registry.register(new TalkCommand(), "talk", "speak", "rede", "sprich");
|
||||||
registry.register(new GiveCommand(), "give");
|
registry.register(new GiveCommand(), "give", "gib");
|
||||||
registry.register(new SaveCommand(saves), "save");
|
registry.register(new SaveCommand(saves), "save", "speichern");
|
||||||
|
|
||||||
Game game = new Game(ctx, registry, new CommandParser());
|
Game game = new Game(ctx, registry, new CommandParser());
|
||||||
|
|
||||||
MenuCommand menu = new MenuCommand(saves);
|
MenuCommand menu = new MenuCommand(saves);
|
||||||
menu.bind(game);
|
menu.bind(game);
|
||||||
registry.register(menu, "menu", "quit", "exit");
|
registry.register(menu, "menu", "quit", "exit", "beenden");
|
||||||
registry.register(new HelpCommand(registry), "help", "?");
|
registry.register(new HelpCommand(registry), "help", "?", "hilfe");
|
||||||
|
|
||||||
if (withTutorial) {
|
if (withTutorial) {
|
||||||
Tutorial tutorial = new TutorialLoader().load();
|
Tutorial tutorial = new TutorialLoader().load();
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ public class CommandParser {
|
|||||||
* small — when adding more, watch out for words that might be valid
|
* small — when adding more, watch out for words that might be valid
|
||||||
* item ids ({@code key}, {@code lamp}, etc.).
|
* item ids ({@code key}, {@code lamp}, etc.).
|
||||||
*/
|
*/
|
||||||
private static final Set<String> FILLERS = Set.of("to", "with", "at", "the", "a", "an", "on");
|
private static final Set<String> FILLERS =
|
||||||
|
Set.of("to", "with", "at", "the", "a", "an", "on", "nach", "den", "die", "das");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses an input line into a verb and its arguments.
|
* Parses an input line into a verb and its arguments.
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package thb.jeanluc.adventure.model;
|
package thb.jeanluc.adventure.model;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compass directions used for navigating between rooms.
|
* Compass directions used for navigating between rooms.
|
||||||
* Each direction has an opposite, which is used when describing
|
* Each direction has an opposite, which is used when describing
|
||||||
@@ -7,9 +9,13 @@ package thb.jeanluc.adventure.model;
|
|||||||
* entering the next room from the SOUTH).
|
* entering the next room from the SOUTH).
|
||||||
*/
|
*/
|
||||||
public enum Direction {
|
public enum Direction {
|
||||||
|
/** Towards the top of the map; opposite of {@link #SOUTH}. */
|
||||||
NORTH,
|
NORTH,
|
||||||
|
/** Towards the bottom of the map; opposite of {@link #NORTH}. */
|
||||||
SOUTH,
|
SOUTH,
|
||||||
|
/** Towards the right of the map; opposite of {@link #WEST}. */
|
||||||
EAST,
|
EAST,
|
||||||
|
/** Towards the left of the map; opposite of {@link #EAST}. */
|
||||||
WEST;
|
WEST;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,13 +44,40 @@ public enum Direction {
|
|||||||
if (s == null) {
|
if (s == null) {
|
||||||
throw new IllegalArgumentException("Direction must not be null");
|
throw new IllegalArgumentException("Direction must not be null");
|
||||||
}
|
}
|
||||||
|
String key = s.trim().toLowerCase();
|
||||||
|
Direction alias = ALIASES.get(key);
|
||||||
|
if (alias != null) {
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return Direction.valueOf(s.trim().toUpperCase());
|
return Direction.valueOf(key.toUpperCase());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new IllegalArgumentException("Unknown direction: '" + s + "'");
|
throw new IllegalArgumentException("Unknown direction: '" + s + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternative spellings accepted from player input: single-letter
|
||||||
|
* shorthands and the German direction names used in the assignment's
|
||||||
|
* examples ("Gehe nach Norden"). YAML exits still use the canonical
|
||||||
|
* English names.
|
||||||
|
*/
|
||||||
|
private static final Map<String, Direction> ALIASES = Map.ofEntries(
|
||||||
|
Map.entry("n", NORTH),
|
||||||
|
Map.entry("s", SOUTH),
|
||||||
|
Map.entry("e", EAST),
|
||||||
|
Map.entry("w", WEST),
|
||||||
|
Map.entry("norden", NORTH),
|
||||||
|
Map.entry("sueden", SOUTH),
|
||||||
|
Map.entry("süden", SOUTH),
|
||||||
|
Map.entry("osten", EAST),
|
||||||
|
Map.entry("westen", WEST),
|
||||||
|
Map.entry("nord", NORTH),
|
||||||
|
Map.entry("sued", SOUTH),
|
||||||
|
Map.entry("süd", SOUTH),
|
||||||
|
Map.entry("ost", EAST),
|
||||||
|
Map.entry("west", WEST));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the human-readable lowercase label, suitable for display
|
* Returns the human-readable lowercase label, suitable for display
|
||||||
* to the player (e.g. "north"). Intentionally not a toString override
|
* to the player (e.g. "north"). Intentionally not a toString override
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package thb.jeanluc.adventure.command;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import thb.jeanluc.adventure.model.Direction;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The assignment's examples are German ("Gehe nach Norden", "Nimm Brief"), so
|
||||||
|
* those inputs must parse. English stays the canonical form used in YAML.
|
||||||
|
*/
|
||||||
|
class GermanAliasTest {
|
||||||
|
|
||||||
|
private final CommandParser parser = new CommandParser();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parse_gehNachNorden_yieldsGoNorth() {
|
||||||
|
ParsedCommand c = parser.parse("Gehe nach Norden");
|
||||||
|
|
||||||
|
assertThat(c.verb()).isEqualTo("gehe");
|
||||||
|
assertThat(c.args()).containsExactly("norden");
|
||||||
|
assertThat(Direction.fromString(c.args().getFirst())).isEqualTo(Direction.NORTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parse_nimmBrief_yieldsTakeWithItemArg() {
|
||||||
|
ParsedCommand c = parser.parse("Nimm Brief");
|
||||||
|
|
||||||
|
assertThat(c.verb()).isEqualTo("nimm");
|
||||||
|
assertThat(c.args()).containsExactly("brief");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fromString_withGermanDirectionNames_resolves() {
|
||||||
|
assertThat(Direction.fromString("norden")).isEqualTo(Direction.NORTH);
|
||||||
|
assertThat(Direction.fromString("sueden")).isEqualTo(Direction.SOUTH);
|
||||||
|
assertThat(Direction.fromString("süden")).isEqualTo(Direction.SOUTH);
|
||||||
|
assertThat(Direction.fromString("osten")).isEqualTo(Direction.EAST);
|
||||||
|
assertThat(Direction.fromString("westen")).isEqualTo(Direction.WEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fromString_withEnglishNames_stillResolves() {
|
||||||
|
assertThat(Direction.fromString("north")).isEqualTo(Direction.NORTH);
|
||||||
|
assertThat(Direction.fromString("n")).isEqualTo(Direction.NORTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user