Encapsulation on zork types

This commit is contained in:
Claudio Maggioni 2022-11-22 11:27:56 +01:00
parent 37f6bc2ff9
commit 718e736032
21 changed files with 188 additions and 134 deletions

View file

@ -25,7 +25,7 @@ public class Zork {
game.changeRoom("Entrance");
/* Print out the first entrance description, starting the game!*/
System.out.println(game.getCurrentRoom().description);
System.out.println(game.getCurrentRoom().getDescription());
ActionDispatcher d = new ActionDispatcher(game);
@ -80,7 +80,7 @@ public class Zork {
private boolean doTriggersContainersInRoom(String input) {
boolean skip = false;
for (String key : game.getCurrentRoom().container) {
for (String key : game.getCurrentRoom().getContainer()) {
skip = skip || doZorkTriggers(game.get(CONTAINER, key), input);
}
return skip;
@ -88,9 +88,9 @@ public class Zork {
private boolean doTriggersItemsInContainersInRoom(String input) {
boolean skip = false;
for (String key : game.getCurrentRoom().container) {
for (String key : game.getCurrentRoom().getContainer()) {
ZorkContainer tempContainer = (ZorkContainer) game.get(CONTAINER, key);
for (String key2 : tempContainer.item) {
for (String key2 : tempContainer.items()) {
skip = skip || doZorkTriggers(game.get(ITEM, key2), input);
}
}
@ -99,7 +99,7 @@ public class Zork {
private boolean doTriggersItemsInRoom(String input) {
boolean skip = false;
for (String key : game.getCurrentRoom().item) {
for (String key : game.getCurrentRoom().getItem()) {
skip = skip || doZorkTriggers(game.get(ITEM, key), input);
}
return skip;
@ -115,7 +115,7 @@ public class Zork {
private boolean doTriggersCreaturesInRoom(String input) {
boolean skip = false;
for (String key : game.getCurrentRoom().creature) {
for (String key : game.getCurrentRoom().getCreature()) {
skip = skip || doZorkTriggers(game.get(CREATURE, key), input);
}
return skip;
@ -127,7 +127,7 @@ public class Zork {
private boolean doZorkTriggers(ZorkObject zorkObject, String input) {
boolean skip = false;
Iterator<ZorkTrigger> iterator = zorkObject.trigger.iterator();
Iterator<ZorkTrigger> iterator = zorkObject.getTrigger().iterator();
while (iterator.hasNext()) {
ZorkTrigger tempTrigger = iterator.next();
if (tempTrigger.evaluate(game, input)) {

View file

@ -27,7 +27,7 @@ public class ZorkConditionHas extends ZorkCondition {
/* is it a room?*/
ZorkRoom roomObject = (ZorkRoom) game.get(ROOM, owner);
if (roomObject != null) {
return evaluateCondition(roomObject.item.contains(object));
return evaluateCondition(roomObject.getItem().contains(object));
}
/* is it a container?*/
else {

View file

@ -45,7 +45,7 @@ public class ZorkGame {
public void addObjectThroughLookup(Type type, ZorkObject object) {
putInMapGivenType(type, object);
objectLookup.put(object.name, type);
objectLookup.put(object.getName(), type);
}
public <T extends ZorkObject> ZorkMap<T> getListThroughLookup(Class<T> cast, String name) {

View file

@ -1,13 +1,12 @@
package com.github.dtschust.zork.parser;
import com.github.dtschust.zork.parser.dom.Elements;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.*;
public final class DOMUtils {
private DOMUtils() {
@ -56,35 +55,15 @@ public final class DOMUtils {
return getInnerText((Element) first);
}
// the isInstance check assures the cast to T is safe, otherwise IllegalStateException is thrown
@SuppressWarnings("unchecked")
private static <T> Iterable<T> iterable(final NodeList nodeList, Class<T> type) {
return () -> new Iterator<>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < nodeList.getLength();
}
@Override
public T next() {
if (!hasNext())
throw new NoSuchElementException();
final Node next = nodeList.item(index++);
if (next == null || !type.isAssignableFrom(next.getClass())) {
throw new IllegalStateException("Element in list is not of type " + type.getCanonicalName());
}
return (T) next;
}
};
}
static Iterable<Node> iterNodes(final NodeList nodeList) {
return iterable(nodeList, Node.class);
}
static Iterable<Element> iterator(final NodeList nodeList) {
return iterable(nodeList, Element.class);
public static List<Element> childrenElements(final Element parent) {
final List<Element> elements = new ArrayList<>();
final NodeList children = parent.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
final Node item = children.item(i);
if (item instanceof Element) {
elements.add((Element) item);
}
}
return elements;
}
}

View file

@ -7,7 +7,6 @@ import com.github.dtschust.zork.types.ZorkCreature;
import com.github.dtschust.zork.types.ZorkItem;
import com.github.dtschust.zork.types.ZorkRoom;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
@ -71,13 +70,8 @@ public class ZorkReader {
Element rootElement = builder.newDocumentBuilder().parse(file).getDocumentElement();
/* Every single first generation child is a room, container, creature, or item. So load them in*/
for (Node node : DOMUtils.iterNodes(rootElement.getChildNodes())) {
Element element;
if (node instanceof Element) {
element = (Element) node;
String tagType = element.getTagName();
switch (tagType) {
for (Element element : DOMUtils.childrenElements(rootElement)) {
switch (element.getTagName()) {
/* If it's a room ... */
case "room":
addRoom(data, element);
@ -95,9 +89,7 @@ public class ZorkReader {
addCreature(data, element);
break;
default:
throw new IllegalStateException("Unexpected value: " + tagType);
}
throw new IllegalStateException("Unexpected value: " + element.getTagName());
}
}
} catch (Exception e) {

View file

@ -3,6 +3,7 @@ package com.github.dtschust.zork.parser.builders;
import com.github.dtschust.zork.ZorkTrigger;
import com.github.dtschust.zork.parser.DOMUtils;
import com.github.dtschust.zork.parser.dom.Elements;
import com.github.dtschust.zork.types.ZorkDirection;
import com.github.dtschust.zork.types.ZorkRoom;
import org.w3c.dom.Element;
@ -35,9 +36,9 @@ public class ZorkRoomParseStrategy implements ZorkParseStrategy<ZorkRoom> {
final List<String> creatures = Elements.innerTextByTagName(source, "creature");
final List<String> containers = Elements.innerTextByTagName(source, "container");
final Map<String, String> borders = Elements.byTagName(source, "border").stream()
final Map<ZorkDirection, String> borders = Elements.byTagName(source, "border").stream()
.collect(Collectors.toMap(
e -> DOMUtils.getInnerTextByTagName(e, "direction"),
e -> ZorkDirection.fromLong(DOMUtils.getInnerTextByTagName(e, "direction")),
e -> DOMUtils.getInnerTextByTagName(e, "name")
));

View file

@ -6,6 +6,7 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.RandomAccess;
import java.util.stream.Collectors;

View file

@ -27,7 +27,7 @@ public class AttackAction implements Action {
final String tempString = arguments.get(1);
final String weapon = arguments.get(3);
if (game.getCurrentRoom().creature.contains(tempString)) {
if (game.getCurrentRoom().getCreature().contains(tempString)) {
ZorkCreature tempCreature = (ZorkCreature) game.get(CREATURE, tempString);
if (tempCreature != null && game.inventory.contains(weapon) && tempCreature.isAttackSuccessful(game, weapon)) {
System.out.println("You assault the " + tempString + " with the " + weapon + ".");

View file

@ -42,12 +42,8 @@ public class DeleteAction implements Action {
switch (game.getTypeFromLookup(object)) {
case ROOM:
for (ZorkRoom tempRoom : game.values(ZorkRoom.class, ROOM)) {
for (String key : tempRoom.border.keySet()) {
if (tempRoom.border.get(key).equals(object)) {
tempRoom.border.remove(key);
}
}
for (final ZorkRoom tempRoom : game.values(ZorkRoom.class, ROOM)) {
tempRoom.removeBorderingRoom(object);
game.put(ROOM, tempRoom);
}
break;

View file

@ -25,7 +25,7 @@ public class DropItemAction implements Action {
if (game.inventory.contains(what)) {
ZorkRoom tempRoom = game.getCurrentRoom();
tempRoom.item.add(what);
tempRoom.getItem().add(what);
game.put(ROOM, tempRoom);
game.inventory.remove(what);
System.out.println(what + " dropped.");

View file

@ -2,11 +2,11 @@ package com.github.dtschust.zork.repl.actions;
import com.github.dtschust.zork.ZorkGame;
import com.github.dtschust.zork.repl.Action;
import com.github.dtschust.zork.types.ZorkDirection;
import com.github.dtschust.zork.types.ZorkRoom;
import java.util.List;
import java.util.Map;
import static java.util.Map.entry;
import java.util.Optional;
/**
* If it's not a "Special Action", just treat it normally
@ -14,16 +14,10 @@ import static java.util.Map.entry;
* Movement
*/
public class MoveAction implements Action {
private static final Map<String, String> fullDirections = Map.ofEntries(
entry("n", "north"),
entry("s", "south"),
entry("e", "east"),
entry("w", "west")
);
@Override
public boolean matchesInput(List<String> arguments) {
return fullDirections.containsKey(arguments.get(0));
return ZorkDirection.fromShort(arguments.get(0)).isPresent();
}
@Override
@ -33,9 +27,14 @@ public class MoveAction implements Action {
@Override
public void run(ZorkGame game, List<String> arguments) {
final String direction = arguments.get(0);
if (game.changeRoom(game.getCurrentRoom().border.get(fullDirections.get(direction)))) {
System.out.println(game.getCurrentRoom().description);
// we are guaranteed to have a valid short direction name by matchesInput
final ZorkDirection direction = ZorkDirection.fromShort(arguments.get(0)).orElseThrow(() ->
new IllegalStateException("unreachable"));
final Optional<String> roomName = game.getCurrentRoom().getBorderingRoom(direction);
if (roomName.isPresent() && game.changeRoom(roomName.get())) {
System.out.println(game.getCurrentRoom().getDescription());
} else {
System.out.println("Can't go that way.");
}

View file

@ -19,7 +19,7 @@ public class OpenAction implements Action {
final String what = arguments.get(1);
if (what.equals("exit")) {
if (game.getCurrentRoom().type.equals("exit")) {
if (game.getCurrentRoom().isExit()) {
System.out.println("Game Over");
game.setGameOver();
} else {
@ -27,15 +27,10 @@ public class OpenAction implements Action {
}
} else {
ZorkContainer tempContainer;
if (game.getCurrentRoom().container.contains(what)) {
if (game.getCurrentRoom().getContainer().contains(what)) {
tempContainer = (ZorkContainer) game.get(CONTAINER, what);
tempContainer.open();
if (tempContainer.item.isEmpty()) {
System.out.println(what + " is empty");
} else {
final String output = String.join(", ", tempContainer.item);
System.out.println(what + " contains " + output + ".");
}
System.out.println(tempContainer.getContents());
} else {
System.out.println("Error");
}

View file

@ -24,10 +24,10 @@ public class PutAction implements Action {
final String what = arguments.get(1);
final String destination = arguments.get(3);
if (game.getCurrentRoom().container.contains(destination)) {
if (game.getCurrentRoom().getContainer().contains(destination)) {
ZorkContainer tempContainer = (ZorkContainer) game.get(CONTAINER, destination);
if (tempContainer.isOpen() && game.inventory.contains(what)) {
tempContainer.item.add(what);
tempContainer.addItem(what);
game.inventory.remove(what);
System.out.println("Item " + what + " added to " + destination + ".");
return;

View file

@ -25,11 +25,7 @@ public class ReadAction implements Action {
if (game.inventory.contains(what)) {
ZorkItem tempItem = (ZorkItem) game.get(ITEM, what);
if (tempItem.writing != null && !tempItem.writing.isEmpty()) {
System.out.println(tempItem.writing);
} else {
System.out.println("Nothing written.");
}
System.out.println(tempItem.getWriting());
} else {
System.out.println("Error");
}

View file

@ -25,19 +25,19 @@ public class TakeAction implements Action {
public void run(ZorkGame game, List<String> arguments) {
final String tempString = arguments.get(1);
if ((game.getCurrentRoom()).item.contains(tempString)) {
if ((game.getCurrentRoom()).getItem().contains(tempString)) {
game.inventory.add(tempString);
ZorkRoom tempRoom = (game.getCurrentRoom());
tempRoom.item.remove(tempString);
tempRoom.getItem().remove(tempString);
game.put(ROOM, tempRoom);
System.out.println("Item " + tempString + " added to inventory.");
} else {
/* Search all containers in the current room for the item! */
for (String key : game.getCurrentRoom().container) {
for (String key : game.getCurrentRoom().getContainer()) {
ZorkContainer tempContainer = (ZorkContainer) game.get(CONTAINER, key);
if (tempContainer != null && tempContainer.isOpen() && tempContainer.item.contains(tempString)) {
if (tempContainer != null && tempContainer.isOpen() && tempContainer.containsItem(tempString)) {
game.inventory.add(tempString);
tempContainer.item.remove(tempString);
tempContainer.removeItem(tempString);
game.put(CONTAINER, tempContainer);
System.out.println("Item " + tempString + " added to inventory.");
return;

View file

@ -9,7 +9,7 @@ import static com.github.dtschust.zork.Zork.Type.ITEM;
/* Container*/
public class ZorkContainer extends ZorkObject implements HasSetOfCollectable {
public final Set<String> item;
private final Set<String> items;
private final List<String> accepts;
private boolean open;
@ -22,24 +22,32 @@ public class ZorkContainer extends ZorkObject implements HasSetOfCollectable {
super(name, description, status, triggers);
// If a container has an accepts attribute, then it is always open
this.open = !accepts.isEmpty();
this.item = new HashSet<>(items);
this.items = new HashSet<>(items);
this.accepts = new ArrayList<>(accepts);
}
public String getContents() {
if (this.items.isEmpty()) {
return getName() + " is empty";
} else {
return getName() + " contains " + String.join(", ", items) + ".";
}
}
public void addItem(final String item) {
this.item.add(item);
this.items.add(item);
}
public void removeItem(final String item) {
this.item.remove(item);
this.items.remove(item);
}
public boolean containsItem(final String item) {
return this.item.contains(item);
return this.items.contains(item);
}
public boolean isEmpty() {
return this.item.isEmpty();
public Iterable<String> items() {
return Collections.unmodifiableSet(items);
}
public boolean isOpen() {
@ -53,7 +61,11 @@ public class ZorkContainer extends ZorkObject implements HasSetOfCollectable {
@Override
public Set<String> getSetFromType(Zork.Type type) {
if (type.equals(ITEM))
return item;
return items;
throw new IllegalStateException("Unexpected value: " + type);
}
public List<String> getAccepts() {
return Collections.unmodifiableList(accepts);
}
}

View file

@ -0,0 +1,40 @@
package com.github.dtschust.zork.types;
import java.util.EnumSet;
import java.util.Optional;
public enum ZorkDirection {
NORTH("north", "n"),
EAST("east", "e"),
SOUTH("south", "s"),
WEST("west", "w");
public String getLongName() {
return longName;
}
public String getShortName() {
return shortName;
}
private final String longName;
private final String shortName;
ZorkDirection(final String longName, final String shortName) {
this.longName = longName;
this.shortName = shortName;
}
public static Optional<ZorkDirection> fromShort(final String shortName) {
return EnumSet.allOf(ZorkDirection.class).stream()
.filter(e -> e.shortName.equals(shortName))
.findFirst();
}
public static ZorkDirection fromLong(final String longName) {
return EnumSet.allOf(ZorkDirection.class).stream()
.filter(e -> e.longName.equals(longName))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(longName + " is not a valid direction long name"));
}
}

View file

@ -9,7 +9,7 @@ import java.util.List;
/* Item*/
public class ZorkItem extends ZorkObject implements HasPrintsAndActions {
public final String writing;
private final String writing;
private final List<String> turnOnPrint;
private final List<String> turnOnAction;
@ -26,6 +26,10 @@ public class ZorkItem extends ZorkObject implements HasPrintsAndActions {
this.turnOnAction = new ArrayList<>(turnOnAction);
}
public String getWriting() {
return writing != null && !writing.isEmpty() ? writing : "Nothing written.";
}
@Override
public List<String> getPrints() {
return Collections.unmodifiableList(turnOnPrint);

View file

@ -6,7 +6,7 @@ import java.util.Iterator;
public class ZorkMap<T extends ZorkObject> extends HashMap<String, T> implements Iterable<T> {
public T put(T object) {
return put(object.name, object);
return put(object.getName(), object);
}
@Override

View file

@ -9,9 +9,9 @@ import java.util.List;
/* Generic object, everything inherits from this*/
public abstract class ZorkObject {
public final String name;
public final String description;
public final List<ZorkTrigger> trigger;
private final String name;
private final String description;
private final List<ZorkTrigger> trigger;
private String status;
protected ZorkObject(String name, String description) {
@ -37,4 +37,15 @@ public abstract class ZorkObject {
return this.status.equals(status);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public List<ZorkTrigger> getTrigger() {
return trigger;
}
}

View file

@ -8,18 +8,18 @@ import java.util.*;
/* Room*/
public class ZorkRoom extends ZorkObject implements HasSetOfCollectable {
public final String type;
public final Map<String, String> border;
public final Set<String> container;
public final Set<String> item;
public final Set<String> creature;
private final String type;
private final Map<ZorkDirection, String> border;
private final Set<String> container;
private final Set<String> item;
private final Set<String> creature;
public ZorkRoom(final String name,
final String description,
final String type,
final String status,
final Collection<ZorkTrigger> triggers,
final Map<String, String> borders,
final Map<ZorkDirection, String> borders,
final Collection<String> containers,
final Collection<String> items,
final Collection<String> creatures) {
@ -35,13 +35,41 @@ public class ZorkRoom extends ZorkObject implements HasSetOfCollectable {
public Set<String> getSetFromType(Zork.Type type) {
switch (type) {
case CONTAINER:
return container;
return getContainer();
case CREATURE:
return creature;
return getCreature();
case ITEM:
return item;
return getItem();
default:
throw new IllegalStateException("Unexpected value: " + type);
}
}
public boolean isExit() {
return "exit".equals(type);
}
public void removeBorderingRoom(String roomName) {
for (final ZorkDirection d : this.border.keySet()) {
if (this.border.get(d).equals(roomName)) {
this.border.remove(d);
}
}
}
public Optional<String> getBorderingRoom(ZorkDirection border) {
return Optional.ofNullable(this.border.get(border));
}
public Set<String> getContainer() {
return container;
}
public Set<String> getItem() {
return item;
}
public Set<String> getCreature() {
return creature;
}
}