Sudoku solver not returning a completed board - python

import numpy as np
def row(board,x,y):
"""Takes in a board and x y coordinates and returns all
the row candidates"""
numbers = np.arange(1,10)
missing = []
for number in numbers:
if number not in board[x]:
missing.append(number)
else:
continue
return missing
def column(board,x,y):
"""Takes an incomplete board and returns all the
column candidates"""
numbers = np.arange(1,10)
missing = []
for number in numbers:
if number not in board[:,y]:
missing.append(number)
else:
continue
return missing
def box(board,x,y):
"""Takes an incomplete board, coordinates and returns the
box wise candidates"""
numbers = list(range(1,10))
missing = []
box1 = np.array([board[0,0:3],board[1,0:3],board[2,0:3]])
box2 = np.array([board[0,3:6],board[1,3:6],board[2,3:6]])
box3 = np.array([board[0,6:10],board[1,6:10],board[2,6:10]])
box4 = np.array([board[3,0:3],board[4,0:3],board[5,0:3]])
box5 = np.array([board[3,3:6],board[4,3:6],board[5,3:6]])
box6 = np.array([board[3,6:10],board[4,6:10],board[5,6:10]])
box7 = np.array([board[6,0:3],board[7,0:3],board[8,0:3]])
box8 = np.array([board[6,3:6],board[7,3:6],board[8,3:6]])
box9 = np.array([board[6,6:10],board[7,6:10],board[8,6:10]])
condition1 = 0<=x<=2 and 0<=y<=2
condition2 = 0<=x<=2 and 3<=y<=5
condition3 = 0<=x<=2 and 6<=y<=8
condition4 = 3<=x<=5 and 0<=y<=2
condition5 = 3<=x<=5 and 3<=y<=5
condition6 = 3<=x<=5 and 6<=y<=8
condition7 = 6<=x<=8 and 0<=y<=2
condition8 = 6<=x<=8 and 3<=y<=5
condition9 = 6<=x<=8 and 6<=y<=8
if condition1:
for number in numbers:
if number not in box1:
missing.append(number)
else:
continue
if condition2:
for number in numbers:
if number not in box2:
missing.append(number)
else:
continue
if condition3:
for number in numbers:
if number not in box3:
missing.append(number)
else:
continue
if condition4:
for number in numbers:
if number not in box4:
missing.append(number)
else:
continue
if condition5:
for number in numbers:
if number in box5:
continue
else:
missing.append(number)
if condition6:
for number in numbers:
if number not in box6:
missing.append(number)
else:
continue
if condition7:
for number in numbers:
if number not in box7:
missing.append(number)
else:
continue
if condition8:
for number in numbers:
if number not in box8:
missing.append(number)
else:
continue
if condition9:
for number in numbers:
if number not in box9:
missing.append(number)
else:
continue
return missing
def sudsolver(board):
"""Give it a board andt it will solve it for your using the functions defined above."""
newboard = board.copy()
for index,n in np.ndenumerate(board):
if board[index] == 0:
x = index[0]
y = index[1]
boxsolution = box(newboard,x,y)
columnsolution = column(newboard,x,y)
rowsolution = row(newboard,x,y)
if len(boxsolution) or len(columnsolution) or len(rowsolution) < 1:
for number in boxsolution:
if number in columnsolution and number in rowsolution:
newboard[index] = number
else:
continue
else:
continue
return newboard
testboard = np.array([
[0, 0, 0, 3, 7, 0, 0, 5, 6],
[5, 0, 0, 0, 1, 0, 9, 7, 0],
[0, 6, 0, 9, 8, 0, 3, 4, 0],
[0, 0, 7, 0, 0, 2, 0, 8, 0],
[0, 0, 9, 0, 3, 0, 6, 0, 0],
[0, 5, 0, 8, 0, 0, 2, 0, 0],
[0, 7, 5, 0, 6, 9, 0, 2, 0],
[0, 4, 8, 0, 2, 0, 0, 0, 5],
[2, 9, 0, 0, 5, 8, 0, 0, 0]
])
pleasework = sudsolver(testboard)
I have created different functions to check the solutions of a given cell, however, when the board is returned from the sudsolver function, the board still has a few 0's in it.
The board which I am using is called testboard and I have been assured that it is possible to solve.
I cannot think of why this is and how to solve it. Any help would be great.

Related

finding possible entries in Sudoku empty cells

I have been packaging a script to calculate all possible entries in the empty cells of a sudoku game. While the algorithm to screen the vertical column and the horizontal row works, it seems that my script is not able to screen the relevant box where the empty cell is located.
The code that I am using is the following:
def possible(y,x,n):
global grid
for i in range(0,9):
if grid[y][i] == n:
return False
for i in range(0,9):
if grid[i][x] == n:
return False
x0 = (x//3)*3
y0 = (y//3)*3
for i in range(0,3):
for j in range(0,3):
if (grid[y0+i][x0+j] == n):
#print((x0+j),end=' ')
#print((y0+i),end=' ')
return False
list.append(y+1)
list.append(x+1)
list.append(n)
return True
It seems that there is some problem with the append procedure.....
Any assistance is welcome
My general comments:
Seems like you are trying to append to a list which might or might
not be defined outside of the possible() function (it's not in the supplied code).
However, as it is
not defined within the scope of that function, you generally can't
access it from the inside. Related read.)
Also you should change a variable name as
list is a built-in type of Python and it is not
recommended to use builtin types as variable
names unless you absolutely need to do so for some reason.
Generally it is not a best practice to use global variables.
My suggestion would be to move the gathering of possible
numbers outside of this function. Example:
def possible(grid: list[list[int]], num: int, pos: tuple[int, int]) -> bool:
# Check row
if num in grid[pos[0]]:
return False
# Check column
if num in [item[pos[1]] for item in grid]:
return False
# Check box
box_x = pos[1] // 3
box_y = pos[0] // 3
for i in range(box_y * 3, box_y * 3 + 3):
if num in grid[i][box_x * 3: box_x * 3 + 3] \
and (i, grid[i].index(num)) != pos:
return False
return True
Then run this 'cleaner' function in a for loop or list comprehension to collect possible numbers for a given position on the Sudoku grid.
For example (for cycle):
possible_values = []
for i in range(1,10):
if possible(grid, i, (x, y)):
possible_values.append(i)
Or this (list comprehension):
possible_values = [n for n in range(1,10) if possible(grid, n, (0, 2))]
In the Sudoku game world one call the "possible entries" of the empty cells, candidates or pencil-marks.
Here is how we can identify the candidates of the empty cells.
grid = [
[0, 0, 0, 6, 0, 8, 9, 1, 0],
[6, 0, 2, 0, 9, 0, 3, 4, 0],
[1, 9, 8, 3, 0, 0, 0, 6, 7],
[0, 5, 9, 0, 0, 0, 4, 2, 3],
[4, 0, 0, 8, 0, 3, 0, 0, 1],
[7, 1, 3, 0, 2, 0, 8, 0, 0],
[9, 6, 0, 5, 3, 7, 2, 8, 0],
[2, 0, 0, 4, 1, 0, 0, 3, 0],
[3, 4, 0, 2, 8, 0, 1, 7, 9],
]
candidates=bytearray(729)
def identifyCandidates():
for cell in range(81):
row,col=divmod(cell,9)
if grid[row][col] == 0 :
for kan in range(1,10):
if not clueInSector(cell, kan):
candidates[9*cell+(kan - 1)]=1
def clueInSector(cell, clue):
cellrow,cellcol = divmod(cell,9)
for col in range(9):
if (col != cellcol and grid[cellrow][col] == clue) :
return True
for row in range(9):
if (row != cellrow and grid[row][cellcol] == clue) :
return True
rowB = 3 * ((cellrow) // 3)
colB = 3 * ((cellcol) // 3)
for row in range (rowB,rowB + 3) :
for col in range(colB,colB +3) :
if (col != cellcol and row != cellrow and grid[row][col] == clue) :
return True
return False
Print of the 13 first cells:
cell:0 candidates: 5.
cell:1 candidates: 3.7.
cell:2 candidates: 4.5.7.
cell:3 no candidate.
cell:4 candidates: 4.5.7.
cell:5 no candidate.
cell:6 no candidate.
cell:7 no candidate.
cell:8 candidates: 2.5.
cell:9 no candidate.
cell:10 candidates: 7.
cell:11 no candidate.
cell:12 candidates: 1.7.

Python: How can I turn every negative number into a zero?

I'm trying an exercise that wants me to return a new list that contains all the same elements except the negative numbers which are turned into zeros in the returned list.
I have used a for loop to loop through the parameter list and if the number is below 0, I would append it to a new list but times it by 0. However, I get weird outputs such as empty lists. For example, the code below should print:
[0, 0, 9, 0, 0, 34, 1]
[9, 34, 1]
[0, 0, 0]
Please stick to using list methods thanks.
The code:
def get_new_list_no_negs(num_list):
new_list = []
for i in range(len(num_list)):
if i < 0:
new_list.append(num_list[i] * 0)
return new_list
def main():
print("1.", get_new_list_no_negs([-3, -6, 9, 0, 0, 34, 1]))
print("2.", get_new_list_no_negs([9, 34, 1]))
print("3.", get_new_list_no_negs([-9, -34, -1]))
main()
This should do:
def get_new_list_no_negs(num_list):
return [max(num, 0) for num in num_list]
the max function is a python builtin that will return the largest between the passed numbers.
Try this
l = [-2, -1, 0, 1, 2]
# this
l = [i for i in l if i > 0 else 0]
# or
l = [max(i, 0) for i in l]
The enumerate() function adds a counter to an iterable.
So for each element in a cursor, a tuple is produced with (counter, element); the for loop binds that to row_number and row, respectively.
l = [-2, -1, 0, 1, 2]
for index, value in enumerate(l):
if value < 0:
l[index] = 0
print(l)
O/P:
[0, 0, 0, 1, 2]

Why is my Sieve of Eratosthenes removing the non-prime numbers?

My code:
def sieve(list1):
not_prime = set()
primes = []
for i in range(2, list1+1):
if i in not_prime:
continue
for x in range(i*2, list1+1, i):
not_prime.add(x)
primes.append(i)
return primes
I am trying to keep the non-prime numebrs and change it to 0. Where am I going wrong? The output doesn't even show any other number besides the prime numbers.
Well in that case, cant you just append zero instead of continue in the no_prime clause?
def sieve(list1):
not_prime = set()
primes = []
for i in range(2, list1+1):
if i in not_prime:
primes.append(0);
continue;
for x in range(i*2, list1+1, i):
not_prime.add(x)
primes.append(i)
return primes
sieve(27)
# output [2, 3, 0, 5, 0, 7, 0, 0, 0, 11, 0, 13, 0, 0, 0, 17, 0, 19, 0, 0, 0, 23, 0, 0, 0, 0]
Or if this is anything beyond a toy, you probably want to look into sparse arrays.

backtracking suduko solver python

I am coding a Suduko Solver in Python that uses back tracking, the logic I feel is correct but the problem I'm having is a traceback (I'm assuming something to do with typecasting) and keep getting the same traceback but I don't know what causes it:
Traceback (most recent call last):
File "a.py", line 78, in <module>
main()
File "a.py", line 75, in main
print(solveSudoku(0))
File "a.py", line 27, in solveSudoku
i,j = findNextCellToFill(grid, i, j)
File "a.py", line 4, in findNextCellToFill
if grid[x][y] == 0:
TypeError: 'int' object is not subscriptable
Here is my code:
def findNextCellToFill(grid, i, j):
for x in range(i,9):
for y in range(j,9):
if grid[x][y] == 0:
return x,y
for x in range(0,9):
for y in range(0,9):
if int(grid[x][y]) == 0:
return x,y
return -1,-1
def isValid(grid, i, j, e):
rowOk = all([e != grid[i][x] for x in range(9)])
if rowOk:
columnOk = all([e != grid[x][j] for x in range(9)])
if columnOk:
# finding the top left x,y co-ordinates of the section containing the i,j cell
secTopX, secTopY = 3 *(i/3), 3 *(j/3)
for x in range(secTopX, secTopX+3):
for y in range(secTopY, secTopY+3):
if grid[x][y] == e:
return False
return True
return False
def solveSudoku(grid, i=0, j=0):
i,j = findNextCellToFill(grid, i, j)
if i == -1:
return True
for e in range(1,10):
if isValid(grid,i,j,e):
grid[i][j] = e
if solveSudoku(grid, i, j):
return True
# Undo the current cell for backtracking
grid[i][j] = 0
return False
def print_grid(grid):
"""
A sloppy function to print the 9 x 9 sudoku grid
so it's a bit easier to visualize
"""
n = len(grid)
for row_ind, row in enumerate(grid):
if row_ind % 3 == 0:
print("-----------------------------")
for col_ind, val in enumerate(row):
if col_ind == 8:
print(" ", val, "|")
elif col_ind % 3 == 0:
print("|", val, end="")
else:
print(" ", val, end="")
print("-----------------------------")
def main():
"""
A test instance for the Sudoku Solver
"""
# here is an easy sample grid. 0 is used for a blank.
# each row, column, and three by three subgrid should contain
# one of each number from 1 to 9
grid = [[0, 0, 8, 9, 3, 0, 0, 1, 0],
[0, 0, 5, 0, 0, 6, 3, 7, 0],
[3, 7, 0, 0, 2, 5, 0, 8, 0],
[0, 0, 0, 0, 0, 0, 0, 6, 0],
[9, 2, 1, 4, 0, 3, 8, 5, 7],
[0, 3, 0, 0, 0, 0, 0, 0, 0],
[0, 6, 0, 5, 9, 0, 0, 4, 8],
[0, 9, 2, 6, 0, 0, 5, 0, 0],
[0, 5, 0, 0, 1, 4, 9, 0, 0]
]
print_grid(grid)
print(solveSudoku(0))
if __name__ == '__main__':
main()
Look carefully at your traceback. Near the bottom of your code you have this:
print_grid(grid)
print(solveSudoku(0))
So, you are calling solveSudoku with grid = 0 which is an integer. Don't you want to call it with grid?
You sent 0 to the solveSudoku function at line 76, in the main function:
print_grid(grid)
print(solveSudoku(0))
Send the grid seems to be a solution:
print(solveSudoku(grid))
Now, the error is:
Traceback (most recent call last):
File "p.py", line 79, in <module>
main()
File "p.py", line 76, in main
print(solveSudoku(grid))
File "p.py", line 31, in solveSudoku
if isValid(grid,i,j,e):
File "p.py", line 19, in isValid
for x in range(secTopX, secTopX+3):
TypeError: 'float' object cannot be interpreted as an integer
It is because you are trying to compare 1 to [1]
Modify first 3 lines of isValid function as below
def isValid(grid, i, j, d):
e = []
e.append(d)

Trying to print vertically in Python

I am trying to create an image that needs to be printed vertically:
From the for loop, I can print a image fine by indenting to a new line; however, I want the image to rotate counter clockwise 90 degrees (Is this transpose?).
I tried to use from itertools import zip_longest but it gives:
TypeError: zip_longest argument #1 must support iteration
class Reservoir:
def __init__(self,landscape):
self.landscape = landscape
self.image = ''
for dam in landscape:
self.image += '#'*dam + '\n'
print(self.image)
landscape = [4, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1, 1, 2, 5, 6, 5, 2, 2, 2, 3, 3, 3, 4, 5, 3, 2, 2]
lake = Reservoir(landscape)
print(lake)
I don't know if you will find a function or a lib that do that for you. But you can code this rotation by hand.
You don't want to display a real image here, but to print chars that represents a landscape. You have to print the "image" line by line, but since your landscape array represents the number of '#' you want in each column, you have to loop over the total number of lines you want, and for each char in that line, print a ' ' or a '#' depending on the corresponding landscape column value
With
h = max(landscape)
you calculate the total number of lines you want to print by finding the max of the landscape values.
Then, you loop over theses lines
for line in reversed(range(h)):
in that loop, line takes values 6, 5, 4, etc.
For each line, you have to loop over the whole landscape array to determine, for each column if you want to print a space or a '#', depending on the value of the landscape column (v) and the current line
for v in self.landscape:
self.image += ' ' if line >= v else '#'
The full program:
class Reservoir:
def __init__(self, landscape):
self.landscape = landscape
h = max(landscape)
self.image = ''
for line in reversed(range(h)):
for v in self.landscape:
self.image += ' ' if line >= v else '#'
self.image += '\n'
landscape = [4, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 5, 6, 5, 2, 2, 2, 3, 3, 3, 4, 5, 3, 2, 2]
lake = Reservoir(landscape)
print(lake.image)
The result:
#
### #
# ### ##
## ### ######
#### ###############
###### #################

Categories