package org.ggp.base.util.gdl.transforms; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.ggp.base.util.gdl.GdlUtils; import org.ggp.base.util.gdl.grammar.Gdl; import org.ggp.base.util.gdl.grammar.GdlDistinct; 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.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.model.SentenceForm; import org.ggp.base.util.gdl.model.SentenceFormModel; import org.ggp.base.util.gdl.model.SentenceFormModelFactory; public class Relationizer { private Relationizer() { } /** * Searches the description for statements that are needlessly treated as * base propositions when they could be expressed as simple relations, and * replaces them with these simpler forms. * * Some games have been written such that unchanging facts of the game * are listed as base propositions. Often, this is so the fact can be * accessed by a visualization. Gamers usually don't need this distinction, * and can reduce the costs in time and memory of processing the game if * these statements are instead transformed into sentences. * @throws InterruptedException */ public static List<Gdl> run(List<Gdl> description) throws InterruptedException { SentenceFormModel model = SentenceFormModelFactory.create(description); List<SentenceForm> nextFormsToReplace = new ArrayList<SentenceForm>(); //Find the update rules for each "true" statement for(SentenceForm nextForm : model.getSentenceForms()) { if(nextForm.getName().equals(GdlPool.NEXT)) { //See if there is exactly one update rule, and it is the persistence rule Set<GdlRule> rules = model.getRules(nextForm); if(rules.size() == 1) { GdlRule rule = rules.iterator().next(); //Persistence rule: Exactly one literal, the "true" form of the sentence if(rule.arity() == 1) { GdlLiteral literal = rule.get(0); if(literal instanceof GdlRelation) { //Check that it really is the true form SentenceForm trueForm = nextForm.withName(GdlPool.TRUE); if(trueForm.matches((GdlRelation) literal)) { GdlSentence head = rule.getHead(); GdlSentence body = (GdlSentence) literal; //Check that the tuples are the same, and that they //consist of distinct variables List<GdlTerm> headTuple = GdlUtils.getTupleFromSentence(head); List<GdlTerm> bodyTuple = GdlUtils.getTupleFromSentence(body); if(headTuple.equals(bodyTuple)) { //Distinct variables? Set<GdlTerm> vars = new HashSet<GdlTerm>(headTuple); if(vars.size() == headTuple.size()) { nextFormsToReplace.add(nextForm); } } } } } } } } List<Gdl> newDescription = new ArrayList<Gdl>(description); //Now, replace the next forms for (SentenceForm nextForm : nextFormsToReplace) { SentenceForm initForm = nextForm.withName(GdlPool.INIT); SentenceForm trueForm = nextForm.withName(GdlPool.TRUE); SentenceForm baseForm = nextForm.withName(GdlPool.BASE); //Go through the rules and relations, making replacements as needed for (int i = 0; i < newDescription.size(); i++) { Gdl gdl = newDescription.get(i); if (gdl instanceof GdlRelation) { //Replace initForm GdlRelation relation = (GdlRelation) gdl; if (initForm.matches(relation)) { GdlSentence newSentence = relation.get(0).toSentence(); newDescription.set(i, newSentence); } else if (baseForm.matches(relation)) { newDescription.remove(i); i--; } } else if (gdl instanceof GdlRule) { GdlRule rule = (GdlRule) gdl; //Remove persistence rule (i.e. rule with next form as head) GdlSentence head = rule.getHead(); if (nextForm.matches(head) || baseForm.matches(head)) { newDescription.remove(i); i--; } else { //Replace true in bodies of rules with relation-only form List<GdlLiteral> body = rule.getBody(); List<GdlLiteral> newBody = replaceRelationInBody(body, trueForm); GdlSentence newHead = head; if (initForm.matches(head)) { newHead = head.get(0).toSentence(); } if (!body.equals(newBody) || !head.equals(newHead)) { GdlRule newRule = GdlPool.getRule(newHead, newBody); newDescription.set(i, newRule); } } } } } return newDescription; } private static List<GdlLiteral> replaceRelationInBody( List<GdlLiteral> body, SentenceForm trueForm) { List<GdlLiteral> newBody = new ArrayList<GdlLiteral>(); for(GdlLiteral literal : body) { newBody.add(replaceRelationInLiteral(literal, trueForm)); } return newBody; } private static GdlLiteral replaceRelationInLiteral(GdlLiteral literal, SentenceForm trueForm) { if(literal instanceof GdlSentence) { GdlSentence sentence = (GdlSentence) literal; if(trueForm.matches(sentence)) { //Replace with the sentence contained in the true sentence... return sentence.get(0).toSentence(); } else { return literal; } } else if(literal instanceof GdlNot) { GdlNot not = (GdlNot) literal; return GdlPool.getNot(replaceRelationInLiteral(not.getBody(), trueForm)); } else if(literal instanceof GdlOr) { GdlOr or = (GdlOr) literal; List<GdlLiteral> newOrBody = new ArrayList<GdlLiteral>(); for(int i = 0; i < or.arity(); i++) newOrBody.add(replaceRelationInLiteral(or.get(i), trueForm)); return GdlPool.getOr(newOrBody); } else if(literal instanceof GdlDistinct) { return literal; } else { throw new RuntimeException("Unanticipated GDL literal type "+literal.getClass()+" encountered in Relationizer"); } } }