I'm making a natural selection simulator program. One of the things I want to happen is when two parent objects collide for an offspring object to be created that randomly inherits some of its parents traits. In particular, I'm looking to pass on the int values width and height. The problem is, I'm only looking to pass on width and height into the object when it is offspring. I want the initial spawn of parent objects to have completely randomized values.
My whole code is:
import pygame, random
pygame.init()
map_width = 800
map_height = 800
size = [map_width, map_height]
screen = pygame.display.set_mode(size)
# pygame already defines a lot of colors, we we just use them
colors = pygame.color.THECOLORS
pygame.display.set_caption("Natural Selection Game")
done = False
clock = pygame.time.Clock()
# just a simple generator to generate an id for each object
def id_generator():
i = 0
while True:
i += 1
yield i
ids = id_generator()
# just a helper function that wraps pygame.sprite.collide_mask
# to prevent a sprite from colliding with itself
def collide(a, b):
if a.id == b.id:
return False
return pygame.sprite.collide_mask(a, b)
class Organism(pygame.sprite.Sprite):
def __init__(self, id, org_list, width, height, color = None):
pygame.sprite.Sprite.__init__(self, org_list)
self.org_list = org_list
self.id = id
# Speed and direction
self.change_x = random.randrange(0,6)
self.change_y = random.randrange(0,6)
# Dimensions
self.width = width or random.randrange(20,60)
self.height = height or random.randrange(20,60)
x = random.randrange(0 + width, map_width - width )
y = random.randrange(0 + height, map_height - height)
self.rect = pygame.rect.Rect(x, y, width, height)
self.image = pygame.surface.Surface((width, height))
self.image.fill(colors['hotpink2'])
self.image.set_colorkey(colors['hotpink2'])
# we either pass in the color, or create a random one
self.color = color or random.choice([colors['red'], colors['green'], colors['blue']])
pygame.draw.ellipse(self.image, self.color, [0, 0, width, height])
self.mask = pygame.mask.from_surface(self.image)
# we keep track of collisions currently happening
# so we only spawn one children for each collisions
self.collisions = set()
# just something to limit the number of organisms
self.age = 0
self.children = 0
# Initiate movement
def update(self):
self.age += 1
# we move by simply moving the rect
# the Group's draw function will look that the rect attribute
# to determine the position for drawing the image
self.rect.move_ip(self.change_x, self.change_y)
# we can make use of a lot of Rect's attributes to make
# this computation simpler
if self.rect.left < 0 or self.rect.right > map_width:
self.change_x *= -1
if self.rect.top < 0 or self.rect.bottom > map_height:
self.change_y *= -1
# only reproduce if we are at least 200 ticks old
# so newly created organisms spwan new ones at the
# very moment they spawned themself
if self.age < 200:
return
# just an arbitary limit so the screen does not get too full
if self.age > 500:
print (self.id, ' died of age')
# kill() removes the Sprite from all its Groups (which is only org_list at the moment)
self.kill()
return
# just an arbitary limit so the screen does not get too full
if self.children > 4:
print (self.id, ' died of too many children')
self.kill()
return
# check if we collided with another Sprite
collided = pygame.sprite.spritecollideany(self, self.org_list, collide)
# also check if this
# - is a new collision
# - the other organism is at least 200 ticks old
# - there are not too many organisms at the screen at the moment
if collided and not collided.id in self.collisions and collided.age > 200 and len(self.org_list) < 100:
# keep track of the current collision, so this code is not triggerd
# every frame while the colliding organisms move other each other
self.collisions.add(collided.id)
collided.collisions.add(self.id)
print (self.id, ' collided with ', collided.id)
# inherit parent dimensions
width = random.choice(self.width, collided.width)
height = random.choice(self.height, collided.height)
# let the dimensions mutate sometimes for fun
if random.randrange(0, 100) < 5:
width = random.randrange(5, 100)
print ('Offspring of', self.id, ' and ', collided.id, ' mutates a dimension change')
elif random.randrange(0, 100) < 5:
height = random.randrange(5, 100)
print ('Offspring of', self.id, ' and ', collided.id, ' mutates a dimension change')
elif random.randrange(0, 100) < 5:
width = random.randrange(5, 100)
height = random.randrange(5, 100)
print ('Offspring of', self.id, ' and ', collided.id, ' mutates a dimension change')
# let the color mutate sometimes for fun
if random.randrange(0, 100) < 5:
color[random.randrange(0, 3)] = random.randrange(0, 256)
print ('Offspring of', self.id, ' and ', collided.id, ' speciates')
# create the new child with the new color
Organism(next(ids), self.org_list, list(map(int, color)))
self.children += 1
collided.children += 1
else:
# if there are currently no collisions, we clear the collisions set
# so new collisions can happen
self.collisions = set()
# we use a Group for all the draw/update/collision magic
org_list = pygame.sprite.Group()
for _ in range(15):
Organism(next(ids), org_list)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# we just call update on the group so update is called
# an every sprite in the group
org_list.update()
screen.fill(colors['white'])
# same for drawing: just call draw on the group
org_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
Again, specifically, I'm looking to pass on width and height when there are parents with width and height, but in the initial genesis spawn when there have been no parents, I want those values to have been randomly generated.
Actually, the constructor of the class organism
has a width
and a height
argument and these then set the attributes self.width
and self.height
with them if they are not empty ( None
) and if they are, they are set by random values:
class Organism(pygame.sprite.Sprite): def __init__(self, id, org_list, width, height, color = None): pygame.sprite.Sprite.__init__(self, org_list) self.width = width or random.randrange(20,60) self.height = height or random.randrange(20,60) # [...]
Therefore, if you want random values, you must pass None
to these arguments:
for _ in range(15):
Organism(next(ids), org_list, None, None)
Alternatively, you can pass concrete values, for example, the width and height of the parent:
class Organism(pygame.sprite.Sprite):
# [...]
def update(self):
# [...]
if collided and not collided.id in self.collisions and collided.age > 200 and len(self.org_list) < 100:
# [...]
Organism(next(ids), self.org_list, self.width, self.height, list(map(int, color)))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.