I made OOP sudoku generator - at this moment it generates only valid table. You can clear it and then generate over.
The case is I struggled with calling "fill_board" and "clear_board" form the button calss. So I made that class method, otherwise instance error appeard. It is finally working as intended but can you comment the code? I think it can be defined way easier.
from tkinter import * from random import randint
class Buttons:
"""
Initiate buttons and all of features
"""
def __init__(self, window, label, x, y, width=5, height=2, rowspan=1):
"""
Key variables,
Depending on the position of the button assing the command
"""
self.button_font = ('Verdana', 15)
self.parent = window
self.__pos_x = x
self.__pos_y = y
self.rowspan = rowspan
self.skip = []
self.height = height
self.width = width
self.container = Frame(self.parent)
self.container.grid(row=self.__pos_x, column=self.__pos_y)
if self.__pos_x <= 9 and self.__pos_y <= 9:
self.com = lambda: self.click()
self.num = label
self.text = self.num
else:
self.text = label
if self.text == Board._label[0]:
self.com = lambda: Board.fill_board(self)
else:
self.com = lambda: Board.new_board()
self.button()
def button(self):
"""
button method
"""
self.b = Button(
self.parent, text=self.text, width=self.width,
height=self.height, font=self.button_font,
command=self.com )
self.b.grid(row=self.__pos_x, column=self.__pos_y, rowspan = self.rowspan)
def click(self):
"""
Creates a window to input number for sudoku
"""
self.top=Toplevel(self.parent)
self.l=Label(self.top,text="Sudoku value")
self.l.pack()
self.e=Entry(self.top)
self.e.pack()
self.bi=Button(self.top,text='Ok',command=self.cleanup)
self.bi.pack()
def cleanup(self):
self.num = int(self.e.get())
self.b["text"] = self.num
self.top.destroy()
class Board():
"""
Board class
"""
size = 9
container = Tk()
_label = ["Generate sudoku", "Make empty board"]
def __init__(self):
"""
Generates empty board,
Adds two additionall buttons
Fills it out with buttons array
"""
self.button_font = ('Verdana', 15)
self.new_board()
self.skip = []
[Buttons(Board.container, Board._label[i], i*2, 12, 20, 5, 2) for i in range(len(Board._label))]
self.fill_board()
@classmethod
def new_board(cls):
"""
Class method to create empty board,
used to be instance method but could be called from button class as instance
"""
cls.board = [[Buttons(cls.container, 0, i, j) for i in range(cls.size)] for j in range(cls.size)]
def fill_board(self):
"""
method that backtracing alghoritm to generate sudoku numbers,
stored as button label
"""
if not Board.find_zero():
return True
for self.num_ in range(20):
self.num_ = randint(0, 9)
self.__pos_x, self.__pos_y = Board.find_zero()
# print(self.skip)
if Board.check_number(self):
Board.board[self.__pos_x][self.__pos_y].b["text"] = self.num_
self.skip = []
if Board.fill_board(self):
return True
self.skip = []
if self.__pos_y == 0:
self.__pos_x -= 1
self.__pos_y = 8
else:
self.__pos_y -= 1
self.skip.append(Board.board[self.__pos_x][self.__pos_y].b["text"])
Board.board[self.__pos_x][self.__pos_y].b["text"] = 0
return False
def check_number(self):
"""
check if number appears in row, column or 3x3 square
"""
self.col = []
for i in range(len(Board.board)):
self.col.append(Board.board[i][self.__pos_y].b["text"])
self.lil_sqr = []
for i in range(3):
for j in range(3):
self.lil_sqr.append(Board.board[(3*(self.__pos_x // 3)) + i][(3*(self.__pos_y // 3)) + j].b["text"])
self.row = []
for i in range(len(Board.board)):
self.row.append(Board.board[self.__pos_x][i].b["text"])
if self.num_ not in self.row \
and self.num_ not in self.col \
and self.num_ not in self.skip \
and self.num_ not in self.lil_sqr:
return True
self.skip.append(self.num_)
return False
@classmethod
def find_zero(cls):
"""
finds zero location
"""
for i in range(9):
for j in range(9):
if cls.board[i][j].b["text"] == 0:
return (i, j) # row, col
Board.container.title("grid method")
Board.container.geometry("1000x800")
sudoku = Board()
Board.container.mainloop()