package ttftcuts.physis.puzzle.oddoneout; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import ttftcuts.physis.utils.TPair; public class OddOneOutConstraintSolver { private static OddOneOutConstraintSolver instance; public static int[] symbolsPerLevel = {1,1,2,2,2,2,2,2,2,2}; public static int[] shapesPerLevel = {1,2,2,2,2,3,3,3,3,3}; public static int[] coloursPerLevel = {2,2,2,3,3,3,3,3,3,3}; public static boolean[] negativesPerLevel = {false,false,false,false,false,true,true,true,true,true}; public static List<List<OddOneOutOption>> permutations;// = new ArrayList<OddOneOutOption>(); public static void makePermutationList() { instance = new OddOneOutConstraintSolver(); permutations = new ArrayList<List<OddOneOutOption>>(); for (int level = 0; level < 10; level++) { List<OddOneOutOption> levelpermutations = new ArrayList<OddOneOutOption>(); //for (int n = OddOneOutProperty.numSymbols.min - 1; n < OddOneOutProperty.numSymbols.max; n++) { for (int n = 0; n < symbolsPerLevel[level]; n++) { makeSymbols(0, n+1, coloursPerLevel[level], shapesPerLevel[level], new ArrayList<TPair<Integer>>(), levelpermutations); } for(OddOneOutOption o : levelpermutations) { o.calculateProperties(); } permutations.add(levelpermutations); } } private static void makeSymbols(int d, int n, int nc, int ns, List<TPair<Integer>> sym, List<OddOneOutOption> states) { if (d >= n) { OddOneOutOption state = new OddOneOutOption(); for (int i=0; i<sym.size(); i++) { state.addSymbol(sym.get(i)); } states.add(state); } else { for (int x = 0; x < nc; x++) { for (int y = 0; y < ns; y++) { List<TPair<Integer>> news = new ArrayList<TPair<Integer>>(); news.addAll(sym); news.add(new TPair<Integer>(x,y)); makeSymbols(d+1, n, nc, ns, news, states); } } } } public static OddOneOutPuzzle buildPuzzle(int difficulty, int seed) { Random rand = new Random(seed); // add a seed! List<Integer> variables = new ArrayList<Integer>(); Map<Integer, List<OddOneOutOption>> domains = new HashMap<Integer, List<OddOneOutOption>>(); for (int i = 1; i<=6; i++) { List<OddOneOutOption> d = new ArrayList<OddOneOutOption>(permutations.size()); d.addAll(permutations.get(difficulty)); Collections.shuffle(d, rand); domains.put(i, d); variables.add(i); } CSP csp = instance.new CSP(variables, domains, instance.new Constraint(variables, negativesPerLevel[difficulty])); Map<Integer, OddOneOutOption> optmap = backtrackingSearch(csp, new HashMap<Integer,OddOneOutOption>()); List<OddOneOutOption> options = new ArrayList<OddOneOutOption>(); /*options.add(permutations.get(0)); options.add(permutations.get(1)); options.add(permutations.get(1)); options.add(permutations.get(1)); options.add(permutations.get(1)); options.add(permutations.get(1));*/ for(OddOneOutOption o : optmap.values()) { options.add(o); } return new OddOneOutPuzzle(options, csp.constraint.previousAnswer); } private class Constraint { public List<Integer> variables; public int previousAnswer = 0; public boolean allowNegatives = false; public Constraint(List<Integer> positions, boolean allowNegatives) { this.variables = positions; this.allowNegatives = allowNegatives; } public boolean isSatisfied(Map<Integer, OddOneOutOption> assignment) { if (assignment.size() != this.variables.size()) { return true; } Map<OddOneOutProperty, Map<Integer,Integer>> totals = new HashMap<OddOneOutProperty, Map<Integer,Integer>>(); Map<OddOneOutProperty, Map<Integer,OddOneOutOption>> lastStates = new HashMap<OddOneOutProperty, Map<Integer,OddOneOutOption>>(); for(OddOneOutProperty p : OddOneOutProperty.propertyList) { totals.put(p, new HashMap<Integer,Integer>()); lastStates.put(p, new HashMap<Integer,OddOneOutOption>()); for (int i=p.min; i<= p.max; i++) { totals.get(p).put(i, 0); lastStates.get(p).put(i, null); } } for(int key : assignment.keySet()) { OddOneOutOption s = assignment.get(key); for(OddOneOutProperty p : s.properties.keySet()) { int val = s.properties.get(p); totals.get(p).put(val, totals.get(p).get(val) + 1); lastStates.get(p).put(val, s); } } int odds = 0; OddOneOutOption oddstate = null; for(OddOneOutProperty p : totals.keySet()) { Map<Integer,Integer> data = totals.get(p); for (int number : data.keySet()) { int n = data.get(number); if (n==1 || (allowNegatives && n == 5)) { OddOneOutOption st = lastStates.get(p).get(number); if (oddstate != null && oddstate != st) { return false; } oddstate = st; odds++; } } } if (odds > 0) { int i = 0; for (OddOneOutOption s : assignment.values()) { if (s == oddstate) { this.previousAnswer = i; break; } i++; } } return odds > 0; } } private class CSP { final List<Integer> variables; final Map<Integer, List<OddOneOutOption>> domains; Constraint constraint; public CSP(List<Integer> variables, Map<Integer, List<OddOneOutOption>> domains, Constraint constraint) { this.variables = variables; this.domains = domains; this.constraint = constraint; } } private static Map<Integer, OddOneOutOption> backtrackingSearch(CSP csp, Map<Integer, OddOneOutOption> assignment) { if (assignment.size() == csp.variables.size()) { return assignment; } int variable = selectUnassignedVariable(assignment, csp); for(OddOneOutOption value : csp.domains.get(variable)) { Map<Integer, OddOneOutOption> oldAssignment = new HashMap<Integer, OddOneOutOption>(); oldAssignment.putAll(assignment); if (isConsistent(variable, value, assignment, csp)) { assignment.put(variable, value); Map<Integer, OddOneOutOption> result = backtrackingSearch(csp, assignment); if (result != null) { return result; } } assignment = oldAssignment; } return null; } private static int selectUnassignedVariable(Map<Integer, OddOneOutOption> assignment, CSP csp) { int maxRemainingValues = 0; int maxVariable = 0; for(int variable : csp.variables) { if (!assignment.containsKey(variable)) { if (csp.domains.get(variable).size() > maxRemainingValues) { maxRemainingValues = csp.domains.get(variable).size(); maxVariable = variable; } } } return maxVariable; } private static boolean isConsistent(int variable, OddOneOutOption value, Map<Integer, OddOneOutOption> assignment, CSP csp) { Map<Integer, OddOneOutOption> tempAssignment = new HashMap<Integer, OddOneOutOption>(); tempAssignment.putAll(assignment); tempAssignment.put(variable, value); return csp.constraint.isSatisfied(tempAssignment); } }