Pygame: Whenever I try to pick up a tile that I have already thrown, I get an error

Viewed 71

I was making a game with pygame where you could pick up tiles and throw them. I ran into an issue where if I threw a block and tried to pick it back up, I get an error. I know what the issue is, but I don't know how to fix it

IndexError: pop index out of range This gets the index of the block that I am going to pick up:

# Checks if player is touching a block and pressing down arrow to remove a block and add a block above the player's head
    if collisions["left"] and pick_block or collisions["right"] and  pick_block:
        for tile in xcollisions:
            if pick_block_count == 0:
                if not holding_block:
                    tpb = tile
                    tile_pops.append(tile_rects.index(tile))
                    holding_block = True
                    pick_block_count += 1
    else:
        xcollisions = 0
# Removes the tile that is picked up
    for tile in tile_pops:
        tile_rects.pop(tile)
        drectp = display_rects.pop(tile)

That removes the block. I know what the problem is, but I don't know how to fix it

Here is my full code: https://pastebin.com/3eszkgkK Here is the map I use: https://pastebin.com/ftWL4mVD For the textures you can use any 16x16 texture

2 Answers

Consider this piece of code:

fruits = [ 'apple', 'banana', 'cherry', 'durian' ]

for i in range( len( fruits ) ):
   print( "i == %d" % ( i ) )
   fruits.pop( i )
   print( "-------------------------" )
   print( "fruits now: "+str( fruits ) )

It fails with:

Traceback (most recent call last):
  File "./bad_pop.py", line 5, in <module>
    fruits.pop( i )
IndexError: pop index out of range

This is because on the first iteration of the loop, it removes fruits[0]. That's fine. Then fruits[1]. But then on the third pass, the list is only two elements long - 0 and 1, but i is 2! It's out of range.

This is what's happening with your pop(), as the list shrinks, your index to remove doesn't shrink with it.

remove fruits[0] - [apple]
fruits now: ['banana', 'cherry', 'durian']
-------------------------
remove fruits[1] - [cherry]
fruits now: ['banana', 'durian']
-------------------------
Traceback (most recent call last):
  File "./bad_pop.py", line 5, in <module>
    f = fruits.pop( i )
IndexError: pop index out of range

So what can you do. Re-work your loop so that it only ever uses pop() to take the 0th element. Or loop from len( list ) down to 0, so the shrinking-size of the list does not matter.

EDIT: Maybe something like:

tile_pops.sort()
tile_pops.reverse()
for tile in tile_pops:
    tile_rects.pop(tile)
    drectp = display_rects.pop(tile)

But that's a fairly computationally-expensive way of doing it.

I finally fixed my code. The issue wasn't because as the list shrinks, the index to remove doesn't shrink with it, but it was what I originally stated. I had to pop the tile from the thrown_drect list and pop the tile from tile_pops. Here is what I am trying to explain:

# If the tile that is being popped has been thrown before, it pops it from thrown_drect (or else it would cause an error)
    if ttpop:
        print(abs((len(tile_rects)-tile_pops[-1]) - len(thrown_drect)))
        tpopped_value = thrown_drect.pop(abs((len(tile_rects)-tile_pops[-1]) - len(thrown_drect)))
        tile_pops.pop(-1)
    # Removes the tile that is picked up
    for tile in tile_pops:
        tile_rects.pop(tile)
        drectp = display_rects.pop(tile)
    if ttpop:
        print("yes")
        drectp = tpopped_value
        print(drectp[0])
        ttpop = False
Related