Python Backtracking (queens checkboard) - python

Ok so finally after hours and hours of struggling with my head I finally understood how backtracking works. Though I still think there are secrets left to be understood. Anyways, I'm trying to create the 4-queens problem on the checkboard. Practically I'm giving myself a blank checkboard (it's 4x4 actually) and I need to find 4 places where those queens won't attack themselves. A queen attacks the row, column and diagonal it belongs too.
here is what I've done so far (The valid func isn't complete yet, but it doesn't really matter. I just need the backtrack function to work).
def findNextCell(grid,i,j):
for index in range(i,4):
for jindex in range(j,4):
return index, jindex
for index in range(0,4):
for jindex in range(0,4):
if grid[index][jindex] == 0:
return index, jindex
return -1,-1
def isValid(grid, index, jindex, queen):
rowOk = all([queen != grid[index][x] for x in range(4)])
if rowOk:
columnOk = all([queen != grid[x][jindex] for x in range(4)])
if columnOk:
return True
return False
def queenSolve(grid, i=0, j=0, num_queens = 0):
i, j = findNextCell(grid, i, j)
print grid
if num_queens == 4:
return True
if isValid(grid, i, j, 1):
grid[i][j]=1
num_queens += 1
if (queenSolve(grid, i, j, num_queens)):
return True
grid[i][j]=0
return False
input = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
print queenSolve(input)

The first problem I see is the initial nested loops in this routine:
def findNextCell(grid,i,j):
for index in range(i,4):
for jindex in range(j,4):
return index, jindex
for index in range(0,4):
for jindex in range(0,4):
if grid[index][jindex] == 0:
return index, jindex
return -1,-1
This appears to be some early test code that never got cleared out:
for index in range(i,4):
for jindex in range(j,4):
return index, jindex
But unfortunately, it causes findNextCell() to always return the i and j it was passed, nothing more:
>>> findNextCell(0, 1, 2)
(1, 2)
>>> findNextCell(0, 3, 2)
(3, 2)
>>> findNextCell(0, 1, 1)
(1, 1)
>>>
The next problem I see is this pair of lines:
num_queens += 1
if (queenSolve(grid, i, j, num_queens)):
This is augmenting the number of queens, succeed or fail! Instead you probably want just one line:
if (queenSolve(grid, i, j, num_queens + 1)):
That is increase the number of queens on recursion but don't affect current number in case this fails.
The third problem I see is that this isn't a loop:
i, j = findNextCell(grid, i, j)
...
if isValid(grid, i, j, 1):
grid[i][j]=1
num_queens += 1
if (queenSolve(grid, i, j, num_queens)):
return True
grid[i][j]=0
You should be trying to place the current queen in all valid spaces until you run out of valid spaces, findNextCell() returns negative, or until the recursion succeeds. You try one location for the current queen and then give up. This problem is iterative on the current queen and recursive on the subsequent ones.

Related

Sudoku Backtracking Python to find Multiple Solutions

I have a code to solve a Sudoku recursively and print out the one solution it founds.
But i would like to find the number of multiple solutions.
How would you modify the code that it finds all possible solutions and gives out the number of solutions?
Thank you! :)
code:
board = [
[7,8,0,4,0,0,1,2,0],
[6,0,0,0,7,5,0,0,9],
[0,0,0,6,0,1,0,7,8],
[0,0,7,0,4,0,2,6,0],
[0,0,1,0,5,0,9,3,0],
[9,0,4,0,6,0,0,0,5],
[0,7,0,3,0,0,0,1,2],
[1,2,0,0,0,7,4,0,0],
[0,4,9,2,0,6,0,0,7]
]
def solve(bo):
find = find_empty(bo)
if not find:
return True
else:
row, col = find
for num in range(1,10):
if valid(bo, num, (row, col)):
bo[row][col] = num
if solve(bo):
return True
bo[row][col] = 0
return False
def valid(bo, num, pos):
# Check row
for field in range(len(bo[0])):
if bo[pos[0]][field] == num and pos[1] != field:
return False
# Check column
for line in range(len(bo)):
if bo[line][pos[1]] == num and pos[0] != line:
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):
for j in range(box_x * 3, box_x*3 + 3):
if bo[i][j] == num and (i,j) != pos:
return False
return True
def print_board(bo):
for i in range(len(bo)):
if i % 3 == 0 and i != 0:
print("- - - - - - - - - - - - - ")
for j in range(len(bo[0])):
if j % 3 == 0 and j != 0:
print(" | ", end="")
if j == 8:
print(bo[i][j])
else:
print(str(bo[i][j]) + " ", end="")
def find_empty(bo):
for i in range(len(bo)):
for j in range(len(bo[0])):
if bo[i][j] == 0:
return (i, j) # row, col
return None
if __name__ == "__main__":
print_board(board)
solve(board)
print("___________________")
print("")
print_board(board)
I already tried to change the return True term at the Solve(Bo) Function to return None/ deleted it(For both return Terms) that it continues…
Then the Algorithm continues and finds multiple solutions, but in the end fills out the correct numbers from the very last found solutions again into 0’s. This is the solution then printed out.
As asked:
How would you modify the code that it finds all possible solutions and gives out the number of solutions?
If you don't want to return ("give out") the solutions themselves, but the number of solutions, then you need to maintain a counter, and use the count you get back from the recursive call to update the owned counter:
def solve(bo):
find = find_empty(bo)
if not find:
return 1
count = 0
row, col = find
for num in range(1, 10):
if valid(bo, num, (row, col)):
bo[row][col] = num
count += solve(bo)
bo[row][col] = 0
return count
In the main program, you would no longer print the board, as you don't expect the filled board now, but a number:
print(solve(board)) # Will output 1 for your example board.
Getting all solutions
If you don't just want to know the count, but every individual solution itself, then I would go for a generator function, that yields each solution:
def solve(bo):
find = find_empty(bo)
if not find:
yield [row[:] for row in bo] # Make a copy
return
row, col = find
for num in range(1, 10):
if valid(bo, num, (row, col)):
bo[row][col] = num
yield from solve(bo)
bo[row][col] = 0
Then the main program can do:
count = 0
for solution in solve(board):
print("SOLUTION:")
print_board(solution)
count += 1
print("NUMBER of SOLUTIONS:", count)
From a high-level view, it seems to me that a recursive approach to this should work as follows:
Check if the grid is valid:
If the grid is invalid, return immediately
Else, check if the grid is complete:
If the the grid is complete, add (a copy of) it to the list of
solutions
Else, the grid is valid and incomplete, so find the first empty cell and run the function recursively on the grid with all possible values filled in for that box (and make sure to clear any modified cells at the end, after the loop)
Then, the list of solutions is generated, and the length of that list is the number of possible solutions. (If you find there are a lot of solutions and generating the list takes a very long time, you may just want to make a counter for the number of solutions found.)
Implementing this shouldn't be too difficult using what you have, since you already have functions for finding the first empty cell and verifying whether the grid is valid, etc.
How does that sound?

Finding the index of a number in a list in python

So I'm working on an easy leetcode problem and I've run into this small error. When 2 of the same number are in a list it refuses to add them together. I made it where it would ignore it if it was the same index but that has the effect of ignoring 2 of the same numbers for some reason. Here is the code
def twoSum(nums, target):
for x in nums:
for z in nums:
if nums.index(z) == nums.index(x):
pass
else:
res = x + z
if res == target:
result = [nums.index(x), nums.index(z)]
return result
newList = twoSum([3,2,3,7], 6)
print(newList)
Is there anything I'm doing wrong here that makes it pass over both 3's and return None?
def twoSum(nums, target):
for i, x in enumerate(nums):
for j, z in enumerate(nums):
if i == j:
continue
res = x + z
if res == target:
result = [i, j]
return result
You can use enumerate to access the indexes.
You can use continue instead of pass, but it's just a preference.
If you use continue you don't need the else since the execution is gonna jump to the next iteration when continue is hit.
This version eliminates the issue of having repeat index and also avoids adding a lower index to a current index (to avoid duplication). as in your code, it stops when it finds the first combination that adds to the target sum.
def twoSum(nums, target):
for pos1, value in enumerate(nums):
for pos2 in range(pos1+1, len(nums)):
# print(pos1, pos2) <- this may help analyze the code
res = value + nums[pos2]
if res == target:
result = [value, nums[pos2]]
return result
newList = twoSum([3, 2, 3, 7], 6)
print(newList)

Palindrome Partitions

Given a string split it into as few strings as possible such that each string is a palindrome
k = "racecarannakayak"
palin = [] # to get palindrome strs
i, j = 0, 2
while j != len(k) + 1:
if k[i:j] == k[:j][::-1]:
palin.append(k[i:j])
i += 1
j += 1
print(palin)
the result is ["racecar"] but it should be ["racecar", "anna", "kayak"]
what is wrong with my code??
There are a few issues with your code.
First, your pointer i keeps iterating even though your point j is
smaller than it in most of the iterations. Meaning you are doing
stuff like k[10:2] which will be pointless.
Second, your pointer j works only once over the whole array. Meaning
you either need multiple passes OR you need to reset the i pointer to
the last position of j pointer IF you find a palindrome.
Third, there is no condition to make sure that single alphabet string
is not considered a palindrome by your inversion logic
Last, there seems to be a typo, as mentioned by #fas in the comments. k[i:j]==k[i:j][::-1] instead of what you have written.
If you consider the above 3 and make the necessary changes you should get the right results to your code as below -
k = "racecarannakayak"
palin = [] # to get palindrome strs
i, j = 0, 2
while j != len(k) + 1:
if k[i:j] == k[i:j][::-1] and len(k[i:j])>2: #Check length is >2
palin.append(k[i:j])
i=j #Reset i pointer to current j
j += 1
print(palin)
['racecar', 'anna', 'kayak']

Backtracking in sudoku solver failing

I'm writing a sudoku solver in Python that takes in a partially filled in board and uses backtracking and forward checking to fill in the rest and solve the puzzle. Forward checking is where every time you assign a value to a blank cell you check whether its row, col, and box unassigned "neighbors" still have nonempty domains after the assignment.
To represent my board (dimenstions: N x N board with P x Q boxes), I'm using a 2D list in which each entry is of the form [value, [domain]], where value is the assigned number of the cell (0 if unassigned) and domain is the possible values for the cell that would keep the sudoku puzzle consistent.
I believe I am doing something wrong with my recursion but can't figure out what. The function always fails and returns False. Below is a portion of my recursive solver function with the preprocessing code taken out. If necessary, I can post the entire function and its helper functions.
def fc_recursive_solve(board, N, P, Q, row, col, outputFile, timeout):
###some preprocessing here to check if puzzle is solved and find next empty cell if not
vals = board[row][col][1] #domain/possible values for the empty cell
for num in vals:
#if num doesn't violate the row, col, and box sudoku constraints
if constraintCheck(row, col, num, P, N, Q, board):
#make copy of cell's domain for backtracking
tempDomain = copy.deepcopy(board[row][col][1])
board[row][col][0] = num #assign num to the cell
#remove num from domains of neighbors,
#if an empty domain results after removing num,
#return False and the original board,
#else return True and the updated board
noEmptyDomains, board = propagate_fc(board, N, P, Q, row, col, num)
if noEmptyDomains:
board[row][col][1] = [num] #update domain to be num and then recurse
if fc_recursive_solve(board, N, P, Q, row, col, outputFile, timeout):
return True
#backtrack -- reset value and domain of assigned cell
board[row][col][1] = tempDomain
board[row][col][0] = 0
else:
board[row][col][0] = 0
return False
EDIT: more code and trying out Blckknght's solution
def fc_recursive_solve(board, N, P, Q, row, col, outputFile, timeout):
if time.clock() >= timeout:
return "timeout"
while row < N and board[row][col][0] != 0: #find next blank
if col == N-1:
row = row + 1
col = 0
else:
col = col + 1
if row == N: #solved
return True
for num in vals:
if constraintCheck(row, col, num, P, N, Q, board):
board[row][col][0] = num
noEmptyDomains, new_board = propagate_fc(board, N, P, Q, row, col, num) # new var
if noEmptyDomains:
new_board[row][col][1] = [num] # this doesn't modify board, only new_board
if fc_recursive_solve(new_board, N, P, Q, row, col, outputFile, timeout):
return True
board[row][col][0] = 0 # the only thing that's required to backtrack
return False
def propagate_fc(board, N, P, Q, row, col, num):
origBoard = copy.deepcopy(board)
#row propagate
for x in range(N):
if board[x][col][0] == 0:
if num in board[x][col][1]:
board[x][col][1].remove(num)
if len(board[x][col][1]) == 0:
return False, origBoard #domain is empty; return original board
#col propagate
for y in range(N):
if board[row][y][0] == 0:
if num in board[row][y][1]:
board[row][y][1].remove(num)
if len(board[row][y][1]) == 0:
return False, origBoard #domain is empty
#box propagate
rDiv = row/P
cDiv = col/P
for i in range((rDiv * P), ((rDiv + 1) * P)):
for j in range((cDiv * Q), ((cDiv + 1) * Q)):
if board[i][j][0] == 0:
if num in board[i][j][1]:
board[i][j][1].remove(num)
if len(board[i][j][1]) == 0:
return False, origBoard #domain is empty
return True, board #success; return board with updated domains
If you're doing backtracking you need to be able to return the state of your board to how it was before. Your current code is not doing that. The propagate_fc function modifies board in a way that is not easy to undo.
Since I don't see an easy way to reverse the logic of propagate_fc, I suggest changing the design of the solver so that it makes copies of the board and only modifies the copies before passing them on to further recursive steps. If the copy doesn't lead to a solution, it can be thrown away, rather than trying to write backtracking code to fix it back up.
(I'm sure it is possible to backtrack, it's just not obvious what a good way to keep track of the changes, and figuring it out is too much work for this answer.)
Anyway, here's what I suggest for the modified version of the solver:
def fc_recursive_solve(board, N, P, Q, row, col, outputFile, timeout):
if time.clock() >= timeout:
return "timeout"
while row < N and board[row][col][0] != 0: #find next blank
if col == N-1:
row = row + 1
col = 0
else:
col = col + 1
if row == N: #solved
return board
for num in vals:
if constraintCheck(row, col, num, P, N, Q, board):
new_board = copy.deepcopy(board)
new_board[row][col][0] = num
if propagate_fc(new_board, N, P, Q, row, col, num):
new_board[row][col][1] = [num]
result = fc_recursive_solve(new_board, N, P, Q, row, col, outputFile, timeout)
if result is not None and result != "timeout":
return result
return None
I've changed it to return the solved board, if it's successful. Otherwise, you'd get a True response, but not be able to see the result (since the top-level code's board won't be modified).
Here's the changed version of propagate_fc to go with it:
def propagate_fc(board, N, P, Q, row, col, num):
# no copying any more here
#row propagate
for x in range(N):
if board[x][col][0] == 0:
if num in board[x][col][1]:
board[x][col][1].remove(num)
if len(board[x][col][1]) == 0:
return False
#col propagate
for y in range(N):
if board[row][y][0] == 0:
if num in board[row][y][1]:
board[row][y][1].remove(num)
if len(board[row][y][1]) == 0:
return False
#box propagate
rDiv = row/P
cDiv = col/P
for i in range((rDiv * P), ((rDiv + 1) * P)):
for j in range((cDiv * Q), ((cDiv + 1) * Q)):
if board[i][j][0] == 0:
if num in board[i][j][1]:
board[i][j][1].remove(num)
if len(board[i][j][1]) == 0:
return False
return board #success; return new board
The only real change here is that I don't bother returning the board, since we're always modifying it in place. Only the bool result is needed (to say if the board is valid or not).
(I'd initially thought there was another issue, with the if len(...) == 0 checks in each block, but it turned out not to be a problem after all. You might get slightly better performance doing those checks only when you've just removed a value from the current board[x][y][1] list (by indenting those blocks by two levels), but it's not likely to be a big performance gain.)
Based on a quick glance, I think you mixed up your row/col propagate:
#row propagate
for x in range(row): <== should this be range(col) ?
if board[row][x][0] == 0: <== row is fixed, but cols (x) changes
if num in board[row][x][1]:
board[row][x][1].remove(num)
if len(board[row][x][1]) == 0:
return False, origBoard #domain is empty; return original board

Python Recursive Sudoku Solver

This is recursive Sudoku solve code, this isn't a school assignment. I can't figure out how to get it to "back up" through my previous moves. It gets stuck at the end of the first row since there isn't a valid number for that spot and just keeps trying to find one that fits there. The function I am having problems with is check.
After reading your answers I've gotten closer with this, but it's not quite there. It backs all the way up and exits the recursion
import sys
class Board:
def __init__(self, grid):
self.grid = grid
self.ogrid = grid
def get_col(self, col):
column = []
for i in self.grid:
column.append(str(i[col]))
return column
def get_row(self, row):
return self.grid[row]
def check_row(self, r, val):
row = self.grid[r]
for i in range(0,9):
if str(val) in row:
return False
return True
def check_col(self, column, val):
col = self.get_col(column)
for i in range(0,9):
if str(val) in col:
return False
return True
def check_square(self, x, y, val):
col = (y//3)*3
row = (x//3)*3
s = ''
for i in range(row, row+3):
for j in range(col, col+3):
s += str(self.grid[i][j])
if str(val) in s:
return False
return True
def check_cell(self, x, y, val):
if self.check_col(y, val) == False:
return False
elif self.check_row(x, val) == False:
return False
elif self.check_square(x, y, val) == False:
return False
return True
def check(self, x, y):
if y == 9:
y = 0
x += 1
if x == 9:
self.print_board()
sys.exit()
if self.ogrid[x][y] == '.':
for val in range(1,10):
if self.check_cell(x, y, val):
self.grid[x][y] = str(val)
self.check(x, y+1)
##I don't think the reset is working and I'm not sure why
if self.ogrid[x][y] == '.': #reset index
self.grid[x][y] = '.'
self.print_board() #Notice it never prints a '.' in spots that have changed
else:
self.check(x,y+1)
return
def print_board(self):
for i in range(0,9):
for j in range(0,9):
sys.stdout.write(self.grid[i][j])
if j == 2 or j == 5:
sys.stdout.write(' ')
if i == 2 or i == 5:
sys.stdout.write('\n')
print('')
def main():
f = open("./easySudoku.txt",'r')
s = ''
grid = []
row = []
for line in f:
s += line
print line
s = s.replace(' ','')
s = s.replace('\n','')
for i in range(0,9):
row = []
for j in range(0,9):
row.append(s[(i*9) + j])
grid.append(row)
sudoku = Board(grid)
sudoku.check(0,0, 1)
if __name__ == "__main__":
main()
Here is how the check function is supposed to work
check takes an x and y coordinate for the board and starts at 0,0 it runs through a for loop from 1-9 and sets the first value that works at that index and then moves to the next index. When it reaches the end of a row it moves down one row and back to the first column. If no values work at an index then reset current index to '.' and move index back one and continue counting towards 9. i.e. if current value at index 0,0 is 2 then continue to 3. If 3 works then move forward one index so on and so forth until the board has been filled. For the simplistic answer it tries every value, 1-9, at every empty index
If that's unclear then let me know
Also here is the board that I am using
..6 ..7 3..
.18 ..9 .5.
5.. ... .64
92. .8. ...
... 763 ...
... .9. .75
63. ... ..8
.9. 3.. 52.
..2 4.. 6..
What I did to check your code: add this line as the first line of your check method:
raw_input('x: %d, y: %d, val: %d' % (x,y,val))
and print the board after inserting a number.
It looks like your solver makes its first mistake at (x,y) = (0,3). It checks all numbers up to 9 and then puts a 9 there. Per your algorithm, it should put a 1. The hang up is your check_square method. You should have
col = (y//3)*3
row = (x//3)*3
After fixing that, the next bug crops up at (x,y) = (1,8), starting with self.check(1, 8, 1). There are no legal values (using your algorithm up to this point) for this square (all the way up to self.check(1, 8, 9)). So next is called self.check(1, 8, 10). Since val==10, it returns, and then within the call to self.check(1, 8, 9), the final line, self.check(x, y-1, val+1), is called, i.e. self.check(1, 7, 10). It, of course immediately returns as well because val == 10. And we are back to self.check(1, 8, 8) and calling the final line of the method definition. Next to execute is self.check(1, 7, 9), which spawns the next self.check(1, 8, 1). Look familiar? We were here already and have had no state change in the meanwhile. Without even realizing it, this becomes an endless loop.
Was that confusing? Of course it was. There is a reason programmers try to avoid recursion, except when teaching about the concept of recursion. Tracking down these types of recursion bugs is difficult, but with a few print lines, it can be done.
P.S. Your algorithm is interesting. I wonder where you found it... It is definitely not how humans play (with all the guessing and editing). FWIW, I would firstly only insert a value into the board if that value were the only legal move for the square and guess only when all blank squares on the board were ambiguous.
EDIT AFTER YOUR EDITS
Add import copy, part of the standard library, at the top and change in __init__ to self.ogrid = copy.deepcopy(grid). Should solve your problem. See https://stackoverflow.com/a/2612815/2100286. Your method of creating a duplicate version of the grid achieves the same thing.
The problem is that you seem to recurse one step for each number you try, that will consume every sane stack. I'd suggest that you use iteration as well and use return in order to do back up (that way you should only use 81 stack frames or so - here it fails when you get a thousand levels of stack frames).
I've done a solver before and it will find the solution quite fast...
I don't get this snippet:
if y == -1:
y = 8
x -= 1
If y equals the last position in the row you're setting it to 8, which is the index of the last position in the row? Could this be the reason of why it doesn't proceed properly?
Alright, I fixed my problem!
Here's what I did. I took the advice #Skyking gave me by recursing on index only and not by values per index which I had originally. Second thing I changed was to take #James Pringles advice on how to fix my check_square function and copying grid so ogrid would not change when grid changed.
Since I can't give two green checks I gave it to #James Pringle since he/she helped me the most, but I couldn't have gotten it without #Skyking's advice
Here's the finished code
import sys
import copy
class Board:
def __init__(self, grid):
self.grid = grid
self.ogrid = copy.deepcopy(grid)
def get_col(self, col):
column = []
for i in self.grid:
column.append(str(i[col]))
return column
def get_row(self, row):
return self.grid[row]
def check_row(self, r, val):
row = self.grid[r]
if str(val) in row:
return False
return True
def check_col(self, column, val):
col = self.get_col(column)
if str(val) in col:
return False
return True
def check_square(self, x, y, val):
col = (y//3)*3
row = (x//3)*3
s = ''
for i in range(row, row+3):
for j in range(col, col+3):
s += str(self.grid[i][j])
if str(val) in s:
return False
return True
def check_cell(self, x, y, val):
if self.check_col(y, val) == False:
return False
elif self.check_row(x, val) == False:
return False
elif self.check_square(x, y, val) == False:
return False
return True
def check(self, x, y):
if y == 9:
y = 0
x += 1
if x == 9:
self.print_board()
sys.exit()
if self.ogrid[x][y] == '.':
for val in range(1,10):
if self.check_cell(x, y, val):
self.grid[x][y] = str(val)
self.check(x, y+1)
if self.ogrid[x][y] == '.':
self.grid[x][y] = self.ogrid[x][y]
else:
self.check(x,y+1)
return True
def print_board(self):
for i in range(0,9):
for j in range(0,9):
sys.stdout.write(self.grid[i][j])
if j == 2 or j == 5:
sys.stdout.write(' ')
if i == 2 or i == 5:
sys.stdout.write('\n')
print('')
def main():
f = open("./easySudoku.txt",'r')
s = ''
grid = []
row = []
for line in f:
s += line
s = s.replace(' ','')
s = s.replace('\n','')
for i in range(0,9):
row = []
for j in range(0,9):
row.append(s[(i*9) + j])
grid.append(row)
sudoku = Board(grid)
sudoku.check(0,0)
print('shouldn\'t be here')
if __name__ == "__main__":
main()

Categories