backtracking suduko solver python - 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)

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.

how can I correct my code that will act as a sort function mimic in python

a_trial_list = [11, 965, 253, 7]
for i in range(len(a_trial_list) * 2):
for j in range(i):
if a_trial_list[j] > a_trial_list[j + 1]:
a_trial_list[j],a_trial_list[j + 1] = a_trial_list[j + 1], a_trial_list[j]
a_trial_list.append(1)
for x in a_trial_list:
if x == 1:
a_trial_list.remove(x)
print(a_trial_list)
# this code is returning [7, 11, 253, 965, 1, 1, 1, 1, 1]
# it should return [7, 11, 253, 956]
can someone please explain to me why it's not removing the last 1's ???
You can use a try-except clause to handle for Index Errors, instead of appending 1 every time, then removing it. Here is the code:
a_trial_list = [11, 965, 253, 7]
for i in range(len(a_trial_list) * 2):
for j in range(i):
try:
if a_trial_list[j] > a_trial_list[j + 1]:
a_trial_list[j],a_trial_list[j + 1] = a_trial_list[j + 1], a_trial_list[j]
except IndexError as e:
pass
print(a_trial_list)
If you just want to delete the 1 values from the list, you can use the following list comprehension:
a_trial_list[:] = [x for x in a_trial_list if x != 1]

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]

Sudoku solver not returning a completed board

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.

python find range of positive element in list

I have a list such as
[0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
I want to find start and end position where element are positive:
[[3,5],[9,12],[16,18]]
what is the best way to do this in python?
(build in function in python such as: find,lambda,itemgetter and so on.)
And lastly, regex version. ;)
input = [0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
input3 = str(list(
map(lambda i_x: i_x[0] * (i_x[1] and (1, -1)[i_x[1] < 0]), enumerate(input))
))
import re
s = re.sub(r'([\[ ]0[\],])+', ' ', input3)
s = s.replace(', ', '], [')
if s[-1:] != ']':
s = s[:-2] + ']'
s = '[' + s[2:]
s = re.sub(r' [0-9]+,', '', s)
output = list(eval(s))
print(output) # [[3, 5], [9, 12], [16, 18]]
Crude for solution. :(
input = [0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
output = []
pair = []
for i in range(len(input)):
if input[i] > 0:
if len(pair) > 1:
pair.pop()
pair.append(i)
else:
if pair:
output.append(pair)
pair = []
print(output) # [[3, 5], [9, 12], [16, 18]]
Not sure if ranges can go off the ends of the array or not.
def get_positive_ranges(a):
in_range = False
result = []
for i in range(len(a)):
if not in_range:
if a[i] > 0:
in_range = True
first = i
else: # Inside a range
if a[i] <= 0: # End of range
in_range = False
result.append([first, i - 1])
if in_range: # Tidy
result.append([first, i])
return result
print(get_positive_ranges([0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]))
print(get_positive_ranges([]))
print(get_positive_ranges([1]))
print(get_positive_ranges([0, 1]))
print(get_positive_ranges([0, 1, 0]))
Here is a numpy solution, not sure if this is better than a naive for-loop though; see inline comments for explanation.
import numpy as np
a = np.array([0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0])
# get indices of non-zero elements in a
nze = a.nonzero()[0]
# check where the differences of these indices are unequal to one; there you have a jump to/from 0
nze_diff = np.where(np.diff(nze) > 1)[0] + 1
# if a starts with 0, add the index 0
if nze_diff[0] != 0:
nze_diff = np.insert(nze_diff, 0, 0)
# store output
res = []
# loop through the indices and add the desired slices
for ix, i in enumerate(nze_diff):
try:
sl = nze[i:nze_diff[ix + 1]]
res.append([sl[0], sl[-1]])
# means we reached the end of nze_diff
except IndexError:
sl = nze[i:]
res.append([sl[0], sl[-1]])
If you run it for your a, you receive the desired output:
[[3, 5], [9, 12], [16, 18]]
There are probably smarter solutions than this, but this might get you started.
If you want to get the entire range, it simplifies a bit:
res2 = []
for ix, i in enumerate(nze_diff):
try:
res2.append(nze[i:nze_diff[ix + 1]])
except IndexError:
res2.append(nze[i:])
Then res2 would be:
[array([3, 4, 5]), array([ 9, 10, 11, 12]), array([16, 17, 18])]
This works
lst = [0, 0, 0, 12, 34, 86, 0, 0, 0, 95, 20, 1, 6, 0, 0, 0, 11, 24, 67, 0, 0, 0]
n = len(lst)
starting_points = [i for i in range(n) if lst[i] > 0 and (lst[i - 1] == 0 or i == 0)]
end_points = [next((i for i in range(j + 1, n) if lst[i] == 0), n) - 1 for j in starting_points]
print zip(starting_points, end_points)
output
[(3, 5), (9, 12), (16, 18)]
If performance is the key, you should test which implementaion is the most fast with your very long list. Anyway, this is 'no array acess by index' version, hopefully for boosting speed. And it uses map, lambda, index(find), if it pleases you. Though, of course it uses while.
input = [0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
output = []
input2 = list(map(lambda x: x and (1, -1)[x < 0], input)) # mapping by 'math.sign'-like func
start = end = 0
while end < len(input2):
try:
start = input2.index(1, end + 1)
end = input2.index(0, start) - 1
output.append([start, end])
except ValueError:
break
if start >= end:
output.append([start, len(input2) - 1])
print(output) # [[3, 5], [9, 12], [16, 18]]

Categories