/// <summary> /// Generates a board to play with /// </summary> public void CreateBoard() { matrix = new Cell[this.numTiles, this.numTiles]; heroList = new List<Hero>(); charInstances = new Dictionary<Guid, GameObject>(); cellInstances = new Dictionary<Guid, GameObject>(); boardHolder = this.transform; bool foundSolution = false; List<Assignment> assignments = new List<Assignment>(); while (!foundSolution) { matrix = InitializeBoard(matrix); matrix = GenerateRandomTiles(matrix); //Create a CSP to fill the remaining data List<Variable> vars = ConvertMatrixToVars(matrix); List<OverFloorType> values = Enum.GetValues(typeof(OverFloorType)).OfType<OverFloorType>().Where(o => Convert.ToInt32(o) >= 0).ToList(); CSP problem = new CSP(vars, values, toleranceFW, toleranceFM); //Use recursive backtracking to get a solution assignments = Search.RecursiveBacktrackingSearch(problem); if (assignments != null) foundSolution = true; } //Loop assignments foreach (var ass in assignments) { int x = (int)ass.variable.pos.x; int y = (int)ass.variable.pos.y; matrix[x,y].overFloor = ass.value; CreateTile(new Vector3(ass.variable.pos.x, ass.variable.pos.y), ass.value); } //Initialize heroes matrix = GenerateRandomHeroes(matrix); //Loop heroes to render foreach (var hero in heroList) { CreateHero(hero.Position, hero); } }
/// <summary> /// Loop all variables and values searching for a solution for the CSP /// </summary> /// <param name="problem"></param> /// <returns></returns> public static List <Assignment> RecursiveBacktrackingSearch(CSP problem) { if (problem.Goal()) { Debug.Log("Goal completed!"); return(problem.GetAssignments()); } //Loop unassigned var orderedVars = problem.OrderVariablesByMinimumRemainingValues(); foreach (var variable in orderedVars) { Debug.Log("Variable evaluated:" + variable.pos.ToString()); var orderedAssignments = problem.OrderAssignmentsByLeastRestrictingValues(variable); foreach (var assign in orderedAssignments) { Debug.Log("Value evaluated:" + assign.value.ToString()); //Assign value bool validResult = problem.AssignValue(assign.variable, assign.value); if (!validResult) { throw new Exception("Error getting available values"); } List <Assignment> validAssignments = null; validAssignments = RecursiveBacktrackingSearch(new CSP(problem)); if (validAssignments != null) { return(validAssignments); } else { Debug.Log("Backtracking value:" + assign.value.ToString()); problem.RemoveValue(assign.variable); } } } Debug.Log("Backtracking"); return(null); }
public CSP(CSP cspToBeCopied) { this.vars = new List <Variable>(); cspToBeCopied.vars.ForEach(v => this.vars.Add(new Variable(v.pos, v.val, v.remDomain))); this.domain = cspToBeCopied.domain.ToList(); this.neighbours = new Dictionary <Vector2, List <Variable> >(); foreach (var key in cspToBeCopied.neighbours.Keys) { var listNeigh = new List <Variable>(); foreach (var neighSour in cspToBeCopied.neighbours[key]) { var variableDest = this.vars.First(v => v.pos == neighSour.pos); listNeigh.Add(variableDest); } this.neighbours[key] = listNeigh; } this.assignments = new List <Assignment>(); foreach (var ass in cspToBeCopied.assignments) { var variableDest = this.vars.First(v => v.pos == ass.variable.pos); var assignDest = new Assignment(variableDest, ass.value); assignDest.domainPrune = new Dictionary <Vector2, List <OverFloorType> >(); foreach (var domPruneKey in ass.domainPrune.Keys) { var listOfdomainprune = new List <OverFloorType>(); foreach (var valuePruned in ass.domainPrune[domPruneKey]) { listOfdomainprune.Add(valuePruned); } assignDest.domainPrune[domPruneKey] = listOfdomainprune; } this.assignments.Add(assignDest); } this.toleranceFM = cspToBeCopied.toleranceFM; this.toleranceFW = cspToBeCopied.toleranceFW; this.MAC3 = cspToBeCopied.MAC3; }
public CSP(CSP cspToBeCopied) { this.vars = new List<Variable>(); cspToBeCopied.vars.ForEach(v=>this.vars.Add(new Variable(v.pos,v.val,v.remDomain))); this.domain = cspToBeCopied.domain.ToList(); this.neighbours = new Dictionary<Vector2, List<Variable>>(); foreach (var key in cspToBeCopied.neighbours.Keys) { var listNeigh = new List<Variable>(); foreach (var neighSour in cspToBeCopied.neighbours[key]) { var variableDest = this.vars.First(v => v.pos == neighSour.pos); listNeigh.Add(variableDest); } this.neighbours[key] = listNeigh; } this.assignments = new List<Assignment>(); foreach (var ass in cspToBeCopied.assignments) { var variableDest = this.vars.First(v => v.pos == ass.variable.pos); var assignDest = new Assignment(variableDest, ass.value); assignDest.domainPrune = new Dictionary<Vector2, List<OverFloorType>>(); foreach (var domPruneKey in ass.domainPrune.Keys) { var listOfdomainprune = new List<OverFloorType>(); foreach (var valuePruned in ass.domainPrune[domPruneKey]) { listOfdomainprune.Add(valuePruned); } assignDest.domainPrune[domPruneKey] = listOfdomainprune; } this.assignments.Add(assignDest); } this.toleranceFM = cspToBeCopied.toleranceFM; this.toleranceFW = cspToBeCopied.toleranceFW; this.MAC3 = cspToBeCopied.MAC3; }
private static List<OverFloorType> MAC3HeadTailChecker(Variable headVar, Variable tailVar, CSP problem) { var ret = new List<OverFloorType>(); //Loop remaining domain values var remainingDomains = tailVar.remDomain.ToList(); foreach (var remDomTail in remainingDomains) { int validTailValue = 0; problem.AssignValue(tailVar, remDomTail, false); var remDomainHead = headVar.remDomain.ToList(); foreach (var remDomHead in remDomainHead) { var alreadyAssigned = problem.IsVariableAssigned(headVar); if(!alreadyAssigned)problem.AssignValue(headVar, remDomHead, false); bool valid = problem.ValidateConstraints(); if (!valid) { validTailValue++; break; } if (!alreadyAssigned) problem.RemoveValue(headVar); } if (validTailValue == remDomainHead.Count()) ret.Add(remDomTail); problem.RemoveValue(tailVar); } return ret; }
/// <summary> /// Loop all variables and values searching for a solution for the CSP /// </summary> /// <param name="problem"></param> /// <returns></returns> public static List<Assignment> RecursiveBacktrackingSearch(CSP problem) { if (problem.Goal()) { Debug.Log("Goal completed!"); return problem.GetAssignments(); } //Loop unassigned var orderedVars = problem.OrderVariablesByMinimumRemainingValues(); foreach (var variable in orderedVars) { Debug.Log("Variable evaluated:" + variable.pos.ToString()); var orderedAssignments = problem.OrderAssignmentsByLeastRestrictingValues(variable); foreach (var assign in orderedAssignments) { Debug.Log("Value evaluated:" + assign.value.ToString()); //Assign value bool validResult = problem.AssignValue(assign.variable, assign.value); if (!validResult) throw new Exception("Error getting available values"); List<Assignment> validAssignments = null; validAssignments = RecursiveBacktrackingSearch(new CSP(problem)); if (validAssignments != null) { return validAssignments; } else { Debug.Log("Backtracking value:" + assign.value.ToString()); problem.RemoveValue(assign.variable); } } } Debug.Log("Backtracking"); return null; }
/// <summary> /// Maintenance Arc Consistency, this algorithm will check how many domain prunes are done when a assignemnt is executed /// </summary> /// <param name="assign">Assignment to be evaluated and updated</param> /// <param name="neighbours">List of neighbours to start with (difference between AC3 and MAC3)</param> /// <returns></returns> public static bool MAC3(ref Assignment assign,CSP problem) { var position = assign.variable.pos; var variableInLocalProblem = problem.vars.First(v => v.pos == position); //Add the assignment to the problem problem.AssignValue(variableInLocalProblem, assign.value,false); List<Variable> queueToPrune = new List<Variable>(); queueToPrune.Add(variableInLocalProblem); while (queueToPrune.Any()) { //Get the first variable var headVariable = queueToPrune.First(); queueToPrune.Remove(headVariable); //Get the neighbours of the variable List<Variable> neighbours = problem.GetNeighbours(headVariable).Where(n => !problem.IsVariableAssigned(n)).ToList(); //Loop over neighbours foreach (var neigh in neighbours) { //Get the list of values to delete for conflict List<OverFloorType> valuesToPrune = new List<OverFloorType>(); //Check if value is compatible with remaining values in head foreach (var valPruned in MAC3HeadTailChecker(headVariable, neigh, problem)) { if (!valuesToPrune.Any(v => v == valPruned)) valuesToPrune.Add(valPruned); } //Remove the value from the variable domain in temporal CSP and add to the domain prune foreach (var prunedVal in valuesToPrune) { //Remove domain from variable in problem problem.RemoveDomainElementFromVariable(neigh, prunedVal); //remove domain from variable in current variable neigh.removeDomainElement(prunedVal); if (!neigh.remDomain.Any()) return false; //Add prune action to assignment assign.AddDomainPrune(neigh.pos, prunedVal); } if (valuesToPrune.Any()) { queueToPrune.Add(neigh); } } } return true; }
/// <summary> /// Generates a random number of tiles using CSP constraint validaton /// </summary> /// <param name="matrix">Cell matrix to be filled</param> /// <returns>Cell matrix with random values generated</returns> private Cell[,] GenerateRandomTiles(Cell[,] matrix) { //Initialize a new random generator System.Random random = new System.Random(); //Get number of tiles var totalNumberOfTiles = this.numTiles * this.numTiles; var numberOfRandomTiles = totalNumberOfTiles * randomGeneratedTilesRate / 100; //List with all possible values List<OverFloorType> values = Enum.GetValues(typeof(OverFloorType)).OfType<OverFloorType>().Where(o => Convert.ToInt32(o) >= 0).ToList(); for (int i = 0; i < numberOfRandomTiles; i++) { bool generatedValue = false; //Loop until a valid random generated value has been created for this turn while (!generatedValue) { //Generate a random number for x and y var x = random.Next(numTiles); var y = random.Next(numTiles); //Only assign value to unassigned if (matrix[x, y].overFloor == OverFloorType.NONE) { //Generate a random value Array overfloortypes = Enum.GetValues(typeof(OverFloorType)); OverFloorType randomType = OverFloorType.NONE; while (randomType == OverFloorType.NONE) randomType = (OverFloorType)overfloortypes.GetValue(random.Next(0, overfloortypes.Length)); //Assign the value matrix[x, y].overFloor = randomType; List<Variable> vars = ConvertMatrixToVars(matrix); CSP problem = new CSP(vars, values, toleranceFW, toleranceFM); if (problem.ValidateConstraints()) { generatedValue = true; Debug.Log(string.Format("Position assigned:[{0},{1}] Value: {2}",x.ToString(),y.ToString(),randomType.ToString())); } else matrix[x, y].overFloor = OverFloorType.NONE; } } } return matrix; }
private static List <OverFloorType> MAC3HeadTailChecker(Variable headVar, Variable tailVar, CSP problem) { var ret = new List <OverFloorType>(); //Loop remaining domain values var remainingDomains = tailVar.remDomain.ToList(); foreach (var remDomTail in remainingDomains) { int validTailValue = 0; problem.AssignValue(tailVar, remDomTail, false); var remDomainHead = headVar.remDomain.ToList(); foreach (var remDomHead in remDomainHead) { var alreadyAssigned = problem.IsVariableAssigned(headVar); if (!alreadyAssigned) { problem.AssignValue(headVar, remDomHead, false); } bool valid = problem.ValidateConstraints(); if (!valid) { validTailValue++; break; } if (!alreadyAssigned) { problem.RemoveValue(headVar); } } if (validTailValue == remDomainHead.Count()) { ret.Add(remDomTail); } problem.RemoveValue(tailVar); } return(ret); }