How to setup functions such that the variables are defined? - python

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)

Related

Can I get the index of an object in an instance array by knowing a value of the object?

Is there a way of getting the index of an instance in an instance array by only knowing a property of that object?
I have something like this:
class NodeGene:
def __init__(self):
self.__initBias()
def __initBias(self):
#Creates a random bias and assigns it to self.bias
class ConnectionGene:
#nodeIn and nodeOut are instances of NodeGene
def __init__(self, nodeIn, nodeOut, innovationNumber):
self.nodeIn = nodeIn
self.nodeOut = nodeOut
self.innovationNumber = innovationNumber
self.__initWeight()
def __initWeight(self):
#Creates a random weight and assigns it to self.weight
class Genome:
def __init__(self, connections):
#connections is an array of ConnectionGene instances
self.connections = connections
How do I get the index of a ConnectionGene in connections if I have the nodeIn and the innovationNumber of the instance I am looking for?
Suppose conn_list is a list of ConnectionGene instances. Then you have a few options:
idx = None
for i, c in enumerate(conn_list):
if c.nodeIn == 0 and c.innovationNumber == 0:
idx = i
break
or
idx_list = [i for i, c in enumerate(conn_list) if c.nodeIn == 0 and c.innovationNumber == 0]
or
idx = next(i for i, c in enumerate(conn_list) if c.nodeIn == 0 and c.innovationNumber == 0)
If you will do this many times, it's probably better to make a reference dictionary and do fast lookups there:
dct = {(c.nodeIn, c.innovationNumber): i for i, c in enumerate(conn_list)}
...
idx = dct[0, 0] # very fast
The following is one way you can do that. I'm not sure where you want your code called, so I'll just refer to connections as "connections" below.
indices = [index for index, elem in enumerate(connections) if elem.nodeIn == ___ if elem.innovationNumber == ____]
if indices:
return indices[0]
return -1
Just fill in the blanks. Obviously you can change whether you want to return first index or just all the indices.
If you want to check if nodeIn is the same object instance as another NodeGene, you can use is instead of ==. If you are using ==, you can define the __eq__ method on the NodeGene class.

Python class returning old value of attribute

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

Appending values to a Python member variable

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'

Example of how to properly define a class in python

I have defined a function as such:
def quicksort(points):
if len(points) < 2: return points
smaller,equal,larger = [], [], []
pivot_angle = find_polar_angle(random.randint(0, len(points) - 1))
for pt in points:
pt_angle = find_polar_angle(pt)
if pt_angle < pivot_angle:
smaller.append(pt)
elif pt_angle == pivot_angle:
equal.append(pt)
else:
larger.append(pt)
return quicksort(smaller) + sorted(equal, key = find_dist) + quicksort(larger)
Now, I want to change my code - which btw is an implementation of the Graham Scan Algorithm - into an object oriented code. So I went ahead and declared a class in a file MyClasses.py:
from MyFunctions import find_anchor, find_polar_angle, find_dist, find_det, quicksort, graham_scan
class Cluster:
def __init__(self):
self.members = []
self.hull = []
self.anchor = None
self.find_anchor = find_anchor
self.find_polar_angle = find_polar_angle
self.find_dist = find_dist
self.find_det = find_det
self.quicksort = quicksort
self.graham_scan = graham_scan
But of course I have to change my functions as well. I don't want to list all the functions here, that's why I stay with the quicksort function as an example. This is where I struggle a lot, since I don't know the python syntax well enough to be sure about what I am doing here. This is my revised form of quicksort:
def quicksort(self, points):
if len(points) < 2: return points
smaller,equal,larger = [], [], []
pivot_angle = self.find_polar_angle(self, random.randint(0, len(self.members) - 1))
for pt in points:
pt_angle = self.find_polar_angle(self, pt)
if pt_angle < pivot_angle:
smaller.append(pt)
elif pt_angle == pivot_angle:
equal.append(pt)
else:
larger.append(pt)
return self.quicksort(self, smaller) + sorted(self, equal, key = self.find_dist) + self.quicksort(self, larger)
Here's the thing: This function is recursive! So I need it to take smaller, equal and larger as arguments. Later, another function graham_scan is going to call this function as such:
self.members = self.quicksort(self, self.members)
I know there are probably many mistakes in here. That's why I'm asking: Is this last expression a valid expression? I mean I am changing a class-variable (self.members) but I do so not by directly changing it, but by assigning it the return value of quicksort.
Anyways, help is very much appreciated!
To make an existing function as a new property of a new class, try this:
def quicksort():
pass # your custom logic
class Cluster:
def __init__(self):
self.members = []
self.quicksort = quicksort
# more properties
Python has quite a different syntax to C++ or Java.
To say about the second question, all the variables used in quicksort function body are only available in that function only.
About second question. All members of classes are PUBLIC in python. By convention you can add "_" and "__" in front of the names for protected and private respectfully. BUT this does NOT prevent you from accessing them, it just means that you (or whoever reading the code) should not misuse them.
Although, __variable must be accessed with following syntax outside of class:
class Square:
def __init__(self, x):
self.__x = x
def get_surface(self):
return self.__x **2
>>> square1 = Square(5)
>>> print(square1.get_surface())
>>> 25
>>> square1._Square__x = 10
>>> print(square1.get_surface())
>>> 100
>>> square1.__x
>>> **AttributeError: 'Square' object has no attribute '__x'**
Or it will raise AttributeError. Hope this helps

Call Function of inside function in Python

I have some Python code in below written in Python 2.7 and I have problem with calling a function form inside another function.
class CSP:
def __init__(self, matrix):
self.X = []
self.D = []
self.C = []
self.matrix = util.copyMatrix(matrix)
self.counter = 0
# Matrix to Vector
vector = [item for line in self.matrix for item in line]
chars = map(str, vector)
result = ['*' if item == '0' else item for item in chars]
def solve(self):
""" Returns the result matrix.
The sudoku matrix is self.matrix.
Use util.printMatrix in purpose of debugging if needed. """
"*** YOUR CODE HERE ***"
def init(self,result):
for i in range(9):
for j in range(1,10):
var = var_char[i]+str(j)
self.X.append(var)
domain = set([1,2,3,4,5,6,7,8,9])
self.D.append(domain)
gamelist = result
for i in range(len(gamelist)):
if(re.match("\d+",gamelist[i])):
self.D[i] = set([int(gamelist[i])])
self.set_constraints()
#########################################################################
def set_constraints(self):
for x in self.X:
for y in self.X:
if((x[0] == y[0] and x[1] != y[1]) or (x[1] == y[1] and x[0] != y[0])):
flag = True
for c in self.C:
if(x in c and y in c):
flag = False
if(flag):
self.C.append(set([x,y]))
for a in [0,3,6]:
for b in [0,3,6]:
self.set_cube_constraints(a,b)
How to call init() function in solve() and also call self.set_constraint() inside init() function?
Within function solve(), init() is a function, not a method. Therefore it can only be called in the same manner that any other unbound function can be called: by passing the correct number of arguments to it. This would work:
init(self, results)
Note that you need to explicitly pass a reference to the object in self because init() is not a method. Within solve() self refers to the CSP instance, so this should work.
However, set_constraints() is also a normal function, so you can not call it from init() with self.set_constraints(), but set_constraints(self) should work. Note that you need to declare function set_constraints() before init() otherwise you will get a "referenced before assignment" error.
Having said all that, this is just awful. Why not make init() and set_constraints() proper methods of the class?
set_constraints is not a part of the class and therefore cannot be called with self.
If you put it one level up (remove one indentation level of it) then your code should work better.
I can see that this is some kind of coding exercise and you are told to write code in one particular place. I think you may be overcomplicating the answer because what you are coding here looks very messy by design and you should probably split out your functionality a lot more if this should be considerered clean code.

Categories

Resources