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:
@@ -0,0 +1,38 @@
|
||||
package thb.jeanluc.adventure.command;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class CommandParserTest {
|
||||
|
||||
private final CommandParser parser = new CommandParser();
|
||||
|
||||
@Test
|
||||
void parse_blankInput_yieldsEmptyVerb() {
|
||||
assertThat(parser.parse("").verb()).isEmpty();
|
||||
assertThat(parser.parse(" ").verb()).isEmpty();
|
||||
assertThat(parser.parse(null).verb()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_lowercases() {
|
||||
ParsedCommand p = parser.parse("Go NORTH");
|
||||
assertThat(p.verb()).isEqualTo("go");
|
||||
assertThat(p.args()).containsExactly("north");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_dropsFillers() {
|
||||
ParsedCommand p = parser.parse("go to the north");
|
||||
assertThat(p.verb()).isEqualTo("go");
|
||||
assertThat(p.args()).containsExactly("north");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_giveItemToNpc_keepsBoth() {
|
||||
ParsedCommand p = parser.parse("give lamp to old_man");
|
||||
assertThat(p.verb()).isEqualTo("give");
|
||||
assertThat(p.args()).containsExactly("lamp", "old_man");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package thb.jeanluc.adventure.command;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import thb.jeanluc.adventure.game.GameContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class CommandRegistryTest {
|
||||
|
||||
private static class StubCommand implements Command {
|
||||
@Override public void execute(GameContext ctx, List<String> args) {}
|
||||
@Override public String help() { return "stub"; }
|
||||
}
|
||||
|
||||
@Test
|
||||
void register_andFindByEachAlias() {
|
||||
CommandRegistry r = new CommandRegistry();
|
||||
StubCommand cmd = new StubCommand();
|
||||
r.register(cmd, "go", "move", "walk");
|
||||
|
||||
assertThat(r.find("go")).contains(cmd);
|
||||
assertThat(r.find("MOVE")).contains(cmd);
|
||||
assertThat(r.find("walk")).contains(cmd);
|
||||
}
|
||||
|
||||
@Test
|
||||
void find_unknownVerb_returnsEmpty() {
|
||||
assertThat(new CommandRegistry().find("nope")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void distinctCommands_deduplicatesAliases() {
|
||||
CommandRegistry r = new CommandRegistry();
|
||||
StubCommand a = new StubCommand();
|
||||
StubCommand b = new StubCommand();
|
||||
r.register(a, "x", "y");
|
||||
r.register(b, "z");
|
||||
|
||||
assertThat(r.distinctCommands()).containsExactlyInAnyOrder(a, b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package thb.jeanluc.adventure.command.impl;
|
||||
|
||||
import thb.jeanluc.adventure.game.GameContext;
|
||||
import thb.jeanluc.adventure.io.TestIO;
|
||||
import thb.jeanluc.adventure.model.Direction;
|
||||
import thb.jeanluc.adventure.model.Npc;
|
||||
import thb.jeanluc.adventure.model.NpcReaction;
|
||||
import thb.jeanluc.adventure.model.Player;
|
||||
import thb.jeanluc.adventure.model.Room;
|
||||
import thb.jeanluc.adventure.model.item.Item;
|
||||
import thb.jeanluc.adventure.model.item.PlainItem;
|
||||
import thb.jeanluc.adventure.model.item.ReadableItem;
|
||||
import thb.jeanluc.adventure.model.item.SwitchableItem;
|
||||
|
||||
/**
|
||||
* Tiny world used by the command tests. Two rooms (kitchen → hallway),
|
||||
* a few items, and one NPC with one reaction.
|
||||
*/
|
||||
public final class CommandTestSupport {
|
||||
|
||||
private CommandTestSupport() {}
|
||||
|
||||
public static class World {
|
||||
public final Room kitchen;
|
||||
public final Room hallway;
|
||||
public final Item lamp;
|
||||
public final Item letter;
|
||||
public final Item key;
|
||||
public final Item shovel;
|
||||
public final Npc oldMan;
|
||||
public final Player player;
|
||||
public final TestIO io = new TestIO();
|
||||
public final GameContext ctx;
|
||||
|
||||
public World() {
|
||||
kitchen = new Room("kitchen", "Kitchen", "kitchen desc");
|
||||
hallway = new Room("hallway", "Hallway", "hallway desc");
|
||||
kitchen.addExit(Direction.NORTH, hallway);
|
||||
hallway.addExit(Direction.SOUTH, kitchen);
|
||||
|
||||
lamp = SwitchableItem.builder().id("lamp").name("Lamp").description("a lamp")
|
||||
.state(false).onText("on").offText("off").build();
|
||||
letter = ReadableItem.builder().id("letter").name("Letter").description("a letter")
|
||||
.readText("dear reader").build();
|
||||
key = PlainItem.builder().id("key").name("Key").description("brass key").build();
|
||||
shovel = PlainItem.builder().id("shovel").name("Shovel").description("rusty").build();
|
||||
|
||||
kitchen.addItem(lamp);
|
||||
kitchen.addItem(letter);
|
||||
|
||||
oldMan = Npc.shell("old_man", "Old Man", "stooped", "greetings");
|
||||
oldMan.putReaction("lamp", NpcReaction.builder()
|
||||
.consumes(lamp).gives(key).response("Take the key.").build());
|
||||
kitchen.addNpc(oldMan);
|
||||
|
||||
player = new Player(kitchen, 0);
|
||||
ctx = new GameContext(null, player, io);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package thb.jeanluc.adventure.command.impl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class GoCommandTest {
|
||||
|
||||
@Test
|
||||
void execute_validDirection_movesPlayer() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
|
||||
new GoCommand().execute(w.ctx, List.of("north"));
|
||||
|
||||
assertThat(w.player.getCurrentRoom()).isEqualTo(w.hallway);
|
||||
}
|
||||
|
||||
@Test
|
||||
void execute_noExit_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
|
||||
new GoCommand().execute(w.ctx, List.of("south"));
|
||||
|
||||
assertThat(w.player.getCurrentRoom()).isEqualTo(w.kitchen);
|
||||
assertThat(w.io.allOutput()).contains("can't go south");
|
||||
}
|
||||
|
||||
@Test
|
||||
void execute_unknownDirection_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
|
||||
new GoCommand().execute(w.ctx, List.of("upwards"));
|
||||
|
||||
assertThat(w.player.getCurrentRoom()).isEqualTo(w.kitchen);
|
||||
assertThat(w.io.allOutput()).contains("upwards");
|
||||
}
|
||||
|
||||
@Test
|
||||
void execute_noArg_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
|
||||
new GoCommand().execute(w.ctx, List.of());
|
||||
|
||||
assertThat(w.io.allOutput()).contains("Go where");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package thb.jeanluc.adventure.command.impl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class InventoryCommandTest {
|
||||
|
||||
@Test
|
||||
void empty_writesEmptyMessage() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new InventoryCommand().execute(w.ctx, List.of());
|
||||
assertThat(w.io.lastOutput()).contains("not carrying");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withItems_listsByName() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new TakeCommand().execute(w.ctx, List.of("letter"));
|
||||
new TakeCommand().execute(w.ctx, List.of("lamp"));
|
||||
|
||||
new InventoryCommand().execute(w.ctx, List.of());
|
||||
|
||||
assertThat(w.io.lastOutput()).contains("Letter").contains("Lamp");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package thb.jeanluc.adventure.command.impl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class LookCommandTest {
|
||||
|
||||
@Test
|
||||
void look_writesNameDescriptionItemsNpcsAndExits() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
|
||||
new LookCommand().execute(w.ctx, List.of());
|
||||
|
||||
String text = w.io.lastOutput();
|
||||
assertThat(text).contains("Kitchen");
|
||||
assertThat(text).contains("kitchen desc");
|
||||
assertThat(text).contains("Lamp").contains("Letter");
|
||||
assertThat(text).contains("Old Man");
|
||||
assertThat(text).contains("north");
|
||||
}
|
||||
|
||||
@Test
|
||||
void look_noItemsNoNpcs_stillWorks() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
w.player.setCurrentRoom(w.hallway);
|
||||
|
||||
new LookCommand().execute(w.ctx, List.of());
|
||||
|
||||
String text = w.io.lastOutput();
|
||||
assertThat(text).contains("Hallway");
|
||||
assertThat(text).contains("south");
|
||||
assertThat(text).doesNotContain("You see");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package thb.jeanluc.adventure.command.impl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TakeDropCommandTest {
|
||||
|
||||
@Test
|
||||
void take_movesItemFromRoomToInventory() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
|
||||
new TakeCommand().execute(w.ctx, List.of("lamp"));
|
||||
|
||||
assertThat(w.player.hasItem("lamp")).isTrue();
|
||||
assertThat(w.kitchen.findItem("lamp")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void take_unknownItem_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new TakeCommand().execute(w.ctx, List.of("dragon"));
|
||||
assertThat(w.io.allOutput()).contains("no 'dragon'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void drop_movesItemFromInventoryToRoom() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new TakeCommand().execute(w.ctx, List.of("lamp"));
|
||||
|
||||
new DropCommand().execute(w.ctx, List.of("lamp"));
|
||||
|
||||
assertThat(w.player.hasItem("lamp")).isFalse();
|
||||
assertThat(w.kitchen.findItem("lamp")).isPresent();
|
||||
}
|
||||
|
||||
@Test
|
||||
void drop_notInInventory_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new DropCommand().execute(w.ctx, List.of("lamp"));
|
||||
assertThat(w.io.allOutput()).contains("not carrying");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package thb.jeanluc.adventure.command.impl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TalkGiveCommandTest {
|
||||
|
||||
@Test
|
||||
void talk_existingNpc_writesGreeting() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new TalkCommand().execute(w.ctx, List.of("old_man"));
|
||||
assertThat(w.io.lastOutput()).contains("Old Man").contains("greetings");
|
||||
}
|
||||
|
||||
@Test
|
||||
void talk_missingNpc_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new TalkCommand().execute(w.ctx, List.of("ghost"));
|
||||
assertThat(w.io.allOutput()).contains("no 'ghost'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void give_matchingReaction_swapsItems() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new TakeCommand().execute(w.ctx, List.of("lamp"));
|
||||
|
||||
new GiveCommand().execute(w.ctx, List.of("lamp", "old_man"));
|
||||
|
||||
assertThat(w.player.hasItem("lamp")).isFalse();
|
||||
assertThat(w.player.hasItem("key")).isTrue();
|
||||
assertThat(w.io.lastOutput()).contains("Take the key");
|
||||
}
|
||||
|
||||
@Test
|
||||
void give_noReaction_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new TakeCommand().execute(w.ctx, List.of("letter"));
|
||||
|
||||
new GiveCommand().execute(w.ctx, List.of("letter", "old_man"));
|
||||
|
||||
assertThat(w.player.hasItem("letter")).isTrue();
|
||||
assertThat(w.io.lastOutput()).contains("does not react");
|
||||
}
|
||||
|
||||
@Test
|
||||
void give_unknownNpc_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new TakeCommand().execute(w.ctx, List.of("lamp"));
|
||||
|
||||
new GiveCommand().execute(w.ctx, List.of("lamp", "ghost"));
|
||||
|
||||
assertThat(w.io.allOutput()).contains("no 'ghost'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void give_itemNotHeld_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new GiveCommand().execute(w.ctx, List.of("lamp", "old_man"));
|
||||
assertThat(w.io.allOutput()).contains("not carrying");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package thb.jeanluc.adventure.command.impl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class UseReadExamineCommandTest {
|
||||
|
||||
@Test
|
||||
void use_readableInInventory_writesText() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new TakeCommand().execute(w.ctx, List.of("letter"));
|
||||
|
||||
new UseCommand().execute(w.ctx, List.of("letter"));
|
||||
|
||||
assertThat(w.io.lastOutput()).isEqualTo("dear reader");
|
||||
}
|
||||
|
||||
@Test
|
||||
void use_unknownItem_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new UseCommand().execute(w.ctx, List.of("dragon"));
|
||||
assertThat(w.io.allOutput()).contains("no 'dragon'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void read_nonReadable_complains() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new TakeCommand().execute(w.ctx, List.of("lamp"));
|
||||
|
||||
new ReadCommand().execute(w.ctx, List.of("lamp"));
|
||||
|
||||
assertThat(w.io.lastOutput()).contains("nothing to read");
|
||||
}
|
||||
|
||||
@Test
|
||||
void examine_item_writesDescription() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new ExamineCommand().execute(w.ctx, List.of("letter"));
|
||||
assertThat(w.io.lastOutput()).isEqualTo("a letter");
|
||||
}
|
||||
|
||||
@Test
|
||||
void examine_npc_writesDescription() {
|
||||
CommandTestSupport.World w = new CommandTestSupport.World();
|
||||
new ExamineCommand().execute(w.ctx, List.of("old_man"));
|
||||
assertThat(w.io.lastOutput()).isEqualTo("stooped");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user