public class Constants {
	/**
	 * Set on STATUS if the player moved since the last time this was cleared.
	 */
	public static final int STATUS_MOVED = 0;

	/**
	 * Only show the short description for places if the player has not been
	 * here before.
	 */
	public static final int STATUS_BRIEF = 1;

	/**
	 * Never show the long descriptions for places unless LOOKING is set.
	 */
	public static final int STATUS_FAST = 2;

	/**
	 * Show the full description for a place.
	 */
	public static final int STATUS_LOOKING = 3;

	/**
	 * Specifies that the interpreter should attempt to combine the previous and
	 * next INPUT.
	 */
	public static final int STATUS_PLSCLRFY = 4;

	/**
	 * Set on STATUS if the player picked up or dropped anything since this bit
	 * was cleared.
	 */
	public static final int STATUS_JUGGLED = 5;

	/**
	 * Set on a PLACE if the player has been here before.
	 */
	public static final int PLACE_BEEN = 1;

	/**
	 * Set on an OBJECT if it appears in the location and the location with ID -
	 * 1.
	 */
	public static final int OBJECT_DUAL = 3;

	/**
	 * Set on ARG1 or ARG2 to indicate that it holds an object. Set on STATUS to
	 * indicate that the first word was an OBJECT. Set on all OBJECTs.
	 */
	public static final int XOBJECT = 15;
	
	/**
	 * Set on ARG1 or ARG2 to indicate that it holds an object. Set on STATUS to
	 * indicate that the first word was a VERB. Set on all VERBs.
	 */
	public static final int XVERB = 14;

	/**
	 * Set on ARG1 or ARG2 to indicate that it holds an object. Set on STATUS to
	 * indicate that the first word was an PLACE. Set on all PLACEes.
	 */
	public static final int XPLACE = 13;
	
	/**
	 * Flag/value set when input is considered an unknown word.
	 */
	public static final int ARG_BADWORD = 12;

	/**
	 * Special entity ID of the badword. 
	 */
	public static final int ARG_VALUE_BADWORD = 9999;

	/**
	 * The ID of an object's location if it's being held.
	 */
	public static final int OBJECT_LOCATION_INHAND = -1;
} public class IdResolver {
	private final Map<String, Integer> addresses;
	private final Map<String, Integer> constants;
	private final Map<String, String> synonyms;
	private final Map<String, Integer> stateCounts;

	public IdResolver() {
		this.addresses = Maps.newHashMap();
		this.constants = Maps.newHashMap();
		this.synonyms = Maps.newHashMap();
		this.stateCounts = Maps.newHashMap();
	}

	public void addConstant(String name, int value) {
		constants.put(name, value);
	}

	public void addSynonym(String synonymName, String word) {
		synonyms.put(synonymName, word);
	}

	public void addEntity(String name, int stateCount, int id) {
		addresses.put(name, id);
		stateCounts.put(name, stateCount);
	}

	public int getIntegerValue(String arg) throws ParseException {
		if (arg.matches("-?[0-9]+"))
			return Integer.parseInt(arg);

		if (arg.contains("-") || arg.contains("+")) {
			int value = 0;
			boolean negative = false;
			StringBuilder soFar = new StringBuilder();
			for (int i = 0; i < arg.length(); i++) {
				char c = arg.charAt(i);
				if (c == '-' || c == '+') {
					if (soFar.length() > 0) {
						int bit = getValue(soFar.toString());
						if (negative) {
							value -= bit;
						} else {
							value += bit;
						}
						soFar.setLength(0);
					}

					if (c == '-') {
						negative = true;
					} else
						negative = false;
				} else {
					soFar.append(c);
				}
			}

			int bit = getValue(soFar.toString());
			if (negative) {
				value -= bit;
			} else {
				value += bit;
			}

			return value;
		}

		arg = ACodeParser.trim(arg);

		return getValue(arg);
	}

	private int getValue(String s) throws ParseException {
		if (s.matches("[0-9]+"))
			return Integer.parseInt(s);

		if (s.startsWith("@"))
			return stateCounts.get(s.substring(1));

		s = ACodeParser.trim(s);

		while (synonyms.containsKey(s))
			s = synonyms.get(s);

		if (constants.containsKey(s))
			return constants.get(s);

		return getEntityIndex(s);
	}

	public int getEntityIndex(String name) throws ParseException {
		name = ACodeParser.trim(name);

		while (synonyms.containsKey(name))
			name = synonyms.get(name);

		Integer address = addresses.get(name);
		if (address == null)
			throw new ParseException("Unknown entity: " + name);

		return address;
	}
} public class Procedure {
	private final int start;
	private final int end;

	public Procedure(int start, int end) {
		this.start = start;
		this.end = end;
	}

	public int getStart() {
		return start;
	}

	public int getEnd() {
		return end;
	}

} public class Program {
	private List<CompiledLine> code = Lists.newArrayList();

	public Program() {
	}
	
	public List<CompiledLine> getCode() {
		return code;
	}
} public class VirtualMachine {
	private static final Splitter COMMA_SPLITTER = Splitter.on(',');

	private Stack<StackFrame> executionStack = new Stack<StackFrame>();
	private Stack<ActiveIterator> iteratorStack = new Stack<ActiveIterator>();
	private boolean lastWasConditional;
	private boolean conditionalSuccess;
	private InterpreterState interpreterState = InterpreterState.RUNNING;
	private Map<Integer, Integer> entityValues = Maps.newHashMap();
	private Map<Integer, Integer> entityFlags = Maps.newHashMap();
	private Map<Integer, Integer> entityLocations = Maps.newHashMap();

	private int executiveSavedValue;

	private List<Entity> input = Lists.newArrayList();
	private String arg1Token, arg2Token;

	public Stack<StackFrame> getExecutionStack() {
		return executionStack;
	}

	public Stack<ActiveIterator> getIteratorStack() {
		return iteratorStack;
	}

	public String getArg1Token() {
		return arg1Token;
	}

	public void setArg1Token(String arg1Token) {
		this.arg1Token = arg1Token;
	}

	public String getArg2Token() {
		return arg2Token;
	}

	public void setArg2Token(String arg2Token) {
		this.arg2Token = arg2Token;
	}

	public boolean isConditionalSuccess() {
		return conditionalSuccess;
	}

	public boolean isLastWasConditional() {
		return lastWasConditional;
	}

	public void setConditionalSuccess(boolean conditionalSuccess) {
		this.conditionalSuccess = conditionalSuccess;
	}

	public void setLastWasConditional(boolean lastWasConditional) {
		this.lastWasConditional = lastWasConditional;
	}

	public List<Entity> getInput() {
		return input;
	}

	public void setInterpreterState(InterpreterState interpreterState) {
		this.interpreterState = interpreterState;
	}

	public InterpreterState getInterpreterState() {
		return interpreterState;
	}

	public int getExecutiveSavedValue() {
		return executiveSavedValue;
	}

	public void setExecutiveSavedValue(int executiveSavedValue) {
		this.executiveSavedValue = executiveSavedValue;
	}

	public int getEntityValue(Entity e) {
		Integer value = entityValues.get(e.getId());
		return (value == null) ? 0 : value;
	}

	public void setEntityValue(Entity e, int value) {
		entityValues.put(e.getId(), value);
	}

	public int getEntityLocation(Entity e) {
		Integer value = entityLocations.get(e.getId());
		return (value == null) ? 0 : value;
	}

	public void setEntityLocation(Entity e, int value) {
		entityLocations.put(e.getId(), value);
	}

	public int getFlags(Entity e) {
		Integer flags = entityFlags.get(e.getId());
		return (flags == null) ? e.getDefaultFlags() : flags;
	}

	public void setFlags(Entity e, int flags) {
		entityFlags.put(e.getId(), flags);
	}

	public Map<String, String> saveInternal() {
		Map<String, String> internalState = Maps.newHashMap();
		internalState.put("0", saveStack(executionStack));
		internalState.put("1", saveStack(iteratorStack));
		internalState.put("2", "" + executiveSavedValue);
		internalState.put("3", "" + interpreterState.ordinal());
		internalState.put("4", conditionalSuccess ? "1" : "0");
		internalState.put("5", lastWasConditional ? "1" : "0");
		internalState.put("6", arg1Token);
		internalState.put("7", arg2Token);
		
		return internalState;
	}
	
	private String saveStack(Stack<? extends SaveAware> stack) {
		
		return null;
	}

	public Map<String, String> save() {
		Set<Integer> keys = Sets.newHashSet();
		keys.addAll(entityFlags.keySet());
		keys.addAll(entityValues.keySet());
		keys.addAll(entityLocations.keySet());
		// TODO: Sort?
//		List<Integer> sortedKeys = Lists.newArrayList(keys);
//		Collections.sort(sortedKeys);
		Map<String, String> save = Maps.newHashMap();
		for (int key : keys) {
			// Don't serialize this
			if (key == Constants.ARG_VALUE_BADWORD)
				continue;
			
			String value = saveInteger(entityFlags.get(key)) + "," + saveInteger(entityValues.get(key));
			if (entityLocations.containsKey(key))
				value += "," + saveInteger(entityLocations.get(key));
			// Hokey test for all zeros
			if (!value.equals("0,0") && !value.equals("0,0,0"))
				save.put("" + (key - Entity.ENTITY_ID_START), value);
		}
		return save;
	}

	private String saveInteger(Integer i) {
		if (i == null)
			return "0";

		// Try to save a shorter integer if it's negative (and would benefit
		// from a shorter string)
		if (i < 0 && i > -0x10000000)
			return "-" + Integer.toHexString(-i);

		return Integer.toHexString(i);
	}

	public void restore(Map<String, String> values) {
		entityFlags.clear();
		entityValues.clear();
		entityLocations.clear();
		
		for (String keyString : values.keySet()) {
			int key = Integer.parseInt(keyString) + Entity.ENTITY_ID_START;
			String vals = values.get(keyString);
			Iterator<String> it = (vals == null) ?  Collections.<String> emptyIterator() : COMMA_SPLITTER.split(vals).iterator();

			if (!it.hasNext())
				continue;
			entityFlags.put(key, (int) Long.parseLong(it.next(), 16));

			if (!it.hasNext())
				continue;
			entityValues.put(key, (int) Long.parseLong(it.next(), 16));

			if (!it.hasNext())
				continue;
			entityLocations.put(key, (int) Long.parseLong(it.next(), 16));
		}
	}

	public VirtualMachine clone() {
		VirtualMachine clone = new VirtualMachine();
		clone.arg1Token = arg1Token;
		clone.arg2Token = arg2Token;
		clone.conditionalSuccess = conditionalSuccess;
		clone.lastWasConditional = lastWasConditional;
		clone.entityFlags = Maps.newHashMap(entityFlags);
		clone.entityLocations = Maps.newHashMap(entityLocations);
		clone.entityValues = Maps.newHashMap(entityValues);
		clone.input = Lists.newArrayList(input);
		clone.interpreterState = interpreterState;
		clone.executiveSavedValue = executiveSavedValue;
		
		// TODO
		clone.executionStack = new Stack<StackFrame>();
		clone.iteratorStack = new Stack<ActiveIterator>();
		
		return clone;
	}

	public void clearExecutionStack() {
		lastWasConditional = conditionalSuccess = false;
		executiveSavedValue = 0;
		executionStack.clear();
		iteratorStack.clear();
		interpreterState = InterpreterState.RUNNING;
	}
} public abstract class Entity implements KernelValue, Cloneable {
	/**
	 * Where IDs for entities start. IDs grow upward. Note that it doesn't start
	 * against the floor of {@link Integer} to avoid underflow when performing
	 * entity ID math.
	 */
	public static final int ENTITY_ID_START = Integer.MIN_VALUE + 0x1000;

	/**
	 * End of entity ID range.
	 */
	public static final int ENTITY_ID_END = ENTITY_ID_START + 0x100000;

	public static boolean isEntityId(int id) {
		return id < ENTITY_ID_END;
	}

	/**
	 * All entities have an id (the index into the global entity list).
	 */
	protected int id;

	protected final String name;

	public Entity(String name) {
		this.name = name;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getId() {
		return id;
	}

	/**
	 * All entities have an integer value that can be set or retrieved.
	 */
	public int getIntValue(VirtualMachine vm) {
		return vm.getEntityValue(this);
	}

	public void setIntValue(VirtualMachine vm, int value) {
		vm.setEntityValue(this, value);
	}

	/**
	 * All entities have a set of bit flags.
	 */
	public void setFlag(VirtualMachine vm, int flag) {
		int flags = vm.getFlags(this);
		flags |= 1 << flag;
		vm.setFlags(this, flags);
	}

	public void clearFlag(VirtualMachine vm, int value) {
		int flags = vm.getFlags(this);
		flags &= ~(1 << value);
		vm.setFlags(this, flags);
	}

	public boolean hasFlag(VirtualMachine vm, int flag) {
		int flags = vm.getFlags(this);
		return (flags & (1 << flag)) != 0;
	}

	public int getFlags(VirtualMachine vm) {
		int flags = vm.getFlags(this);
		return flags;
	}

	public void setFlags(VirtualMachine vm, int flags) {
		vm.setFlags(this, flags);
	}

	public String getName() {
		return name;
	}

	public String getTextValue() {
		// Bad SAY
		return "";
	}

	public int getDefaultFlags() {
		return 0;
	}

	public int getStateCount() {
		return 0;
	}

	public <T extends Entity> T clone(T clone) {
		clone.id = id;
		return clone;
	}

	public abstract Entity clone();
} public class ObjectEntity extends Entity {
	private final String desc;
	private final List<String> states;

	public ObjectEntity(String name, String desc, List<String> states) {
		super(name);
		this.desc = desc;
		this.states = states;
	}
	
	@Override
	public int getDefaultFlags() {
		return 1 << Constants.XOBJECT;
	}
	
	public String getDesc() {
		return desc;
	}
	
	public List<String> getStates() {
		return states;
	}
	
	public int getLocation(VirtualMachine vm) {
		return vm.getEntityLocation(this);
	}
	
	public void setLocation(VirtualMachine vm, int location) {
		vm.setEntityLocation(this, location);
	}
	
	@Override
	public String getTextValue() {
		return desc;
	}
	
	@Override
	public int getStateCount() {
		return states.size();
	}

	@Override
	public String toString() {
		return "{OBJECT: " + name + "(" + desc + ")}";
	}
	
	@Override
	public Entity clone() {
		ObjectEntity clone = clone(new ObjectEntity(desc, desc, states));
		return clone;
	}
} public class PlaceEntity extends Entity {
	private final String shortDesc;
	private final List<String> states;

	public PlaceEntity(String name, String shortDesc, List<String> states) {
		super(name);
		this.shortDesc = shortDesc;
		this.states = states;
	}

	@Override
	public int getDefaultFlags() {
		return 1 << Constants.XPLACE;
	}

	public String getShortDesc() {
		return shortDesc;
	}

	public String getLongDesc() {
		return states.get(0);
	}

	@Override
	public int getStateCount() {
		return states.size();
	}

	@Override
	public String toString() {
		return "{PLACE " + name + "}";
	}
	
	@Override
	public PlaceEntity clone() {
		return clone(new PlaceEntity(shortDesc, shortDesc, states));
	}
} public class TextEntity extends Entity {
	private final String textValue;

	public TextEntity(String name, String textValue) {
		super(name);
		this.textValue = textValue;
	}

	public String getTextValue() {
		return textValue;
	}

	@Override
	public String toString() {
		return "{TEXT: " + name + " (" + trim(10) + ")}";
	}

	private String trim(int maxLength) {
		if (textValue.length() > maxLength)
			return textValue.replace("\n", "\\n").substring(0, 10) + "...";
		return textValue.replace("\n", "\\n");
	}
	
	@Override
	public TextEntity clone() {
		return clone(new TextEntity(name, textValue));
	}
} public class VariableEntity extends Entity {
	public VariableEntity(String name) {
		super(name);
	}

	public boolean isEntity(VirtualMachine vm) {
		return Entity.isEntityId(vm.getEntityValue(this));
	}

	public void setEntity(VirtualMachine vm, Entity entity) {
		setEntity(vm, entity.getId());
	}

	public void setEntity(VirtualMachine vm, int id) {
		assert Entity.isEntityId(id) || id == 9999 : "Not a valid entity ID: " + id;
		super.setIntValue(vm, id);
	}

	@Override
	public String toString() {
		return "{VARIABLE " + name + "}";
	}

	public void setFrom(VirtualMachine vm, VariableEntity other) {
		setIntValue(vm, vm.getEntityValue(other));
		setFlags(vm, vm.getFlags(other));
	}
	
	@Override
	public Entity clone() {
		return clone(new VariableEntity(name));
	}
} public class RestartableTypeFilteringListIterable<U, T extends U> implements Iterable<T> {
	private List<U> list;
	private Predicate<? super U> isOfT;

	public RestartableTypeFilteringListIterable(List<U> list, Predicate<? super U> isOfT) {
		this.list = list;
		this.isOfT = isOfT;
	}

	public RestartableTypeFilteringListIterator<U, T> iterator() {
		return new RestartableTypeFilteringListIterator<U, T>(list, isOfT);
	}
}
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.grack.adventure.kernel.Constants;
import com.grack.adventure.kernel.IdResolver;
import com.grack.adventure.kernel.Procedure;
import com.grack.adventure.kernel.Program;
import com.grack.adventure.kernel.VirtualMachine;
import com.grack.adventure.kernel.entity.Entity;
import com.grack.adventure.kernel.entity.ObjectEntity;
import com.grack.adventure.kernel.entity.PlaceEntity;
import com.grack.adventure.kernel.entity.TextEntity;
import com.grack.adventure.kernel.entity.VariableEntity;
import com.grack.adventure.util.RestartableTypeFilteringListIterable;
package com.grack.adventure.kernel.world;



/**
 * Manages the current states of the game by keeping track of the set of
 * {@link Entity} objects, {@link Procedure}s, and vocabulary.
 */
public class World {
	private Set<String> nullWords = Sets.newHashSet();

	private List<Entity> entities = Lists.newArrayList();

	private List<Procedure> initialProcedures = Lists.newArrayList();
	private List<Procedure> repeatProcedures = Lists.newArrayList();
	private Multimap<String, Procedure> labelProcedures = ArrayListMultimap.create();
	private Map<String, Entity> vocabulary = Maps.newHashMap();

	private final IdResolver resolver;