from copy import deepcopy import random class sudoku_grid(): def __init__(self, prefilled_cells): # cell = {'possible': set([i for i in range(1,10)]), 'solution': 0} self.grid = [[{'possible': set([i for i in range(1,10)]), 'solution': 0} for _ in range(9)] for _ in range(9)] # prefilled_cells is a nested list (9x9) of values (1-9), 0 specifies an empty cell try: ctr = 0 if len(prefilled_cells) != 9: raise ValueError.add_note(f'wrong number of rows') for i in range(9): if len(prefilled_cells[i]) != 9: raise ValueError.add_note(f'wrong number of cells in row {i}') for j in range(9): # if prefilled_cell in valid range: fill in matching cell an mark as solved if 1 <= prefilled_cells[i][j] <= 9: self.grid[i][j]['possible'] = {prefilled_cells[i][j]} self.grid[i][j]['solution'] = prefilled_cells[i][j] ctr += 1 if ctr == 0: raise ValueError.add_note(f'prefilled_cells is empty') except ValueError as e: print(f'{e}') def __str__(self): retstr = 'sudoku: \n' for i in range(9): for j in range(9): if self.grid[i][j]['solution']: current_val = self.grid[i][j]['solution'] retstr += f'{current_val} ' else: retstr += '_ ' if j == 2 or j == 5: retstr += '# ' if i == 2 or i == 5: retstr += '\n# # # # # # # # # # # ' retstr += '\n' return retstr def removeValue(self, row, col, value): if self.grid[row][col]['possible']: self.grid[row][col]['possible'].discard(value) pass def iterate(self): # iterate over all cells for i in range(9): for j in range(9): # get the value current_value = self.grid[i][j]['solution'] if current_value: for k in range(9): self.removeValue(i, k, current_value) # remove value from current row self.removeValue(k, j, current_value) # remove value from current column for k in range(3): for l in range(3): self.removeValue((i//3)*3+(i+k)%3, (j//3)*3+(j+l)%3, current_value) # remove value from current 3x3 box current_set = set() add = 0 for k in range(8): current_set = current_set and self.grid[i][(j+k)%9]['possible'] current_set = current_set and self.grid[(i+k)%9][j]['possible'] if (i//3)*3+k//3 == i and (j//3)*3+k%3 == j: add = 1 l = k + add row = (i//3)*3+l//3 col = (j//3)*3+l%3 current_set = current_set and self.grid[row][col]['possible'] new_set = self.grid[i][j]['possible']-self.grid[i][j]['possible'].intersection(current_set) if new_set: self.grid[i][j]['possible'] = new_set for i in range(9): for j in range(9): if len(self.grid[i][j]['possible']) == 1: self.grid[i][j]['solution'] = list(self.grid[i][j]['possible'])[0] def find_lowest_entropy(self): lowest_i = -1 lowest_j = -1 sols = None lowest_e = 10 for i in range(9): for j in range(9): if self.grid[i][j]['solution']: continue e = len(self.grid[i][j]['possible']) if e < lowest_e: sols = list(self.grid[i][j]['possible']) lowest_i = i lowest_j = j lowest_e = e if lowest_e == 0: lowest_i = -1 lowest_j = -1 sols = None lowest_e = 10 return (lowest_i, lowest_j, lowest_e, sols) return (lowest_i, lowest_j, lowest_e, sols) def collapse_cell(self, row, col): if self.grid[row][col]['solution']: return None possible = self.grid[row][col]['possible'] if len(possible) == 1: self.grid[row][col]['solution'] = list(possible)[0] def single_solutions_exist(self): for i in range(9): for j in range(9): if self.grid[i][j]['possible']: if len(self.grid[i][j]['possible']) == 1: return True return False def is_solved(self): for i in range(9): for j in range(9): if not self.grid[i][j]['solution']: return False return True iteration = 1 if __name__ == '__main__': prefilled = [ [0,4,9,7,0,5,0,0,0], [0,0,0,0,0,4,0,0,3], [6,0,1,2,0,0,0,7,0], [0,0,0,0,9,1,0,0,5], [0,2,4,0,6,8,7,3,1], [1,5,8,0,2,7,4,9,0], [0,0,0,0,0,2,6,4,0], [0,6,0,1,0,0,0,0,0], [4,0,5,0,0,0,3,0,2]] sudoku = sudoku_grid(prefilled) print(sudoku) while not sudoku.is_solved(): sudoku.iterate() # cnt = 0 # [row, col, entropy, solutions] = sudoku.find_lowest_entropy() # if row == -1: # print(f'No solution found! Iteration {iteration}') # break # print(f'Lowest Entropy in Iteration {iteration} ({cnt}): {entropy} in ({row},{col}) with solutions {solutions}') # sudoku.collapse_cell(row, col) # while sudoku.single_solutions_exist(): # cnt += 1 # [row, col, entropy, solutions] = sudoku.find_lowest_entropy() # print(f'Lowest Entropy in Iteration {iteration} ({cnt}): {entropy} in ({row},{col}) with solutions {solutions}') # sudoku.collapse_cell(row,col) # print(sudoku) iteration += 1 print(sudoku)