package util.gdl.model;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import util.gdl.grammar.Gdl;
import util.gdl.grammar.GdlConstant;
import util.gdl.grammar.GdlPool;
import util.gdl.grammar.GdlProposition;
import util.gdl.grammar.GdlRelation;
import util.gdl.grammar.GdlRule;
import util.gdl.grammar.GdlTerm;
import util.gdl.model.SentenceModel.SentenceForm;
import util.gdl.transforms.DeORer;
import util.gdl.transforms.LegalSplitter;
import util.gdl.transforms.VariableConstrainer;
import util.statemachine.Role;
public class MoveMutexFinder {
private static final GdlConstant LEGAL = GdlPool.getConstant("legal");
public static Set<Mutex> findMutexes(List<Gdl> description) {
//The kind of logic we're using is as follows:
//First, we need to know the flow of the game, as in
//what happens regardless of the players' states.
//Then we find sentence forms for roles that only one player
//can make at a time. We can automatically add all the mutexes
//involving a specific player. However, the really interesting
//mutexes are those that have a variable player; i.e., only one
//player can make this move type at any given time. This can be
//very useful to know. For example, with a little extra
//information, we can often figure out whether this is
//an alternating game, solely from analysis of the GDL.
//For each player, we record on which turns they can make
//roles of a given sentence type. This comes from examining
//all the rules that could make roles of that type legal.
//For now, we assume that some sentence defined by the flow
//of the game (or its constants) is directly present in each
//rule defining the move's sentence type.
//These transformations help...
description = DeORer.run(description);
description = VariableConstrainer.replaceFunctionValuedVariables(description);
description = LegalSplitter.run(description);
SentenceModel model = new SentenceModel(description);
GameFlow flow = new GameFlow(description);
//What operations would I use on it?
Map<SentenceForm, Map<Role, Set<Integer>>> moveTurnsByForm = new HashMap<SentenceForm, Map<Role, Set<Integer>>>();
for(SentenceForm form : model.getSentenceForms()) {
if(form.getName().equals(LEGAL)) {
//Look for rules with this head
Set<GdlRule> generatingRules = model.getRules(form);
if(!moveTurnsByForm.containsKey(form))
moveTurnsByForm.put(form, new HashMap<Role, Set<Integer>>());
Map<Role, Set<Integer>> moveTurnsByRole = moveTurnsByForm.get(form);
for(GdlRule rule : generatingRules) {
//We have some rule defining some move as legal.
//First we get the appropriate set of turns.
GdlTerm playerName = rule.getHead().get(0);
if(!(playerName instanceof GdlConstant))
throw new RuntimeException("LegalSplitter failed on rule " + rule + " (reported by MoveMutexFinder)");
Role role = new Role((GdlProposition)playerName.toSentence());
if(!moveTurnsByRole.containsKey(role))
moveTurnsByRole.put(role, new HashSet<Integer>());
Set<Integer> moveTurns = moveTurnsByRole.get(role);
Set<Integer> curMoveTurns = flow.getTurnsConjunctsArePossible(rule.getBody());
moveTurns.addAll(curMoveTurns);
}
//Handle relations
Set<GdlRelation> legalRelations = model.getRelations(form);
for(GdlRelation relation : legalRelations) {
GdlTerm playerName = relation.get(0);
Role role = new Role((GdlProposition)playerName.toSentence());
if(!moveTurnsByRole.containsKey(role))
moveTurnsByRole.put(role, new HashSet<Integer>());
Set<Integer> moveTurns = moveTurnsByRole.get(role);
//The move is always possible
Set<Integer> curMoveTurns = flow.getCompleteTurnSet();
moveTurns.addAll(curMoveTurns);
}
}
}
//Now we have moveTurnsByForm filled, but we need to process
//it to figure out what the actual mutexes are
Set<Mutex> mutexes = new HashSet<Mutex>();
for(Entry<SentenceForm, Map<Role, Set<Integer>>> entry : moveTurnsByForm.entrySet()) {
SentenceForm form = entry.getKey();
Map<Role, Set<Integer>> moveTurnsByRole = entry.getValue();
//What does it take to add the sentence form with a
//variable player? All the sets of move IDs of all the
//players must be disjoint.
boolean allDisjoint = true;
Set<Integer> allTurnsSoFar = new HashSet<Integer>();
for(Set<Integer> moveTurns : moveTurnsByRole.values()) {
if(!Collections.disjoint(moveTurns, allTurnsSoFar)) {
allDisjoint = false;
break;
}
allTurnsSoFar.addAll(moveTurns);
}
if(allDisjoint) {
//Make the mutex
Mutex mutex = new Mutex(form.getCopyWithName("does")); //all variables
mutexes.add(mutex);
}
}
return mutexes;
}
}