/** * eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the * <e-UCM> research group. * * Copyright 2005-2010 <e-UCM> research group. * * You can access a list of all the contributors to eAdventure at: * http://e-adventure.e-ucm.es/contributors * * <e-UCM> is a research group of the Department of Software Engineering * and Artificial Intelligence at the Complutense University of Madrid * (School of Computer Science). * * C Profesor Jose Garcia Santesmases sn, * 28040 Madrid (Madrid), Spain. * * For more info please visit: <http://e-adventure.e-ucm.es> or * <http://www.e-ucm.es> * * **************************************************************************** * * This file is part of eAdventure, version 2.0 * * eAdventure is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * eAdventure is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with eAdventure. If not, see <http://www.gnu.org/licenses/>. */ package es.eucm.ead.importer.testers; import es.eucm.ead.model.Commands; import es.eucm.ead.model.elements.extra.EAdList; import es.eucm.eadventure.common.data.chapter.Chapter; import es.eucm.eadventure.common.data.chapter.conditions.Condition; import es.eucm.eadventure.common.data.chapter.conditions.Conditions; import es.eucm.eadventure.common.data.chapter.conditions.FlagCondition; import es.eucm.eadventure.common.data.chapter.conditions.GlobalState; import es.eucm.eadventure.common.data.chapter.conditions.VarCondition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class VarsMap { private Logger logger = LoggerFactory.getLogger(VarsMap.class); private Chapter chapter; private Map<String, Boolean> flags; private Map<String, Integer> vars; private List<Condition> conditions; private Map<String, FlagResult> flagResults; private Map<String, VarResult> varResults; private boolean[] combinations; private OldConditionsEvaluator oldConditionsEvaluator; public VarsMap() { flags = new HashMap<String, Boolean>(); vars = new HashMap<String, Integer>(); conditions = new ArrayList<Condition>(); oldConditionsEvaluator = new OldConditionsEvaluator(this); flagResults = new HashMap<String, FlagResult>(); varResults = new HashMap<String, VarResult>(); } public void setChapter(Chapter chapter) { this.chapter = chapter; } public void setVariable(String id, int value) { vars.put(id, value); } public void setFlag(String id, boolean value) { flags.put(id, value); } public boolean isActiveFlag(String id) { return flags.containsKey(id) ? flags.get(id) : false; } public int getValue(String id) { return vars.containsKey(id) ? vars.get(id) : 0; } public GlobalState getGlobalState(String globalStateId) { return chapter.getGlobalState(globalStateId); } public Chapter getChapter() { return chapter; } public void clear() { flags.clear(); vars.clear(); conditions.clear(); flagResults.clear(); varResults.clear(); } // Conditions manipulation /** * Sets the vars and flags to make true conditions[index] and false the rest of conditions in the array * * @param index * @param conditions */ public boolean makeTrue(int index, Conditions... conditions) { clear(); for (int i = 0; i <= index; i++) { analyze(conditions[i]); } combinations = new boolean[this.conditions.size()]; boolean indexTrue = false; boolean restFalse; boolean nextCombination = true; while (nextCombination && !indexTrue) { if (updateConditions()) { restFalse = true; for (int i = 0; i < index && restFalse; i++) { restFalse = !oldConditionsEvaluator .allConditionsOk(conditions[i]); } if (restFalse) { indexTrue = oldConditionsEvaluator .allConditionsOk(conditions[index]); } } if (!indexTrue) { nextCombination = nextCombination(); } } return !nextCombination; } /** Sets the vars map to satisfy the condition **/ public boolean make(Conditions c, boolean satisfaction) { clear(); analyze(c); combinations = new boolean[this.conditions.size()]; boolean nextCombination = true; boolean done = false; while (nextCombination && !done) { if (updateConditions()) { done = oldConditionsEvaluator.allConditionsOk(c) == satisfaction; } if (!done) { nextCombination = nextCombination(); } } return !nextCombination; } private void analyze(Conditions conditions) { for (List<Condition> l : conditions.getConditionsList()) { for (Condition c : l) { if (c.getType() == Condition.GLOBAL_STATE_CONDITION) { analyze(chapter.getGlobalState(c.getId())); } else { if (!contains(c)) { this.conditions.add(c); } } } } } private boolean make(Condition c, boolean result) { if (result) { return makeTrue(c); } else { return makeFalse(c); } } private boolean makeFalse(Condition c) { try { Condition c1 = (Condition) c.clone(); if (c.getType() == Condition.FLAG_CONDITION) { c1 .setState(c1.getState() == FlagCondition.FLAG_ACTIVE ? FlagCondition.FLAG_INACTIVE : FlagCondition.FLAG_ACTIVE); } else if (c.getType() == Condition.VAR_CONDITION) { switch (c.getState()) { case VarCondition.VAR_EQUALS: c1.setState(VarCondition.VAR_NOT_EQUALS); break; case VarCondition.VAR_LESS_EQUALS_THAN: c1.setState(VarCondition.VAR_GREATER_THAN); break; case VarCondition.VAR_LESS_THAN: c1.setState(VarCondition.VAR_GREATER_EQUALS_THAN); break; case VarCondition.VAR_NOT_EQUALS: c1.setState(VarCondition.VAR_EQUALS); break; case VarCondition.VAR_GREATER_THAN: c1.setState(VarCondition.VAR_LESS_EQUALS_THAN); break; case VarCondition.VAR_GREATER_EQUALS_THAN: c1.setState(VarCondition.VAR_LESS_THAN); break; } } return makeTrue(c1); } catch (CloneNotSupportedException e) { logger.error("Error cloning:", e); } return true; } /** * Returns if there was contradiction * * @param c the condition * @return */ private boolean makeTrue(Condition c) { if (c.getType() == Condition.FLAG_CONDITION) { FlagResult flagResult = flagResults.get(c.getId()); if (flagResult == null) { flagResults.put(c.getId(), flagResult = new FlagResult()); } return flagResult.update(c.getState() == FlagCondition.FLAG_ACTIVE); } else if (c.getType() == Condition.VAR_CONDITION) { VarResult varResult = varResults.get(c.getId()); if (varResult == null) { varResults.put(c.getId(), varResult = new VarResult()); } int value = ((VarCondition) c).getValue(); switch (c.getState()) { case VarCondition.VAR_EQUALS: case VarCondition.VAR_LESS_THAN: case VarCondition.VAR_NOT_EQUALS: case VarCondition.VAR_GREATER_THAN: return varResult.update(c.getState(), value); case VarCondition.VAR_GREATER_EQUALS_THAN: return varResult.update(VarCondition.VAR_GREATER_THAN, value - 1); case VarCondition.VAR_LESS_EQUALS_THAN: return varResult.update(VarCondition.VAR_LESS_THAN, value + 1); } } return true; } /** * Returns false if there's no more combinations * */ private boolean nextCombination() { boolean allTrue = true; for (int i = 0; i < combinations.length && allTrue; i++) { allTrue = combinations[i]; } if (allTrue) { return false; } addOne(0); return true; } /** * Returns if it was possible to set the conditions, and there was no contradiction with the current values * */ private boolean updateConditions() { boolean contradiction = false; flagResults.clear(); varResults.clear(); for (int i = 0; i < conditions.size() && !contradiction; i++) { contradiction = make(conditions.get(i), combinations[i]); } if (!contradiction) { for (Entry<String, FlagResult> e : flagResults.entrySet()) { setFlag(e.getKey(), e.getValue().value); } for (Entry<String, VarResult> e : varResults.entrySet()) { setVariable(e.getKey(), e.getValue().value); } } return !contradiction; } private void addOne(int index) { if (index < combinations.length) { if (combinations[index]) { combinations[index] = false; addOne(index + 1); } else { combinations[index] = true; } } } private boolean contains(Condition c) { for (Condition c1 : this.conditions) { if (c1.getId().equals(c.getId()) && c.getType() == c1.getType() && c.getState().equals(c1.getState())) { if (c instanceof VarCondition && c1 instanceof VarCondition) { if (((VarCondition) c1).getValue().equals( ((VarCondition) c).getValue())) { return true; } } else if (c instanceof FlagCondition && c1 instanceof FlagCondition) { return true; } } } return false; } public void writeState(String chapterId, ConverterTester converterTester) { writeState(chapterId, converterTester, flags); writeState(chapterId, converterTester, vars); } @SuppressWarnings("unchecked") private void writeState(String chapterId, ConverterTester converterTester, Map map) { Set<Entry> s = map.entrySet(); for (Entry<Object, Object> e : s) { converterTester.command(Commands.SET + " " + chapterId + "." + e.getKey() + " " + e.getValue()); } } private class FlagResult { public Boolean value; public boolean update(boolean newValue) { if (value == null) { value = newValue; return false; } else { return value == newValue; } } public String toString() { return value + ""; } } private class VarResult { public Integer greaterThan; public Integer lessThan; public Integer equal; public Integer value; public EAdList<Integer> notEqual; public VarResult() { notEqual = new EAdList<Integer>(); } public String toString() { return value + ":" + (greaterThan != null ? ">" + greaterThan : "") + (lessThan != null ? "<" + lessThan : "") + (equal != null ? "==" + equal : "") + (notEqual.size() > 0 ? notEqual : ""); } public boolean update(int type, int newValue) { boolean contradiction = false; switch (type) { case VarCondition.VAR_LESS_THAN: if (lessThan == null) { lessThan = newValue; } else { lessThan = Math.min(newValue, lessThan); } break; case VarCondition.VAR_GREATER_THAN: if (greaterThan == null) { greaterThan = newValue; } else { greaterThan = Math.max(newValue, greaterThan); } break; case VarCondition.VAR_EQUALS: if (equal == null) { equal = newValue; } else { contradiction = equal != newValue; } break; case VarCondition.VAR_NOT_EQUALS: if (!notEqual.contains(newValue)) { notEqual.add(newValue); } break; } contradiction |= checkContradiction(); if (!contradiction) { this.value = result(); } return contradiction || value == null; } public boolean checkContradiction() { boolean contradiction = lessThan != null && greaterThan != null && lessThan <= greaterThan + 1; contradiction |= equal != null && ((lessThan != null && equal > lessThan) || (greaterThan != null && equal < greaterThan) || notEqual .contains(equal)); return contradiction; } public Integer result() { if (equal != null) { return equal; } else if (lessThan != null) { int start = lessThan - 1; while ((greaterThan != null && start <= greaterThan) || notEqual.contains(start)) { start--; } if (greaterThan == null || start > greaterThan) { return start; } else { return null; } } else if (greaterThan != null) { int start = greaterThan + 1; while (notEqual.contains(start)) { start++; } return start; } else if (notEqual.size() > 0) { int i = 0; while (notEqual.contains(i)) { i++; } return i; } else { return null; } } } }