-1

So I'm trying to re-create a spaceInvaders game with python. Here is where my aliens are stored:

aliens = dict()

and each time we increase the game level then a new alien is added with it's id as a key and his positions and health(each alien has 2 health and dies after two shots which deal one damage each) as his value, so the aliens dictionary can look like that:

aliens = {
0: [(100, 50), 2],
1: [(50, 200), 1],
etc...
}

now each time an alien dies I set it's health to -1 and set his positions to (-100, -100) to put him out of the view, now the problem occurs when I call a cleanAlien() function which iterates through the alien array and pops every alien that has a health equal to -1 or an out of bounds position, here is that function:

def cleanAliens():
    global aliens
    for i in range(len(aliens)):
        if(aliens[i][0] == (-100, -100) or aliens[i][1] < 0):
            aliens.pop(i);
            i -= i

However when I try to run that I get an error:

if(aliens[i][0] == (-100, -100) or aliens[i][1] < 0):
KeyError: 0

Any ideas?

Thanks in advance.

Alex
  • 840
  • 7
  • 23
  • add `print(aliens)` in each iteration of the loop and make sure that there is a key=0 in the dict – Meccano Jun 12 '20 at 11:08
  • @Meccano this is the last output before it raises the error: `{1: [(540, 103), 2], 2: [(218, 110), 2]}` – Alex Jun 12 '20 at 11:09
  • Why are doing `i -= i` at end of your loop? Why you choose to write `i = 0` in such untypical manner? – Daweo Jun 12 '20 at 11:09
  • @Daweo I did that because when I pop an element then the length of the dictionary is reduced, but even without the `i -= 1` it throws me the same error. – Alex Jun 12 '20 at 11:10
  • 1
    Please [edit] your question and provide a [mre]. – martineau Jun 12 '20 at 11:26

3 Answers3

1

I'm not sure for the reason of this specific error, but it seems like the structure of aliens is not as expected.

However, you can iterate over the keys of the dictionary instead of over the size of it like so:

for i in list(aliens.keys()):
    if aliens[i][0] == (-100, -100) or aliens[i][1] < 0:
        aliens.pop(i)

And that way you don't get into this key trouble.

The list(aliens.keys()) returns a copy of the keys in the dictionary so you iterate over it, and after you remove one from the dictionary, you just continue to the next one.

(btw, the i -= i line is meaningless because i will be set next iteration according to its iterator, so you can't actually change its value)

Meccano
  • 537
  • 4
  • 8
  • Thank you for your useful response, got me other the error however i now get that: `for i in alien.keys(): RuntimeError: dictionary changed size during iteration` – Alex Jun 12 '20 at 11:15
  • In the same place in the code? I've tested this method on a dummy dictionary and it seems to work. – Meccano Jun 12 '20 at 11:23
1

Instead of:

for i in range(len(aliens)):
    if(aliens[i][0] == (-100, -100) or aliens[i][1] < 0):
        aliens.pop(i);
        i -= i

you should have used a comprehension:

aliens = { k:v for k,v in aliens.items() if v[0] != (-100,100) and v[1] >= 0 }

UPDATE: testing after receiving the comment about "the same error":

>>> aliens = {
... 0: [(100, 50), 2],
... 1: [(50, 200), 1],
... }
>>> aliens = { k:v for k,v in aliens.items() if v[0] != (-100,100) and v[1] >= 0 }
>>> 

There's no error.

lenik
  • 23,228
  • 4
  • 34
  • 43
  • Thanks for your response however that does not work in my case and throws the same error – Alex Jun 12 '20 at 11:13
0

EDIT: Fixed my error by creating a list which is appended each key to be removed which is then passed to this function:

def Aliens():
    global aliens
    clean = list()
    for i in aliens.keys():
        if(aliens[i][0] == (-100, -100) or aliens[i][1] < 0):
             clean.append(i)
    cleanAliens(clean)

and

def cleanAliens(cleanList):
    global aliens
    for i in range(len(cleanList)):
            aliens.pop(cleanList[i]);
Alex
  • 840
  • 7
  • 23