开发者

Why does this code return a list index error?

开发者 https://www.devze.com 2023-01-31 08:24 出处:网络
Basically it\'s supposed to take a set of coordinates and return a list of coordinates of it\'s neighbors. However, when it hits here:

Basically it's supposed to take a set of coordinates and return a list of coordinates of it's neighbors. However, when it hits here:

if result[i][0] < 0 or result[i][0] >= board.dimensions:
    result.pop(i)

when i is 2, it gives me an out of index error. I can manage to have it print result[2][0] but at the if statement it throws the errors. Why is this happening?

def neighborGen(row,col,board):
    """
    returns lists of coords of neighbors, in order of up, down, left, right
    """

    result = []
    result.append([row-1 , col])
    result.append([row+1 , col])
    result.append([row , col-1])
    result.append([row , col+1])

     #prune off invalid neighbors (such as (0,-1),开发者_如何学Go etc etc)
     for i in range(len(result)): 
        if result[i][0] < 0 or result[i][0] >= board.dimensions:
            result.pop(i)
        if result[i][1] < 0 or result[i][1] >= board.dimensions:
            result.pop(i)

    return result 


You get the indexes to iterate over from the list, and then you proceed to remove elements from the list. Eventually you're going to hit an index that no longer exists. Use a list comprehension to filter instead.

result = [entry for entry in result if entry[0] >= 0 and
  entry[0] < board.dimensions and entry[1] >= 0 and
  entry[1] < board.dimensions]


During the for loop, you are popping elements which decreases the size of result. Yet, i will still go from 0 to 4.


If you pop off an element, then it's no longer in the list - but your index doesn't move, so it's now pointing to the next element. Say you pop off two elements... your list size after that would be 2, but the for loop is still going to try to go to 4.


def neighborGen(row,col,board):
    """
    returns lists of coords of neighbors, in order of up, down, left, right
    """

    return [
        x for x in (
            [row-1 , col], [row+1 , col], [row , col-1], [row , col+1]
        ) 
        if x[0] >= 0 and x[0] < board.dimensions and
        x[1] >= 0 and x[1] < board.dimensions
    ]


Use a list comprehension to remove the invalid points. For example:

result = [coords for coords in result
          if all(0 <= c < board.dimension for c in coords)]


As others said, the indexes of the remaining elements change when you remove elements from the middle of the list. To avoid problems, you could process the elements from last index to first:

for i in range(len(result)-1, -1, -1): 
   ...

Now, when the current element is removed, only indexes for already processed elements change. Since those elements are already processed and don't need to be accessed again, it doesn't matter if their indexes are no longer valid.


The canonical way to avoid this kind of off-the-board testing is to have a buffer zone of one or two rows/columns at the edges of the board. The board positions would have, as well as the usual values of "empty" or which side owned the square with what type of piece, a special "invalid" code. This would usually be coupled with a board representation as a ONE-dimensional array.

For example, chess would use an array of size 120 (10 columns x 12 rows). You need a margin of 2 to cater for knights ... 2 rows at each end but you need only one vertical margin as it can be shared.

E.g.

KNIGHT_DELTAS = (-21, -19, -12, -8, 8, 12, 19, 21)
# I have a knight at "currpos"; what are its options?
for delta in KNIGHT_DELTAS:
    newpos = currpos + delta
    target = board[newpos]
    if target & EMPTY:
        can_move_to(newpos)
    elif target & OTHER_TEAM:
        can_capture(at=newpos, captured_piece=target & PIECE_VALUE)
    else:
        # Either off the board or occupied by MY_TEAM.
        # Notice that we didn't even need to test explicitly for "off the board".
        pass
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号