package com.iambookmaster.client.paragraph;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import com.iambookmaster.client.beans.AbstractParameter;
import com.iambookmaster.client.beans.Alchemy;
import com.iambookmaster.client.beans.Battle;
import com.iambookmaster.client.beans.Modificator;
import com.iambookmaster.client.beans.NPC;
import com.iambookmaster.client.beans.ObjectBean;
import com.iambookmaster.client.beans.Paragraph;
import com.iambookmaster.client.beans.ParagraphConnection;
import com.iambookmaster.client.beans.Parameter;
import com.iambookmaster.client.exceptions.TimeoutException;
import com.iambookmaster.client.model.Model;
import com.iambookmaster.client.model.Model.FullParagraphDescriptonBuilder;
import com.iambookmaster.client.model.ParagraphParsingHandler;
import com.iambookmaster.client.paragraph.PathFinder.WayFinder.ParagraphTransition;
public class PathFinder {
public static final int FIND_ALL_USED = 0;
public static final int FIND_ALL = 1;
public static final int FIND_ONE = 2;
public static final int FIND_MIN_MAX = 3;
private static final int STEP_INIT = 0;
private static final int STEP_PREPARE = 1;
private static final int STEP_CHECK_ALL_SUCCESS = 2;
private static final int STEP_CHECK_UNUSED_PARAGRAPHS = 3;
private static final int STEP_CHECK_UNUSED_CONNECTIONS = 4;
private Model model;
private PathFinderErrorListener errorListener = new PathFinderErrorListener() {
public void uselessObjectInFailOrSuccess(Paragraph paragraph) {
}
public void updateStatus(int paragraphs, int connections) {
}
public void unusedParameter(Parameter parameter) {
}
public void unusedParagraphConnection(ParagraphConnection bean) {
}
public void unusedModificator(Modificator modificator) {
}
public void unriachebleParagraph(Paragraph bean) {
}
public void twoOutputConnectionsWithTheSameObject(Paragraph paragraph, ObjectBean object) {
}
public void twoInputConnectionsWithTheSameObject(Paragraph paragraph, ObjectBean object) {
}
public void startLocationIsNotDefined() {
}
public void startHasIncomeConection(Paragraph paragraph) {
}
public void startFromFialOrSuccessParagraph(Paragraph paragraph) {
}
public void parametersInFromFialOrSuccessParagraph(Paragraph paragraph) {
}
public void parameterNotSetInConnection(ParagraphConnection connection) {
}
public void outwayFromFialOrSuccessParagraph(Paragraph paragraph) {
}
public void objectCannotBeUsed(ObjectBean bean) {
}
public void objectCannotBeFound(ObjectBean object) {
}
public void noWayToSuccess(Paragraph paragraph) {
}
public void noWayFromNormalParagraph(Paragraph paragraph) {
}
public void noSuccessParagraphs() {
}
public void mustGoAndNormaConnectionsInParagraph(Paragraph paragraph) {
}
public void modificatorsInFialOrSuccessParagraph(Paragraph paragraph) {
}
public void modificatorNotSetInConnection(ParagraphConnection connection) {
}
public void modificatorIsSetNowhere(Modificator modificator) {
}
public void gotAndLostObjectInTheSameParagraph(Paragraph paragraph, ObjectBean bean) {
}
public void duplicateConnectionBetweenParagraphs(ParagraphConnection connection) {
}
public void done() {
}
public void conditionalChain(ParagraphConnection connection) {
}
public boolean checkTimeout() {
return false;
}
public boolean canContinue() {
return true;
}
public boolean canBePassed(ParagraphConnection connection, Paragraph paragraph) {
return true;
}
public void bothDirConnectionHasObject(ParagraphConnection connection) {
}
public void battleIsUsedNowhere(Battle battle) {
}
public void alreadyHaveThatObject(Paragraph current, ObjectBean object) {
}
public void alchemyIsUsedNowhere(Alchemy alchemy) {
}
public void NPCIsUsedNowhere(NPC npc) {
}
};
public PathFinder(Model mod) {
this.model = mod;
}
public PathFinderErrorListener getErrorListener() {
return errorListener;
}
public void setErrorListener(PathFinderErrorListener errorListener) {
this.errorListener = errorListener;
}
private int step;
private Stepper stepper = new Stepper();
private WayFinder finder = new WayFinder();
private boolean checkSuccess;
private boolean showDiagInfo;
private HashMap<ObjectBean,HashSet<Paragraph>> alreadyHaveThatObjects;
public void setShowDiagInfo(boolean showDiagInfo) {
this.showDiagInfo = showDiagInfo;
}
public ArrayList<ArrayList<Paragraph>> findWays(Paragraph start, Paragraph end, GameState objects, HashSet<Paragraph> notUsed, HashSet<ParagraphConnection> unUsedConnections,int findMode) {
try {
return finder.findWays(start,end,null,objects,notUsed,unUsedConnections,findMode);
} catch (TimeoutException e) {
return null;
}
}
public void continueProgess() throws TimeoutException {
switch (step) {
case STEP_PREPARE:
stepper.prepare();
break;
case STEP_CHECK_ALL_SUCCESS:
stepper.checkSuccess();
break;
case STEP_CHECK_UNUSED_PARAGRAPHS:
stepper.checkUnusedParagraphs();
break;
case STEP_CHECK_UNUSED_CONNECTIONS:
stepper.checkUnusedConnections();
break;
}
}
public HashMap<Paragraph, ArrayList<ParagraphTransition>> getMap() {
return finder.getMap();
}
public void validate(PathFinderErrorListener listener) throws TimeoutException{
step=STEP_INIT;
errorListener = listener;
finder.init();
step=STEP_PREPARE;
if (listener.checkTimeout()) {
return;
}
stepper.prepare();
}
/**
* Top level class in checking
* @author ggadyatskiy
*/
class Stepper {
private ArrayList<Paragraph> list;
private ArrayList<Paragraph> success;
private HashSet<Paragraph> unUsed;
private HashSet<ParagraphConnection> unUsedConnections;
private Paragraph start;
private void prepare() throws TimeoutException {
//scan for dumb paragraph (not fail, but without output)
list = model.getParagraphs();
success = new ArrayList<Paragraph>();
HashSet<ObjectBean> unriachableObjects = new HashSet<ObjectBean>();
unriachableObjects.addAll(model.getObjects());
HashSet<Modificator> unriachableModificators = new HashSet<Modificator>();
HashSet<Parameter> unusedParameters = new HashSet<Parameter>();
HashSet<Battle> unusedBattles = new HashSet<Battle>();
HashSet<NPC> unusedNPC = new HashSet<NPC>();
HashSet<Alchemy> unusedAlchemy = new HashSet<Alchemy>();
ArrayList<AbstractParameter> params = model.getParameters();
for (AbstractParameter parameter : params) {
if (parameter instanceof Modificator) {
unriachableModificators.add((Modificator)parameter);
} else if (parameter instanceof Parameter) {
unusedParameters.add((Parameter)parameter);
} else if (parameter instanceof Battle) {
unusedBattles.add((Battle)parameter);
} else if (parameter instanceof Alchemy) {
Alchemy alchemy = (Alchemy)parameter;
if (alchemy.isOnDemand()) {
unusedAlchemy.add(alchemy);
}
} else if (parameter instanceof NPC) {
unusedNPC.add((NPC)parameter);
}
}
for (AbstractParameter parameter : params) {
for (Iterator<Parameter> iterator = unusedParameters.iterator(); iterator.hasNext();) {
Parameter param = iterator.next();
if (parameter != param && parameter.dependsOn(param)) {
iterator.remove();
}
}
}
//list of unused paragraphs
unUsed = new HashSet<Paragraph>(list.size());
unUsed.addAll(model.getParagraphs());
//list of unused connections
unUsedConnections = new HashSet<ParagraphConnection>(model.getParagraphConnections().size());
unUsedConnections.addAll(model.getParagraphConnections());
start = model.getStartParagraph();
boolean critError=false;
HashSet<Paragraph> inConnections = new HashSet<Paragraph>(model.getParagraphs().size());
HashMap<Paragraph,ArrayList<ObjectBean>> inConditionsConnections = new HashMap<Paragraph,ArrayList<ObjectBean>>();
ArrayList<ParagraphConnection> cons = model.getParagraphConnections();
for (ParagraphConnection connection : cons) {
if (inConnections.contains(connection.getTo())==false) {
inConnections.add(connection.getTo());
}
switch (connection.getType()) {
case ParagraphConnection.TYPE_MODIFICATOR:
case ParagraphConnection.TYPE_NO_MODIFICATOR:
if (connection.getModificator()==null) {
errorListener.modificatorNotSetInConnection(connection);
} else {
unriachableModificators.remove(connection.getModificator());
}
break;
case ParagraphConnection.TYPE_PARAMETER_LESS:
case ParagraphConnection.TYPE_PARAMETER_MORE:
if (connection.getParameter()==null) {
errorListener.parameterNotSetInConnection(connection);
} else {
unusedParameters.remove(connection.getParameter());
}
break;
default:
//normal - check Object
if (connection.getObject() != null) {
ArrayList<ObjectBean> objs = inConditionsConnections.get(connection.getTo());
if (objs==null) {
//first income conditional connection
objs = new ArrayList<ObjectBean>();
objs.add(connection.getObject());
inConditionsConnections.put(connection.getTo(),objs);
} else if (objs.contains(connection.getObject())){
if (model.getSettings().isHiddenUsingObjects()) {
//two income connections with the same object, impossible to have for secret keys
errorListener.twoInputConnectionsWithTheSameObject(connection.getTo(),connection.getObject());
}
} else {
objs.add(connection.getObject());
}
}
}
if (connection.isBothDirections()) {
if (inConnections.contains(connection.getFrom())==false) {
inConnections.add(connection.getFrom());
}
}
}
//check unused stuff
for (Modificator modificator : unriachableModificators) {
errorListener.unusedModificator(modificator);
}
for (Parameter parameter : unusedParameters) {
errorListener.unusedParameter(parameter);
}
unriachableModificators.clear();
for (AbstractParameter parameter : params) {
if (parameter instanceof Modificator) {
unriachableModificators.add((Modificator)parameter);
}
}
for (ParagraphConnection connection : cons) {
if (connection.getObject() != null && inConditionsConnections.containsKey(connection.getFrom())) {
// critError = true;
errorListener.conditionalChain(connection);
}
}
for (Paragraph paragraph:list) {
if (paragraph.getBattle() != null) {
unusedBattles.remove(paragraph.getBattle());
if (paragraph.getEnemies() != null && paragraph.getEnemies().size()>0) {
unusedNPC.removeAll(paragraph.getEnemies());
}
}
if (paragraph.getAlchemy() != null && paragraph.getAlchemy().size()>0) {
for (Alchemy alchemy : paragraph.getAlchemy().keySet()) {
boolean value = paragraph.getAlchemy().get(alchemy);
if (value) {
unusedAlchemy.remove(alchemy);
}
}
}
ArrayList<WayFinder.ParagraphTransition> out = finder.connections.get(paragraph);
if (out != null && model.getSettings().isSkipMustGoParagraphs()) {
int must=0;
int norm=0;
for (ParagraphTransition transition : out) {
if (transition.connection.isBothDirections()==false) {
if (transition.connection.getStrictness()==ParagraphConnection.STRICTNESS_MUST) {
must++;
} else {
norm++;
}
}
}
if (must>0 && norm>0) {
errorListener.mustGoAndNormaConnectionsInParagraph(paragraph);
}
}
if (paragraph.isSuccess()) {
success.add(paragraph);
}
if (paragraph.getGotObjects().size()>0 || paragraph.getLostObjects().size()>0) {
if (paragraph.isFail() || paragraph.isSuccess()) {
errorListener.uselessObjectInFailOrSuccess(paragraph);
}
unriachableObjects.removeAll(paragraph.getGotObjects());
for (ObjectBean bean : paragraph.getLostObjects()) {
if (paragraph.getGotObjects().contains(bean)) {
errorListener.gotAndLostObjectInTheSameParagraph(paragraph,bean);
}
}
}
if (paragraph.getChangeModificators() != null) {
for (Modificator modificator : paragraph.getChangeModificators().keySet()) {
boolean value = paragraph.getChangeModificators().get(modificator);
if (value) {
unriachableModificators.remove(modificator);
}
}
}
if (paragraph.isFail() || paragraph.isSuccess()) {
if (paragraph.getChangeModificators() != null && paragraph.getChangeModificators().size()>0) {
errorListener.modificatorsInFialOrSuccessParagraph(paragraph);
}
if (paragraph.getChangeParameters() != null && paragraph.getChangeParameters().size()>0) {
errorListener.parametersInFromFialOrSuccessParagraph(paragraph);
}
if (out != null) {
critError = true;
errorListener.outwayFromFialOrSuccessParagraph(paragraph);
}
if (paragraph==start) {
critError = true;
errorListener.startFromFialOrSuccessParagraph(paragraph);
}
} else if (out == null) {
critError = true;
errorListener.noWayFromNormalParagraph(paragraph);
} else {
//check for two connection with the same object (impossible for secret keys)
HashSet<ObjectBean> objs = null;
for (WayFinder.ParagraphTransition transition:out) {
if (transition.connection.getObject() != null) {
if (objs == null) {
objs = new HashSet<ObjectBean>();
} else if (objs.contains(transition.connection.getObject())){
if (model.getSettings().isHiddenUsingObjects()) {
//two connections with the same object, impossible for secret keys
errorListener.twoOutputConnectionsWithTheSameObject(transition.connection.getFrom(),transition.connection.getObject());
}
continue;
}
objs.add(transition.connection.getObject());
}
}
}
if (paragraph==start) {
if (inConnections.contains(paragraph)) {
critError = true;
errorListener.startHasIncomeConection(paragraph);
}
} else if (paragraph.getType()==Paragraph.TYPE_NORMAL || paragraph.isFail() || paragraph.isSuccess()) {
if (inConnections.contains(paragraph)==false) {
critError = true;
errorListener.unriachebleParagraph(paragraph);
}
}
}
//check for objects, which cannot be found
for (Iterator<ObjectBean> iter = unriachableObjects.iterator(); iter.hasNext();) {
ObjectBean bean = iter.next();
errorListener.objectCannotBeFound(bean);
}
if (unriachableObjects.isEmpty()) {
unriachableObjects = null;
}
//check for modificators, which cannot be set
for (Modificator modificator : unriachableModificators) {
errorListener.modificatorIsSetNowhere(modificator);
}
if (unriachableModificators.isEmpty()) {
unriachableModificators = null;
}
//unused Alchemy
for (Alchemy alchemy : unusedAlchemy) {
errorListener.alchemyIsUsedNowhere(alchemy);
}
unusedAlchemy = null;
for (Battle battle : unusedBattles) {
errorListener.battleIsUsedNowhere(battle);
}
unusedBattles = null;
for (NPC npc : unusedNPC) {
errorListener.NPCIsUsedNowhere(npc);
}
unusedNPC = null;
if (start == null) {
errorListener.startLocationIsNotDefined();
} else if (critError){
//critical error found, stop
} else if (unriachableModificators != null || unriachableObjects != null){
//critical error
return;
} else {
//check that each success can be reached
step=STEP_CHECK_ALL_SUCCESS;
if (errorListener.checkTimeout()) {
throw new TimeoutException();
}
checkSuccessCounter = 0;
checkSuccess();
}
}
private int checkSuccessCounter=0;
private void checkSuccess() throws TimeoutException{
if (success.isEmpty()) {
errorListener.noSuccessParagraphs();
return;
}
boolean passed=false;
for (;checkSuccessCounter < success.size(); checkSuccessCounter++) {
GameState objects = new GameState();
Paragraph sc = success.get(checkSuccessCounter);
ArrayList<ArrayList<Paragraph>> result = finder.findWays(start,sc,null,objects,unUsed,unUsedConnections,FIND_ONE);
if (result==null || result.size()==0) {
errorListener.noWayToSuccess(sc);
} else {
passed = true;
unUsed.remove(sc);
}
}
step=STEP_CHECK_UNUSED_PARAGRAPHS;
if (errorListener.checkTimeout()) {
throw new TimeoutException();
}
if (passed) {
if (checkSuccess==false) {
if (unUsed.size()>0) {
checkUnusedParagraphs();
} else if (unUsedConnections.size()>0) {
checkUnusedConnections();
}
}
} else {
printUnused();
}
}
private void printUnused() {
for (Paragraph paragraph : unUsed) {
errorListener.unriachebleParagraph(paragraph);
}
for (ParagraphConnection connection : unUsedConnections) {
errorListener.unusedParagraphConnection(connection);
}
}
private void checkUnusedParagraphs() throws TimeoutException{
//check that all unused in success paragraphs can be reached
GameState objects = new GameState();
ArrayList<ArrayList<Paragraph>> result = findWays(start,null,objects,unUsed,unUsedConnections,FIND_ALL_USED);
if (result==null) {
printUnused();
} else {
objects.clear();
if (errorListener.canContinue()) {
if (unUsedConnections.size()>0) {
step=STEP_CHECK_UNUSED_CONNECTIONS;
if (errorListener.checkTimeout()) {
throw new TimeoutException();
}
checkUnusedConnections();
}
} else {
printUnused();
}
}
}
private void checkUnusedConnections() throws TimeoutException{
//check that all unused connections can be used
GameState objects = new GameState();
while (true) {
Iterator<ParagraphConnection> iterator = unUsedConnections.iterator();
if (iterator.hasNext()==false) {
break;
}
ParagraphConnection connection = iterator.next();
objects.clear();
ArrayList<ArrayList<Paragraph>> result = finder.findWays(start,connection,objects,unUsed,unUsedConnections,FIND_ALL);
if (result==null) {
break;
} else if (result.size()==0) {
errorListener.unusedParagraphConnection(connection);
unUsedConnections.remove(connection);
}
}
printUnused();
}
}
/*-------------------------------------------------------------------*/
public class WayFinder {
private ArrayList<ArrayList<Paragraph>> findWays(Paragraph start, ParagraphConnection connection, GameState objects, HashSet<Paragraph> notUsed, HashSet<ParagraphConnection> unUsedConnections, int findMode) throws TimeoutException{
return findWays(start,null,connection,objects,notUsed,unUsedConnections,findMode);
}
private HashMap<Paragraph, ArrayList<ParagraphTransition>> connections;
private void init() {
if (connections != null) {
return;
}
connections = new HashMap<Paragraph, ArrayList<ParagraphTransition>>(model.getParagraphs().size());
LinkedHashSet<ObjectBean> unusedObjects = new LinkedHashSet<ObjectBean>();
unusedObjects.addAll(model.getObjects());
ArrayList<ParagraphConnection> list = model.getParagraphConnections();
for (int i = 0; i < list.size(); i++) {
ParagraphConnection connection = list.get(i);
if (connection.getObject() != null) {
unusedObjects.remove(connection.getObject());
}
ArrayList<ParagraphTransition> out = connections.get(connection.getFrom());
if (out==null) {
out = new ArrayList<ParagraphTransition>();
connections.put(connection.getFrom(), out);
}
if (out.contains(connection.getTo())) {
//duplicate
if (errorListener != null) {
errorListener.duplicateConnectionBetweenParagraphs(connection);
}
} else {
out.add(new ParagraphTransition(connection.getTo(),connection));
}
if (connection.isBothDirections()) {
if (connection.getObject() != null && errorListener != null) {
errorListener.bothDirConnectionHasObject(connection);
}
out = connections.get(connection.getTo());
if (out==null) {
out = new ArrayList<ParagraphTransition>();
connections.put(connection.getTo(), out);
}
if (out.contains(connection.getFrom())) {
//duplicate
if (errorListener != null) {
errorListener.duplicateConnectionBetweenParagraphs(connection);
}
} else {
out.add(new ParagraphTransition(connection.getFrom(),connection));
}
}
}
FullParagraphDescriptonBuilder builder = model.getFullParagraphDescriptonBuilder();
builder.setHiddenUsingObjects(false);
builder.setCheckSecretKeys(false);
builder.setPlayerMode(true);
FindParsingHandler handler = new FindParsingHandler();
builder.setParagraphParsingHandler(handler);
for (Paragraph paragraph : connections.keySet()) {
ArrayList<ParagraphTransition> mess = connections.get(paragraph);
if (mess.size()==1) {
continue;
}
boolean found=false;
for (ParagraphTransition transition : mess) {
if (isMustFollow(transition.connection)) {
//these connections has to be ordered by the proper way
found = true;
break;
}
}
if (found) {
ArrayList<ParagraphConnection> conns = new ArrayList<ParagraphConnection>(mess.size());
for (ParagraphTransition transition : mess) {
conns.add(transition.connection);
}
handler.reset(mess);
builder.getFullParagraphDescripton(paragraph, null, conns);
//to fill result list
handler.getResult();
// connections.put(paragraph,handler.getResult());
}
}
//check for objects, which cannot be used
if (errorListener != null) {
for (Iterator<ObjectBean> iter = unusedObjects.iterator(); iter.hasNext();) {
ObjectBean bean = iter.next();
errorListener.objectCannotBeUsed(bean);
}
}
}
/**
* Main method, it looks for ways from Paragraph "start" to Paragraph "end"
* @param start
* @param end
* @param endConnection
* @param objects
* @param notUsed
* @param unUsedConnections
* @param findMode
* @return possible way
*/
private ArrayList<ParagraphStep> way;
private Paragraph current;
private int score;
private ArrayList<ArrayList<Paragraph>> findWays(Paragraph start, Paragraph end, ParagraphConnection endConnection, GameState objects, HashSet<Paragraph> notUsed, HashSet<ParagraphConnection> unUsedConnections,int findMode) throws TimeoutException{
ArrayList<ArrayList<Paragraph>> result = new ArrayList<ArrayList<Paragraph>>();
if (start==end) {
result.add(new ArrayList<Paragraph>());
return result;
}
init();
way = new ArrayList<ParagraphStep>();
current = start;
score=0;
// int iteration=0;
main:
while (true) {
// iteration++;
// if (iteration==22383) {
// System.out.println("!!!");
// }
if (errorListener.canContinue()==false) {
return null;
}
if (showDiagInfo) {
System.out.print("-------------\nWAY(");
System.out.print(score);
System.out.print("): ");
for (ParagraphStep step : way) {
System.out.print(step.paragraph.getName());
System.out.print('(');
System.out.print(step.wayCounter);
System.out.print('/');
System.out.print(step.outcome.size());
System.out.print(')');
System.out.print(',');
}
System.out.println(current.getName());
}
if (notUsed != null) {
if (notUsed.remove(current)) {
passedParagraph(current);
errorListener.updateStatus(notUsed.size(),unUsedConnections == null ? 0:unUsedConnections.size());
}
}
if (current==end) {
//got it
break;
}
if (objects.apply(current,errorListener)) {
score++;
if (showDiagInfo) {
System.out.println("New score="+score+", unUsedConnections.size()="+unUsedConnections.size());
}
}
ArrayList<ParagraphTransition> out = connections.get(current);
if (out != null) {
//next level
ParagraphStep step = new ParagraphStep(current,out,score,way,findMode==FIND_ONE ? null:unUsedConnections,objects);
Paragraph next = step.getNextParagraph(end,endConnection,objects,notUsed,unUsedConnections,way);
if (next==null) {
if (step.isFound()) {
//go it
way.add(step);
if (findMode==FIND_ONE) {
addWay(findMode,result,way,end,endConnection);
break;
} else if (findMode==FIND_ALL_USED) {
if (notUsed.size()==0 && unUsedConnections.size()==0) {
break;
}
} else if (findMode==FIND_ALL) {
addWay(findMode,result,way,end,endConnection);
}
}
} else {
way.add(step);
current = next;
continue;
}
}
//not way to go - go back
if (way.size()>0) {
//check for timeout
if (errorListener != null && errorListener.checkTimeout()) {
throw new TimeoutException();
}
//go back
int len = way.size()-1;
//get prev. step
ParagraphStep step = way.get(len);
while (true) {
Paragraph next = step.getNextParagraph(end,endConnection,step.state,notUsed,unUsedConnections,way);
if (next==null) {
if (step.isFound()) {
//go it
addWay(findMode,result,way,end,endConnection);
if (findMode==FIND_ONE) {
break main;
} else if (findMode==FIND_ALL_USED) {
if (notUsed.size()==0 && unUsedConnections.size()==0) {
break main;
}
}
} else if (len>0){
//go back-back
way.remove(len);
len--;
step = way.get(len);
} else {
//end
break main;
}
} else {
score = step.getScore();
objects = new GameState(step.state);
current = next;
continue main;
}
}
}
//no way at all
break;
}
// System.out.println(iteration);
return result;
}
private void addWay(int findMode,ArrayList<ArrayList<Paragraph>> result, ArrayList<ParagraphStep> wayStep, Paragraph end,ParagraphConnection endConnection) {
ArrayList<Paragraph> way = new ArrayList<Paragraph>(wayStep.size()+1);
for (ParagraphStep paragraphStep : wayStep) {
way.add(paragraphStep.paragraph);
}
if (end != null) {
way.add(end);
} else {
//endConnection
if (way.get(way.size()-1)==endConnection.getFrom()) {
//TO paragraph
way.add(endConnection.getTo());
} else {
//FROM paragraph
way.add(endConnection.getFrom());
}
}
if (findMode==FIND_MIN_MAX) {
if (result.size()==0) {
//first
result.add(way);
result.add(way);
} else {
if (result.get(0).size()>way.size()) {
result.set(0, way);
}
if (result.get(1).size()<way.size()) {
result.set(1, way);
}
}
} else if (findMode==FIND_ALL_USED){
if (result.size()==0) {
//we need just one
result.add(way);
}
} else {
result.add(way);
}
}
public class ParagraphStep {
private GameState state;
private Paragraph paragraph;
private int wayCounter;
private ArrayList<ParagraphTransition> outcome;
private boolean found;
private int score;
public int getWayCounter() {
return wayCounter;
}
public Paragraph getNextParagraph(Paragraph end,ParagraphConnection endConnection, GameState objects, HashSet<Paragraph> notUsed, HashSet<ParagraphConnection> unUsedConnections, ArrayList<ParagraphStep> way) {
found = false;
next:
for (; wayCounter < outcome.size();) {
for (int i = 0; i < wayCounter; i++) {
ParagraphTransition transition = outcome.get(i);
if (isMustFollow(transition.getConnection())) {
if (objects.canBePassed(transition)) {
//must-connection is active, others cannot be used now
wayCounter = outcome.size();
return null;
}
}
}
ParagraphTransition transition = outcome.get(wayCounter++);
// if (transition.getParagraph().getNumber()==108) {
// System.out.println("!!!");
// }
if (objects.canBePassed(transition)) {
//can be passed
if (transition.paragraph==end) {
//FOUND!!!!
found = true;
useTransition(transition,notUsed,unUsedConnections);
return null;
}
if (transition.paragraph.isFail() || transition.paragraph.isSuccess()) {
//fail or other success, nothing to do
useTransition(transition,notUsed,unUsedConnections);
continue;
}
//check for end-connection
if (transition.getConnection()==endConnection) {
//FOUND !!!
found = true;
useTransition(transition,notUsed,unUsedConnections);
return null;
}
//drill it down
for (int i = way.size()-1; i >=0; i--) {
ParagraphStep step = way.get(i);
if (step!=this) {
//skip yourself
if (step.paragraph==transition.paragraph) {
//already use this way
if (step.getScore()<score) {
//we can continue, something happened
break;
} else {
//useless way
if (unUsedConnections != null && unUsedConnections.contains(transition.connection)) {
//useless way...but we can pass it
unUsedConnections.remove(transition.connection);
}
continue next;
}
}
}
}
useTransition(transition,notUsed,unUsedConnections);
return transition.paragraph;
}
}
//no next
return null;
}
private void useTransition(ParagraphTransition transition, HashSet<Paragraph> notUsed, HashSet<ParagraphConnection> unUsedConnections) {
boolean update=false;
if (notUsed != null) {
update = notUsed.remove(transition.paragraph);
}
if (unUsedConnections != null) {
update = unUsedConnections.remove(transition.getConnection()) || update;
}
if (update) {
passedParagraph(current);
errorListener.updateStatus(notUsed == null ? 0: notUsed.size(),unUsedConnections == null ? 0:unUsedConnections.size());
}
}
public ParagraphStep(Paragraph paragraph, ArrayList<ParagraphTransition> out, int score, ArrayList<ParagraphStep> way,HashSet<ParagraphConnection> unusedConnection,GameState state) {
this.score = score;
this.paragraph = paragraph;
this.state = new GameState(state);
// if (unusedConnection==null) {
this.outcome = out;
for (int i = way.size()-1; i >=0; i--) {
ParagraphStep step = way.get(i);
if (step.paragraph==paragraph && step.outcome.size() > step.wayCounter+1) {
//found it
ParagraphTransition transition = step.outcome.get(step.wayCounter-1);
if (isMustFollow(transition.connection)==false) {
//can be re-ordered
outcome = new ArrayList<ParagraphTransition>(step.outcome.size()+1);
outcome.addAll(step.outcome);
outcome.remove(step.wayCounter-1);
outcome.add(transition);
}
break;
}
}
// } else {
// this.outcome = new ArrayList<ParagraphTransition>(out.size());
// for (ParagraphTransition transition : out) {
// if (isMustFollow(transition.connection) || unusedConnection.contains(transition.connection)) {
// //add must-go or unknown connections first
// outcome.add(transition);
// }
// }
// for (ParagraphTransition transition : out) {
// if (isMustFollow(transition.connection) || unusedConnection.contains(transition.connection)) {
// //add already passed connections later
// continue;
// }
// outcome.add(transition);
// }
// }
}
public int getScore() {
return score;
}
public boolean isFound() {
return found;
}
}
public class ParagraphTransition {
private Paragraph paragraph;
private ParagraphConnection connection;
public ObjectBean getObject() {
return connection.getObject();
}
public ParagraphConnection getConnection() {
return connection;
}
public ParagraphTransition(Paragraph paragraph, ParagraphConnection connection) {
super();
this.paragraph = paragraph;
this.connection = connection;
}
public Paragraph getParagraph() {
return paragraph;
}
}
public HashMap<Paragraph, ArrayList<ParagraphTransition>> getMap() {
init();
return connections;
}
}
public class FindParsingHandler implements ParagraphParsingHandler {
private ArrayList<ParagraphTransition> list;
private ArrayList<ParagraphTransition> result;
public void reset(ArrayList<ParagraphTransition> list) {
this.list = new ArrayList<ParagraphTransition>(list.size());
this.list.addAll(list);
result = list;
result.clear();
}
public void addAlchemy(Paragraph paragraph, String toValue,Alchemy alchemy) {
}
public void addLinkTo(Paragraph current, Paragraph next,ParagraphConnection connection) {
for (int i = 0; i < list.size(); i++) {
ParagraphTransition transition = list.get(i);
if (transition.connection==connection) {
list.remove(i);
result.add(transition);
break;
}
}
}
public void addAlchemyFromValue(Paragraph paragraph, String value) {
}
public void addObject(Paragraph paragraph,ObjectBean objectBean, String key) {
}
public void addText(Paragraph paragraph,String text) {
}
public ArrayList<ParagraphTransition> getResult() {
if (list.size()>0) {
result.addAll(list);
list.clear();
}
return result;
}
public void addBattle(Battle battle, Paragraph paragraph) {
}
}
public class GameState {
private HashSet<ObjectBean> objects;
private HashSet<ObjectBean> allObjects;
private HashSet<Modificator> modificators;
private HashSet<Modificator> allModificators;
public GameState() {
objects=new HashSet<ObjectBean>();
allObjects=new HashSet<ObjectBean>();
modificators = new HashSet<Modificator>();
allModificators = new HashSet<Modificator>();
}
public GameState(GameState clone) {
objects=new HashSet<ObjectBean>(clone.objects);
allObjects=new HashSet<ObjectBean>(clone.allObjects);
modificators = new HashSet<Modificator>(clone.modificators);
allModificators = new HashSet<Modificator>(clone.allModificators);
}
public void clear() {
objects.clear();
allObjects.clear();
modificators.clear();
allModificators.clear();
}
// public void clear() {
// objects.clear();
// modificators.clear();
// allObjects.clear();
// allModificators.clear();
// };
public boolean canBePassed(ParagraphTransition transition) {
if (errorListener.canBePassed(transition.connection,transition.paragraph)==false) {
return false;
}
switch (transition.getConnection().getType()) {
case ParagraphConnection.TYPE_ENEMY_VITAL_LESS:
return true;
case ParagraphConnection.TYPE_MODIFICATOR:
return modificators.contains(transition.getConnection().getModificator());
case ParagraphConnection.TYPE_NO_MODIFICATOR:
return modificators.contains(transition.getConnection().getModificator())==false;
case ParagraphConnection.TYPE_PARAMETER_LESS:
//TODO add parameters check
return true;
case ParagraphConnection.TYPE_PARAMETER_MORE:
//TODO add parameters check
return true;
case ParagraphConnection.TYPE_VITAL_LESS:
return true;
default:
//normal
if (transition.getObject()==null) {
return true;
} else if (transition.getConnection().getStrictness()==ParagraphConnection.STRICTNESS_MUST_NOT) {
return objects.contains(transition.getObject())==false;
} else {
return objects.contains(transition.getObject());
}
}
}
public boolean apply(Paragraph paragraph, PathFinderErrorListener errorListener) {
boolean step=false;
HashMap<Modificator,Boolean> mods = paragraph.getChangeModificators();
if (mods != null && mods.size()>0) {
for (Modificator modificator : mods.keySet()) {
boolean value = mods.get(modificator);
if (value) {
if (modificators.add(modificator) && allModificators.add(modificator)) {
//does not exists, add
// System.out.println("add "+modificator.getName());
step = true;
}
} else if (modificators.remove(modificator)) {
// System.out.println("clear "+modificator.getName());
step = true;
}
}
}
if (paragraph.getGotObjects().isEmpty()==false) {
for (ObjectBean bean : paragraph.getGotObjects()) {
allObjects.add(bean);
if (objects.add(bean)) {
//next level
// System.out.println("got "+bean.getName());
step = true;
} else if (bean.isUncountable()==false && errorListener != null) {
//already have it
if (alreadyHaveThatObjects==null) {
alreadyHaveThatObjects = new HashMap<ObjectBean, HashSet<Paragraph>>();
}
HashSet<Paragraph> set;
if (alreadyHaveThatObjects.containsKey(bean)) {
set = alreadyHaveThatObjects.get(bean);
if (set.contains(paragraph)) {
continue;
}
} else {
set = new HashSet<Paragraph>();
alreadyHaveThatObjects.put(bean,set);
}
set.add(paragraph);
errorListener.alreadyHaveThatObject(paragraph,bean);
}
}
}
if (paragraph.getLostObjects().isEmpty()==false) {
for (ObjectBean bean : paragraph.getLostObjects()) {
if (objects.remove(bean)) {
//next level
// System.out.println("lost "+bean.getName());
step = true;
}
}
}
return step;
}
public void remove(Modificator modificator) {
modificators.remove(modificator);
}
public boolean contains(ObjectBean bean) {
return objects.contains(bean);
}
public HashSet<ObjectBean> getObjects() {
return objects;
}
public HashSet<Modificator> getModificators() {
return modificators;
}
}
public boolean isMustFollow(ParagraphConnection connection) {
//TODO add parameters control
if (connection.getStrictness()==ParagraphConnection.STRICTNESS_MUST) {
return connection.getType() == ParagraphConnection.TYPE_NORMAL ||
connection.getType() == ParagraphConnection.TYPE_MODIFICATOR ||
connection.getType() == ParagraphConnection.TYPE_NO_MODIFICATOR;
} else {
return false;
}
}
protected void passedParagraph(Paragraph current) {
}
public void setCheckSuccessOnly(boolean checkSuccess) {
this.checkSuccess = checkSuccess;
}
}