# Name: Rushil Umaretiya # Date: 12/1/2020 import os, time, operator, copy def solve(puzzle, neighbors): ''' suggestion: # q_table is quantity table {'1': number of value '1' occurred, ...} variables, puzzle, q_table = initialize_ds(puzzle, neighbors) return recursive_backtracking(puzzle, variables, neighbors, q_table) ''' variables, puzzle, q_table = initialize_ds(puzzle, neighbors) return recursive_backtracking(puzzle, variables, neighbors, q_table) def initialize_ds(puzzle, neighbors): vars = {} q_table = {x: 0 for x in range(1, 10)} for i in range(len(puzzle)): if puzzle[i] == '.': vars[i] = list(range(1,10)) else: q_table[int(puzzle[i])] += 1 return vars, puzzle, q_table def recursive_backtracking(puzzle, variables, neighbors, q_table): if check_complete(puzzle, neighbors, q_table): return puzzle var = select_unassigned_var(puzzle, variables, neighbors) #for value in [x for _,x in sorted(zip([puzzle.count(str(i)) for i in range(1, 10)],list(range(1,10))), reverse=True)]: for quantity in sorted(q_table.items(), key=operator.itemgetter(1), reverse=True): value = quantity[0] if value in variables[var] and isValid(value, var, puzzle, neighbors): puzzle = puzzle[:var] + str(value) + puzzle[var + 1:] copy = update_variables(value, var, puzzle, variables, neighbors) #copy = {k: list(variables[k]) for k in variables} q_table[value] += 1 result = recursive_backtracking(puzzle, copy, neighbors, q_table) if result != None: return result puzzle = puzzle[:var] + '.' + puzzle[var + 1:] q_table[value] -= 1 def check_complete(puzzle, neighbors, q_table): if puzzle.find('.') != -1: return False for index in range(len(puzzle)): for neighbor in neighbors[index]: if puzzle[index] == puzzle[neighbor]: return False return True def select_unassigned_var(assignment, variables, csp_table): min_val, index = 9999, -1 for i in range(len(assignment)): if assignment[i] == '.': if len(variables[i]) < min_val: min_val = len(variables[i]) index = i return index def isValid(value, var_index, puzzle, neighbors): for i in neighbors[var_index]: if puzzle[i] == str(value): return False return True def update_variables(value, var_index, puzzle, variables, neighbors): updated = {k: list(variables[k]) for k in variables} for i in neighbors[var_index]: if i in updated and value in updated[i]: updated[i].remove(value) return updated def sudoku_neighbors(csp_table): # each position p has its neighbors {p:[positions in same row/col/subblock], ...} neighbors = {} for i in range(81): temp = [] for constraint in csp_table: if i in constraint: removed = list(constraint) removed.remove(i) temp += removed neighbors[i] = set(temp) return neighbors def sudoku_csp(n=9): csp_table = [[k for k in range(i*n, (i+1)*n)] for i in range(n)] # rows csp_table += [[k for k in range(i,n*n,n)] for i in range(n)] # cols temp = [0, 1, 2, 9, 10, 11, 18, 19, 20] csp_table += [[i+k for k in temp] for i in [0, 3, 6, 27, 30, 33, 54, 57, 60]] # sub_blocks return csp_table def checksum(solution): return sum([ord(c) for c in solution]) - 48*81 # One easy way to check a valid solution def main(): #filename = input("file name: ") filename = "" if not os.path.isfile(filename): filename = "puzzles.txt" csp_table = sudoku_csp() # rows, cols, and sub_blocks neighbors = sudoku_neighbors(csp_table) # each position p has its neighbors {p:[positions in same row/col/subblock], ...} start_time = time.time() for line, puzzle in enumerate(open(filename).readlines()): #if line == 50: break # check point: goal is less than 0.5 sec line, puzzle = line+1, puzzle.rstrip() print ("Line {}: {}".format(line, puzzle)) solution = solve(puzzle, neighbors) if solution == None:print ("No solution found."); break print ("{}({}, {})".format(" "*(len(str(line))+1), checksum(solution), solution)) print ("Duration:", (time.time() - start_time)) if __name__ == '__main__': main() """ 26.63564682006836 """