0

I have a main class, a second class that handles getting a sudoku grid (2D array), and a third class that solves the puzzle and is supposed to return a completed puzzle. When I run the program, the solve method works fine and prints out the solved puzzle. However, when I call SudokuSolver.get_grid() from another class, the original, unsolved grid that was passed into SudokuSolver is being returned. Also, when I tried to return self.grid from the solve() method, it always returns [[None]].

Here is my solver class:

import numpy as np


class SudokuSolver:

    def __init__(self, in_grid):
        self.grid = in_grid

    def get_grid(self):
        return self.grid

    def solve(self):

        for y in range(9):
            for x in range(9):
                if self.grid[y][x] == 0:
                    for n in range(1, 10):
                        if self.possible(y, x, n):
                            self.grid[y][x] = n
                            self.solve()
                            self.grid[y][x] = 0
                    return
        print('Solved')
        print(np.matrix(self.grid))

    def possible(self, y, x, n):

        for i in range(0, 9):
            if self.grid[y][i] == n:
                return False
        for i in range(0, 9):
            if self.grid[i][x] == n:
                return False

        x0 = (x//3) * 3
        y0 = (y//3) * 3

        for i in range(0, 3):
            for j in range(0, 3):
                if self.grid[y0 + i][x0 + j] == n:
                    return False

        return True

Second Class:

solver = SudokuSolver(self.get_grid())
solver.solve()
completed = solver.get_grid()
print('Completed')
print(np.matrix(completed))

Why is SudokuSolver returning the old grid value that was passed in, and not the completed one printed at the end of the solve() method?

wwii
  • 23,232
  • 7
  • 37
  • 77
CWheel
  • 3
  • 1
  • 1
    Could you post (or link to) an example of the format it expects for its grid? That would make it easier to reproduce and test the issue. (Also, I don't think you need the `get_grid` method—in Python, it's usually good form to avoid getters unless they actually do something. Simply access `solver.grid` directly.) – CrazyChucky Jul 12 '20 at 04:56
  • `SudokuSolver(self.get_grid())` produces a NameError. – wwii Jul 12 '20 at 05:11
  • If either of the answers solved your problem, please consider upvoting and accepting. Otherwise, you can comment with questions or issues. – CrazyChucky Jul 15 '20 at 05:36

2 Answers2

0

Since python passes lists by reference, you are passing a reference to the old solver's grid. You need to initialize your solver with a copy of the other grid like this:

import copy

class OtherClass():

    def start_solver(self):
        grid_copy = copy.deepcopy(self.get_grid())
        solver = SudokuSolver(grid_copy)
        #...

Because you have a list of lists, you have to use deep copy. If it was just a single level list you could use grid.copy() or grid[:] to do a shallow copy.

It might be a good idea to move the copy operation into the constructor of the solver or into get_grid().

deep copy

pass by reference

Ian Wilson
  • 6,223
  • 1
  • 16
  • 24
  • Are you sure that's the issue? That just means that the solver's grid and the original grid are the same, so they should *both* change (which may or may not be fine in this case). But it appears that *neither* changes, and adding the deepcopy doesn't change the program output for me. – CrazyChucky Jul 12 '20 at 05:14
  • I think there are a lot of issues but you're right. Reading their question again, they are expecting the opposite of what I thought was the problem. They **want** the grid to be synced across all 3 classes but it is not synced. Like your comment said, seems that we need to see there other 2 classes (all the code) to debug properly. I probably jumped the gun. – Ian Wilson Jul 12 '20 at 05:26
0

This line:

self.grid[y][x] = 0

is getting called repeatedly after the solution is complete. To verify, put any print statement immediately before or after it. You'll see that its output will appear both before and after your "Solved" output.

I am not well-versed in Sudoku theory, but this simple fix worked for me. Initialize a solved attribute in the initializer:

self.solved = False

When you know you're done, set it to True:

print('Solved')
print(np.matrix(self.grid))
self.solved = True

And then nest the zeroing code in a conditional:

if not self.solved:
    self.grid[y][x] = 0

Now that command will be skipped if your puzzle is already solved. There may be a more elegant way to implement the algorithm, but this fixes the functionality of your code.

(Note that as pointed out in Ian Wilson's answer, in your current code, solver.grid and the containing class's self.grid are one and the same, so the solver is actively changing the original grid supplied to it. This may or may not be what you intended.)

CrazyChucky
  • 3,263
  • 4
  • 11
  • 25