I am new to Python and OO programming in general and so please forgive the, probably, very poorly designed code (any tips would be greatly appreciated).
In this, contrived, MWE which is purely to illustrate a similar problem in my larger project. I want to iterate through a 3x3 grid and fill it, so that it contains all the digits 1-9, the only values I can change are ones which are currently set a 0. i.e. If the grid currently has the digits 1-7 and two positions are 0 then one of these 0s becomes an 8 and one becomes a 9, in this instance there are two solutions since the order of the 8 and 9 can also be swapped.
I have designed a backtracking solver (runSolver()) and it does solve this problem, what I am struggling to do though is store the solutions when I reach them. I have added a print statement for when a solution is reached and this prints out the solution as expected, I then try to append this solution to a list and instead of appending the solution that has just been found it instead appends the initial, unsolved, grid.
class Grid:
def __init__(self):
self.grid = np.zeros((3, 3))
def writeGrid(self, grid):
self.grid = grid
def printGrid(self):
print(self.grid)
def getValue(self, col, row):
return self.grid[row][col]
def setValue(self, col, row, num):
self.grid[row][col] = num
class Solver:
def __init__(self, grid):
self.grid = grid
self.solutions = []
self.n_solutions = 0
def isValid(self, num):
for i in range(3):
for j in range(3):
if self.grid.getValue(i, j) == num:
return False
return True
def runSolver(self):
for row in range(3):
for col in range(3):
if (self.grid.getValue(col, row)) == 0:
for num in range(1,10):
if self.isValid(num):
self.grid.setValue(col, row, num)
self.runSolver()
self.grid.setValue(col, row, 0)
return
self.grid.printGrid() # this line prints the actual solutions when reached (it works)
self.solutions.append(self.grid) # this should append the solution to 'solutions'
self.n_solutions += 1 # keeps track of how many solutions there are
The main function which actually shows the problem is then,
# Set up game
gameGrid = Grid()
gameGrid.writeGrid([[1, 4, 5],
[0, 6, 0],
[7, 8, 9]])
solverGrid = Solver(gameGrid)
# Run the solver
solverGrid.runSolver()
# This should print out the found solutions,
# It actually prints out the initial, unsolved, grid twice
for i in range(solverGrid.n_solutions):
solverGrid.solutions[i].printGrid()
From some searching online I think that I may be getting confused between instance attributes and class attributes and the scope with which they are accessible however I am really not sure.
When you run self.solutions.append(self.grid) you basically just append a reference to the self.grid to self.solutions. So at the end of your runSolver you have a list of references in self.solutions that all point to the same object.
This has to do with the fact that both your Grid object and Numpy arrays are mutable objects. In contrast to Python strings, for example, when you modify them (with self.grid.setValue(col, row, num) for example), the same object is modified in place instead of a new object being created.
Here is the same issue illustrated with a list of lists:
>>> l = []
>>> x = [1]
>>> l.append(x)
>>> l
[[1]]
>>> x.append(2)
>>> l.append(x)
>>> l
[[1, 2], [1, 2]]
You'll have to create a copy of the grid every time you add it to self.solutions so that you can have a "snapshot" of the grid as it was at that point.
You could do something like this:
class Grid:
def __init__(self, grid=None):
if grid == None:
self.grid = np.zeros((3, 3))
else:
# Copy the array, otherwise we'll have the same mutability issue as above.
self.grid = np.copy(grid)
In runSolver:
grid_copy = Grid(self.grid.grid)
self.solutions.append(grid_copy) # this should append the solution to 'solutions'
Related
I have no idea what the difference is between these two codes. When I rum those codes in the scoring python code, it marks mine wrong. I would appreciate it if you can tell me the different between using a variable to make a new empty list and appending values verses just making a list with values in it. The first code is mine and the second code is the code from the answer sheet. Thank you very much :)
class Server:
def __init__(self):
self.list = []
def makeOrder(self,orderNum, orderList):
existance = False
for order in self.list:
if order[0]==orderNum:
existance = True
if existance == True:
return -1
else:
self.list.append([orderNum,orderList])
return [orderNum,orderList]
class Server:
def __init__(self):
self.q = []
# 1
def makeOrder(self, orderNumber, orderList):
existAlready = False
for order in self.q:
if order[0] == orderNumber:
existAlready = True
if existAlready == True:
return -1
else:
tmp = []
tmp.append(orderNumber)
tmp.append(orderList)
self.q.append(tmp)
return tmp
Functionally, these are both very similar. The only difference of substance I see (other than obvious variable names, etc) is in the return value.
In the first option, you can see that one list is appended to self.list, and a new (albeit identical in value) list is returned. This means that this is a different object.
self.list.append([orderNum,orderList])
return [orderNum,orderList]
However in the second option, you can clearly see that tmp is both pushed AND returned:
tmp = []
tmp.append(orderNumber)
tmp.append(orderList)
self.q.append(tmp)
return tmp
This is a single object that gets appended and returned.
Fundamentally, this mens that any modification to the returned list in the second option will be reflected inside self.q, while in the first option, the returned value is wholly independent. If you wanted to more or less replicate the behavior of option 1, you can change:
return tmp
with
return list(tmp)
Although keep in mind that if orderList is itself a mutable list, the same behavior will occur if you modify the returned value's [1] element. Since it is a reference to a data structure, modification of that list will also affect self.q (or self.list in the first option)
Example:
>>> class Foo:
... def __init__(self):
... self.q = []
... def bar(self, i, l):
... tmp = [i, l]
... self.q.append(tmp)
... return list(tmp)
...
>>> f = Foo()
>>> f.q
[]
>>> x = f.bar(1, [1,2,3])
>>> x
[1, [1, 2, 3]]
>>> x[1].append(4)
>>> x
[1, [1, 2, 3, 4]]
>>> f.q # it changed here as well
[[1, [1, 2, 3, 4]]]
I = [[140,50],[140,80],[140,110],[140,140]]
O = [[140,50],[170,50],[140,80],[170,80]]
tomato = [I,O]
class Change():
def __init__(self,):
self.liste = []
def change(self,):
self.liste.extend(tomato[0])
for i in range(10):
self.liste[0][0] += 10
That class when ı use change method this changing global tomato[0] how can i fix that.
Here is a simplified example illustrating your problem.
Initialize a list object x:
x = [0]
Initialize another list a, storing the previous list x in it as an element:
a = [x]
Initialize yet another list b and add the elements from a to it;
the only element in a is the list object x, which is now appended to b:
b = []
b.extend(a)
This means that the first (and only) element in b is that same list object x;
not a clone, not another list with its elements, but that exact same object:
print(b[0] is x)
Gives True.
Notice that I did not check equality, but identity using is.
This means I can write
b[0][0] = 1
print(x[0] == 1)
and get True.
Your code is a great example of why it is usually a terrible idea to muck around with global variables.
Soo, i'm trying to create a list of lists in python. Here is the code:
class CacheL1:
def __init__(self):
self.vias = []
for via in range(2):
self.conjuntos = []
for conj in range(4):
self.conjunto = Via(0, 0, 0)
self.conjuntos.append(self.conjunto)
self.vias.append(self.conjuntos[:])
All positions should have different objects, but I am storing the same lists in different positions.
For example, the object of position [0][0] is a different object of [0][1], and this is what i expected. But the object of position [0][0] is the same object of [1][0], what i'm not expecting.
I've searched a similar question, but i couldn't find. Sorry if this is a recurrent question. If i wasn't clear, i will try again(as u can see english isn't my main language).
Edit, my Via:
class Via:
def __init__(self, mru, validade, bloco):
self.mru = mru
self.validade = validade
self.bloco = bloco
if type(bloco) == int:
self.tag = 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?
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.)
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
I'm new to programming, and I'm having trouble relating my functions in such a manner that the variables I'm using are defined. For example, I initialized the following board for a game I'm developing:
def initialize(dimension, nb_of_pieces, difficulty):
board = []
for i in range(dimension):
row = [None] * dimension
board.append(row)
i = 0
while i < nb_of_pieces:
initial_element = math.pow(2, difficulty)
row_position = randint(0, dimension-1)
column_position = randint(0, dimension-1)
if initial_element not in board:
board[row_position][column_position] = initial_element
i = i + 1
return board
This produces a board with all 'none' values except at two random positions, where there is a float value. Now I need to write a function that checks for possible matches in a row. A match is found when two identical elements ('None' values are ignored) are next to each other or only separated by 'None'. For example [2, None, 2, None] would count as a match. I now want to write a new function def check(row) that checks the indices of the initial elements (the float value '2.0') in each row, and then compares if they are next to each other or separated by 1. The function would return True if this is the case. I wrote:
def check(row):
for row in board:
for i in range(dimension):
if board[i] == None:
continue
else:
if board[i] == board[i + 1] or board[i] == board[i + 2]:
return True
else:
return False
I'm not sure if this is correct, since Python keeps saying the variables are undefined, so I cannot check my program. How can I use the variables I defined in other functions? Also, I want to print the result somewhere (i.e True or False). Should this be done inside the function?
I recommend using a Class here. This will eliminate the need to constantly pass around your board variable, and make it much cleaner to write more behaviors of boards into class methods. Note that the class's __init__() method doesn't need to return anything explicitly.
class Board:
def __init__(self, dimension, nb_of_pieces, difficulty):
self.board = []
for i in range(dimension):
row = [None] * dimension
self.board.append(row)
(etc...)
def check(self, row):
for row in self.board:
(etc...)
Then simply instantiate a Board by doing:
my_board = Board()
if you want to use a variable defined in a function in an other function. You best have to pass it in argument.
def initialize(dimension, nb_of_pieces, difficulty):
board = [2, None, 2, None]
return board
def check(row, board):
return True
board = initialize(5,4,3)
check(None, board )
There's still a possibility to use nested function or global variable. but when i can i prefer to use the option i describe above..
so to use global variable you can do :
def initialize(dimension, nb_of_pieces, difficulty):
global board
board = [2, None, 2, None]
def check(row):
global board
if len(board)> 5:
return True
else
return False
board = []
initialize(5,4,3)
check(None)