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)