简体   繁体   中英

Error in writing a backtracking solver for sudoku

so recently I was attempting to write a backtracking method to solve a sudoku puzzle (2d list), and I came upon some errors I can't seem to figure out debugging.

My code:

def areLegalValues(values: list):
    if len(values) in [1,4,9,16,25]:
        for value in values:
            if value <= len(values):
                if value != 0 and values.count(value)>1:
                    return False
            else:
                return False
        return True
    return False

def isLegalRow(board: list, row: list):
    compareList = [ ]

    for r in range(len(board)):

        if r == row:

            for c in range(len(board[0])):
                #print(board[r][c])
                compareList.append(board[r][c])
    return areLegalValues(compareList)

def isLegalCol(board: list, col: list):
    compareList = [ ]
    for r in range(len(board)):
        for c in range(len(board[0])):
            if c == col:
                compareList.append(board[r][c])
                #print(compareList)
    return areLegalValues(compareList)

def isLegalBlock(board: list, block: list):
    compareList = [ ]
    N = int((len(board))**(1/2))
    blockRowNumber = int(block//N)
    blockColNumber = int(block%N)
    for row in range(blockRowNumber*N, blockRowNumber*N+N):
        #print(row)
        for col in range(len(board[0])):
            if col in range(blockColNumber*N, blockColNumber*N+N):
                #print(board[row][col])
                compareList.append(board[row][col])
                # print(compareList)
    return areLegalValues(compareList)

def isLegalSudoku(board: list):
    boardLength = len(board)
    for row in range(len(board)):
        if isLegalRow(board,row) != True:
            return False
        for col in range(len(board[0])):
            if isLegalCol(board,col) != True:
                return False
    for block in range(boardLength):
        if isLegalBlock(board, block) != True:
            return False
    return True

def solveSudoku(board, row=0):
    if row == len(board):
        return board
    else:
        for col in range(len(board[row])):
            if board[row][col] == 0:
                for number in range(1, len(board)+1):
                    board[row][col] == number
                    if isLegalSudoku(board) == True:
                        solution = solveSudoku(board, row+1)
                        if solution != None:
                            return solution
                    board[row][col] == 0
        return None

Test Code:

def testSolveSudoku():
    print('Testing solveSudoku()...', end='')
    Board = [
        [1, 0, 3, 0],
        [4, 3, 0, 1],
        [2, 0, 1, 0],
        [0, 0, 4, 2]
    ]
    Sol = [
        [1, 2, 3, 4],
        [4, 3, 2, 1],
        [2, 4, 1, 3],
        [3, 1, 4, 2]
    ]
    print(solveSudoku(Board))
    assert(solveSudoku(Board) == Sol)
    print('Passed!')

*As the names might suggest, isLegalValues takes in a list and returns if that list contains legal values, aka, the list cannot have duplicates of non zero numbers, 0's can work. isLegalRow and isLegalCol iterate through the sudoku board and build up a list with values of the certain row/column and plug them into isLegalValues. isLegalBlock finds the blocks in the board, and takes in a block number and plugs it into isLegalValues. (Through extensive testing it seems like these all work)

I currently am running into the problem in which, for some reason, my code finish solving the puzzle but manages to finish running. example:

    [1, 2, 3, 0],
    [4, 3, 2, 1],
    [2, 4, 1, 0],
    [3, 0, 4, 2]

should be changed to

[1, 2, 3, 4],
[4, 3, 2, 1],
[2, 4, 1, 3],
[3, 1, 4, 2]

But I keep returning the code 2 lines above. (the one with 0's)

It seems that part of the code is working, as all the values that were replaced are valid, but it seems the backtracking has issues. I was wondering if someone could point out what is going wrong and how might I go about fixing it?

Thanks

Basically, I was making the error in which I was constantly skipping values in the same column with this code, and also was never attempting to solve the last column.

Solution:

def solveSudoku(board: list):
    """takes in a sudoku board and solves the board through use of backtracking
    and returns the dectructively changed board"""
    checkZeroes = True
    for row in range(len(board)):
        for col in range(len(board[0])):
            if board[row][col] == 0:
                checkZeroes = False
    
    if checkZeroes == True:
        return board
    else:
        for row in range(len(board)):
            for col in range(len(board[0])):
                if board[row][col] == 0:
                    for number in range(1,len(board)+1):
                        board[row][col] = number
                        if isLegalSudoku(board) == True:
                            solution = solveSudoku(board)
                            if solution != None:
                                return solution
                        board[row][col] = 0
                    return None

This accomplishes the task, albeit a bit slow, by first checking if the whole board has 0's (base case), after that, it loops through all values in the board and if the value is a 0, it tests all possible (legal) numbers. Then it checks if the whole board is legal or not, and continues on. If there is an error, it backtracks. If there is no solution, None is returned.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM