# How to make a proper nested for loop for checking each item in an array?

im recreating the asteroids game that the coding train did in processing.py. im in the part of the video when daniel is coding the hit detection of the laser ( i named them bullets) to the asteroids. but when i try to do a nested for in range loop in the draw function so that each bullet checks if it hits any asteroids i get a index out of range error in the console.

here is the segment of code im having trouble with (in the draw function)

``````for x in range(len(bullet)):
bullet[x].renderbullet()
bullet[x].update()
for i in range(len(asteroids)):
if (bullet[x].hits(asteroids[i])):
del asteroids[i]

``````

to test if the nested for loop works properly, iâ€™ve planned that if a bullet impacts an asteroid, it would be deleted.

here is the whole code ( i didnt open any tabs)

``````class Ship:

def __init__(self):
self.ssize = 10
self.facing = 0
self.rotation = 0
self.position = PVector(1000/2, 500/2)
self.velocity = PVector(0, 0)
self.isthrusting = False

def display(self):
pushMatrix()
stroke(255)
fill(0)
translate(self.position.x ,self.position.y)
rotate(self.facing + PI/2 )
triangle(-self.ssize, self.ssize, self.ssize, self.ssize, 0, -self.ssize)
popMatrix()

def turn(self):
self.facing  += self.rotation

def putrotation(self, a):
self.rotation = a

def move(self):
if self.isthrusting == True:
self.thrust()
self.velocity.mult(0.95)

def thrusting(self, b):
self.isthrusting = b

def thrust(self):
force = PVector.fromAngle(self.facing)

def edges(self):
if (self.position.x > width + self.ssize):
self.position.x = -self.ssize
elif (self.position.x < -self.ssize):
self.position.x = width + self.ssize
elif (self.position.y > height + self.ssize):
self.position.y = - self.ssize
elif (self.position.y < -self.ssize):
self.position.y = height + self.ssize

class Asteroids:

def __init__(self):
self.pos = PVector(random(1000), random(500))
self.vel = PVector.random2D()
self.r = random(5, 55)
self.reso = floor(random(5, 10))
self.offset = []
for i in range(0, self.reso ):
self.offset.append(random(-12, 6))

def render(self):
pushMatrix()
translate(self.pos.x, self.pos.y)
stroke(255)
noFill()
beginShape()
for i in range(0, self.reso):
angle = map(i, 0 , self.reso, 0, TWO_PI)
self.spike = self.r + self.offset[i]
x = self.spike*cos(angle)
y = self.spike*sin(angle)
vertex(x,y)
endShape(CLOSE)
popMatrix()

def edges(self):
if (self.pos.x > width + self.r):
self.pos.x = -self.r
elif (self.pos.x < -self.r):
self.pos.x = width + self.r
elif (self.pos.y > height + self.r):
self.pos.y = - self.r
elif (self.pos.y < -self.r):
self.pos.y = height + self.r

def update(self):

class Bullet:
def __init__(self, spos, svel):
self.pos = PVector(spos.x, spos.y)
self.vel = PVector.fromAngle(svel)
self.vel.mult(20)

def update(self):

def renderbullet(self):
pushStyle()
pushMatrix()
stroke(255)
strokeWeight(4)
point(self.pos.x, self.pos.y)
popMatrix()
popStyle()

def hits(self, asteroids):
d = dist(self.pos.x, self.pos.y, asteroids.pos.x, asteroids.pos.y)
if ( d < asteroids.r):
return True
else:
return False

asteroids = []
for i in range (0, 8):
asteroids.append(Asteroids())
ship = Ship()
bullet = []

def keyPressed():
if key == ' ':
bullet.append(Bullet(ship.position, ship.facing))
elif keyCode == RIGHT:
ship.putrotation(0.1)
elif keyCode == LEFT:
ship.putrotation(-0.1)
elif keyCode == UP:
ship.thrusting(True)

def keyReleased():
ship.putrotation(0)
ship.thrusting(False)

def setup():
size(1000,500)

def draw():
background(0)
for x in range(len(bullet)):
bullet[x].renderbullet()
bullet[x].update()
for i in range(len(asteroids)):
if (bullet[x].hits(asteroids[i])):
del asteroids[i]

ship.display()
ship.turn()
ship.move()
ship.edges()
for i in range(0,len(asteroids)):
asteroids[i].render()
asteroids[i].update()
asteroids[i].edges()
``````

here is the coding train video im basing myself on

Hi @nothome123,

Does the following snippet solve your issue ?

``````for b in bullet:
b.renderbullet()
b.update()
for i, a in enumerate(asteroids):
if b.hits(a): del asteroids[i]
``````

YES, WOW THANK YOU SOOOOOOO MUCH. may i ask you a favor? could you please explain the code youâ€™ve made? iâ€™m in the process of learning python and i would like to learn as much as i can.

``````for b in bullet:
b.renderbullet()
b.update()
``````

Iâ€™m just referring to the objects in the `bullet` and `asteroids` array list directly instead of manipulating them with their indices (`b` instead of `bullet[x]`)

`for i, a in enumerate(asteroids)`

• `enumerate()` is a builtin function that adds a counter to an iterable.
• `a` is each `Asteroids()` object contained in the `asteroids` array list and `i` is its index.

Itâ€™s helpful because you can loop over `asteroids` and have the corresponding index counter available at the same time.

1 Like

youâ€™re a genius. thank you so much.

The real reason `for i in range(len(asteroids)):` fails when you use `del asteroids[i]` is b/c the len() of the asteroids[] list is decreased, and the loop still â€śthinksâ€ť it has the same len() as when the loop started!

It iterates over the sequence created by the range() function. It isnâ€™t directly aware of asteroids[] at all!

Now, when we iterate over the target container, be it directly or via the function enumerate(), its `for` loop becomes aware of the current len() of it.

Therefore, when we issue a `del` command this time, it immediately knows its len() had decreased, and it adjusts the number of times it iterates over it accordingly.

Thereâ€™s still a small issue here: Is 1 Bullet object supposed to destroy more than 1 Asteroid?

Even after using `del` once, that loop goes on, checking whether the same Bullet may have destroyed the next Asteroid in the list!

If thatâ€™s an undesirable behavior, we also need to issue a `break` statement in order to stop that loop to continue after we had used `del`:
https://Docs.Python.org/2.7/reference/simple_stmts.html#break

``````for i,a in enumerate(asteroids):
if b.hits(a):
del asteroids[i]
break
``````

Coincidentally, `break` woulda avoided the original `for` loop w/ range() to crash!

And while weâ€™re at it, shouldnâ€™t the Bullet which hits() an Asteroid be `del` along as well?

``````for i,b in enumerate(bullets):
b.update()
b.render()

for j,a in enumerate(asteroids):
if b.hits(a):
del asteroids[j], bullets[i]
break
``````