Loop creates too many children [duplicate] - python

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.

Related

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.

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()

Program does not recognize value as a valid variable value?

The program I am trying to create involves writing a method called monster_fight(monster1,monster2) to have the two monsters "Fight". However I am having one issue retrieving the damage value stored in each of the monster object dictionaries named 'self.attacks'.
I am trying to retrieve a value from dictionary 'monster1.attacks' to reduce the hp of the monster2 object. However with the current code I have in place, the program does not recognize the value of the keys when I call the dictionary. Can anybody show what I am doing wrong?
Thanks!
class Monster():
def __init__(self, name, max_hp = 20, hp=20):
self.name = name
self.type = type
self.current_hp = max_hp
self.attacks = {'wait': 0}
self.possible_attacks = {'sneak_attack': 1,
'slash': 2,
'ice_storm': 3,
'fire_storm': 3,
'whirlwind': 3,
'earthquake': 2,
'double_hit': 4,
'wait': 0}
self.exp = 0
def add_attack(self, attack_name):
if attack_name in self.possible_attacks:
self.attacks[attack_name] = self.possible_attacks.get(attack_name)
return True
else:
return False
if attack_name in self.attacks:
return False
def remove_attack(self, attack_name):
if attack_name in self.attacks:
self.attacks.pop(attack_name)
if len(self.attacks) == 0:
self.attacks['wait'] = 0
return True
else:
return False
def win_fight(self):
self.exp += 5
self.current_hp = self.max_hp
def lose_fight(self):
self.exp += 1
self.current_hp = self.max_hp
def monster_fight(monster1,monster2):
round1 = 0
moves1 = []
moves2 = []
list1 = []
list2 = []
for i in monster1.attacks:
values = ''
values = monster1.attacks.get(i)
list1.append(values)
for i in range(0,len(monster2.attacks)):
values = monster2.attacks.get(i)
list2.append(values)
while monster1.current_hp > 0 or monster2.current_hp > 0:
round1 += 1
monster1_attack = int(monster1.attacks[list1[(round1-1)%len(list1)]])
monster2.current_hp -= monster1_attack
moves1.append(list1[(round1-1)%len(list1)])
monster2_attack= monster2.attacks[list2[(round1-1)%len(list2)]]
monster1.current_hp -= monster2_attack
moves2.append(list2[(round1-1)%len(list2)])
if monster1.current_hp <= 0:
monster1.lose_fight()
monster2.win_fight()
return round1, monster2.name, moves2
elif monster1.current_hp <= 0:
monster2.lose_fight()
monster1.win_fight()
return round1,monster1.name, moves1
else:
return -1,"None","None"
a = Monster("a", 9)
b = Monster("b", 9)
a.add_attack("ice_storm")
b.add_attack("ice_storm")
b.remove_attack("wait")
a.remove_attack("wait")
round1, winner, moves = monster_fight(a, b)
print(round1)
print(winner.name)
print(moves)
monster1_attack = int(monster1.attacks[list1[(round1-1)%len(list1)]])
KeyError: 3
Well, let's see. You're calling:
monster1_attack = int(monster1.attacks[list1[(round1-1)%len(list1)]])
monster1 is a Monster object, created from:
a = Monster("a", 9)
a.add_attack("ice_storm")
a.remove_attack("wait")
So monster1.attacks looks like:
{
'ice_storm': 3,
}
You're trying to access that dictionary using key dervied from list1[(round1-1)%len(list1)].
list1 is set here:
for i in monster1.attacks:
values = ''
values = monster1.attacks.get(i)
list1.append(values)
After the above code runs, list1 is a list that looks like:
[3]
(Because you ask for monster1.attacks.get(i), which will return the value associated with key i.)
So when you ask for list1[(round1-1)%len(list1)], you get the value 3, which means you're asking for monster1.attacks[3].
There is no key named 3 in monster1.attacks. As we saw earlier, the only key is ice_storm which has the value 3. It looks like you're trying to figure out the damage that monster1's attack will do. That is actually what you have in list1, so in theory you could just write:
monster1_attack = list1[(round1-1)%len(list1)]
monster2.current_hp -= monster1_attack
I think your logic here may be a bit convoluted. You should probably think carefully about exactly what you're trying to accomplish and try to simplify your code with that goal in mind. Using the Python debugger to see the value of your variables prior to the error -- or using print statements to accomplish the same thing -- can help diagnose this sort of problem.

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

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)]

Python - function is not returning variable (string) [duplicate]

This question already has an answer here:
python function called but not returning value. Should be simple
(1 answer)
Closed 6 years ago.
I'm quite new to Python and this is for programming project in my CS course. The code seems to work perfectly EXCEPT on the last line of code:
return finalWord
does not return anything. I have tried replacing this with:
print(finalWord)
and it does print everything correctly.
Essentially in the program, I must create a Random class that pseudo-randomly generates numbers and then in the class Nonce, add several different words to the dictionary, and using the dictionary, create new, random words.
class Random:
def __init__(self, seed):
self.seed = seed
def next(self, range):
self.seed = (7 ** 5 * self.seed) % (2 ** 31 - 1)
return self.seed % range
def choose(self, objects):
return objects[self.next(len(objects))]
class Nonce:
def __init__(self, seed):
self.first = []
self.follow = {}
self.random = Random(seed)
def add(self, word):
self.first += [word[0]]
for x in range(0, len(word) - 1):
self.follow.setdefault(word[x], []).append(word[x + 1])
return None
def make(self, size):
nextLetter = self.random.choose(self.first)
finalWord = nextLetter
for x in range(1, size):
if nextLetter in self.follow:
followList = self.follow[nextLetter]
nextLetter = self.random.choose(followList)
finalWord += nextLetter
else:
finalWord += self.random.choose(self.first)
return finalWord
potato = Nonce(101)
potato.add('python')
potato.add('java')
potato.add('html')
potato.make(5)
Thanks in advance.
If you are calling the function directly, then you must either do something with the returned value (like printing it) or store it in a variable for later use. The code is "not working" because you are never calling the function, at least not in the excerpt that you provided.
Printing the response:
print(instance_name.make())
Storing it in a variable:
variable_name = instance_name.make())
The code is where the instance of the class is called instance_name
In your code, you wrote potato.make(5). The function runs, but does not print anything or store it in a variable. To print the response, you would need to do the following:
print(potato.make(5))

Categories

Resources