Python for loop range function causing an infinite loop - python

I am building a sudoku function to learn how to code in python. I seem to be creating an infinite loop with a for loop but I don't understand how. The code attempts to look at each empty square of the sudoku board and check if a value counter is allowed by the rules of sudoku. If counter is allowed the board is updated and the function moves on to the next empty square. If counter is not allowed than counter is incremented by 1 and tested again.
The issue I am having comes when counter is greater then 9. When this happens I want to look at the previous square which was empty on the original board (named puzzle) and delete the value in this square. The function than should set counter equal to the value in the previous square +1 and call itself to run again.
In essence the function is testing each empty square for possible values until it finds a value and than move on to the next square. If there are no possible values the function will back track, delete the last square and try running again.
My problem seems to happen in the else condition when counter is greater than 9. This part of the function is causing an infinite loop which prints out 'no' repeatedly.
I'm assuming that my function is getting stuck on the while loop but I'm not sure why.
puzzleBoard =[[1,2,3,4,5,6,7,8,9],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0]]
def solvePuzzle():
#start by looking at each square in the 9x9 sudoku grid and check if that square is empty (=0)
for i in range(9):
for j in range(9):
counter = 1
topX = 3*(i//3)
topY = 3*(j//3)
# while the board at index [i][j] is empty check if the value of 'counter' fits in the square and adheres to the sudoku rules
# if counter is not an allowed value increment counter by 1 and try again
while puzzleBoard[i][j] ==0:
if counter < 10:
row = all([counter != puzzleBoard[i][x] for x in range(9)])
column = all([counter != puzzleBoard[y][j] for y in range(9)])
box = all([counter != puzzleBoard[x][y] for x in range(topX, topX+3) for y in range(topY, topY+3)])
if row and column and box == True:
puzzleBoard[i][j]=counter
uploadBoard()
else:
counter = counter + 1
# if counter is larger than ten set the previous square ([i][j-1]) equal to zero, set the counter equal to one more than the previous squares value, and call the solvePuzzle function again.
else:
for k in range(i,0,-1):
for l in range(j-1,0,-1):
if puzzle[k][l]==0:
counter = puzzleBoard[k][l] + 1
puzzleBoard[k][l]=0
solvePuzzle()
return
else:
print("no")

I was able to work out the answer. There are a few issues with the code but the main problem is that in the lower else statement counter = puzzleBoard[k][l] + 1 and then the function was being called again, which resets the variable counter to 1.
I was able to fix this problem by creating a global variable countholder and modifying the else statement to say countholder = puzzleBoard[k][l] + 1
The full working code looks like this:
puzzleBoard =[[0,2,0,0,0,0,0,0,0],[0,0,0,6,0,0,0,0,3],
[0,7,4,0,8,0,0,0,0],[0,0,0,0,0,3,0,0,2],
[0,8,0,0,4,0,0,1,0],[6,0,0,5,0,0,0,0,0],
[0,0,0,0,1,0,7,8,0],[5,0,0,0,0,9,0,0,0],
[0,0,0,0,0,0,0,4,0]]
puzzle =[[0,2,0,0,0,0,0,0,0],[0,0,0,6,0,0,0,0,3],
[0,7,4,0,8,0,0,0,0],[0,0,0,0,0,3,0,0,2],
[0,8,0,0,4,0,0,1,0],[6,0,0,5,0,0,0,0,0],
[0,0,0,0,1,0,7,8,0],[5,0,0,0,0,9,0,0,0],
[0,0,0,0,0,0,0,4,0]]
countholder = 1
def solvePuzzle():
#start by looking at each square in the 9x9 sudoku grid and check if that square is empty (=0)
for i in range(9):
for j in range(9):
global countholder
counter = countholder
topX = 3*(i//3)
topY = 3*(j//3)
# while the board at index [i][j] is empty check if the value of 'counter' fits in the square and adheres to the sudoku rules
# if counter is not an allowed value increment counter by 1 and try again
while puzzleBoard[i][j] ==0:
if counter < 10:
row = all([counter != puzzleBoard[i][x] for x in range(9)])
column = all([counter != puzzleBoard[y][j] for y in range(9)])
box = all([counter != puzzleBoard[x][y] for x in range(topX, topX+3) for y in range(topY, topY+3)])
if (row and column and box) == True:
puzzleBoard[i][j]=counter
print(puzzleBoard)
countholder = 1
else:
counter = counter + 1
# if counter is larger than ten set the previous square ([i][j-1]) equal to zero, set the counter equal to one more than the previous squares value, and call the solvePuzzle function again.
else:
run_One = True
for k in range(i,-1,-1):
if run_One == True:
run_One = False
for l in range(j,0,-1):
if l == 0:
print(run_One)
elif puzzle[k][l-1]==0:
countholder = puzzleBoard[k][l-1] + 1
puzzleBoard[k][l-1]=0
solvePuzzle()
return
else:
continue
else:
for l in range(8,-1,-1):
if puzzle[k][l]==0:
countholder = puzzleBoard[k][l] + 1
puzzleBoard[k][l]=0
solvePuzzle()
return
else:
continue
solvePuzzle()

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?

I am looking for a way to make my Minesweeper game add numbers around the bombs

Currently my code adds bombs through random x and y coordinates on my hidden grid. What would I have to add here in order for the code to add numbers if the grid space is touching a bomb?
def addBombNum(bombs,gSize,hiddenGrid,bCount):
fNum = ""
for i in range(bombs):
randPlace = True
while randPlace:
x = random.randint(0,gSize - 1)
y = random.randint(0,gSize - 1)
if hiddenGrid[y][x] == "[-]": hiddenGrid[y][x] = "\033[90m[*]\033[m" ; randPlace = False
if hiddenGrid[y][x] == "\033[90m[*]\033[m": pass
for i in range(gSize):
for j in range(gSize):
if hiddenGrid[i][j] == "\033[90m[*]\033[m": continue
This is my hiddenGrid if needed.
hiddenGrid = [["[-]" for y in range(gSize)] for x in range(gSize)]
I would personally recomment storing the bomb array as a numerical array and having a way to process it into the output afterwards. However, to answer your question directly, what I would recommend doing is each time you place down a bomb, you increment a counter for every cell adjacent to it.
So, at the top of your function, you could first create a number grid: grid = [[0 for y in range(gSize)] for x in range(gSize)].
Next, each time you add a bomb, increment the counter for each grid space that touches it:
for a in range(y - 1, y + 2):
for b in range(x - 1, x + 2):
if 0 <= a < gSize and 0 <= b < gSize:
grid[a][b] += 1
Finally, at the very end, you can just relay this information from the grid to the output. Thus, after the if hiddenGrid[i][j] == "\033[90m[*]\033[m": continue line (which I assume you added to prevent it from overwriting bombs with numbers), you can add this block:
if grid[i][j] > 0:
hiddenGrid[i][j] = "\033[90m[%d]\033[m" % grid[i][j]
A different way you could do it is just add this block to the very end after the bomb skip check:
count = 0
for a in range(i - 1, i + 2):
for b in range(j - 1, j + 2):
if 0 <= a < gSize and 0 <= b < gSize:
if hiddenGrid[a][b] == "\033[90m[*]\033[m":
count += 1
if count > 0:
hiddenGrid[i][j] = "\033[90m[%d]\033[m" % count
This way, you don't need to have the counter grid and don't need to increment it each time. However, having the grid means if you need to use it for other things later on, you have it around and don't need to recompute it every time.

traversing through a list using recursion

So I am new to recursion and I am trying to make a program where you can enter a list and python tests each integer (lets say 9 for example) and sees if the integer following it is doubled. So if I entered a list of 2 4 8 16 32, would return 4, and -5 -10 0 6 12 9 36, would return 2 because -5 followed by -10 is one and 6 followed by 12 is the second. This is the code I have so far. I feel like I am very close. but just a few thing stand in my way. Any help would be great!
L = []
def countDouble(L):
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
print(y[1])
print(y[0])
count = 0
y[0] += y[0]
# unsure of how to multiple y[0] by 2
if y[0]*2 == y[1]:
count += 1
else:
count += 0
#how would I traverse through the rest of the entered list using recursion?
print(count)
countDouble(L)
If you want/need to solve it using recursion, the following will do the trick:
def count_sequential_doubles(li, count=0):
return count_sequential_doubles(li[1:], count + int(li[0] * 2 == li[1])) if len(li) > 1 else count
I would suggest this recursive way:
def countDouble(L):
count = 0
if len(L) == 1:
return count
else:
if int(L[0])*2 == int(L[1]):
count += 1
return count + countDouble(L[1:])
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = countDouble(y)
print(count)
I urge you to read the entire answer, but in case you are not interested in tips, notes and the process of finding the solution, here are two solutions:
solution using recursion (not recommended):
x = input()
y = x.split(' ')
count = 0
def countDouble(i):
if(i+1 == len(y)):
return 'recursion ends here when'
if(int(y[i])*2==int(y[i+1])):
count += 1
countDouble(i+1)
countDouble(0)
print(count)
this solution just imitates a while loop:
solution using a while loop (recommended):
x = input()
y = x.split(' ')
count = 0
i = 0
while(i < len(y) - 1):
if(int(y[i]) * 2 == int(y[i+1])):
count += 1
i += 1
print(count)
Before I continue, here are a few tips and notes: (some of them will only make sense after)
I assume the 14 in your example is a typo
I didn't put the code in a function because it's not needed, but you can change it easily.
In your code, you are passing L as a parameter to the countDouble() function, but you don't use it. if you don't need a parameter don't pass it.
when splitting the input, the values of the list are still strings. so you have to invert them to integers (for instance, you can do that with the int() 'function') before comparing their values - otherwise multiplying by 2 will just repeat the string. for example: '13'*2 is the string '1313'
I don't know why you why you added y[0] to itself in line 9, but based on the code that comes after this would yield incorrect results, you don't need to change the elements in order to get their value multiplied by 2.
notice that in the else block, nothing has changed. adding 0 to the count doesn't change it. so you can remove the else block entirely
While it's possible to solve the problem in recursion, there's something else designed for these kind of problems: loops.
The problem is essentially repeating a simple check for every element of a list.
This is how I would arrive to a solution
so we want to run the following 'code':
if(y[0]*2 == y[1]):
count += 1
if(y[1]*2 == y[2]):
count += 1
if(y[2]*2 == y[3]):
count += 1
...
of course the computer doesn't understand what "..." means, but it gives us an idea to the pattern in the code. now we can do the following:
divide the extended 'code' into similar sections.
identify the variables in the pattern - the values that change between sections
find the starting values of all variables
find a pattern in the changes of each variable
find a breaking point, a condition on one of the variables that tells us we have reached the last repeating section.
here are the steps in this specific problem:
the sections are the if statements
the variables are the indexes of the elements in y we compare
the first index starts at 0 and the second at 1
both indexes increase by one after each if-statement
when the second index is bigger then the last index of y then we already checked all the elements and we can stop
so all is left is to set the needed variables, have a while loop with the breaking condition we found, and in the while loop have the general case of the repeating sections and then the changing of the variables.
so:
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = 0
# setting the starting values of the variables
index1 = 0
index2 = 1
# creating a loop with the breaking condition
while(index2 < len(y)):
# the general case of the repeated code:
if(int(y[index1]) * 2 == int(y[index2])):
count += 1
# changing the variables for the next loop
index1 += 1
index2 += 1
print(count)
We see that the index2 is just index1 + 1 at all time. so we can replace it like that:
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = 0
index1 = 0
while(index1 + 1 < len(y)):
if(int(y[index1]) * 2 == int(y[index1 + 1])):
count += 1
index1 += 1
print(count)
Note: You can use a for loop similarly to the while loop
So in summary, you can use recursion to solve the problem, but the recursion would just be imitating the process of a loop:
in each call, the breaking condition will be checked, the repeated code would run and the variables/parameters would change.
Hope you find this answer useful :)
Final edit: OP edited his example so my other code didnt apply
Some good questions people are asking, but in the spirit of helping, here's a recursive function that returns the count of all doubles.
def get_doubles_count_with_recursion(a_list, count, previous=None):
while a_list:
try:
first = previous if previous else a_list.pop(0)
next_item = a_list.pop(0)
except IndexError:
return count
if next_item / 2 == first:
count += 1
return get_doubles_count_with_recursion(a_list, count, next_item)
return count
a_list = [1, 3, 5, 10, 11, 14, 28, 56, 88, 116, 232, 464, 500]
doubles = get_doubles_count_with_recursion(a_list, 0)
print(doubles == 5)
Probably could clean it up a bit, but it's a lot easier to read than the other guy's ;)
If I'm reading your question right, you want a count of all pairs where the 2nd item is double the first. (and the 14 in the first list is a typo). In which case a simple function like this should do the job:
#a = [2,4,8,16,32]
a = [-5, -10, 0, 16, 32]
count = 0
for i, x in enumerate(a):
# Stop before the list overflows
if i < len(a) - 1:
# If the next element is double the current one, increment the counter
if a[i+1] == x * 2:
count = count + 1
else:
break
print(count)

manhattan distance heuristic in n puzzle python

I have a problem with my function that calculates the manhattan distance for the 8-puzzle in python. My code have a problem especially with a particular state: [4,3,7,5,8,6,1,0,2] ,(on some other state I tried it and it worked..) my code runs but doesn't stop running and doesn't print the result then I don't know where the problem is in my function.
The code I run:
def search(n):
s=[[4,3,7,5,8,6,1,0,2],""]
print(s)
f=frontier.create(s)
while not frontier.is_empty(f):
s=frontier.remove(f)
if state.is_target(s):
return [s, f[1], f[3]]
ns=state.get_next(s)
for i in ns:
frontier.insert(f,i)
return 0
print(search(3))
My function that calculates the Manhattan distance:
def hdistance(s):
# the heuristic value of s
i = 0 #represent the row number
j = 0 #represent the column number
sum = 0 #the manhattan distance
n = math.sqrt(len(s[0])) #calculate the size of the n puzzle
for k in s[0]: #run through the list
if k != 0:
row = int(k / n)
column = int(k % n)
sum = sum + abs(i - row) + abs(j - column)
j += 1 #increment the column
if j == n: #if I get to the end of the line
i += 1 #increment the row
j = 0
return sum #return the manhattan distance
The frontier file:
def create(s):
return [[s], 0, 0 ,0]
def is_empty(f):
return f[0]==[]
def val(s):
return state.hdistance(s)+state.path_len(s)
# Uniform Cost: return state.path_len(s)
# Greedy Best First: return state.hdistance(s)
def insert(h, s):
#inserts state s to the frontier
f=h[0]
h[1]+=1
h[2]+=1
if h[2]>h[3]:
h[3]=h[2]
f.append(s)
i=len(f)-1
while i>0 and val(f[i])<val(f[(i-1)//2]):
t=f[i]
f[i]=f[(i-1)//2]
f[(i-1)//2]=t
i=(i-1)//2
def remove(h):
if is_empty(h):
return 0
h[2]-=1
f=h[0]
s=f[0]
f[0]=f[len(f)-1]
del f[-1]
heapify(f,0)
return s
def heapify(f,i):
minSon=i
if 2*i+1<len(f) and val(f[2*i+1])<val(f[minSon]):
minSon=2*i+1
if 2*i+2<len(f) and val(f[2*i+2])<val(f[minSon]):
minSon=2*i+2
if minSon!=i:
t=f[minSon]
f[minSon]=f[i]
f[i]=t
heapify(f, minSon)
The state file:
def get_next(x): # returns a list of the children states of x
ns=[] # the next state list
for i in "<>v^":
s=x[0][:] # [:] - copies the board in x[0]
if_legal(s,i) # try to move in direction i
# checks if the move was legal and...
if s.index(0)!=x[0].index(0) and \
(x[1]=="" or x[1][-1]!="><^v"["<>v^".index(i)]): # check if it's the first move or it's a reverse move
ns.append([s,x[1]+i]) # appends the new state to ns
return ns
def path_len(x):
return len(x[1])
def is_target(x):
n=len(x[0]) # the size of the board
return x[0]==list(range(n)) # list(range(n)) is the target state
#############################
def if_legal(x,m): # gets a board and a move and makes the move if it's legal
n=int(math.sqrt(len(x))) # the size of the board is nXn
z=x.index(0) # z is the place of the empty tile (0)
if z%n>0 and m=="<": # checks if the empty tile is not in the first col and the move is to the left
x[z]=x[z-1] # swap x[z] and x[z-1]...
x[z-1]=0 # ...and move the empty tile to the left
elif z%n<n-1 and m==">": # check if the empty tile is not in the n's col and the move is to the right
x[z]=x[z+1]
x[z+1]=0
elif z>=n and m=="^": # check if the empty tile is not in the first row and the move is up
x[z]=x[z-n]
x[z-n]=0
elif z<n*n-n and m=="v": # check if the empty tile is not in the n's row and the move is down
x[z]=x[z+n]
x[z+n]=0
The rest of the code (all the other functions) are given in my assignment then the problem is certainly in my hdistance function.
If you could find where the problem is in my function, maybe there is an infinite loop...
Thank u!!

Modifying a set within a recursive function?

I'm trying to write a program to check if a particular word can be made using a given "boggle board". There full details of the challenge are here: Boggle Word Checker.
Basically, the program is supposed find the first letter of a word, on the board, and then check if any of the adjacent letters to it on the board match the next letter of the word.
This is what I've got so far (not pretty I know):
def pos(board, x, y):
# function to get value from a grid, but return None if not on grid,to
# prevent wraparound
if x >= 0 and y >= 0 and x < len(board[0]) and y < len(board):
return board[y][x]
else:
return None
def surrounds(board, x, y):
# make a dictionary which to store the positions and values of adjacent
# letters on board, given a single postision as input
return {
(x-1,y-1) : pos(board,x-1,y-1), #aboveLeft
(x,y-1) : pos(board,x,y-1), #aboveMiddle etc...
(x+1,y-1) : pos(board,x+1,y-1),
(x-1,y) : pos(board,x-1,y),
(x+1,y) : pos(board,x+1,y),
(x-1,y+1) : pos(board,x-1,y+1),
(x,y+1) : pos(board,x,y+1),
(x+1,y+1) : pos(board,x+1,y+1)
}
def find_word(board, word):
# initialise
listOfCoords = []
# find all occurrences of the first letter, and store their board
# position in a list
for i in range(len(board)):
for j in range(len(board[0])):
if board[i][j] == word[0]:
listOfCoords.append([j,i])
print('list of ' + word[0] + 's on board:')
print(listOfCoords)
print()
# if word is only 1 letter long then we can return True at this point
if listOfCoords and len(word) == 1:
return True
# otherwise we move on to look for the second letter
return findnext(board,word,listOfCoords)
def findnext(board, word, mylist):
for x, y in mylist:
print("Current coords: {},{}\n".format(x,y))
surroundings = surrounds(board,x,y)
listFounds = []
for k, v in surroundings.items():
if v == word[1]:
print("{} found at {}".format(v,k))
print()
if len(word) == 2:
print()
return True
listFounds.append(k)
if findnext(board, word[1:], listFounds) == True:
return True
return False
testBoard = [
["E","A","R","A"],
["N","L","E","C"],
["I","A","I","S"],
["B","Y","O","R"]
]
print(find_word(testBoard, "CEREAL"))
However, I've encountered a problem, as the challenge specifies that no position on the board can be used more than once. Therefore, in the above example, the program should return False for "CEREAL", but mine returns True.
I was thinking a way around this could be to use a set, which adds the coordinates to the set once a next letter is found. However I'm a bit lost as to where I would need to create the empty set, and how it would work with all the loops and recursion going on...
For example, let's say we were looking for "CEREAL" on a different board, which has 2 Es adjacent to C. let's say the first path only leads to CER and the other leads to CEREAL. If we go down the CER path first, the positions for it would be added to the set, and I would somehow need to remove them again before it goes down the CEREAL path.
I'm struggling to think how to implement this in my program.
You need to have an array of booleans the size of the board all set to False. When you use a letter and call your function recursively, set the cell to True for the used letter. When returning from the recursive call, set the cell value back to False. You should only use letters with False values indicating that they have not been previously used.
Alternatively, you can replace the used letter by None after keeping the letter in a temporary variable then do the recursive call. Upon the return from the recursive call put back the value of the cell from the temp variable.
Steps:
Find the 1st character match against the board
Once a match is found, do a DFS with backtracking till you completely match the word or exhaust the search due to mismatch.
If complete match is not found in above step, continue scanning the board. (i.e. go to step 1) for the next character that matches on the board.
If a match is found in step 3, report success.
This solution works for NxM board as well.
def is_within_bounds(row, col, row_dim, col_dim):
return row >= 0 and row < row_dim and col >= 0 and col < col_dim
def get_neighbors(row, col, row_dim, col_dim):
for r_offset in (-1, 0, 1):
for c_offset in (-1, 0, 1):
if r_offset == 0 and c_offset == 0:
continue
r_new = row + r_offset
c_new = col + c_offset
if is_within_bounds(r_new, c_new, row_dim, col_dim):
yield (r_new, c_new)
def is_word_found(board, word, row, col, visited):
if word[0] != board[row][col]:
return False
if len(word) == 1:
return True
for neighbor in get_neighbors(row, col, len(board), len(board[0])):
if neighbor not in visited:
visited.add(neighbor)
if is_word_found(board, word[1:], neighbor[0], neighbor[1], visited):
return True
visited.remove(neighbor)
return False
def find_word(board, word):
for row in range(len(board)):
for col in range(len(board[0])):
if word[0] != board[row][col]:
continue
if is_word_found(board, word, row, col, set()):
return True
return False

Categories