package org.ggp.base.util.gdl.transforms; import java.util.ArrayList; import java.util.List; import org.ggp.base.util.gdl.grammar.Gdl; import org.ggp.base.util.gdl.grammar.GdlConstant; import org.ggp.base.util.gdl.grammar.GdlDistinct; import org.ggp.base.util.gdl.grammar.GdlFunction; import org.ggp.base.util.gdl.grammar.GdlLiteral; import org.ggp.base.util.gdl.grammar.GdlNot; import org.ggp.base.util.gdl.grammar.GdlOr; import org.ggp.base.util.gdl.grammar.GdlPool; import org.ggp.base.util.gdl.grammar.GdlProposition; import org.ggp.base.util.gdl.grammar.GdlRelation; import org.ggp.base.util.gdl.grammar.GdlRule; import org.ggp.base.util.gdl.grammar.GdlSentence; import org.ggp.base.util.gdl.grammar.GdlTerm; import org.ggp.base.util.gdl.grammar.GdlVariable; //Cleans up various issues with games to make them more standardized. public class GdlCleaner { private static final int MAX_ITERATIONS = 100; private GdlCleaner() { } public static List<Gdl> run(List<Gdl> description) { for (int i = 0; i < MAX_ITERATIONS; i++) { List<Gdl> newDescription = runOnce(description); if (newDescription.equals(description)) { break; } description = newDescription; } return description; } private static List<Gdl> runOnce(List<Gdl> description) { List<Gdl> newDescription = new ArrayList<Gdl>(); //First: Clean up all rules with zero-element bodies for(Gdl gdl : description) { if(gdl instanceof GdlRule) { GdlRule rule = (GdlRule) gdl; if(rule.getBody().size() == 0) { newDescription.add(rule.getHead()); } else { newDescription.add(gdl); } } else { newDescription.add(gdl); } } //TODO: Add (role ?player) where appropriate, i.e. in rules for //"legal" or "input" where the first argument is an undefined //variable //Get rid of "extra parentheses", i.e. zero-arity functions description = newDescription; newDescription = new ArrayList<Gdl>(); for(Gdl gdl : description) { if(gdl instanceof GdlRelation) { newDescription.add(cleanParentheses((GdlRelation)gdl)); } else if(gdl instanceof GdlRule) { newDescription.add(cleanParentheses((GdlRule)gdl)); } else { newDescription.add(gdl); } } //TODO: Get rid of GdlPropositions in the description //TODO: Get rid of (not (or ...)) //Get rid of (not (distinct _ _)) literals in rules //TODO: Expand to functions description = newDescription; newDescription = new ArrayList<Gdl>(); for(Gdl gdl : description) { if(gdl instanceof GdlRule) { GdlRule cleaned = removeNotDistinctLiterals((GdlRule)gdl); if(cleaned != null) newDescription.add(cleaned); } else { newDescription.add(gdl); } } //Get rid of the old style of "base" sentences (with arity more than 1, not in rules) //See e.g. current version of Qyshinsu on the Dresden server description = newDescription; newDescription = new ArrayList<Gdl>(); boolean removeBaseSentences = false; for (Gdl gdl : description) { if (gdl instanceof GdlRelation) { GdlRelation relation = (GdlRelation) gdl; if (relation.getName() == GdlPool.BASE && relation.arity() != 1) { removeBaseSentences = true; break; } } } //Note that in this case, we have to remove ALL of them or we might //misinterpret this as being the new kind of "base" relation for (Gdl gdl : description) { if (gdl instanceof GdlRelation) { GdlRelation relation = (GdlRelation) gdl; if (removeBaseSentences && relation.getName() == GdlPool.BASE) { //Leave out the relation } else { newDescription.add(gdl); } } else { newDescription.add(gdl); } } return newDescription; } private static GdlRule removeNotDistinctLiterals(GdlRule rule) { while(rule != null && getNotDistinctLiteral(rule) != null) { rule = removeNotDistinctLiteral(rule, getNotDistinctLiteral(rule)); } return rule; } private static GdlNot getNotDistinctLiteral(GdlRule rule) { for(GdlLiteral literal : rule.getBody()) { if(literal instanceof GdlNot) { GdlNot not = (GdlNot) literal; if(not.getBody() instanceof GdlDistinct) { //For now, we can only deal with this if not both are functions. //That means we have to skip that case at this point. GdlDistinct distinct = (GdlDistinct) not.getBody(); if(!(distinct.getArg1() instanceof GdlFunction) || !(distinct.getArg2() instanceof GdlFunction)) return not; } } } return null; } //Returns null if the rule is useless. private static GdlRule removeNotDistinctLiteral(GdlRule rule, GdlNot notDistinctLiteral) { //Figure out the substitution we want... //If we have two constants: Either remove one or //maybe get rid of the ___? //One is a variable: Replace the variable with the other thing //throughout the rule GdlDistinct distinct = (GdlDistinct) notDistinctLiteral.getBody(); GdlTerm arg1 = distinct.getArg1(); GdlTerm arg2 = distinct.getArg2(); if(arg1 == arg2) { //Just remove that literal List<GdlLiteral> newBody = new ArrayList<GdlLiteral>(); newBody.addAll(rule.getBody()); newBody.remove(notDistinctLiteral); return GdlPool.getRule(rule.getHead(), newBody); } if(arg1 instanceof GdlVariable) { //What we return will still have the not-distinct literal, //but it will get replaced in the next pass. //(Even if we have two variables, they will be equal next time through.) return CommonTransforms.replaceVariable(rule, (GdlVariable)arg1, arg2); } if(arg2 instanceof GdlVariable) { return CommonTransforms.replaceVariable(rule, (GdlVariable)arg2, arg1); } if(arg1 instanceof GdlConstant || arg2 instanceof GdlConstant) { //We have two non-equal constants, or a constant and a function. //The rule should have no effect. return null; } //We have two functions. Complicated! (Have to replace them with unified version.) //We pass on this case for now. //TODO: Implement correctly. throw new UnsupportedOperationException("We can't currently handle (not (distinct <function> <function>))."); } private static GdlRule cleanParentheses(GdlRule rule) { GdlSentence cleanedHead = cleanParentheses(rule.getHead()); List<GdlLiteral> cleanedBody = new ArrayList<GdlLiteral>(); for(GdlLiteral literal : rule.getBody()) cleanedBody.add(cleanParentheses(literal)); return GdlPool.getRule(cleanedHead, cleanedBody); } private static GdlLiteral cleanParentheses(GdlLiteral literal) { if(literal instanceof GdlSentence) { return cleanParentheses((GdlSentence)literal); } else if(literal instanceof GdlDistinct) { GdlDistinct distinct = (GdlDistinct) literal; GdlTerm term1 = cleanParentheses(distinct.getArg1()); GdlTerm term2 = cleanParentheses(distinct.getArg2()); return GdlPool.getDistinct(term1, term2); } else if(literal instanceof GdlNot) { GdlLiteral body = ((GdlNot) literal).getBody(); return GdlPool.getNot(cleanParentheses(body)); } else if(literal instanceof GdlOr) { GdlOr or = (GdlOr) literal; List<GdlLiteral> disjuncts = new ArrayList<GdlLiteral>(); for(int i = 0; i < or.arity(); i++) disjuncts.add(cleanParentheses(or.get(i))); return GdlPool.getOr(disjuncts); } throw new RuntimeException("Unexpected literal type in GdlCleaner"); } private static GdlSentence cleanParentheses(GdlSentence sentence) { if(sentence instanceof GdlProposition) return sentence; List<GdlTerm> cleanedBody = new ArrayList<GdlTerm>(); for(GdlTerm term : sentence.getBody()) cleanedBody.add(cleanParentheses(term)); if (cleanedBody.isEmpty()) { return GdlPool.getProposition(sentence.getName()); } else { return GdlPool.getRelation(sentence.getName(), cleanedBody); } } private static GdlTerm cleanParentheses(GdlTerm term) { if(term instanceof GdlConstant || term instanceof GdlVariable) return term; if(term instanceof GdlFunction) { GdlFunction function = (GdlFunction) term; //The whole point of the function if(function.arity() == 0) return function.getName(); List<GdlTerm> cleanedBody = new ArrayList<GdlTerm>(); for(GdlTerm functionTerm : function.getBody()) cleanedBody.add(cleanParentheses(functionTerm)); return GdlPool.getFunction(function.getName(), cleanedBody); } throw new RuntimeException("Unexpected term type in GdlCleaner"); } }