Apr-02-2018, 12:58 PM
*** UPDATES ***
- save function checks wether sudokufile dir already exists. if not -> makedir
- filepaths should be platform independent
- init function can now handle cases: where there are non sudoku.txt files in the dir, where sudoku.txt file does not contain a valid sudoku
- sudoku.txt files can also use "0" as " "
- progressbar implemented
- print button works
- main function changed the way you suggested
- the "check" button overwrites the original sudoku to the current sudoku. if you press check before save, then the sudoku.txt and the solution.txt file are identical. this should be relativly easy to fix
- implementing a stop button to interrupt the backtracking algorithm. this is probably a bit harder.
- making some kind of .exe file so that someone cand launch the program without using cmd or IDE
- ...
- save function checks wether sudokufile dir already exists. if not -> makedir
- filepaths should be platform independent
- init function can now handle cases: where there are non sudoku.txt files in the dir, where sudoku.txt file does not contain a valid sudoku
- sudoku.txt files can also use "0" as " "
- progressbar implemented
- print button works
- main function changed the way you suggested
# SudokuSolver_main.py if __name__ == '__main__': import SudokuSolver_class as Sudokuolver from tkinter import Tk def main(): root = Tk() app = Sudokuolver.SudokuSolver(root) root.mainloop() main()
# SudokuSolver_class.py import os import SudokuSolver_solver as Solver from random import randint from tkinter import ttk from tkinter import * from tkcallasync import tk_call_async, MULTIPROCESSING class SudokuSolver(Frame): disabled = False backtrack_ctr = 0 sudoku = [[" " for col in range(9)] for row in range(9)] solution = [[" " for col in range(9)] for row in range(9)] exampleSudoku = [[' ',' ',' ','2','1',' ',' ',' ',' '], [' ',' ','7','3',' ',' ',' ',' ',' '], [' ','5','8',' ',' ',' ',' ',' ',' '], ['4','3',' ',' ',' ',' ',' ',' ',' '], ['2',' ',' ',' ',' ',' ',' ',' ','8'], [' ',' ',' ',' ',' ',' ',' ','7','6'], [' ',' ',' ',' ',' ',' ','2','5',' '], [' ',' ',' ',' ',' ','7','3',' ',' '], [' ',' ',' ',' ','9','8',' ',' ',' ']] def __init__(self, parent): Frame.__init__(self, parent, name='frame') self.parent = parent self.initUI() self.blankSUDOKU() self.sudokudir = os.path.join(os.getcwd(), 'sudokus') def initUI(self): self.message_txt = StringVar() self.values = {} self.parent.title('Sudoku Solver') self.pack(fill=BOTH, expand=True) self.message = Label(self, textvariable=self.message_txt) self.message.grid(row=10,column=0,columnspan=9, sticky=E+W) self.solveBtn = Button(self, text="solve",width=6,command=self.execute_solver) self.solveBtn.grid(row=0,column=0) self.newBtn = Button(self, text="new",width=6,command=self.blankSUDOKU) self.newBtn.grid(row=0,column=1) self.initBtn = Button(self, text="init",width=6,command=self.initSUDOKU) self.initBtn.grid(row=0,column=2) self.printBtn = Button(self, text="print",width=6,command=self.printALL) self.printBtn.grid(row=0,column=8) self.checkBtn = Button(self, text="check",width=6,command=self.checkSOLUTION) self.checkBtn.grid(row=0,column=6) self.saveBtn = Button(self, text="save",width=6,command=self.saveSUDOKU) self.saveBtn.grid(row=0,column=7) self.pbar = ttk.Progressbar(self, mode='indeterminate') self.pbar.grid(row=11,column=0, columnspan=9, sticky=W+E) # --- clear all fields and delete all values in sudoku[][] and solution[][] --- # def blankSUDOKU(self): self.message_txt.set('') for row in range(9): for col in range(9): self.sudoku[row][col] = ' ' self.solution[row][col] = ' ' self.numEntry = Entry(self, width=5, justify=CENTER, font="Helvetica 12 bold") self.values[row,col] = self.numEntry self.numEntry.grid(row=row+1,column=col, ipady=5) # ----- get a random Sudoku from textfiles ----- # def initSUDOKU(self): self.blankSUDOKU() self.message_txt.set('') Files = [] valid = True for filename in os.listdir(self.sudokudir): if filename.startswith('sudoku') and filename.endswith('.txt'): Files.append(filename) L = len(Files) if L < 1: self.message_txt.set('No Sudokus saved, yet! Try this one') self.sudoku = self.copySUDOKU(self.exampleSudoku) else: # ----- fill sudoku array with values from textfile ----- # R = randint(0, L-1) sudokuFile = open(os.path.join(self.sudokudir,Files[R])) row, col = 0, 0 for value in sudokuFile.read(): if value.isdigit() or value==' ': if value=='0': value = ' ' # zeros can also be used as blanks self.sudoku[row][col] = value col += 1 if col==9: if row==8: break row += 1 col = 0 sudokuFile.close() valid = self.checkSUDOKU(self.sudoku)[0] if not valid: self.message_txt.set('%s does not contain a valid sudoku' % Files[R]) else: # ----- make values from random sudoku appear in the GUI ----- # for row in range(9): for col in range(9): value = self.sudoku[row][col] if value!=' ': self.values[row,col].insert(0,value) def printSUDOKU(self, matrix): for row in range(9): print("|-----------------------------------|") for col in range(9): print("| " + matrix[row][col] + " ",end='') print("|") print("|-----------------------------------|") def printALL(self): print('Sudoku:') self.printSUDOKU(self.sudoku) print('Solution:') self.printSUDOKU(self.solution) def saveSUDOKU(self): CHECK = self.checkSUDOKU(self.solution) if CHECK[0] and CHECK[1]: # check if Sudoku is valid and complete filecount = 1 # variable for how many sudoku files are already existing if not os.path.exists(self.sudokudir): os.makedirs(self.sudokudir) while os.path.isfile(self.path_to("sudoku%s.txt" % filecount)): filecount += 1 sudokuFile = open(self.path_to("sudoku%s.txt" % filecount), 'w') solutionFile = open(self.path_to("solution%s.txt" % filecount), 'w') # ----- create textfiles for sudoku and the solution ----- # for row in range(9): for col in range(9): sudokuFile.write(self.sudoku[row][col]) solutionFile.write(self.solution[row][col]) sudokuFile.write('\n') solutionFile.write('\n') sudokuFile.close() solutionFile.close() self.message_txt.set('Files saved...') else: self.message_txt.set('Sudoku must be valid and complete!') def copySUDOKU(self, matrix): matrix_copy = [[" " for col in range(9)] for row in range(9)] for row in range(9): for col in range(9): matrix_copy[row][col] = matrix[row][col] return matrix_copy # ----- check if user input makes sense -----# def checkSUDOKU(self, matrix): valid, complete, ctr = True, True, 0 for row in range(9): for col in range(9): value = matrix[row][col] if value!=' ': matrix[row][col]=' ' if not self.consistent(matrix, row, col, value): valid, complete = False, True return valid, complete, row, col matrix[row][col] = value ctr += 1 else: complete = False if ctr<17: valid = False return valid, complete # ----- check if solution is valid ----- # def checkSOLUTION(self): self.getinput(self.sudoku) CHECK = self.checkSUDOKU(self.sudoku) if CHECK[0] and CHECK[1]: self.message_txt.set("Solution correct!") elif not CHECK[1]: self.message_txt.set("Sudoku is not complete!") elif not CHECK[0]: self.message_txt.set("Mistake at row %s, col %s" % (CHECK[2], CHECK[3])) # ----- write user input into values{} ----- # def getinput(self, matrix): for row in range(9): for col in range(9): value = self.values[row,col].get() if value=='': matrix[row][col] = ' ' elif len(value)!=1: matrix[row][col] = ' ' elif not value.isdigit(): matrix[row][col] = ' ' elif int(value)<1 or int(value)>9: matrix[row][col] = ' ' else : matrix[row][col] = value # ----- check if this input leads to a valid solution ----- # def consistent(self, matrix, row, col, value): for i in range(9): if matrix[row][i]==value: return False if matrix[i][col]==value: return False rowStart = row - row%3 colStart = col - col%3 for m in range(3): for k in range(3): if matrix[rowStart+k][colStart+m]==value: return False return True def path_to(self, name): return os.path.join(self.sudokudir, name) def execute_solver(self): self.getinput(self.sudoku) # user input from GUI -> sudoku array if not self.checkSUDOKU(self.sudoku)[0]: # check if user input makes sense self.message_txt.set("This sudoku does not have a solution") else: if self.disabled: self.message_txt.set("warning, It's still calculating...") return def callback(result): self.disabled self.disabled = False status, self.solution = result self.pbar.stop() self.printSUDOKU(self.sudoku) self.printSUDOKU(self.solution) self.message_txt.set('Solution found!' if status else 'No Solution found!') self.fill_solution() self.disabled = True self.pbar.start(20) tk_call_async(self, Solver.do_solve, args=(self.sudoku,), callback=callback, method=MULTIPROCESSING) def fill_solution(self): for row in range(9): for col in range(9): self.values[row,col].delete(0,END) self.values[row,col].insert(0,self.solution[row][col])what I want to do/fix next:
- the "check" button overwrites the original sudoku to the current sudoku. if you press check before save, then the sudoku.txt and the solution.txt file are identical. this should be relativly easy to fix
- implementing a stop button to interrupt the backtracking algorithm. this is probably a bit harder.
- making some kind of .exe file so that someone cand launch the program without using cmd or IDE
- ...