このプログラムを実行するたびに、次のエラーが発生します。
_ValueError: list.remove(x): x not in list
_
エイリアンがボルトに当たったときはいつでも、エイリアンの体力を下げようとしています。その単一のエイリアンも、そのヘルスが_<= 0
_の場合は破棄する必要があります。同様に、ボルトも破壊されます。これが私のコードです:
_def manage_collide(bolts, aliens):
# Check if a bolt collides with any alien(s)
for b in bolts:
for a in aliens:
if b['rect'].colliderect(a['rect']):
for a in aliens:
a['health'] -= 1
bolts.remove(b)
if a['health'] == 0:
aliens.remove(a)
# Return bolts, aliens dictionaries
return bolts, aliens
_
ValueError
はaliens.remove(a)
の行で発生します。明確にするために、aliens
とbolts
は両方とも辞書のリストです。
何が悪いのですか?
ループしているリストから項目を削除しないでください。代わりにコピーを作成します。
for a in aliens[:]:
そして
for b in bolts[:]:
ループ中にリストを変更すると、ループに影響します。
>>> lst = [1, 2, 3]
>>> for i in lst:
... print i
... lst.remove(i)
...
1
3
>>> lst
[2]
2回ループしているリストから項目を削除すると、処理が少し複雑になり、ValueErrorが発生します。
>>> lst = [1, 2, 3]
>>> for i in lst:
... for a in lst:
... print i, a, lst
... lst.remove(i)
...
1 1 [1, 2, 3]
1 3 [2, 3]
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: list.remove(x): x not in list
ループのeachレベルで変更するリストのコピーを作成すると、問題を回避できます。
>>> lst = [1, 2, 3]
>>> for i in lst[:]:
... for i in lst[:]:
... print i, lst
... lst.remove(i)
...
1 [1, 2, 3]
2 [2, 3]
3 [3]
衝突が発生した場合は、b
ボルトonceを削除するだけで済みます。エイリアンを傷つけるループでは必要ありません。後でエイリアンを個別に一掃してください:
def manage_collide(bolts, aliens):
for b in bolts[:]:
for a in aliens:
if b['rect'].colliderect(a['rect']) and a['health'] > 0:
bolts.remove(b)
for a in aliens:
a['health'] -= 1
for a in aliens[:]:
if a['health'] <= 0:
aliens.remove(a)
return bolts, aliens
これを引き起こしているコードにバグがあります。簡略化したコードは次のようになります。
for b in bolts:
for a in aliens:
for a in aliens:
bolts.remove(b)
これにより、aliens
のすべてのエントリに対してb
を複数回ループすることになります。 aliens
の最初のループでbが削除された場合、2回目にループすると、エラーが発生します。
修正するいくつかのこと。まず、aliens
以外の何かを使用するようにa
の内部ループを変更します。
for b in bolts:
for a in aliens:
for c in aliens:
if hit:
bolts.remove(b)
次に、b
をbolts
から一度だけ削除します。そう:
for b in bolts:
for a in aliens:
should_remove = False
for c in aliens:
if hit:
should_remove = True
if should_remove:
bolts.remove(b)
このコードには他にも問題があると思いますが、それが主な問題の原因です。 Martijnの投稿も役立つかもしれません。
ボルトにも「健康」を与え、1に初期化します。次に、1つのネストされたループを実行してすべての損傷を計算し、2つの別々のネストされていない「ループ」を実行して、「死んだ」ものをすべて削除します。ただし、ループしているリストを変更したくないので、そのようにしないでください。コピーを作成することはまだ複雑すぎます。あなたが本当にやりたいことは直接まだ「生きている」ものだけの新しいリストを作成するであり、リスト内包表記(またはここに示すようにfilter
)。
# for example
class Alien:
# ... other stuff
def damage(self): self.hp -= 1
def alive(self): return self.hp > 0
# similarly for Bolt
def collide(an_alien, a_bolt):
# etc.
def handle_collisions(aliens, bolts):
for a in aliens:
for b in bolts:
if collide(a, b):
a.damage()
b.damage()
return list(filter(Alien.alive, aliens)), list(filter(Bolt.alive, bolts))