I am not an experienced programmer, and I decided to make a program that could find all possible sudoku arrangements just for fun. As far as I can see, the program is working all right, but I'd like to know how could I improve it, since there are some parts that are a little "polluted". Specially the way I deal with the blocks of the board.
#####################################################################################
### Sudoku Valid Boards Generator using recursive depth-first search approach ###
### by Anderson Freixo ###
### [email protected] ###
#####################################################################################
## The board is represented by lists of lists from board[0][0] to board[9][9].
## There are three functions which tests the rules for choosing a valid number in sudoku.
## Then, after we call the search function for the first time, and it checks for valid numbers
## it calls itself again adding the new valid number to the board.
## (The commented prints are just for debugging)
blocks = []
solutions = []
for n in range(3): #Initializes the 'blocks' list which contains the groups
for m in range(3): #of positions of each of the nine blocks of the game
blocks.append(list(itertools.product((0+3*n,1+3*n,2+3*n),(0+3*m,1+3*m,2+3*m)))) #the result is a list of 9 lists with 9 tuples each
#containing the position
#(I know it's really awkward, but I couldn't think of a clear
# and fast way of doing it)
#Functions to test the selected number:
def is_in_line(num, board, line): # #1 - Is the number already in the line?
if num in board[line]:
#print("Number", num, "already in the line!")
return True
def is_in_column(num, board, line, column): # #2 - Is the number already in the column?
for line_idx in range(len(board)):
if line_idx < line:
if board[line_idx][column] == num:
#print("Number", num, "already in the column!")
return True
def is_in_block(num, board, line, column): # #3 - Is the number already in the block?
global blocks
for block in blocks:
if (line, column) in block: # First finds out to which block the current position belongs to
myblock = block
break
for l, c in myblock: # Then, tests if the position is filled
if l < len(board) and c < len(board[l]): # and checks if the number in the position
if board[l][c] == num: # is the number being tested
#print("Number", num, "in the same block!")
return True
def search(board):
if len(solutions) > 1000: return # Just a random limit for the program to stop
# (there are more than 6x10**21 possible solutions)
global solutions # If the board is complete, than this board is a solution
if len(board) == 9 and len(board[8]) == 9:
print("A solution was found!")
solutions.append(board)
print("Solution n.", len(solutions),":")
print(board)
return
myboard = list(board) # I had to create this new board to be able to append a new line in the board
# without interfering in the original one. This is necessary when the
# length of the line is 9 because of the situation below.
next_line = len(board)-1 # Finds out which is the next position to evaluate
if len(board[next_line]) == 9: # Filling up one line at a time. If the line has been filled
next_line+=1 # (length = 9) then the position to be analyzed must be
myboard.append([]) # line+1 column 0.
next_column = 0 # (If I didn't append the empty list, there would be problems
else: # regarding the index range in the test functions)
next_column = len(board[next_line])
for n in range(1,10): # If any of the tests return True, skip to the next iteration
if is_in_line(n, myboard, next_line): continue
if is_in_column(n, myboard, next_line, next_column): continue
if is_in_block(n, myboard, next_line, next_column): continue
# Otherwise...
new_board = copy.deepcopy(myboard) # Create a new board with the valid number in it
new_board[next_line].append(n) # (Found it necessary to generate a deep copy, otherwise all the new numbers
search(new_board) # would be added to the same list)
# and then call the function again
return
search([[]]) # starts the search with an empty board