Parser strategies for all entities

This commit is contained in:
Claudio Maggioni 2022-11-22 10:12:04 +01:00
parent 214bed9a13
commit 37f6bc2ff9
16 changed files with 219 additions and 160 deletions

View file

@ -17,11 +17,8 @@ import static com.github.dtschust.zork.Zork.Type.*;
/* And away we go*/
public class Zork {
public enum Type {ROOM, ITEM, CONTAINER, CREATURE}
ZorkGame game;
Scanner source = new Scanner(System.in);
public Zork(String filename) {
game = new ZorkReader(filename).build();
@ -50,7 +47,6 @@ public class Zork {
System.exit(0);
}
/* I love how basic java main functions are sometimes.*/
public static void main(String[] args) {
if (args.length != 1) {
@ -129,7 +125,6 @@ public class Zork {
return doZorkTriggers(game.getCurrentRoom(), input);
}
private boolean doZorkTriggers(ZorkObject zorkObject, String input) {
boolean skip = false;
Iterator<ZorkTrigger> iterator = zorkObject.trigger.iterator();
@ -152,4 +147,7 @@ public class Zork {
return skip;
}
public enum Type {ROOM, ITEM, CONTAINER, CREATURE}
}

View file

@ -3,7 +3,8 @@ package com.github.dtschust.zork;
import com.github.dtschust.zork.types.ZorkContainer;
import com.github.dtschust.zork.types.ZorkRoom;
import static com.github.dtschust.zork.Zork.Type.*;
import static com.github.dtschust.zork.Zork.Type.CONTAINER;
import static com.github.dtschust.zork.Zork.Type.ROOM;
/* Has conditions*/

View file

@ -4,16 +4,12 @@ import java.util.List;
/*Trigger*/
public class ZorkTrigger {
private final List<ZorkCondition> conditions;
private final List<ZorkCommand> commands;
public final List<String> print;
public final List<String> action;
/* By default, "single" */
public final String type;
public boolean hasCommand() {
return !this.commands.isEmpty();
}
private final List<ZorkCondition> conditions;
private final List<ZorkCommand> commands;
public ZorkTrigger(final String type,
final List<ZorkCondition> conditions,
@ -27,6 +23,10 @@ public class ZorkTrigger {
this.type = type;
}
public boolean hasCommand() {
return !this.commands.isEmpty();
}
public boolean evaluate(ZorkGame game, String input) {
if (!commands.stream().allMatch(c -> c.matchesInput(input))) {
return false;

View file

@ -5,7 +5,9 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.*;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;
public final class DOMUtils {
private DOMUtils() {

View file

@ -1,25 +1,16 @@
package com.github.dtschust.zork.parser;
import com.github.dtschust.zork.ZorkCondition;
import com.github.dtschust.zork.ZorkGame;
import com.github.dtschust.zork.ZorkTrigger;
import com.github.dtschust.zork.parser.builders.Parsers;
import com.github.dtschust.zork.parser.dom.Elements;
import com.github.dtschust.zork.types.ZorkContainer;
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 org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.nio.channels.NonReadableChannelException;
import static com.github.dtschust.zork.Zork.Type.*;
@ -33,49 +24,9 @@ public class ZorkReader {
}
private static void addCreature(ZorkGame data, Element element) {
final Set<String> vulnerabilities = new HashSet<>();
final List<ZorkCondition> conditions = new ArrayList<>();
final List<String> prints = new ArrayList<>();
final List<String> actions = new ArrayList<>();
final ZorkCreature tempCreature = Parsers.creature.parse(element);
NodeList attacks = element.getElementsByTagName("attack");
for (Element attack : DOMUtils.iterator(attacks)) {
final List<ZorkCondition> conditionsList = Elements.byTagName(attack, "condition").stream()
.map(Parsers.condition::parse)
.collect(Collectors.toList());
conditions.addAll(conditionsList);
for (Element print : DOMUtils.iterator(attack.getElementsByTagName("print"))) {
prints.add(DOMUtils.getInnerText(print));
}
for (Element action : DOMUtils.iterator(attack.getElementsByTagName("action"))) {
actions.add(DOMUtils.getInnerText(action));
}
}
for (Element vuln : DOMUtils.iterator(element.getElementsByTagName("vulnerability"))) {
vulnerabilities.add(DOMUtils.getInnerText(vuln));
}
/* Get all possible creature attributes */
ZorkCreature tempCreature = new ZorkCreature(
DOMUtils.getInnerTextByTagName(element, "name", ""),
DOMUtils.getInnerTextByTagName(element, "description", ""),
vulnerabilities,
conditions,
prints,
actions
);
final List<ZorkTrigger> triggers = Elements.byTagName(element, "trigger").stream()
.map(Parsers.trigger::parse)
.collect(Collectors.toList());
tempCreature.trigger.addAll(triggers);
tempCreature.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", ""));
/* Put each creature in the creatures hashmap, the generic object hashmap, and the objectlookup hashmap*/
/* Put each creature in the creatures hashmap, the generic object hashmap, and the object lookup hashmap*/
data.addObjectThroughLookup(CREATURE, tempCreature);
}
@ -87,74 +38,15 @@ public class ZorkReader {
}
private static void addItem(ZorkGame data, Element element) {
final List<String> prints = new ArrayList<>();
final List<String> actions = new ArrayList<>();
NodeList turnOn = element.getElementsByTagName("turnon");
if (turnOn.getLength() > 0) {
for (Element print : DOMUtils.iterator(element.getElementsByTagName("print"))) {
prints.add(DOMUtils.getInnerText(print));
}
for (Element action : DOMUtils.iterator(element.getElementsByTagName("action"))) {
actions.add(DOMUtils.getInnerText(action));
}
}
/* Get all possible item attributes*/
ZorkItem tempItem = new ZorkItem(
DOMUtils.getInnerTextByTagName(element, "name", ""),
DOMUtils.getInnerTextByTagName(element, "description", ""),
DOMUtils.getInnerTextByTagName(element, "writing", ""),
prints,
actions
);
final List<ZorkTrigger> triggers = Elements.byTagName(element, "trigger").stream()
.map(Parsers.trigger::parse)
.collect(Collectors.toList());
tempItem.trigger.addAll(triggers);
final ZorkItem tempItem = Parsers.item.parse(element);
/* Put each item in the items hashmap, the generic objects hashmap, and store its type in object lookup */
data.addObjectThroughLookup(ITEM, tempItem);
tempItem.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", ""));
}
private static void addRoom(ZorkGame data, Element element) {
final ZorkRoom tempRoom = Parsers.room.parse(element);
/*Get all possible Room attributes*/
ZorkRoom tempRoom = new ZorkRoom(
DOMUtils.getInnerTextByTagName(element, "name", ""),
DOMUtils.getInnerTextByTagName(element, "description", ""),
DOMUtils.getInnerTextByTagName(element, "type", "regular")
);
final List<ZorkTrigger> triggers = Elements.byTagName(element, "trigger").stream()
.map(Parsers.trigger::parse)
.collect(Collectors.toList());
tempRoom.trigger.addAll(triggers);
tempRoom.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", ""));
for (Element item : DOMUtils.iterator(element.getElementsByTagName("item"))) {
String itemName = DOMUtils.getInnerText(item);
tempRoom.item.add(itemName);
}
for (Element creature : DOMUtils.iterator(element.getElementsByTagName("creature"))) {
String creatureName = DOMUtils.getInnerText(creature);
tempRoom.creature.add(creatureName);
}
for (Element container : DOMUtils.iterator(element.getElementsByTagName("container"))) {
String containerName = DOMUtils.getInnerText(container);
tempRoom.container.add(containerName);
}
for (Element border : DOMUtils.iterator(element.getElementsByTagName("border"))) {
String borderDirection = DOMUtils.getInnerText((Element) border.getElementsByTagName("direction").item(0));
String borderName = DOMUtils.getInnerText((Element) border.getElementsByTagName("name").item(0));
tempRoom.border.put(borderDirection, borderName);
}
/*Add this room to the rooms hashmap, put it in the generic objects hashmap, and store it's type in the objectlookup hashmap*/
data.addObjectThroughLookup(ROOM, tempRoom);
}

View file

@ -3,16 +3,21 @@ package com.github.dtschust.zork.parser.builders;
import com.github.dtschust.zork.ZorkCondition;
import com.github.dtschust.zork.ZorkTrigger;
import com.github.dtschust.zork.types.ZorkContainer;
import com.github.dtschust.zork.types.ZorkCreature;
import com.github.dtschust.zork.types.ZorkItem;
import com.github.dtschust.zork.types.ZorkRoom;
/**
* Inversion of control for Zork parse strategies
*/
public final class Parsers {
public static final ZorkParseStrategy<ZorkCondition> condition = new ZorkConditionParseStrategy();
public static final ZorkParseStrategy<ZorkTrigger> trigger = new ZorkTriggerListParseStrategy(condition);
private static final ZorkParseStrategy<ZorkCondition> condition = new ZorkConditionParseStrategy();
private static final ZorkParseStrategy<ZorkTrigger> trigger = new ZorkTriggerListParseStrategy(condition);
public static final ZorkParseStrategy<ZorkContainer> container = new ZorkContainerParseStrategy(trigger);
public static final ZorkParseStrategy<ZorkItem> item = new ZorkItemParseStrategy(trigger);
public static final ZorkParseStrategy<ZorkRoom> room = new ZorkRoomParseStrategy(trigger);
public static final ZorkParseStrategy<ZorkCreature> creature = new ZorkCreatureParseStrategy(condition, trigger);
private Parsers() {
}
}

View file

@ -0,0 +1,52 @@
package com.github.dtschust.zork.parser.builders;
import com.github.dtschust.zork.ZorkCondition;
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.ZorkCreature;
import org.w3c.dom.Element;
import java.util.List;
import java.util.stream.Collectors;
public class ZorkCreatureParseStrategy implements ZorkParseStrategy<ZorkCreature> {
private final ZorkParseStrategy<ZorkCondition> conditionStrategy;
private final ZorkParseStrategy<ZorkTrigger> triggerStrategy;
public ZorkCreatureParseStrategy(final ZorkParseStrategy<ZorkCondition> conditionStrategy,
final ZorkParseStrategy<ZorkTrigger> triggerStrategy) {
this.conditionStrategy = conditionStrategy;
this.triggerStrategy = triggerStrategy;
}
@Override
public ZorkCreature parse(final Element source) {
// Get all possible creature attributes
final List<ZorkCondition> conditions = Elements.byTagName(source, "attack").stream()
.flatMap(e -> Elements.byTagName(e, "condition").stream())
.map(conditionStrategy::parse)
.collect(Collectors.toList());
final List<String> prints = Elements.byTagName(source, "attack").stream()
.flatMap(e -> Elements.innerTextByTagName(e, "print").stream())
.collect(Collectors.toList());
final List<String> actions = Elements.byTagName(source, "attack").stream()
.flatMap(e -> Elements.innerTextByTagName(e, "action").stream())
.collect(Collectors.toList());
final List<String> vulnerabilities = Elements.innerTextByTagName(source, "vulnerability");
final List<ZorkTrigger> triggers = Elements.byTagName(source, "trigger").stream()
.map(triggerStrategy::parse)
.collect(Collectors.toList());
final String name = DOMUtils.getInnerTextByTagName(source, "name", "");
final String description = DOMUtils.getInnerTextByTagName(source, "description", "");
final String status = DOMUtils.getInnerTextByTagName(source, "status", "");
return new ZorkCreature(name, description, status, triggers, vulnerabilities, conditions, prints, actions);
}
}

View file

@ -0,0 +1,45 @@
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.ZorkItem;
import org.w3c.dom.Element;
import java.util.List;
import java.util.stream.Collectors;
public class ZorkItemParseStrategy implements ZorkParseStrategy<ZorkItem> {
private final ZorkParseStrategy<ZorkTrigger> triggerStrategy;
public ZorkItemParseStrategy(final ZorkParseStrategy<ZorkTrigger> triggerStrategy) {
this.triggerStrategy = triggerStrategy;
}
@Override
public ZorkItem parse(final Element source) {
final List<String> prints = Elements.byTagName(source, "turnon").stream()
.flatMap(e -> Elements.innerTextByTagName(e, "print").stream())
.collect(Collectors.toList());
final List<String> actions = Elements.byTagName(source, "turnon").stream()
.flatMap(e -> Elements.innerTextByTagName(e, "action").stream())
.collect(Collectors.toList());
final List<ZorkTrigger> triggers = Elements.byTagName(source, "trigger").stream()
.map(triggerStrategy::parse)
.collect(Collectors.toList());
/* Get all possible item attributes*/
return new ZorkItem(
DOMUtils.getInnerTextByTagName(source, "name", ""),
DOMUtils.getInnerTextByTagName(source, "description", ""),
DOMUtils.getInnerTextByTagName(source, "status", ""),
DOMUtils.getInnerTextByTagName(source, "writing", ""),
triggers,
prints,
actions
);
}
}

View file

@ -0,0 +1,46 @@
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.ZorkRoom;
import org.w3c.dom.Element;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ZorkRoomParseStrategy implements ZorkParseStrategy<ZorkRoom> {
private final ZorkParseStrategy<ZorkTrigger> triggerStrategy;
public ZorkRoomParseStrategy(final ZorkParseStrategy<ZorkTrigger> triggerStrategy) {
this.triggerStrategy = triggerStrategy;
}
@Override
public ZorkRoom parse(final Element source) {
// Get all possible Room attributes
final String name = DOMUtils.getInnerTextByTagName(source, "name", "");
final String description = DOMUtils.getInnerTextByTagName(source, "description", "");
final String type = DOMUtils.getInnerTextByTagName(source, "type", "regular");
final List<ZorkTrigger> triggers = Elements.byTagName(source, "trigger").stream()
.map(triggerStrategy::parse)
.collect(Collectors.toList());
final String status = DOMUtils.getInnerTextByTagName(source, "status", "");
final List<String> items = Elements.innerTextByTagName(source, "item");
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()
.collect(Collectors.toMap(
e -> DOMUtils.getInnerTextByTagName(e, "direction"),
e -> DOMUtils.getInnerTextByTagName(e, "name")
));
return new ZorkRoom(name, description, type, status, triggers, borders, containers, items, creatures);
}
}

View file

@ -13,6 +13,10 @@ import java.util.stream.Collectors;
public class Elements extends AbstractList<Element> implements RandomAccess {
private final NodeList list;
private Elements(final NodeList l) {
list = l;
}
public static Elements byTagName(final Element parent, final String name) {
return new Elements(parent.getElementsByTagName(name));
}
@ -23,10 +27,6 @@ public class Elements extends AbstractList<Element> implements RandomAccess {
.collect(Collectors.toList());
}
private Elements(final NodeList l) {
list = l;
}
@Override
public Element get(int index) {
final Node e = list.item(index);

View file

@ -1,7 +1,7 @@
package com.github.dtschust.zork.repl.actions;
import com.github.dtschust.zork.ZorkGame;
import com.github.dtschust.zork.Zork.Type;
import com.github.dtschust.zork.ZorkGame;
import com.github.dtschust.zork.repl.Action;
import com.github.dtschust.zork.types.HasSetOfCollectable;
import com.github.dtschust.zork.types.ZorkObject;

View file

@ -16,6 +16,16 @@ import static com.github.dtschust.zork.Zork.Type.*;
* Delete: figure out what object it is and delete it accordingly. Rooms are especially tricky
*/
public class DeleteAction implements Action {
private static void deleteElementFromSpace(ZorkGame game, Type space, Type element, String object) {
for (ZorkObject tempObject : game.values(ZorkObject.class, space)) {
Set<String> set = ((HasSetOfCollectable) tempObject).getSetFromType(element);
if (set.contains(object)) {
set.remove(object);
game.put(space, tempObject);
}
}
}
@Override
public boolean matchesInput(List<String> arguments) {
return arguments.get(0).equals("Delete");
@ -32,7 +42,7 @@ public class DeleteAction implements Action {
switch (game.getTypeFromLookup(object)) {
case ROOM:
for (ZorkRoom tempRoom :game.values(ZorkRoom.class, 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);
@ -53,14 +63,4 @@ public class DeleteAction implements Action {
break;
}
}
private static void deleteElementFromSpace(ZorkGame game, Type space, Type element, String object) {
for (ZorkObject tempObject : game.values(ZorkObject.class, space)) {
Set<String> set = ((HasSetOfCollectable) tempObject).getSetFromType(element);
if (set.contains(object)) {
set.remove(object);
game.put(space, tempObject);
}
}
}
}

View file

@ -7,7 +7,8 @@ import com.github.dtschust.zork.types.ZorkRoom;
import java.util.List;
import static com.github.dtschust.zork.Zork.Type.*;
import static com.github.dtschust.zork.Zork.Type.CONTAINER;
import static com.github.dtschust.zork.Zork.Type.ROOM;
public class TakeAction implements Action {
@Override

View file

@ -2,6 +2,7 @@ package com.github.dtschust.zork.types;
import com.github.dtschust.zork.ZorkCondition;
import com.github.dtschust.zork.ZorkGame;
import com.github.dtschust.zork.ZorkTrigger;
import java.util.*;
@ -14,11 +15,13 @@ public class ZorkCreature extends ZorkObject implements HasPrintsAndActions {
public ZorkCreature(final String name,
final String description,
final String status,
final Collection<ZorkTrigger> triggers,
final Collection<String> vulnerabilities,
final Collection<ZorkCondition> conditions,
final Collection<String> prints,
final Collection<String> actions) {
super(name, description);
super(name, description, status, triggers);
this.vulnerabilities = new HashSet<>(vulnerabilities);
this.conditions = new ArrayList<>(conditions);
this.print = new ArrayList<>(prints);

View file

@ -1,6 +1,9 @@
package com.github.dtschust.zork.types;
import com.github.dtschust.zork.ZorkTrigger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -12,10 +15,12 @@ public class ZorkItem extends ZorkObject implements HasPrintsAndActions {
public ZorkItem(final String name,
final String description,
final String status,
final String writing,
final List<String> turnOnPrint,
final List<String> turnOnAction) {
super(name, description);
final Collection<ZorkTrigger> triggers,
final Collection<String> turnOnPrint,
final Collection<String> turnOnAction) {
super(name, description, status, triggers);
this.writing = writing;
this.turnOnPrint = new ArrayList<>(turnOnPrint);
this.turnOnAction = new ArrayList<>(turnOnAction);

View file

@ -1,25 +1,34 @@
package com.github.dtschust.zork.types;
import com.github.dtschust.zork.Zork;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.github.dtschust.zork.Zork;
import com.github.dtschust.zork.ZorkTrigger;
import java.util.*;
/* Room*/
public class ZorkRoom extends ZorkObject implements HasSetOfCollectable {
public final String type;
public final Map<String, String> border = new HashMap<>();
public final Set<String> container = new HashSet<>();
public final Set<String> item = new HashSet<>();
public final Set<String> creature = new HashSet<>();
public final Map<String, String> border;
public final Set<String> container;
public final Set<String> item;
public final Set<String> creature;
public ZorkRoom(final String name,
final String description,
final String type) {
super(name, description);
final String type,
final String status,
final Collection<ZorkTrigger> triggers,
final Map<String, String> borders,
final Collection<String> containers,
final Collection<String> items,
final Collection<String> creatures) {
super(name, description, status, triggers);
this.type = type;
this.border = new HashMap<>(borders);
this.container = new HashSet<>(containers);
this.item = new HashSet<>(items);
this.creature = new HashSet<>(creatures);
}
@Override