2012-05-10 15 views
1

私はPythonを使って簡単な進化シミュレータを作っています。リストを反復処理中に要素を削除する効果はありますか?

ユニット/動物と食品の両方を格納するengine.Allと呼ばれるリストがあります。私はそれを繰り返し、私が動物に出くわしたら、もう一度それを繰り返して、彼が食べ物のどれかに衝突しているかどうかを調べる。

もしそうなら、私は彼のエネルギーを高め、食べ物を食べてフラグを立てて、それをから削除するために後で使用するtoRemoveリストに追加します。

これはコードですが、削除されたすべての冗長なもので:

def remove(l, who): #This should remove all the elements contained in who from the list l 
    offset = 0 

    for i in who: 
     l.pop(i + offset) 
     offset -= 1 

    return l 


for ob in engine.All: 
    if ob.skip: 
     continue; 

    if ob.drawable: 
     ob.draw() 

    if isinstance(ob, Flatlander): #If it is an animal 
     #Do speed stuff 
     ob.energy -= decay #Lower its energy 

     for i in range(len(engine.All)): #Iterate through the list again 
      if collides(ob.pos, ob.r, engine.All[i].pos, engine.All[i].r) and isinstance(engine.All[i], Food) and ob.energy + engine.All[i].r < ob.r**2*3.14 and not engine.All[i].skip: #If it collides with a food piece, the food piece isn't about to be deleted and it can take the energy in (their maximum is defined by their radiuses) 
       ob.energy += engine.All[i].r #Increase the his energy 
       toRemove.append(i) #Add the food piece to the toRemove list 
       engine.All[i].skip = True #Flag it as skipped 

     if ob.energy < 0 and not ob.skip: #If its energy is 0 and if it isn't already about to be deleted 
      toRemove.append(engine.All.index(ob)) #Add it to the toRemove list 
      ob.skip = True #Flag it as skipped 


engine.All = remove(engine.All, toRemove) 

私はこれが動作しないことはほぼ確実だ、とこれを行うのは良い方法があること。私がとても確信しているのは、時には画面上で物事がちょうど「点滅」していることが突然消えて再び現れることです。また、「ゴースト」動物(コードではフラットランダーと呼ばれる)があるようですが、時には食品が永久に消えてしまうことがあるので、私はこれを結論づけます。

より効率的な方法をお勧めします。

答えて

1

skip属性のみを削除する必要があり、それらのオブジェクトに対して設定されている場合は、単にそのことを利用することが:

import math 
import itertools 

for ob in engine.All: 
    if isinstance(ob, Flatlander): 
     ob.energy -= decay 

for pair in itertools.combinations(engine.All, 2): 
    fooditems = [x for x in pair if isinstance(x, Food) and not x.skip] 
    animals = [x for x in pair if isinstance(x, Flatlander) and not x.skip] 
    if not (fooditems and animals): 
     continue 
    animal = animals[0] 
    food = fooditems[0] 
    if collides(animal.pos, animal.r, food.pos, food.r): 
     # This seems an odd calculation to me but I think it follows your code. 
     if animal.energy + food.r < animal.r ** 2 * math.pi: 
      # eating is feasible; the animal always eats the food if it can 
      food.skip = True 
      animal.energy += food.r # Not the area? 

for ob in engine.All: 
    if isinstance(ob, Flatlander) and ob.energy < 0: 
     ob.skip = True # dead 

# Remove dead things 
engine.All = [ob for ob in engine.All if not ob.skip] 

# Draw everything (no dead things remain) 
for ob in engine.All: 
    if ob.drawable: 
     ob.draw() 

一般的に私はおそらく別々のリストに食品やザ・フラットランダースを維持することを好むだろう。

衝突検出をより効率的に行うための手法がいくつかあります(例えば、世界を四角形に分割し、同じまたは周囲の四角形の動物のみをチェックするなど)がありますが、動物/食料品は必要ありません。

+0

答えをありがとう、これは動作するようです。ほんの1つの質問、Pythonにガベージコレクタがあるのですか、またはリストから削除した後にオブジェクトを 'del'する必要がありますか? – corazza

+1

Pythonにガベージコレクションがあります。さまざまな実装(例えば、JythonとCPythonとの比較)は、発生時にちょっと違うかもしれませんが、そのことに頼ることができます。 –

0

私は進化のゲームに精通していませんが、2Dマップ(mb 3D)のいくつかの動物と、それらが衝突した最初の食品を食べるいくつかの動物があると思いますか?それとも人生のゲームですか?これは基本的に同じです:p

もしそうなら、正方形の中に内容フィールドを追加します。動物が正方形に移動すると、隣り合う四角形をチェックし、食品が存在する場合は、その四角形の内容フィールドをNullに設定するだけです。 これは、2Dゲームで各要素の位置を保存する上での一般的な最適化であり、必要に応じて調整することができます。

削除されたアイテムのリストをタイマーフィールドに追加して、各アイテムがいつ再現されるべきかを通知できます。 このリストは、アイテムを再挿入する必要があるかどうかを簡単に検出できるように、再生成時間によってソートされます。

4

これをジェネレータ関数として実行し、不要な要素をポップするのではなく、必要な結果を得る方が簡単です。

def valid_engines(): 
    for ob in engine.All: 
     if should_use_engine(ob): 
      yield ob 

engines_to_use = valid_engines() 

should_use_engine()は、エンジンを組み込むかどうかを決定するために上記のロジックで置き換えられます。

+0

一般的には、この手法がこれを行う方法です。コードが小さい場合は、ジェネレータ式またはリストの理解を使用してもそのトリックを行います。インプレースでリストを変更する必要がある場合は、逆方向に作業することができます。逆順でリストを反復し、そのように要素を削除します。 – Darthfett

+0

これについては決して考えませんでしたが、私はリスト内包を使用して既存のリストを再形成またはフィルタリングしています。 – monkut

関連する問題