NEAT算法实现小行星躲避AI时出现列表索引越界问题求助
排查NEAT小行星游戏的列表索引越界问题
我帮你梳理下这个问题的核心原因,再给出适配你需求的解决方案——毕竟你是NEAT新手,还希望每次只运行单个genome,先把这个基础逻辑跑通会更省心。
核心问题分析
你怀疑的for x in range(len(players))确实是问题根源,具体有两个点:
- 动态列表的遍历冲突:你在循环内部的碰撞逻辑里执行了
del players[x]、del nets[x]、del ge[x],这会直接缩短三个列表的长度。但循环的遍历范围是一开始的列表长度,当x递增到超过新的列表长度时,自然就会触发索引越界。 - 不符合单Genome运行需求:你想要每次只运行一个genome,但当前代码是一次性初始化所有genome对应的玩家、网络、基因组对象,再逐个循环运行,既违背了你的需求,又增加了索引管理的复杂度。
解决方案
1. 改成单Genome运行模式(最适合你的新手需求)
直接调整main函数的逻辑,不用维护三个关联列表,逐个处理每个genome即可,这样完全避免索引问题:
def main(genomes, config): # 逐个处理每个genome,每次只运行一个 for genome_id, g in genomes: net = neat.nn.FeedForwardNetwork.create(g, config) current_player = player() g.fitness = 0 game_over = False # 初始化小行星(去掉原列表里多余的0,直接存4个 asteroid 对象) asteroids = [ asteroid(1, playable_height-39, 10.5, 35, 39, 0), asteroid(2, playable_height-25, 15, 25, 25, 10), asteroid(3, playable_height-30, 12.5, 30, 30, 15), asteroid(4, playable_height-35, 10, 35, 35, 20) ] while not game_over: # 处理退出事件 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() quit() # 神经网络决策输出 output = net.activate(( current_player.y / 169, GetDistance(0, current_player, asteroids), # 注意索引改成0,因为列表从0开始了 GetDistance(1, current_player, asteroids), GetDistance(2, current_player, asteroids), GetDistance(3, current_player, asteroids) )) # 玩家移动逻辑 if output[0] > 0: current_player.moveDOWN() elif output[0] < 0: current_player.moveUP() # 边界限制 if current_player.y > playable_height - current_player.height: current_player.y = playable_height - current_player.height elif current_player.y < 0: current_player.y = 0 # 画面绘制 window.fill((0, 0, 0)) window.blit(bg_img, (0, 0)) current_player.draw() # 小行星逻辑处理 collision_happened = False for ast in asteroids: if ast.x < 0: current_player.score += 1 g.fitness += 5 ast.reset(asteroids) # 重置已躲避的小行星 ast.move(current_player) # 碰撞判定 if ast.collision(current_player): g.fitness -= 1 collision_happened = True break # 碰撞后直接跳出小行星循环 if collision_happened: game_over = True # 文本显示 Text_display("Score: " + str(current_player.score * 100), white, 0, 200) Text_display("Asteroid 1: " + str(GetDistance(0, current_player, asteroids)), white, 0, 220) Text_display("Asteroid 2: " + str(GetDistance(1, current_player, asteroids)), white, 0, 240) Text_display("Asteroid 3: " + str(GetDistance(2, current_player, asteroids)), white, 0, 260) Text_display("Asteroid 4: " + str(GetDistance(3, current_player, asteroids)), white, 0, 280) Text_display("Space Ship Position: " + str(current_player.y / 169), white, 0, 300) pygame.display.update()
2. 额外的细节优化
- 原代码里
asteroids列表第一个元素是0,导致你访问小行星时要从索引1开始,很容易混淆,改成直接存储4个asteroid对象的列表更合理。 - 碰撞时的
reset方法,建议确保它能独立重置当前小行星的位置,不要过度依赖外部列表参数。
3. 如果之后想尝试多Genome运行
要是你之后想让多个AI同时跑,那不能用for x in range(len(players))的正向遍历,应该用倒序遍历,这样删除元素不会影响未处理的索引:
# 倒序遍历列表索引,删除元素后不会打乱前面的索引 for x in range(len(players)-1, -1, -1): game_over = False while not game_over: # 你的原有逻辑(碰撞后删除元素的代码保留)
内容的提问来源于stack exchange,提问作者Zafirmk
相关产品推荐
相关产品推荐

