def minConflicts(csp, variables): """ Min-conflicts approach to solving the Sudoku puzzle """ assignment, domains = csp # Domains not applicable for this problem, but included for readability # Iterate through list, and randomly assign numbers for var in variables: # Randomly assign number num = random.randint(1,9) # Update the stack to include the new number, whether it works or not assignment[var[0]][var[1]] = num # Loop while the problem is not solved while variables != []: Graphics.showPuzzle(assignment) print variables # Randomly choose a variable from the list var = random.choice(variables) # Create value for least-conflict value bestValue, leastConstraints = None, sys.maxsize # Loop over possible domain values, and update best value if applicable for value in range(1,10): conflicts = countConflicts(assignment, var, value) if conflicts < leastConstraints: bestValue, leastConstraints = value, conflicts # Update the state with the new value assignment[var[0]][var[1]] = bestValue # If the variable does not violate any constraints, remove it from conflicted if leastConstraints == 0: variables.remove(var) print "Solution Found!" return assignment
def betterSimulatedAnnealing(csp, variables): """ Better Min-conflicts with a factor of randomness approach. Schedule is a mapping of time to temperature """ # Unpack the CSP assignment, domains = csp # Create Dictionary of conflicted variables and number of conflicts conflicted = {} # Iterate through list, randomly assign numbers, and add conflicted variables to array for var in variables: # Randomly assign number num = random.choice(domains[var]) # Update the stack to include the new number, whether it works or not assignment[var[0]][var[1]] = num for var in variables: conflicted[var] = countConflicts(assignment, var, assignment[var[0]][var[1]]) t = 1 while assessValue(assignment) != 0 and t < 1000000: print "Completion =", 100-assessValue(assignment),"%" changingVar = random.choice(conflicted.keys()) totalConflicts = assessValue(assignment) originalVal = assignment[changingVar[0]][changingVar[1]] newVal = random.choice(domains[changingVar]) assignment[changingVar[0]][changingVar[1]] = newVal newTotalConflicts = assessValue(assignment) deltaE = newTotalConflicts - totalConflicts if deltaE > 0: if random.random() > P(deltaE, T(t)): #reset to original assignment[changingVar[0]][changingVar[1]] = originalVal Graphics.showPuzzle(assignment) t+=1 for var in variables: conflicted[var] = countConflicts(assignment, var, assignment[var[0]][var[1]]) if t != 1000000: Graphics.showPuzzle(assignment) print "Solution Found!" return assignment else: Graphics.showPuzzle(assignment) print "Failed with Completion =", 100-assessValue(assignment),"%" return -1
def simulatedAnnealing(csp, variables, schedule): """ Min-conflicts with a factor of randomness approach. Schedule is a mapping of time to temperature """ # Unpack the CSP assignment, domains = csp # Create list of conflicted variables conflictedList = [] # Iterate through list, randomly assign numbers, and add conflicted variables to array for var in variables: # Randomly assign number num = random.randint(1,9) # Check if random number violates constraints, and add it to list if it does if not isConsistant(assignment, var, num): conflictedList.append(var) # Update the stack to include the new number, whether it works or not assignment[var[0]][var[1]] = num # Duplicate the variable for readability current = assignment # Initialize time variable t = 1 # Slowly increment the time while t < 100**100: # Set the temperature based on the passed schedule temp = schedule[t] # Cannot divide by zero - return the current assignment if temp == 0 or assessValue(current) == 0: return assignment # Randomly assign number num = random.randint(1,9) # Randomly choose a variable from the list (row, column) = random.choice(variables) # Get the random successor state, and duplicate assignment prospective = assignment prospective[row][column] = num # Find the change in E valueChange = assessValue(current) - assessValue(prospective) # If there is a positive energy change, then update the current state if valueChange > 0: current = prospective else: # With some probability (depending on change in E and temperature), step backwards if random.randint(1, math.e**(valueChange/temp)) == 1: current = prospective Graphics.showPuzzle(assignment) # Increment time t += 1 # Return failure return -1
def recursiveBacktracking(csp, conflicted): """ Recursive backtracking function that takes a CSP of the form (assignment, domains), and a list of not yet set variables (for efficiency) """ # If every variable is assigned, return assignment (solution) if not conflicted: return csp[0] # Unpack the CSP assignment, domains = csp # Enforce arc consistency modifiedDomains = AC3(domains) if GameController.arcConsistency else domains # Return failure if any of the domains are empty for v in modifiedDomains: if domains[v] == []: return -1 # Update the domains domains = modifiedDomains # Show the puzzle to the screen Graphics.showPuzzle(assignment) # Pop a variable (without any kind of order) variable = conflicted.pop() # Assign row and column for readability row, column = variable # Loop through all values in the variable's domain for value in domains[variable]: # Check if adding the value would break a constraint if isConsistant(assignment, variable, value): # Update the assignment assignment[row][column] = value # Implement forward checking, using the getArcs() method (not elegant, but reuses function) pruned = [] for arc in getArcs(domains, variable, False): # Make sure forward checking is turned on if not GameController.forwardChecking: continue # Get the connected variable, and its components var = arc[0] tailRow, tailColumn = var # Prune its domain, if it contains the value, and is not yet assigned if value in domains[var] and assignment[tailRow][tailColumn] == 0: domains[var].remove(value) pruned.append(var) # Create CSP tuple for recursive call csp = (assignment, domains) # Add the recursive call result = recursiveBacktracking(csp, conflicted) # Only return result if it is not failure if result != -1: return result # Reset the assignment assignment[row][column] = 0 # Reset forward-checking for var in pruned: domains[var].append(value) # Add back the variable that was popped off conflicted.append(variable) # If it is a dead-end, return failure return -1
if value in initialDomains[arc[0]]: initialDomains[arc[0]].remove(value) # Run initialization function init() # Create CSP for readability csp = (initialAssignment, initialDomains) # Fill the schedule for simulated annealing schedule = [] i = 0 while i < 100*100: schedule.append(i/100 + 1) i += 1 if GameController.simulatedAnnealing: solution = betterSimulatedAnnealing(csp, initialVariables) else: # Run the selected algorithm with the puzzle solution = backtrackingSearch(csp, initialVariables) if GameController.backtrackingSearch else minConflicts(csp, initialVariables) # Show the answer on the screen Graphics.showPuzzle(solution) # Pause until the user exits while True: time.sleep(1)