package org.ggp.base.util.propnet.factory.converter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; 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.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.propnet.architecture.Component; import org.ggp.base.util.propnet.architecture.PropNet; import org.ggp.base.util.propnet.architecture.components.And; import org.ggp.base.util.propnet.architecture.components.Constant; import org.ggp.base.util.propnet.architecture.components.Not; import org.ggp.base.util.propnet.architecture.components.Or; import org.ggp.base.util.propnet.architecture.components.Proposition; import org.ggp.base.util.propnet.architecture.components.Transition; import org.ggp.base.util.statemachine.Role; /** * The PropNetConverter class defines PropNet conversion for the PropNetFactory * class. This takes in a flattened game description, and converts it into an * equivalent PropNet. */ public final class PropNetConverter { /** An archive of Propositions, indexed by name. */ private Map<GdlSentence, Proposition> propositions; /** An archive of Components. */ private Set<Component> components; /** * Converts a game description to a PropNet using the following process * (note that this method and all of the methods that it invokes assume that * <tt>description</tt> has already been flattened by a PropNetFlattener): * <ol> * <li>Transforms each of the rules in <tt>description</tt> into * equivalent PropNet Components.</li> * <li>Adds or gates to Propositions with more than one input.</li> * <li>Adds inputs that are implicitly specified by <tt>description</tt>.</li> * </ol> * * @param description * A game description. * @return An equivalent PropNet. */ public PropNet convert(List<Role> roles, List<GdlRule> description) { propositions = new HashMap<GdlSentence, Proposition>(); components = new HashSet<Component>(); for ( GdlRule rule : description ) { if ( rule.arity() > 0 ) { convertRule(rule); } else { convertStatic(rule.getHead()); } } fixDisjunctions(); addMissingInputs(); return new PropNet(roles, components); } /** * Creates an equivalent InputProposition for every LegalProposition where * none already exists. */ private void addMissingInputs() { List<Proposition> addList = new ArrayList<Proposition>(); for ( Proposition proposition : propositions.values() ) { if ( proposition.getName() instanceof GdlRelation ) { GdlRelation relation = (GdlRelation) proposition.getName(); if ( relation.getName().getValue().equals("legal") ) { addList.add(proposition); } } } for ( Proposition addItem : addList ) { GdlRelation relation = (GdlRelation) addItem.getName(); components.add(getProposition(GdlPool.getRelation(GdlPool.DOES, relation.getBody()))); } } /** * Converts a literal to equivalent PropNet Components and returns a * reference to the last of those components. * * @param literal * The literal to convert to equivalent PropNet Components. * @return The last of those components. */ private Proposition convertConjunct(GdlLiteral literal) { if ( literal instanceof GdlDistinct ) { GdlDistinct distinct = (GdlDistinct) literal; Proposition proposition = new Proposition(GdlPool.getProposition(GdlPool.getConstant("anon"))); Constant constant = new Constant(!distinct.getArg1().equals(distinct.getArg2())); link(constant, proposition); components.add(proposition); components.add(constant); return proposition; } else if ( literal instanceof GdlNot ) { GdlNot not = (GdlNot) literal; Proposition input = convertConjunct(not.getBody()); Not no = new Not(); Proposition output = new Proposition(GdlPool.getProposition(GdlPool.getConstant("anon"))); link(input, no); link(no, output); components.add(input); components.add(no); components.add(output); return output; } else { GdlSentence sentence = (GdlSentence) literal; Proposition proposition = getProposition(sentence); components.add(proposition); return proposition; } } /** * Converts a sentence to equivalent PropNet Components and returns the * first of those components. * * @param sentence * The sentence to convert to equivalent PropNet Components. * @return The first of those Components. */ private Proposition convertHead(GdlSentence sentence) { if ( sentence.getName().getValue().equals("next") ) { Proposition head = getProposition(GdlPool.getRelation(GdlPool.TRUE, sentence.getBody())); Transition transition = new Transition(); Proposition preTransition = new Proposition(GdlPool.getProposition(GdlPool.getConstant("anon"))); link(preTransition, transition); link(transition, head); components.add(head); components.add(transition); components.add(preTransition); return preTransition; } else { Proposition proposition = getProposition(sentence); components.add(proposition); return proposition; } } /** * Converts a rule into equivalent PropNet Components by invoking the * <tt>convertHead()</tt> method on the head, and the * <tt>convertConjunct</tt> method on every literal in the body and * joining the results by an and gate. * * @param rule * The rule to convert. */ private void convertRule(GdlRule rule) { Proposition head = convertHead(rule.getHead()); And and = new And(); link(and, head); components.add(head); components.add(and); for ( GdlLiteral literal : rule.getBody() ) { Proposition conjunct = convertConjunct(literal); link(conjunct, and); } } /** * Converts a sentence to equivalent PropNet Components. * * @param sentence * The sentence to convert to equivalent PropNet Components. */ private void convertStatic(GdlSentence sentence) { if ( sentence.getName().getValue().equals("init") ) { Proposition init = getProposition(GdlPool.getProposition(GdlPool.getConstant("INIT"))); Transition transition = new Transition(); Proposition proposition = getProposition(GdlPool.getRelation(GdlPool.TRUE, sentence.getBody())); link(init, transition); link(transition, proposition); components.add(init); components.add(transition); components.add(proposition); } Constant constant = new Constant(true); Proposition proposition = getProposition(sentence); link(constant, proposition); components.add(constant); components.add(proposition); } /** * Creates an or gate to combine the inputs to a Proposition wherever one * has more than one input. */ private void fixDisjunctions() { List<Proposition> fixList = new ArrayList<Proposition>(); for ( Proposition proposition : propositions.values() ) { if ( proposition.getInputs().size() > 1 ) { fixList.add(proposition); } } for ( Proposition fixItem : fixList ) { Or or = new Or(); int i = 0; for ( Component input : fixItem.getInputs() ) { i++; Proposition disjunct = null; if ( fixItem.getName() instanceof GdlProposition ) { GdlProposition proposition = (GdlProposition) fixItem.getName(); disjunct = new Proposition(GdlPool.getProposition(GdlPool.getConstant(proposition.getName().getValue() + "-" + i))); } else { GdlRelation relation = (GdlRelation) fixItem.getName(); disjunct = new Proposition(GdlPool.getRelation(GdlPool.getConstant(relation.getName().getValue() + "-" + i), relation.getBody())); } input.getOutputs().clear(); link(input, disjunct); link(disjunct, or); components.add(disjunct); } fixItem.getInputs().clear(); link(or, fixItem); components.add(or); } } /** * Returns a Proposition with name <tt>term</tt>, creating one if none * already exists. * * @param sentence * The name of the Proposition. * @return A Proposition with name <tt>term</tt>. */ private Proposition getProposition(GdlSentence sentence) { if ( !propositions.containsKey(sentence) ) { propositions.put(sentence, new Proposition(sentence)); } return propositions.get(sentence); } /** * Adds inputs and outputs to <tt>source</tt> and <tt>target</tt> such * that <tt>source becomes an input to <tt>target</tt>. * @param source A component. * @param target A second component. */ private void link(Component source, Component target) { source.addOutput(target); target.addInput(source); } }