score:2

Accepted answer

Scala's collections generally don't assume that you'll be manipulating them while they're using a method like foreach (or executing a for loop). If you want to do things that way, the easiest class to use is Java's java.util.concurrent.ConcurrentSkipListMap.

// This helps you use Java collections like Scala ones
import collection.JavaConversions._

case class Monster(name: String, hp: Int) {}
val horde = new java.util.concurrent.ConcurrentSkipListMap[Int,Monster]

horde put (0, Monster("wolf",7))
horde put (1, Monster("orc",3))

for (h <- horde) println(h)   // Prints out both

Iterator.iterate(Option(horde.firstEntry)) {
  case None => None
  case Some(e) =>
    val m = e.getValue
    if (m.name=="wolf") horde.remove(1)     // Kill the orc
    else if (m.name=="orc") horde.remove(0) // Kill the wolf
    Option(horde.higherEntry(e.getKey))
}.takeWhile(_.isDefined).foreach(_=>())

for (h <- horde) println(h)   // Prints out just the wolf

Now, granted, this is rather a mess, but it does work, and it gives nice random access to your monsters. You have to maintain the keys in a sensible order, but that's not too hard.

Alternatively, as others have indicated, you could add an isAlive or isDead method, and only act on monsters that are alive. Then, after you've passed through the list once, you .filter(_.isAlive) to throw away all the dead monsters (or .filter(! _.isDead)), and run it again.

score:0

If you delete an entry in an array, this element would be null.
Therefore you should check every element of your array, e.g.:

for(int i = 0; i <= array.lenght - 1; i++) {
  // check null
  if(array[i] != null) {
    // do stuff
  }
}

Hope this helped, have Fun!

score:0

make a copy of the original array, and traverse the copy. if a monster would remove a monster, it would be removed only from the original

score:0

Deleting elements from an array while looping over it is generally a horrible idea. What if you delete an element before you reach it in the loop? Should you not actually delete it until the end of the loop, or should you skip over it?

I recommend something more like this:

var monsters = ... initial list of monsters ...
monsters = for (m <- monsters; if m.alive) yield { m.act; m }

Take advantage of yield and if in conjunction with the for loop, which allows you to build a new list.

score:1

EDIT: This first graph misinterperets your question. However, my solution should still work for you.

What you're asking for is a thread-safe array - one that can be accessed by multiple "threads" of execution at a time. Seeing as you're new to Java, my guess is that your game is not going to be multithreaded, and so if you delete an item in an array, that's going to happen for sure before your next loop runs.

That said, if you really want to, you can add a "monster.dead" boolean function to your array, and set that to true whenever a monster dies. In your loop, then, you'd say:

for( i <- 0 to monsters.length-1) 
    if (monsters[i].dead == false)
        monsters(i).act  

Most likely, though, you won't run into this issue.

Edit: just reread your post, and realized that you'll be deleting monsters as your array is running. Remember that each line you execute happens sequentially, so when you remove monsters[i], it will be gone the next time the for loop is evaluated. If you have an array of monsters with 5 monsters in it and you delete the second one, when the loop executes again,

monsters.length - 1  

is going to evaluate to 3 now. You'll never run into a moment where you hit a deleted array element.

score:2

I would either use a conditional statement to check the monster's isAlive property in the loop before I called act, or do that check inside the act method itself.

score:2

I imagine that your monster[i] is not going to die on his turn, but rather off some other hapless monster?

If you're hooked on arrays, or don't mind the processing time (and for what you're doing, i reckon you don't care), just keep a boolean on each monster of isDead. If a monster dies due to some ... i dunno, reason, just mark the "isDead" as true.

Then, in your monster "act" method, just check if the monster "isDead" or not.

After each loop, you can just prune the list to keep the alive monsters (move all the ones that are alive to a new list and begin again, prune the list in place, whatever is easier for you).


Related Query

More Query from same tag