Python: storing object in 2d array and calling its method - python

I'm trying my hand at making a chess app. Code below:
#file containing pieces classes
class Piece(object):`
name = "piece"
value = 0
grid_name = "____"
class Pawn(Piece):
# Rules for pawns.
#If first move, then can move forward two spaces
name = "Pawn"
value = 1
grid_name = "_PN_"
first_move = True
#Main file
from Piece import *
class GameBoard:
pieces = []
grid = [][]
def __init__(self):
self.grid[1][0] = self.pieces.append(Pawn())
currentBoard = GameBoard()
I want to call the value variable for the object located at grid[1][0]
It would look something like:
print currentBoard.grid[1][0].value
This code doesn't work which tells me I'm missing something regarding the scope of the objects and variables. Is this something that is possible in Python?
EDIT - Solution
I did find a solution to this in using the grid list to hold a reference to the indexing of the objects in the pieces list. Code below:
class GameBoard:
# initialize empty board
grid = [["____" for i in range(8)] for j in range(8)]
pieces = []
def __init__(self):
self.grid[0][0] = 0
self.grid[0][1] = 1
self.grid[0][2] = 2
self.grid[0][3] = 3
self.grid[0][4] = 4
self.grid[0][5] = 5
self.grid[0][6] = 6
self.grid[0][7] = 7
self.grid[1][0] = 8
self.grid[1][1] = 9
self.grid[1][2] = 10
self.grid[1][3] = 11
self.grid[1][4] = 12
self.grid[1][5] = 13
self.grid[1][6] = 14
self.grid[1][7] = 15
pieces = []
pieces.append(Pawn())
#grid will return the integer which can be passed to the other list to pull an
#object for using the .value attribute
print pieces[currentBoard.grid[1][0]].value

Rewriting your code so that it simply runs as a single file:
#file containing pieces classes
class Piece(object):
name = "piece"
value = 0
grid_name = "____"
class Pawn(Piece):
# Rules for pawns.
#If first move, then can move forward two spaces
name = "Pawn"
value = 1
grid_name = "_PN_"
first_move = True
class GameBoard:
pieces = []
grid = [[],[]]
def __init__(self):
self.grid[0][1] = self.pieces.append(Pawn())
currentBoard = GameBoard()
There are a few things that need to be corrected. For one, the variables defined in Piece, Pawn and GameBoard are not defined under the __init__() method. This means that these variables will be shared by all instances of the class.
Example:
>>> pawn1 = Pawn() # Make two Pawns
>>> pawn2 = Pawn()
>>> print pawn1.first_move, pawn2.first_move
True, True
>>> pawn1.first_move = False # Change the first pawns attribute
>>> print pawn1.first_move, pawn2.first_move # But both change
False, False
To avoid this, define your class attributes under the method __init__() for all three of your classes.
Example:
class Pawn(Piece):
# Rules for pawns.
#If first move, then can move forward two spaces
def __init__(self):
self.name = "Pawn"
self.value = 1
self.grid_name = "_PN_"
self.first_move = True
Next, your variable grid is not properly defined in python. If you wanted a list with two empty lists you could do the following
grid = [[], []]
But an easy way to make an 8x8 structure of empty lists would be to do the following
grid = [[[] for i in xrange(8)] for j in xrange(8)]

Related

Create x instances of a class and store the order they were created in

I want to create x amount of instances of a class and store (inside the instance) the order they were created in. Is there a way that I can do that?
MaxAnts = 100
class Ant:
def __init__(self,id):
self.id = id
# then I will use the id to do math
i = 0
if i <= MaxAnts:
i += 1
i = Ant(i)
But for some reason this doesn't work.
The class itself can give ids to its instances.
class Ant:
counter = 0 # counter is a class variable
def __init__(self):
cls = type(self)
self.id = cls.counter # id is an instance variable
cls.counter += 1
# example:
a = Ant()
b = Ant()
c = Ant()
print(a.id, b.id, c.id) # prints: 0 1 2
Using list comprehension as described in the comments is not a bad way to go but I'll put this here just to give you a introduction to lists.
MaxAnts = 100
class Ant:
def __init__(self,id):
self.id = id
#then i will use the id to do math
ants = [] # or ants = list() , same thing
i = 0
if i <= MaxAnts:
i += 1
ants.append(Ant(i))
The only wierd thing here is that because lists are 0 indexed ants[0].id will be 1 and ant[99].id will be 100. If you do ants = [Ant(i) for i in range(MaxAnts)] you will have your index lined up with your ids, ants[7].id = 7. But you could get the same effect by putting i+=1 after ants.append(Ant(i)) instead of the line before.
To create x amount of instances of some class you would need to use some form of iteration and append them to a collection that you would be able to access.
MaxAnts = 100
class Ant:
def __init__(self,id):
self.id = id
#then i will use the id to do math
ants = [] # A list to store your ants
for i in range(MaxAnts): # Iterating i from 0 to MaxAnts - 1
ant = Ant(i) # Creating your new ant with the i as a parameter
ants.append(ant) # Adding your new ant to the ants list
Since by default 'range' gives you a range from 0 to argument - 1, in case you want to start your order from 1, you should start your iteration from 1, and end it on MaxAnts:
for i in range(1, MaxAnts + 1): # Iterating i from 1 to MaxAnts
More on lists: https://docs.python.org/3/tutorial/datastructures.html
More on range: https://docs.python.org/3/library/functions.html#func-range

AttributeError in Python when attempting to initialize a class in a class

I am attempting to call a class into a class's initializing method, but I am receiving an attribute error. Does anyone have any ideas why this might be occurring? I was able to do this exact thing with another class, but it doesn't seem to like the Movement() class I made.
The add_piece() method in Board() seems to be the one causing the issues.
class Movement:
"""
Class to help separate movements from the board methods
"""
def __init__(self):
pass
def ind(self, pos):
"""
Converts the string input to an easier index for the board
"""
row = int(pos[1:]) - 1
column = self.letter_to_column(pos)
return row, column
def letter_to_column(self, pos):
"""
Method to convert string letters of columns to int to index
"""
column_dict = {}
column_dict['a'] = 0
column_dict['b'] = 1
column_dict['c'] = 2
column_dict['d'] = 3
column_dict['e'] = 4
column_dict['f'] = 5
column_dict['g'] = 6
column_dict['h'] = 7
column_dict['i'] = 8
return column_dict[pos[0]]
# BOARD -------------------------------------------------------------------
class Board:
"""
Represents the board and its pieces
"""
def __init__(self):
"""
Builds the board and places the pieces in its spots
"""
self._board = []
for i in range(10):
self._board.append([None for i in range(9)])
self.place_pieces()
self._move = Movement()
def add_piece(self, pos, piece):
"""
pos is a string of a letter (column) and a number (row)
for example pos = 'a2' which is the first column in the second row
"""
self._board[self._move.ind(pos)[0]][self._move.ind(pos)[1]] = piece
The issue isn't related to Movement: it's related to a missing method in Board. Board.place_pieces is not defined, so the expression self.place_pieces() in Board.__init__ will throw an AttributeError.
As an asideā€”if the only purpose of the Movement class is to collect related helper functions, you might want to consider making those classmethods or staticmethods instead (which you can read more about here):
class Movement:
COLUMNS = {
letter: index
for index, letter in enumerate("ABCDEFGH")
}
#classmethod
def get_indices(cls, coordinate): # cls is to class methods what self is to instance methods.
# Rough validation of the coordinate:
if not len(coordinate) == 2:
... # Raise some exception.
# Assignment via sequence unpacking
# See https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
column, string_row = coordinate.upper()
# Row should be representable as a digit and
# in a specific range.
row_index = int(string_row)
if row_index < 1 or row_index > 8:
... # Raise an exception.
# If column isn't in ABCDEFGH, the dictionary
# access here will throw (desirable).
return row_index - 1, cls.COLUMNS[column]
Movement.COLUMNS is a class variable generated using a dictionary comprehension. enumerate is a built-in function that we use to return an index for each letter in the string ABCDEFG.
You can then skip the creation of self._mode and write:
row_index, column_index = Movement.get_indices("A2")
in Board.add_piece, for example.

Python saving value as an instancemethod instead of integer

So, I am currently writing a program that randomly generates a grid maze. The following code segment is the class definition of a single "cell" of the grid. I have defined 2 getter methods get_row and get_col, which can retrieve the coordinates of the cell. I believe this is where the issue lies.
class cell: #Node storing each cell of grid
def __init__(self, rowNum=None, colNum=None):
self.rowNum = rowNum
self.colNum = colNum
self.blocked = 0 # 0 denotes False, all cells are initially unblocked
self.f = 0 # Distance from start node
self.g = 0 # Distance from goal node
self.h = 0 # Total cost ( h = f + g )
self.parent = None
def get_row(self):
return self.rowNum
def get_col(self):
return self.colNum
def isBlocked(self):
if self.blocked == 0:
return 0
if self.blocked != 0:
return 1
Then, I use the following segment to traverse the grid and randomly determine certain squares as the start and end. I save these coordinates as start_row, start_col, end_row, and end_col.
# List containing all the unblocked cells
unblockedCells = [cell() for i in range(numUnblocked)]
start_row = -1
start_col = -1
end_row = -1
end_col = -1
# Randomly picks 2 unblocked cells to be the starting and ending point
def set_start_and_end():
global start_row
global start_col
global end_row
global end_col
ctr1 = 0
for i in range(totalRows):
for j in range(totalCols):
if cellsArray[i][j].blocked == 0:
unblockedCells[ctr1] = cellsArray[i][j]
ctr1 = ctr1+1
#Initialize start position
startIndex = random.randint(0,len(unblockedCells)-1)
start_row = unblockedCells[startIndex].get_row
start_col = unblockedCells[startIndex].get_col
#Initialize target position
endIndex = random.randint(0, len(unblockedCells)-1)
end_row = unblockedCells[endIndex].get_row
end_col = unblockedCells[endIndex].get_col
print("Start: (",start_row,", ",start_col,") End: (",end_row,", ",end_col,")")
set_start_and_end()
However, when I try to access the values of start_row and start_col later in the program, I receive the error list indices must be integers, not instancemethod
My question is, why are start_row and start_col being stored as instancemethod and not as integers?
Here's a small snippet to illustrate the different options
class Cell:
"""Doc strings go here btw, not as comments. Also take a look at pep8 naming conventions: https://www.python.org/dev/peps/pep-0008/#naming-conventions"""
def __init__(self, row_num = None, col_num = None):
self.row_num = row_num # A public instance member - you can get and set this directly without the need for function
self._col_num = col_num # A private member. By convention, you should not get or set this directly
def get_row(self):
"""This is a method, you need so call it using parenthesis i.e. .get_row()"""
return self.row_num
#property
def col_num(self):
"""This is property being used to expose a private member. You don't need parenthesis to call it."""
return self._col_num
my_cell = Cell(1, 2)
print(my_cell.row_num) # row_num is a public member, you can just access it directly
print(my_cell.get_row) # get_row is a method, so this will print the actual function
print(my_cell.get_row()) # this calls the function and prints the integer
print(my_call.col_num) # because of the property decorator, we don't have to call this function but can treat is as if it was a member. But note it would be weird to name it get_... now as verbs imply functions.
In your case I would just use a public instance member. There is no need for any of your functions. In fact this whole object looks like it could just have been a named tuple:
from typing import NamedTuple
class Cell(NamedTuple):
row: int
col: int
blocked: bool
or if you're using python>=3.8 a dataclass
from dataclasses import dataclass
#dataclass
class Cell:
row = None
col = None
blocked = False

access list of instantiated objects form within the object

I want to access a list of instantiated objects with a method inside the objects' class in Python 3.
I assume I can't give the the whole list to the object, as it would contain itself.
Concretely: how do I access cells[] from within the class cell? Or is this the wrong way to think about it? the end goal is to easily program cell behavior like cell.moveUp() -- all cells are connected to 8 neighbors.
I am missing something, probably since I don't have much experience in python/programming.
#!/usr/bin/env python3
import random
class cell:
""" cell for celluar automata """
def __init__(self, n=0, nghbrs=[], a=0.00, b=0.00, c=0.00):
self.n = n #id
self.nghbrs = nghbrs #list of connected neighbors
self.a = a #a value of the cell
self.b = b
self.c = c
def growUp(self):
if self.a > .7: # if cell is "bright"
cells[self.nghbrs[7]].a = self.a # update cell above (nghbrs[7] = cell above )
def main():
iterations = 4
links = initLinks() # 150 random links [link0, link2, ... , link7]*150
val1 = initval() # 150 random values
cells = [cell(nghbrs[0], nghbrs[1], val1[nghbrs[0]])for nghbrs in enumerate(
links)] # create cell objects, store them in cells and init. neigbours , a
for i in range(iterations): # celluar automata loop
for c in cells:
c.growUp()
def initLinks(): #for stackoverflow; in real use the cells are arranged in a grid
nghbrs = []
for i in range(150):
links = []
for j in range(8):
links.append(random.randrange(0, 150))
nghbrs.append(links)
return nghbrs
def initval():
vals = []
for i in range(150):
vals.append(random.random())
return vals
if __name__ == "__main__":
main()
run as is cells cannot be accessed in the method growUp():
NameError: name 'cells' is not defined
You could make a CellsList class (subclass of list) that has a method which you call to get a new cell.
class CellsList(list):
def add_cell(self, *args, **kwargs):
"""
make a cell, append it to the list, and also return it
"""
cell = Cell(cells_list=self, *args, **kwargs)
self.append(cell)
return cell
then in the cell itself (I've renamed the class Cell and above I am using cell as in instance variable in accordance with usual capitalisation convention) you have an attribute cells_list where you store a back-reference to the cells list. (I'm also fixing the initialisation of nghbrs to avoid a mutable object in the defaults.)
class Cell:
""" cell for celluar automata """
def __init__(self, n=0, nghbrs=None, a=0.00, b=0.00, c=0.00, cells_list=None):
self.n = n #id
self.nghbrs = (nghbrs if nghbrs is not None else []) #list of connected neighbors
self.a = a #a value of the cell
self.b = b
self.c = c
self.cells_list = cells_list
def growUp(self):
if self.a > .7: # if cell is "bright"
self.cells_list[self.nghbrs[7]].a = self.a # update cell above (nghbrs[7] = cell above )
And then inside main, you can change your current code that instantiates Cell (or what you call cell) directly (your line with cells = ...) to instead use cells.add_cell
cells = CellsList()
for nghbrs in enumerate(links):
cells.add_cell(nghbrs[0], nghbrs[1], val1[nghbrs[0]])
Here we're not actually using the value returned by add_cell, but we return it anyway.
Note: this approach allows you to maintain multiple independent lists of cells if you wish, because it does not rely on any class variables to hold the list -- everything is held in instance variables. So for example, your main program could model multiple regions, each containing a different cells list, by instantiating CellsList more than once, and calling the add_cell method of the relevant CellsList instance to create a new cell.
You can track instances of cell() by making the cells list a static variable of your class, which can be easily accessed from within all instances of the class.
import random
class cell:
""" cell for celluar automata """
cells = []
def __init__(self, n=0, nghbrs=[], a=0.00, b=0.00, c=0.00):
self.n = n #id
self.nghbrs = nghbrs #list of connected neighbors
self.a = a #a value of the cell
self.b = b
self.c = c
def growUp(self):
if self.a > .7: # if cell is "bright"
self.cells[self.nghbrs[7]].a = self.a # update cell above (nghbrs[7] = cell above )
def main():
iterations = 4
links = initLinks() # 150 random links [link0, link2, ... , link7]*150
val1 = initval() # 150 random values
cell.cells = [cell(nghbrs[0], nghbrs[1], val1[nghbrs[0]])for nghbrs in enumerate(
links)] # create cell objects, store them in cells and init. neigbours , a
for i in range(iterations): # celluar automata loop
for c in cell.cells:
c.growUp()
def initLinks(): #for stackoverflow; in real use the cells are arranged in a grid
nghbrs = []
for i in range(150):
links = []
for j in range(8):
links.append(random.randrange(0, 150))
nghbrs.append(links)
return nghbrs
def initval():
vals = []
for i in range(150):
vals.append(random.random())
return vals
if __name__ == "__main__":
main()

Loop creates too many children [duplicate]

This question already has answers here:
New instance of class with a non-None class attribute?
(4 answers)
Closed 5 years ago.
I've been working on an implementation for a problem from HackerRank. Namely this one: https://www.hackerrank.com/challenges/battleship1p/problem
My language of choice is Python3 and it will be a good fit, I guess. Now here is a basic construct on which my plan was, to build the project upon:
class Cell():
# - status open
# h hit
# m miss
neighboring = []
status = None
cell_x = None
cell_y = None
def __init__(self, cell_x, cell_y):
self.cell_x = cell_x
self.cell_y = cell_y
status = '-'
def check_status(self):
return self.status
def check_ut(self):
if (self.status == "-"):
return True
def check_hit(self):
if (self.status == "h"):
return True
def check_miss(self):
if (self.status == "m"):
return True
def add_neighboring(self, c):
self.neighboring.append(c)
def check_neighboring(self):
for x in self.neighboring:
if (x.check_ut()):
return x
def check_neighboring_is_hit(self):
if self.check_hit():
for x in self.neighboring:
if (x.check_ut()):
return x
class Row():
Cells = []
y = None
def __init__(self, y):
for i in range(10):
self.Cells.append(Cell(i, y))
class Board():
Rows = None
def populate_neighbors(self):
for l in self.Rows:
for c in l.Cells:
if (c.cell_x > 0):
prev_cell = l.Cells[c.cell_x - 1]
prev_cell.add_neighboring(c)
c.add_neighboring(prev_cell)
if (c.cell_y > 0):
above_cell = self.Rows[c.cell_y - 1].Cells[c.cell_x]
above_cell.add_neighboring(c)
c.add_neighboring(above_cell)
print("test")
def NewRow(self):
self.Rows.append(Row(len(self.Rows)))
def __init__(self, rows):
self.Rows = []
for i in range(rows):
self.NewRow()
list_ships = [1, 1, 2, 2, 3, 4, 5]
z = Board(10)
z.populate_neighbors()
It tries to rebuild a Board of a player, which I initalise with 10 rows Board(10) and it should also create 10 of Cells per row. But because of something happening in the background it seems to create 100 fields per row, at least my debugger says that. I would appreciate if you can give me a hind where duplication or recreation or something like that happens.
In populate_neighbors, my goal was to find all adjacent cells for a particular cell, by iterating through first all rows and then cell by cell, to add to the previous the current selected and to the current selected the previous, same for the height of the gamefield. Now I hope you understood what this intentionally was about.
Well, in your code Row.Cells is a class attribute and is shared between all of the instances of Row. The loop always append to the same list that's why we have 100 cells. To fix you will need:
class Row():
Cells = [] # this is not a field declaration like Java
y = None
def __init__(self, y):
self.Cells = [] # need this
for i in range(10):
self.Cells.append(Cell(i, y))
I'd recommend you to re-read the basics of Python.

Categories

Resources