Search code examples
pythonarraysreturnbacktracking

Problems with returns in a backtracking loop


I'm working on a backtracking algorithm and it works fine but I have issues with outputs i need to display the result. The code first imports a .txt file with a sudoku board in this format (underscores as empty cases)(line breaks are here because it would display it on a single line on the forum, there isn't line breaks in the file):

729___3

_1__6_8

___4__6

96___41_8

_487_5_96

__56_8__3

It then transform it in a Python array with 0 in lieu of underscores.

After that I created a class SudokuSolver (the code comes from here: text. This class solves the board with the methodsolve() and print it with the method print_board():

from termcolor import colored


def fileToArray(inputFile):    
        
        createdBoard = []
        
        with open(inputFile, "r", encoding='utf-8') as fichier: 
            for line in fichier:
                createdBoard.append(line.rstrip())
                
        for i in range(len(createdBoard)):
            createdBoard[i] = list(createdBoard[i].replace(u'_',u'0').strip())
            
        for i in range(len(createdBoard)):
            for j in range(len(createdBoard[i])):
                createdBoard[i][j] = int(createdBoard[i][j])
        return createdBoard        
        print(createdBoard)

array1 = fileToArray("/home/dennis/Dev/PythonDev/Divers/grilles_sudoku/sudoku2.txt")
print(array1)


class SudokuSolver:
    
    """
    
    Cette classe résout une grille de sudoku et l'affiche dans le terminal
    avec les chiffres de la grille initiale en couleur
    
    """
    
    
    def __init__(self,input_array):
        self.input_array = input_array
        self.unsolved1 = self.input_array.copy()
        self.unsolved2 = self.input_array[:]
        self.unsolved3 = copy.deepcopy(self.input_array)

    def print_board(self,x):
        
        """
        La méthode print_board se charge de l'affichage de la solution.
        En vert les chiffres de la grille initiale, en blanc les chiffres trouvés pour la solution.
        """
        for i in range(0, 9):
            for j in range(0, 9):
                print(str(x[i][j]) if self.unsolved1[i][j] == 0 else colored(str(self.unsolved1[i][j]), 'green'), end=" ")
            print() 
            
        for i in range(0, 9):
            for j in range(0, 9):
                print(str(x[i][j]) if self.unsolved2[i][j] == 0 else colored(str(self.unsolved2[i][j]), 'green'), end=" ")
            print()       
        for i in range(0, 9):
            for j in range(0, 9):
                print(str(x[i][j]) if self.unsolved3[i][j] == 0 else colored(str(self.unsolved3[i][j]), 'green'), end=" ")
            print()
    
    def isPossible(self,board, row, col, val):
        for j in range(0, 9):
            if board[row][j] == val:
                return False

        for i in range(0, 9):
            if board[i][col] == val:
                return False

        startRow = (row // 3) * 3
        startCol = (col // 3) * 3
        for i in range(0, 3):
            for j in range(0, 3):
                if board[startRow+i][startCol+j] == val:
                    return False

        return True      
          
    def solve(self):
        
        self.board = self.input_array

        for i in range(0, 9):
            for j in range(0, 9):
                if self.board[i][j] == 0:
                    for val in range(1, 10):
                        if self.isPossible(self.board, i, j, val):
                            self.board[i][j] = val

                            self.solve()

                            # Bad choice, make it blank and check again
                            self.board[i][j] = 0
  
                    return self.board
        #putting return self.board here the lopp dosen't stop
        # return self.board
        self.print_board(self.board)

a = SudokuSolver(array1)
b = a.solve()

#when I call the "solve" method and try to print it it prints the initial unsolved board
print("a.solve :", b)
               

In this state the program works, it solves the board and print it. In order to display the result with the initial ciphers in color, I want to retrieve the solved board.

If I place the return in the end of the solve() method, out of the loops, the program doesn't stop. As placed like here, it works with the print_board method using it outside the loop (thing I don't understand).

When writing this program, at first I placed the import function in the class, but when I call the input_array it is modified and I retrieve a solve board. The fact is that I face the same problem. So an auxiliary question to the return problem is: how to store a copy of an array in the program while another is modified ? Even I tried to declare a variable like self.unsolved_array = input_array in the init, just under the self.input_array, but it was modified to when I called it. After searching and trying a few things my mind is a little messy now.

EDIT

I tried the solution proposed by Nolann Boyere and @colidyre and only the deepcopy() worked. If I understood the problem, with a copy or a slice the 2nd list still points to the adress of the list 1, yes ?
With a deepcopy(), a new adress is created to the list 2 object, yes ?

I'm able now to make my program work as I wish but I'm still confuse with the return indentation problem if someone would like to enlighten me on that.


Solution

  • When you do list_2 = list_1, you don't say you're creating a new list called list_2 with the contents of list_1. You're actually linking list_2 to the object that list_1 points to. As a result, modifying one will modify the other. Here's an example:

    list_1 = [1,2,3]
    list_2 = list_1
    list_1.append(4)
    print(list_2) # [1,2,3,4]
    

    Instead, you can use the slice list_1[:] operator to select the contents of the list :

    list_1 = [1,2,3]
    list_2 = list_1[:]
    list_1.append(4)
    print(list_2) # [1,2,3]
    

    Doing list_1[:] is the same as doing list_1[0:len(list_1)], so selecting contents from the start to the end of the list.