I'm fairly new to classes in Python. While coding a battleship game I ran into a problem with choosing random x,y coordinates for the locations of computer's ships and the computer's attack coordinates. I am confused about whether to generate random numbers as a local variable in one of the functions or as Class attribute or instance attribute.
Initially I thought to create an instance attribute (below), but i got rand_x is not defined. I tried creating a Battleship function that generated random numbers, but it returned the same pair of coordinates every time it was called. Is the only way to do this to create a local variable for random #s? Because I will be using the random generator more than once it would be nice not to have to repeat that code.
Thanks for your patience.
EDIT: I've included more code with the randomness function, and replaced size in randomness function with self.size.
For example, Battleship(4,2,0,0) might give me a hitlist of [[2,1],[2,1]] I would like random #s inside hitlist.
import random
hitlist=[]; #a global variable
class Battleship(object):
""" Ship object container. A game where the user tries to destroy the enemy's ships User tries to guess computer's position x and y """
def __init__(self, size, numberships,position_x,position_y):
self.position_x=position_x
self.position_y=position_y
self.numberships=numberships
self.size = size
def plotships(self,r):
"""input is integer coordinates for ships and output is an array of arrays with battleship locations CREATES THE HITLIST DONT REPEAT"""
print('plotships function running')
for i in range(self.numberships):
hitlist.append(r) #random number from function randomness
print(hitlist)
return hitlist
def randomness(self):
rand_x=random.choice(range(self.size))
rand_y=random.choice(range(self.size))
randcoord=[rand_x,rand_y]
return randcoord
#Game Interface
size=int(input('Gameboard size'))
numberships=int(input('Waiting for Number of enemy ships'))
b=Battleship(size,numberships,0,0)
random=b.randomness() #create a random x y coordinate
b.plotships(random) #create a hitlist
I think it's because you're calling random.choice with size and not self.size.
i.e.
rand_x = random.choice(range(self.size))
Also, where are you defining self.rand? Surely you're getting problems in the constructor trying to print it?
EDIT - IN RESPONSE TO COMMENT BELOW:
If you want hitlist to be populated with self.numberships pairs of independent random number pairs, the write the plotships method as:
def plotships(self):
"""input is integer coordinates for ships and output is an
array of arrays with battleship locations CREATES THE
HITLIST DONT REPEAT"""
print('plotships function running')
for i in range(self.numberships):
hitlist.append(randomness()) #random number from function randomness
print(hitlist)
return hitlist
To get a random number maybe you could import the random library.
You could use it to initialize your (X, Y) coordenates on an instance of your class.
import random
Battleship(size, numberships, random.randint(0, WIDTH), random.randint(0, HEIGHT))
I'm assuming you have the screen's widht and Height available. Hope it helps.
Related
Title isn't great, sorry.
I am new to python and I am playing around with dictionaries to further my understanding of them.
To practice, I am making a football team of 11 players. Each player is a dictionary stored in a list.
So each player will have its own dictionary but all the keys will be the same throughout, it's just the values that will change.
I have made the players positions and now I want to add the age of the player. This is what I have:
footballers = []
for populating in range(11): #populating = to get footballers
new_player = {"position": 'goalkeeper',}
footballers.append(new_player)
for baller in footballers[1:5]:
baller["position"] = 'defender'
print (baller)
for player in footballers[5:8]:
player["position"] = "midfield"
for player in footballers[8:11]:
player["position"] = "forward"
import random
for baller in footballers:
baller["age"] = random.randint (17, 34)
print (baller)
This works and I get the desired result. However, the age changes every time I run the code.
How would I make it so that I run it once and the value of the key stays the same?
I know I could just type the ages out myself but if I wanted to populate a whole league, I'm not doing that.
I've tried other ways such as making the age:value in another list of dictionaries but I couldn't figure out how to put the 2 together.
Is there something I'm missing here?
Thanks
A seed allows to 'randomly' populate a list with the same values every call.
It's important to have the seed outside the loop.
import random # good practice is to have imports at the top
footballers = []
for populating in range(11):
new_player = {"position": 'goalkeeper',}
footballers.append(new_player)
for baller in footballers[1:5]:
baller["position"] = 'defender'
print (baller)
for player in footballers[5:8]:
player["position"] = "midfield"
for player in footballers[8:11]:
player["position"] = "forward"
random.seed(42)
# the correct position is anywhere before the loop to have the same ages every call
for baller in footballers:
## random.seed(42) # Wrong position - will result in each player have the same age
baller["age"] = random.randint (17, 34)
print (baller)
Notes:
When you run your code in jupyter random.seed() needs to be in the same cell as the random call
42 is just an example, you can use any positive integer
I need a loop over all of my clans, which are instances of a class. Each clan needs to be assigned a position, a x and a y coordinate. Preferably as two lists or as a single tuple (but no idea how I specify that). This is how it works for the 1st clan. Afterwards I always have to check, if the position is already assigned. If so, you have to search for a new position until it is free.
I then coded my class like this:
width = 20
height = 20
no_of_clans = 50
import random
class clan:
def __init__(self, index, position_list):
self.index = index
self.position_list = position_list
def index(no_of_clans):
return list(range(1, no_of_clans +1))
def position_list(self):
for i in range(1, no_of_clans +1):
positions = ()
if i ==1: #i can do it either like this
positions = [(random.randint(0, width)), (random.randint(0, height))]
positions.append(x,y)
else: #or like this, I do not know, which one is better, both are running
x = (random.randint(0, width))
y = (random.randint(0, height))
#check if x and y not already used
#assert
positions.append(x,y)
return positions
print(positions)
how can I assign positions randomly when also using 0.5 steps? I always get an error message.
I never get a list of my positions back, but I do not know what I did wrong.
I know those are probably fairly basic questions but I am quite new to python an already searched for 5h today and I really do ot know where to start anymore. I would be really happy if someon could help me. Thank you so much in advance <3
I found the answer to my original question in this post:
Coordinates of the edges of a honeycomb grid in python
Will update if I am finished with the class-thing :)
This is surely a beginner's question, I'm just having trouble searching for it.
What I want to do is calculate points on a circle for thousands of circles which I will later render. Because I will be rendering many thousands of circles a second, I thought I'd try to avoid unncessary overhead. To do this, I've created a function that calculates a number of points (which I want to be variable, a larger circle will need more points calculated) on a unit circle, and another function which can take these points, translate them and then scale by the radius.
My original code ended up something like this:
class Circle():
...
def CalcCircle(segments):
does some stuff to calculate generic coordinates
def CreateCircle(x, y, r, segments):
does some stuff to create a circle using CalcCircle(segments)
Obviously the problem was that even though I might only want to create circles with 20 segments, I was calling the CalcCircle function (and repeating the same calculations) every time I called CreateCircle.
The only way I could figure out how to fix this was:
class Circle():
...
def CalcCircle(segments):
does some stuff to calculate generic coordinates
CreateCircle_has_not_been_run = True
def CreateCircle(x, y, r, segments):
if Circle.TransCircle_has_not_been_run:
generic_circle = Circle.CalcCircle(segments)
Circle.CreateCircle_has_not_been_run = False
does some stuff to create a circle using generic_circle
I've never formally learnt programming so I'm not sure if this is considered good design. Surely it would become messy if every time I wanted to "initialize" data or call a function only on the first run through I had to make a random class variable. The reason I ask is I'm constantly running into this problem, so I assume there must be a standard way of doing it.
Edit: An example of how the call will be made.
#window.event
def on_draw():
window.clear()
width = window.get_size()[0]
height = window.get_size()[1]
radius = int(width/50)
segments = int(radius*1.5)
for i in range(N):
pyglet.gl.glColor3f(0.05,0.2,0.9)
DrawCircle(positions[i][0],positions[i][1],width,segments)
DrawCage(width,height)
DrawLabel(width,height)
etc.
I'm aware that there's problems here but I'm just trying to illustrate the example (positions comes from the update function if anyone is wondering). As I've said earlier, this is a problem I run into all the time.
I could call Circle.CalcCircle() from the on_resize() function as per Achim's suggestion. I have a hard time believing however that standard practice is to stick two random functions into a class (as it stands neither of them necessarily need to even be in the Circle class), one of which is implicitly dependent on the other and both of which are called in different parts of the code.
I would do something like this:
class Circle:
def __init__(self):
self.unit_circle_points = None
def CalcCircle(self, segments):
# Do some stuff to calculate segments,
# assign calculated values to class attribute
self.unit_circle_points = calculated_points
def CreateCircle(self, X, y, r, segments):
# If circle points have not yet been calculated then calculate
# and store, else just load stored points
if self.unit_circle_points is None:
self.CalcCircle(segments)
unit_circle_points = self.unit_circle_points
# Now use unit_circle_points to do some calculations
Every time you instantiate a circle object it will come with an attribute named unit_circle_points that is initialized to None. When you call the CreateCircle method on that object for the first time it will see that the unit_circle_points attribute is None and perform the necessary computations by calling CalcCircle, storing the results. On subsequent calls to the CreateCircle method of this Circle object the unit_circle_points attribute will no longer be None, and the method will simply use the values stored in the attribute.
Edit:
If this requires to much "implicit" behavior for your taste, you can shift things around so that CalcCircle must be called explicitly by the user to generate the pre-calculated data.
class Circle:
def __init__(self):
self.unit_circle_points = None
def CalcCircle(self, segments):
# Do some stuff to calculate segments,
# assign calculated values to class attribute
self.unit_circle_points = calculated_points
return self
def CreateCircle(self, X, y, r):
# If circle points have not yet been calculated then raise an error,
# else load previously calculated points
if self.unit_circle_points is None:
raise Exception("You'd better explicitly call CalcCircle first.")
unit_circle_points = self.unit_circle_points
# Now use unit_circle_points to do some calculations
The section of code is:
originalpipey = random.randint(0, 500)
pipey = 0 - originalpipey
twopipey = pipey + 650
def spawnpipe(originalpipey, pipey, twopipey):
screen.blit(pipe,(pipex, 0 - originalpipey))
screen.blit(pipe2,(pipex, twopipey))
The original pipe y is how far down the page I want the pipe to be. The final pipe y is then 0 minus that, so that it goes above the screen and then goes that far down (I worked it out). The two pipe y is the second pipe y, basically just the original one add 650, which creates a 100 pixel gap between the two because I made the pipe png in photoshop 550 pixels tall.
I am making a Flappy Bird type game and this code is to make the Y size of the pipe. Whenever I use the function like this, it is the same size. However, if I put the random integer into the function, to make a new size, it just constantly changes the size. Is there any way to pick a new size each time is is repeated? Thanks.
To have a new random integer every time a function is called call randint inside the function.
def myfunc():
r = random.randint(0, 500) # R is set every time function is called
I have a code which creates a class called 'frame'. When you call it, you define a set of arbitrary coordinates and it will create the necessary amount of self.x'n' and self.y'n' variables where 'n' is the number of coordinates you have input. Here is the code so far:
class NoCoords(Exception): "raised if no coordinates are input"
class frame:
def __init__(self,*coords):
try:
for count,pos in enumerate(coords,1):
exec('self.x%s,self.y%s=%s'%(count,count,pos))
except(IndexError): raise NoCoords()
The issue I'm having is that if I define frame1 as a frame class with 2 coordinates and then frame2 as a frame class with 3 coordinates, it won't create a separate amount of x and y coordinates for frame2. It will simply create the x1,y1 and x2,y2 variables required for frame1, and when frame2 is created it will only create the x1,y1 and x2,y2 variables, it won't create a third set of variables (x3,y3) to contain the 3rd coordinate designated in frame2.
So this brings about a few questions:
1. What could be done (if anything) to fix this?
2. Is this a stupid way of going about doing this?
3. If the answer to 2 is 'yes' then what is a better way of doing what I want to accomplish?
def __init__(self,*coords):
self.coords = {}
for count,pos in enumerate(coords,1):
self.coords["x%s" % count] = pos
self.coords["y%s" % count] = pos
Dynamically creating variables in that manor is frowned upon. Its far nicer to just store these "variables" in a dict.
Rather than doing
something.x1
You can do
something.coords["x1"]
An alternative as pointed out would be to use
for count,pos in enumerate(coords,1):
setattr(self, "x%s" % count, pos)
But to me, that just feels a touch dirty, it makes it harder to get the total number of "x" variables for a start.