Python Forum

Full Version: Help with sudoku
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Just for learning purposes, I am trying to make a script that returns the candidates of every empty cell (I have just put 0) in a sudoku.

I just try to recollect all the numbers in the corresponding line, column and square (the 3x3 region) and get the numbers not included, this is, the candidates for that cell.

For some reason, as I explain in the script itself, the main loop (at the bottom) doesn´t work.
It seems to work fine the first time for any i, j parameters, but starts to fail in the following ones.

I have included some "debug printing" lines (now commented) to check what is happening, and it seems that the first time the "acumulator" function is called, it modifies the original sudoku.
I don´t understand why this happens, for inside the function I am working all the time with a copy of the sudoku list and, as far as I know, it shouldn´t change.

In this image you can see the sudoku and the candidates that the script should return.
[Image: sudoku.jpg]


Any help or advice would be very welcome.
Many thanks.

def acumulator(sudoku, i, j): # Retruns possible candidates for every cell.
	
	sudoku_copy = sudoku.copy() # Makes copy of sudoku list object, for not altering it.

	# acum acumulates values of the line i.
	acum = sudoku_copy[i] 
	
	#print("Debug: acum + line:", acum)

	# acum acumulates values of the column j.
	for n in range(9):
		acum.append(sudoku_copy[n][j]) 

	#print("Debug: acum + column:", acum)

	# acum acumulates values of corresponding square (the 3x3 subsquares that form the sudoku).
	# First line
	if i <= 2 and j <= 2:
		for f in range(3):
			for c in range(3):
				acum.append(sudoku_copy[f][c])

	elif i <= 2 and j <= 5:
		for f in range(3):
			for c in range(3, 6):
				acum.append(sudoku_copy[f][c])

	elif i <= 2 and j <= 8:
		for f in range(3):
			for c in range(6, 9):
				acum.append(sudoku_copy[f][c])

	# Second line:
	elif i > 2 and i <= 5 and j <= 2:
		for f in range(3, 6):
			for c in range(3):
				acum.append(sudoku_copy[f][c])

	elif i > 2 and i <= 5 and j <= 5:
		for f in range(3, 6):
			for c in range(3, 6):
				acum.append(sudoku_copy[f][c])

	elif i > 2 and i <= 5 and j <= 8:
		for f in range(3, 6):
			for c in range(6, 9):
				acum.append(sudoku_copy[f][c])

	# Third line:
	elif i > 5 and j <= 2:
		for f in range(6, 9):
			for c in range(3):
				acum.append(sudoku_copy[f][c])

	elif i > 5 and j <= 5:
		for f in range(6, 9):
			for c in range(3, 6):
				acum.append(sudoku_copy[f][c])

	elif i > 5 and j <= 8:
		for f in range(6, 9):
			for c in range(6, 9):
				acum.append(sudoku_copy[f][c])

	#print("Debug: acum + square:", acum)

	possible_values = [1, 2, 3, 4, 5, 6, 7, 8, 9]

	# candidates recollects values not present in acum (this is, not present in the corresponding line, column or square)
	candidates = []

	for a in range(9):
		if not(possible_values[a] in acum):
			candidates.append(possible_values[a])		

	return candidates

def main():

	sudoku =[[5,0,9,0,0,0,4,0,0],
			[7,0,8,3,0,4,9,0,0,],
			[6,0,1,0,0,0,7,3,0],
			[4,6,2,5,0,0,0,0,0,],
			[3,8,5,7,2,0,6,4,9],
			[1,0,7,4,0,8,2,0,0],
			[2,0,0,1,0,0,0,0,4],
			[0,0,3,0,4,0,0,8,7,],
			[0,7,0,0,5,3,0,0,6]]

	# The functions works fine when invoked once:
	# val = acumulator(sudoku, 0, 1) # así sí funciona
	# print(val)

	# But starts failing when invoked again:
	# val = acumulator(sudoku, 0, 3) # así sí funciona
	# print(val)

	# it also fails when included in this loop for checking the candidates of all the cells.

	for i in range(9): # Con esto falla, no sé por qué
		for j in range(9):
			if sudoku[i][j] == 0:
				print("line.", i, " col.", j, " candidates:", sep ="", end =" ")
				val = acumulator(sudoku, i, j)
				print(val)
				
				#print("Debug: sodoku[i]:", sudoku[i])

			else:
				continue

main()
possible_values = [1, 2, 3, 4, 5, 6, 7, 8, 9]

def rowvals(board, index):
    """Return values that appear in row[index]"""
    return board[index]

def colvals(board, index):
    """Return values that appear in column[index]"""
    return [row[index] for row in board]

def squarevals(board, row, column):
    """Return values that appear in the square containing board[row, column]"""
    row = (row // 3) * 3
    column = (column // 3) * 3
    values = []
    for r in board[row : row+3]:
        values += r[column: column+3]
    return values

def available(board, r, c):
    used = set(rowvals(board, r) + colvals(board, c) + squarevals(board, r, c))
    return [c for c in possible_values if not c in used]

def main():
    sudoku =[[5,0,9,0,0,0,4,0,0],
            [7,0,8,3,0,4,9,0,0,],
            [6,0,1,0,0,0,7,3,0],
            [4,6,2,5,0,0,0,0,0,],
            [3,8,5,7,2,0,6,4,9],
            [1,0,7,4,0,8,2,0,0],
            [2,0,0,1,0,0,0,0,4],
            [0,0,3,0,4,0,0,8,7,],
            [0,7,0,0,5,3,0,0,6]]
 
    for r in range(9):
        for c in range(9):
            if sudoku[r][c] == 0:
                candidates = available(sudoku, r, c)
                print(r, c, candidates)
 
main()
Did not try the above, but want to point out that your algorithm is flawed as it stands, for example cell 3,7 should also have the possible value 7, which is indeed the correct value for that cell.
Sure, but that was my mistake making manually the image.

Many thanks, deanhystad.
That´s a much more advanced code. Very useful for learning.
Do you have any idea why mine worked well only for the first iteration but not the rest?
Many thanks again.
The line below does not initialize acum to the contents of sudoku_copy[i], it makes them the same. Appending anything to accum appends it to sudoku_copy[i]. This is why the first open cell in every row is correct and subsequent open cells are usually missing available values
    acum = sudoku_copy[i] # This is wrong.  Appending to acum appends to sudoku_copy

    acum = sudoku_copy[i].copy()  # This make a copy.  Change acum does not change sudoku_copy
This is what sudoku_copy looks like after processing the first empty cell:
Output:
[5, 0, 9, 0, 0, 0, 4, 0, 0, 0, 0, 0, 6, 8, 0, 0, 0, 7, 5, 0, 9, 7, 0, 8, 6, 0, 1] [7, 0, 8, 3, 0, 4, 9, 0, 0] [6, 0, 1, 0, 0, 0, 7, 3, 0] [4, 6, 2, 5, 0, 0, 0, 0, 0] [3, 8, 5, 7, 2, 0, 6, 4, 9] [1, 0, 7, 4, 0, 8, 2, 0, 0] [2, 0, 0, 1, 0, 0, 0, 0, 4] [0, 0, 3, 0, 4, 0, 0, 8, 7] [0, 7, 0, 0, 5, 3, 0, 0, 6]