Deduplicated print-action logic
This commit is contained in:
parent
40675f5a39
commit
a782ec5ad7
13 changed files with 199 additions and 145 deletions
|
@ -1,7 +1,5 @@
|
|||
package com.github.dtschust.zork;
|
||||
|
||||
import com.github.dtschust.zork.parser.ZorkGame;
|
||||
|
||||
public interface ZorkEvaluatable {
|
||||
boolean evaluate(Zork zork);
|
||||
}
|
||||
|
|
|
@ -8,13 +8,13 @@ import java.util.Set;
|
|||
|
||||
public class ZorkGame {
|
||||
|
||||
public final Set<String> inventory = new HashSet<>();
|
||||
protected boolean running = false;
|
||||
protected String currentRoom;
|
||||
protected ZorkMap<ZorkRoom> rooms = new ZorkMap<>();
|
||||
protected ZorkMap<ZorkItem> items = new ZorkMap<>();
|
||||
protected ZorkMap<ZorkContainer> containers = new ZorkMap<>();
|
||||
protected ZorkMap<ZorkCreature> creatures = new ZorkMap<>();
|
||||
public final Set<String> inventory = new HashSet<>();
|
||||
protected HashMap<String, String> objectLookup = new HashMap<>();
|
||||
|
||||
public ZorkRoom getCurrentRoom() {
|
||||
|
|
|
@ -3,13 +3,13 @@ package com.github.dtschust.zork.parser;
|
|||
import com.github.dtschust.zork.*;
|
||||
import com.github.dtschust.zork.types.*;
|
||||
import org.w3c.dom.CharacterData;
|
||||
import org.w3c.dom.*;
|
||||
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.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.*;
|
||||
|
||||
public class ZorkReader {
|
||||
|
||||
|
@ -34,90 +34,43 @@ public class ZorkReader {
|
|||
return (field.getLength() > 0) ? getString((Element) field.item(0)) : base;
|
||||
}
|
||||
|
||||
public ZorkGame build() {
|
||||
|
||||
ZorkGame data = new ZorkGame();
|
||||
|
||||
File file = new File(filename);
|
||||
if (!file.canRead()) {
|
||||
System.out.println("Error opening file. Exiting...");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
try {
|
||||
/* Open the xml file*/
|
||||
DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();
|
||||
// Limit XML features to mitigate vulnerabilities
|
||||
builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
|
||||
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 : iterNodes(rootElement.getChildNodes())) {
|
||||
Element element;
|
||||
if (node instanceof Element) {
|
||||
element = (Element) node;
|
||||
String tagType = element.getTagName();
|
||||
|
||||
switch (tagType) {
|
||||
/* If it's a room ... */
|
||||
case "room":
|
||||
addRoom(data, element);
|
||||
break;
|
||||
/* If it's an item... */
|
||||
case "item":
|
||||
addItem(data, element);
|
||||
break;
|
||||
/* If it's a container... */
|
||||
case "container":
|
||||
addContainer(data, element);
|
||||
break;
|
||||
/* And finally, if it's a creature...*/
|
||||
case "creature":
|
||||
addCreature(data, element);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Invalid XML file, exiting");
|
||||
System.exit(-1);
|
||||
//e.printStackTrace();
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static void addCreature(ZorkGame data, Element element) {
|
||||
/* Get all possible creature attributes*/
|
||||
ZorkCreature tempCreature = new ZorkCreature(
|
||||
getItemOrDefault(element, "name", ""),
|
||||
getItemOrDefault(element, "description", "")
|
||||
);
|
||||
final Set<String> vulnerabilities = new HashSet<>();
|
||||
final List<ZorkCondition> conditions = new ArrayList<>();
|
||||
final List<String> prints = new ArrayList<>();
|
||||
final List<String> actions = new ArrayList<>();
|
||||
|
||||
readTriggersInObject(element, tempCreature);
|
||||
tempCreature.updateStatus(getItemOrDefault(element, "status", ""));
|
||||
|
||||
for (Element vuln : iterElements(element.getElementsByTagName("vulnerability"))) {
|
||||
tempCreature.vulnerability.add(getString(vuln));
|
||||
}
|
||||
|
||||
NodeList attacks = element.getElementsByTagName("attack");
|
||||
for (Element attack : iterElements(attacks)) {
|
||||
|
||||
readConditionsInTrigger(attack, tempCreature.conditions);
|
||||
readConditionsInTrigger(attack, conditions);
|
||||
|
||||
for (Element print : iterElements(attack.getElementsByTagName("print"))) {
|
||||
tempCreature.print.add(getString(print));
|
||||
prints.add(getString(print));
|
||||
}
|
||||
for (Element action : iterElements(attack.getElementsByTagName("action"))) {
|
||||
tempCreature.action.add(getString(action));
|
||||
actions.add(getString(action));
|
||||
}
|
||||
}
|
||||
|
||||
for (Element vuln : iterElements(element.getElementsByTagName("vulnerability"))) {
|
||||
vulnerabilities.add(getString(vuln));
|
||||
}
|
||||
|
||||
/* Get all possible creature attributes */
|
||||
ZorkCreature tempCreature = new ZorkCreature(
|
||||
getItemOrDefault(element, "name", ""),
|
||||
getItemOrDefault(element, "description", ""),
|
||||
vulnerabilities,
|
||||
conditions,
|
||||
prints,
|
||||
actions
|
||||
);
|
||||
|
||||
readTriggersInObject(element, tempCreature);
|
||||
tempCreature.updateStatus(getItemOrDefault(element, "status", ""));
|
||||
|
||||
/* Put each creature in the creatures hashmap, the generic object hashmap, and the objectlookup hashmap*/
|
||||
data.addObjectThroughLookup("creature", tempCreature);
|
||||
}
|
||||
|
@ -149,29 +102,33 @@ 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 : iterElements(element.getElementsByTagName("print"))) {
|
||||
prints.add(getString(print));
|
||||
}
|
||||
for (Element action : iterElements(element.getElementsByTagName("action"))) {
|
||||
actions.add(getString(action));
|
||||
}
|
||||
}
|
||||
|
||||
/* Get all possible item attributes*/
|
||||
ZorkItem tempItem = new ZorkItem(
|
||||
getItemOrDefault(element, "name", ""),
|
||||
getItemOrDefault(element, "description", ""),
|
||||
getItemOrDefault(element, "writing","")
|
||||
getItemOrDefault(element, "writing", ""),
|
||||
prints,
|
||||
actions
|
||||
);
|
||||
|
||||
readTriggersInObject(element, tempItem);
|
||||
tempItem.updateStatus(getItemOrDefault(element, "status", ""));
|
||||
|
||||
NodeList turnon = element.getElementsByTagName("turnon");
|
||||
if (turnon.getLength() > 0) {
|
||||
for (Element print : iterElements(element.getElementsByTagName("print"))) {
|
||||
tempItem.turnOnPrint.add(getString(print));
|
||||
}
|
||||
for (Element action : iterElements(element.getElementsByTagName("action"))) {
|
||||
tempItem.turnOnAction.add(getString(action));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Put each item in the items hashmap, the generic objects hashmap, and store its type in object lookup */
|
||||
data.addObjectThroughLookup("item", tempItem);
|
||||
tempItem.updateStatus(getItemOrDefault(element, "status", ""));
|
||||
}
|
||||
|
||||
private static void addRoom(ZorkGame data, Element element) {
|
||||
|
@ -258,10 +215,12 @@ public class ZorkReader {
|
|||
private static <T> Iterable<T> iterable(final NodeList nodeList) {
|
||||
return () -> new Iterator<>() {
|
||||
private int index = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return index < nodeList.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (!hasNext())
|
||||
|
@ -278,4 +237,61 @@ public class ZorkReader {
|
|||
private static Iterable<Element> iterElements(final NodeList nodeList) {
|
||||
return iterable(nodeList);
|
||||
}
|
||||
|
||||
public ZorkGame build() {
|
||||
|
||||
ZorkGame data = new ZorkGame();
|
||||
|
||||
File file = new File(filename);
|
||||
if (!file.canRead()) {
|
||||
System.out.println("Error opening file. Exiting...");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
try {
|
||||
/* Open the xml file*/
|
||||
DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();
|
||||
// Limit XML features to mitigate vulnerabilities
|
||||
builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
|
||||
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 : iterNodes(rootElement.getChildNodes())) {
|
||||
Element element;
|
||||
if (node instanceof Element) {
|
||||
element = (Element) node;
|
||||
String tagType = element.getTagName();
|
||||
|
||||
switch (tagType) {
|
||||
/* If it's a room ... */
|
||||
case "room":
|
||||
addRoom(data, element);
|
||||
break;
|
||||
/* If it's an item... */
|
||||
case "item":
|
||||
addItem(data, element);
|
||||
break;
|
||||
/* If it's a container... */
|
||||
case "container":
|
||||
addContainer(data, element);
|
||||
break;
|
||||
/* And finally, if it's a creature...*/
|
||||
case "creature":
|
||||
addCreature(data, element);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Invalid XML file, exiting");
|
||||
System.exit(-1);
|
||||
//e.printStackTrace();
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,14 @@ import java.util.List;
|
|||
|
||||
public abstract class Action {
|
||||
public abstract boolean matchesInput(final List<String> arguments);
|
||||
|
||||
public int getMinimumArgCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int getMaximumArgCount() { return Integer.MAX_VALUE; }
|
||||
public int getMaximumArgCount() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public abstract void run(final ZorkGame game, final List<String> arguments);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.github.dtschust.zork.repl.actions;
|
|||
|
||||
import com.github.dtschust.zork.parser.ZorkGame;
|
||||
import com.github.dtschust.zork.repl.Action;
|
||||
import com.github.dtschust.zork.repl.ActionDispatcher;
|
||||
import com.github.dtschust.zork.types.ZorkCreature;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -29,15 +28,9 @@ public class AttackAction extends Action {
|
|||
if (game.getCurrentRoom().creature.contains(tempString)) {
|
||||
ZorkCreature tempCreature = (ZorkCreature) game.get("creature", tempString);
|
||||
if (tempCreature != null && game.inventory.contains(weapon)) {
|
||||
if (tempCreature.attack(game, weapon)) {
|
||||
if (tempCreature.isAttackSuccessful(game, weapon)) {
|
||||
System.out.println("You assault the " + tempString + " with the " + weapon + ".");
|
||||
for (String print : tempCreature.print) {
|
||||
System.out.println(print);
|
||||
}
|
||||
final ActionDispatcher effectsDispatcher = new ActionDispatcher(game);
|
||||
for (final String action : tempCreature.action) {
|
||||
effectsDispatcher.dispatch(action);
|
||||
}
|
||||
tempCreature.printAndExecuteActions(game);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.github.dtschust.zork.repl.actions;
|
|||
|
||||
import com.github.dtschust.zork.parser.ZorkGame;
|
||||
import com.github.dtschust.zork.repl.Action;
|
||||
import com.github.dtschust.zork.repl.ActionDispatcher;
|
||||
import com.github.dtschust.zork.types.ZorkItem;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -29,13 +28,7 @@ public class TurnOnAction extends Action {
|
|||
ZorkItem tempItem = (ZorkItem) game.get("item", what);
|
||||
System.out.println("You activate the " + what + ".");
|
||||
if (tempItem != null) {
|
||||
for (String print : tempItem.turnOnPrint) {
|
||||
System.out.println(print);
|
||||
}
|
||||
final ActionDispatcher effectsDispatcher = new ActionDispatcher(game);
|
||||
for (final String action : tempItem.turnOnAction) {
|
||||
effectsDispatcher.dispatch(action);
|
||||
}
|
||||
tempItem.printAndExecuteActions(game);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package com.github.dtschust.zork.types;
|
||||
|
||||
import com.github.dtschust.zork.parser.ZorkGame;
|
||||
import com.github.dtschust.zork.repl.ActionDispatcher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface HasPrintsAndActions {
|
||||
List<String> getPrints();
|
||||
|
||||
List<String> getActions();
|
||||
|
||||
default void printAndExecuteActions(final ZorkGame game) {
|
||||
for (final String print : getPrints()) {
|
||||
System.out.println(print);
|
||||
}
|
||||
final ActionDispatcher effectsDispatcher = new ActionDispatcher(game);
|
||||
for (final String action : getActions()) {
|
||||
effectsDispatcher.dispatch(action);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ public class ZorkContainer extends ZorkObject {
|
|||
public boolean isOpen() {
|
||||
return open;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
open = true;
|
||||
}
|
||||
|
|
|
@ -1,36 +1,51 @@
|
|||
package com.github.dtschust.zork.types;
|
||||
|
||||
import com.github.dtschust.zork.Zork;
|
||||
import com.github.dtschust.zork.ZorkCondition;
|
||||
import com.github.dtschust.zork.parser.ZorkGame;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/* Creature*/
|
||||
public class ZorkCreature extends ZorkObject {
|
||||
public final Set<String> vulnerability = new HashSet<>();
|
||||
public final List<ZorkCondition> conditions = new ArrayList<>();
|
||||
public final List<String> print = new ArrayList<>();
|
||||
public final List<String> action = new ArrayList<>();
|
||||
public class ZorkCreature extends ZorkObject implements HasPrintsAndActions {
|
||||
private final Set<String> vulnerabilities;
|
||||
private final List<ZorkCondition> conditions;
|
||||
private final List<String> print;
|
||||
private final List<String> action;
|
||||
|
||||
public ZorkCreature(String name, String description) {
|
||||
public ZorkCreature(final String name,
|
||||
final String description,
|
||||
final Collection<String> vulnerabilities,
|
||||
final Collection<ZorkCondition> conditions,
|
||||
final Collection<String> prints,
|
||||
final Collection<String> actions) {
|
||||
super(name, description);
|
||||
this.vulnerabilities = new HashSet<>(vulnerabilities);
|
||||
this.conditions = new ArrayList<>(conditions);
|
||||
this.print = new ArrayList<>(prints);
|
||||
this.action = new ArrayList<>(actions);
|
||||
}
|
||||
|
||||
|
||||
/* Evaluate the success of an attack*/
|
||||
public boolean attack(ZorkGame game, String weapon) {
|
||||
if (!vulnerability.contains(weapon)) {
|
||||
return false;
|
||||
|
||||
/**
|
||||
* Given a game instance and a weapon, returns whether the attack is successful, i.e. if the creature is vulnerable
|
||||
* to the weapon and all conditions for a successful attack are satisfied
|
||||
*
|
||||
* @param game the game
|
||||
* @param weapon the weapon
|
||||
* @return true if the attack is successful
|
||||
*/
|
||||
public boolean isAttackSuccessful(final ZorkGame game, final String weapon) {
|
||||
return vulnerabilities.contains(weapon) && conditions.stream().allMatch(c -> c.evaluate(game));
|
||||
}
|
||||
for (ZorkCondition condition : conditions) {
|
||||
if (!condition.evaluate(game)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@Override
|
||||
public List<String> getPrints() {
|
||||
return Collections.unmodifiableList(print);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getActions() {
|
||||
return Collections.unmodifiableList(action);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,29 @@
|
|||
package com.github.dtschust.zork.types;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/* Item*/
|
||||
public class ZorkItem extends ZorkObject {
|
||||
public class ZorkItem extends ZorkObject implements HasPrintsAndActions {
|
||||
public final String writing;
|
||||
public final List<String> turnOnPrint = new ArrayList<>();
|
||||
public final List<String> turnOnAction = new ArrayList<>();
|
||||
private final List<String> turnOnPrint;
|
||||
private final List<String> turnOnAction;
|
||||
|
||||
public ZorkItem(String name, String description, String writing) {
|
||||
public ZorkItem(String name, String description, String writing, List<String> turnOnPrint, List<String> turnOnAction) {
|
||||
super(name, description);
|
||||
this.writing = writing;
|
||||
this.turnOnPrint = new ArrayList<>(turnOnPrint);
|
||||
this.turnOnAction = new ArrayList<>(turnOnAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPrints() {
|
||||
return Collections.unmodifiableList(turnOnPrint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getActions() {
|
||||
return Collections.unmodifiableList(turnOnAction);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,8 @@ import java.util.List;
|
|||
public abstract class ZorkObject {
|
||||
public final String name;
|
||||
public final String description;
|
||||
private String status;
|
||||
|
||||
public final List<ZorkTrigger> trigger = new ArrayList<>();
|
||||
private String status;
|
||||
|
||||
protected ZorkObject(String name, String description) {
|
||||
this.name = name;
|
||||
|
|
|
@ -18,7 +18,7 @@ class ZorkTest {
|
|||
String gameExecution = "RunThroughResults.txt";
|
||||
|
||||
CommandReader run = new CommandReader(gameExecution);
|
||||
IOWrapper io = new IOWrapper(false);
|
||||
IOWrapper io = new IOWrapper(true);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
catchSystemExit(() -> new Zork(gameConfig));
|
||||
|
|
Reference in a new issue