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
Related
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'm working on algos in python and tried to implement python sudoku solver. After many tries I've decided to look for the solution and decided to use the one listed here
I've tried to play with the code and modify it to accept a local variable sudoku instead of the global one, however it seems like my code solves the sudoku and then replaces the solved sudoku with the unsolved one probably due to the recursion modification.
Here is my modified code:
#Forming the Puzzle Grid
def form_grid(puzzle_string):
global grid
print('The Sudoku Problem')
for i in range(0, len(puzzle_string), 9):
row = puzzle_string[i:i+9]
temp = []
for block in row:
temp.append(int(block))
grid.append(temp)
printGrid()
Function to print the Grid
#Function to print the grid
def printGrid():
global grid
for row in grid:
print(row)
#Function to check if a digit can be placed in the given block
def possible(grid,row,col,digit):
for i in range(0,9):
if grid[row][i] == digit:
return False
for i in range(0,9):
if grid[i][col] == digit:
return False
square_row = (row//3)*3
square_col = (col//3)*3
for i in range(0,3):
for j in range(0,3):
if grid[square_row+i][square_col+j] == digit:
return False
return True
def solve(grid):
for row in range(9):
for col in range(9):
if grid[row][col] == 0:
for digit in range(1,10):
if possible(grid, row,col,digit):
grid[row][col] = digit
solve(grid)
grid[row][col] = 0 #Backtrack step
return grid
puzzle_string = "004300209005009001070060043006002087190007400050083000600000105003508690042910300"
solve(form_grid(puzzle_string))
Can't figure out how to modify the code to accept the sudoku as a parameter while returning the correct result, I've also tried to check for validity in every cal such as:
if 0 not in grid.flatten():
return grid
but that yielded the same result
The main problem is that you were assigning the current cell always back to 0 and checked all possible digits regardless of the result of the recursive solve. you should check if the recursive solve is valid and decide what would be the next step.
The following is a small fix to your code. First, solve should return bool (i.e., True or False) as the pointer to grid remains the same, so you ddon't really need to return it. Then you should use the recursive call to check if a valid solution was found. If not, continue to the next digit, and if you checked all digits, assign 0 back to the current cell and return False. Otherwise (if a valid solution was found) return True.
def solve(grid):
for row in range(9):
for col in range(9):
if grid[row][col] == 0:
valid = False
for digit in range(1, 10):
if possible(grid, row,col,digit):
grid[row][col] = digit
if solve(grid):
return True
grid[row][col] = 0 # Backtrack step
return False
return True
puzzle_string = "004300209005009001070060043006002087190007400050083000600000105003508690042910300"
grid = form_grid(puzzle_string)
printGrid()
solve(grid)
print()
printGrid()
BTW, there were few bugs in your code, such as relating to a global grid but not initializing it anywhere.
Also, solve is not very efficient as in each recursive call you are searching for the next empty cell. Currently is just a 9X9 grid, so it would be fast anyway, but for generalization, or just as a principal, a better way to use the recursive call is that solve would receive the row and col of the next position to check.
Finally, it might be better to remove the global grid and just return it from the form_grid function. the same for printGrid that should get the grid to print. The following is a version with the mentioned modifications:
#Forming the Puzzle Grid
def form_grid(puzzle_string):
grid = []
print('The Sudoku Problem')
for i in range(0, len(puzzle_string), 9):
row = puzzle_string[i:i+9]
temp = []
for block in row:
temp.append(int(block))
grid.append(temp)
return grid
#Function to print the grid
def printGrid(grid):
for row in grid:
print(row)
print()
#Function to check if a digit can be placed in the given block
def possible(grid, row, col, digit):
for i in range(0, 9):
if grid[row][i] == digit:
return False
for i in range(0, 9):
if grid[i][col] == digit:
return False
square_row = (row // 3) * 3
square_col = (col // 3) * 3
for i in range(0, 3):
for j in range(0, 3):
if grid[square_row + i][square_col + j] == digit:
return False
return True
def get_next_position(current_row, current_col):
if current_col == 8:
if current_row == 8:
return None, None
return current_row + 1, 0
return current_row, current_col + 1
def solve(grid, row, col):
if row is None:
return True
next_row, next_col = get_next_position(row, col)
if grid[row][col] != 0:
return solve(grid, next_row, next_col)
for digit in range(1, 10):
if possible(grid, row, col, digit):
grid[row][col] = digit
if solve(grid, next_row, next_col):
return True
grid[row][col] = 0
return False
puzzle_string = "004300209005009001070060043006002087190007400050083000600000105003508690042910300"
grid = form_grid(puzzle_string)
print("Initial grid")
printGrid(grid)
is_solved = solve(grid, 0, 0)
print(f"Solved: {is_solved}")
print("Solved grid")
printGrid(grid)
You could take the following steps:
Remove the global variable and all global statements
Turn your functions into methods of a new Sudoku class
Make from_grid the constructor of that class, i.e. name it __init__
Define self.grid = [] in this constructor. This way grid is an instance member, so it is separate for each instance you create of Sudoku
Remove all grid function parameters, but add self as the first parameter of all these methods, and make sure to reference self.grid where ever you need access to the grid
solve has an algorithmic problem: it returns grid when it has already backtracked a working "move". Instead make solve such that it returns a boolean: True when the solution is found, and False if not, and make sure it does not clear any moves when a solution is found, and does not try any alternatives. That way self.grid will have the solution.
Here are those things implemented, with a few other cosmetic changes:
class Sudoku:
def __init__(self, puzzle_string):
self.grid = [
list(map(int, puzzle_string[i:i+9]))
for i in range(0, len(puzzle_string), 9)
]
#Function to print the grid
def printGrid(self):
for row in self.grid:
print(row)
#Function to check if a digit can be placed in the given block
def possible(self, row, col, digit):
if digit in self.grid[row]:
return False
for i in range(0,9):
if self.grid[i][col] == digit:
return False
square_row = (row//3)*3
square_col = (col//3)*3
for i in range(0,3):
for j in range(0,3):
if self.grid[square_row+i][square_col+j] == digit:
return False
return True
def solve(self):
for row in range(9):
for col in range(9):
if self.grid[row][col] == 0:
for digit in range(1,10):
if self.possible(row, col, digit):
self.grid[row][col] = digit
if self.solve():
return True
self.grid[row][col] = 0 #Backtrack step
return False
return True
puzzle_string = "004300209005009001070060043006002087190007400050083000600000105003508690042910300"
puzzle = Sudoku(puzzle_string)
print('The Sudoku Problem:')
puzzle.printGrid()
puzzle.solve()
if puzzle.solve():
print("Solved:")
puzzle.printGrid()
else:
print("Sorry, no solution found")
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()
Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent
cell, where "adjacent" cells are those horizontally or vertically
neighboring. The same letter cell may not be used more than once.
For example, Given
board = [ ['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E'] ]
word = "ABCCED", -> returns true
word = "SEE", -> returns true
word = "ABCB", -> returns false
This is a typical DFS + backtracking solution. It compares board[row][col] with word[start]. If they match, change board[row][col] to ‘#’ to mark it as visited. Then move to the next one (i.e. word[start + 1]) and compare it to the current neighbors (doing it by recursion).
Below is my code which is not working. I tried debugging but I feel there is off by one error somewhere which I am not able to track.
class Solution(object):
def exist(self, board, word):
def match(board, word, r, c, index):
if r < 0 or r >= len(board) or c < 0 or c >= len(board[0]) or index < 0 or index > len(word):
return False
if index == len(word):
return True
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
for x, y in directions:
tmp = board[r][c]
board[r][c] = "#"
if tmp == word[index] and match(board, word, r+x, r+y, index+1):
return True
board[r][c] = tmp
return False
"""
:type board: List[List[str]]
:type word: str
:rtype: bool
"""
if board == word:
return True
if not board and word or board and not word:
return False
for r in range(len(board)):
for c in range(len(board[0])):
if match(board, word, r, c, 0):
return True
return False
It is fixed by following #jeff suggestions from the comments to this question i.e. "r+y" should be "c+y".
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()