Log In  


so i have a function where the enemies have enemy1.isalive = true enemy2.isalive = false so i got that system working so how can i check to see if all enemy1.isalive are false (BY the way all enemies are in a group called enemy = {} i add every one of them sorry for no snippets)

1


Firstly rename your "enemy" table to "enemies".

function are_enemies_alive()
  for index, enemy in ipairs(enemies) do
    if enemy.isalive then
      return true
    end
  end
  return false
end

Thanks for an answer i didn't know i pairs existed

what does ipairs do


@bekey - that's new to me, thanks. I would have done something like this. Perhaps I should have learnt Lua before diving into PICO-8 :D

local count=0
for the_enemy in all(enemy_list) do
 if not the_enemy.isalive then
  count+=1
 end
end
if count==#enemy_list then 
 -- all enemies are dead. 
end

@bekey
there's no reason to use ipairs here, since order does not matter; just use pairs. it also might be slightly more performant. ( @yippiez: ipairs iterates over a table in guaranteed index order)

also if you don't need the index, the convention is to use an underscore for that variable

also you don't want to return early if you only checked one enemy, if the name of your function says you are checking to see if all enemies are alive.

to answer the original question, i think you want something like this:

function enemies_are_dead()
  for _, enemy in pairs(enemies) do
    if enemy.isalive then
      return false
    end
  end
  return true
end

pairs() is standard Lua but:

Cleanest is

function enemies_are_dead()
  for enemy in all(enemies) do
    if enemy.isalive then
      return false
    end
  end
  return true
end

Fastest runtime is(because no function call is made)

function enemies_are_dead()
  for i=1,#enemies do
    if enemies[i].isalive then
      return false
    end
  end
  return true
end

To go even faster than that requires tracking the enemy count as a variable and incrementing it on the event of the enemy dying. Then you never have to loop over the enemies to do this test, you can just test whether the counter is zero. This is some tricky bookkeeping to get right(if the event fails to fire or fires twice your count gets off by one - if it gets tested before you spawn everything it will think they're dead, then spawn them in just afterwards) so it's best to avoid it for as long as possible. In practice simple test loops like this can soak up hundreds of elements with little impact on your CPU times.


actually apparently Felice ran a test a little while ago and found that in PICO-8 (unlike standard Lua) pairs() is faster than a regular for loop, believe it or not. all() is also slower than pairs(), iirc

that's why I choose pairs by default now, since I leaned that :p


This is true. Zep is very kind to us sometimes with cycle counts, and pairs() is indeed very, very cheap.

If you don't need to hit everything in a guaranteed order, and if performance is an important factor for you, pairs() is the go-to method.

(That being said, even I, cycle-counter extraordinare, like to use the rather-slow all() iterator for code that isn't time-intensive, purely because it makes the source code read really clearly.)

Details:

  • pairs() is an internal/intrinsic iterator, meaning it is not written in Lua, but rather is incorporated into the Lua interpreter, and thus runs very fast
  • pairs() is about 1 cycle per iteration (for reference, an add or multiply also costs 1 cycle)
  • numeric iteration (for i=1,10) is also 1 cycle per iteration but costs a few more cycles to dereference the table(s) inside the loop to get to the member you're operating on
  • all() is an iterator written in some hidden lua, which must be interpreted, so IIRC it costs about 20 cycles per iteration

I'd write the function like this, since it doesn't care what order you traverse the table:

function enemies_are_dead()
  for _,enemy in pairs(enemies) do
    if enemy.isalive then
      return false
    end
  end
  return true
end

(Note that I'm using a common notation for dummy variables, where I don't care about the key value in the key,value pairs, so I just use a single underscore for the unused key variable's name.)


As an aside, there's another paradigm you could use, which is to bucket your enemies into two lists, one which is alive and one which is dead. Start with all in alive, and when one dies, move it to the dead list. Then simply check whether the alive list is empty.

As an aside to my aside, this is the best way I know of checking for an empty table:

-- save off the stateless pairs() iterator function
-- by calling pairs() on an empty table.
-- (in standard lua this would already be available.)
-- note we can omit the parens from "pairs({})",
-- thanks to lua's syntactic sugar.
next=pairs{}

function is_empty(t)
  return next(t)==nil
end

If you don't mind being a little obtuse with your source code, you can skip is_empty() entirely and just check the result of next():

if next(live_enemies) then
  ...keep playing...
else
  ...stage over...
end

For that matter, if you're just using add() and del() to manage your tables, which would mean the # operator should be correct, then you can just check the table length:

if #live_enemies != 0 then
  ...keep playing...
else
  ...stage over...
end

Basically, you have a whole host of guns in your arsenal and you can try each one to see which suits you best. Just be mindful of aiming any of them at your feet. ;)


I'm going to have to stop reading threads where Felice is the last commented. Mainly because, as I read the progression of the thread, I get this nice idea of what I'd like to post and then I come to Felice's comment and it's all there. It's ALL THERE.

Also, additional side edit: @Felice if you have some way I could contact you email or otherwise I would greatly appreciate it, I was hoping I could discuss an idea I'm tossing around for using pico-8 for an upcoming gamejam.

Specifically, this one.

If you're interested let me know. I hate to make any of your information public, but I don't know a decent way to communicate with ya that doesn't clog up the boards.


My email is blessedly free from spam, so I'd never put it on a forum, but you could DM me on twitter @Felice_Enellen if you like. :) I think I have it set not to accept DMs unless I follow the sender, so I'll need to know your handle to get that set up.


I think the only thing I've ever used my twitter for was to sign up for a giveaway, but yeah my thing should be @tokytokytoky I attempted to follow you


Felice, do you realize you wrote literally the exact same code as I did (minus one whitespace character) XD

also you then repeated what I said about the underscore variable thing lol :D

too many helpful people in one thread, it would seem. I think we get carried away sometimes :D at least it's a pretty good problem to have :p


@kittenm4ster

I have no idea how I managed to skip over your post. I'm usually pretty thorough. Sorry to step on your toes! :)

I seem to recall that I only set out to explain the cycle counts, and then decided to write one using the best method. I probably dropped the short-term memory of the other examples by the time I got to that point. Human memory is weird.


I propose another solution:
use a counter of alive ennemies:
+1 when spawn
-1 when destroy
that will remove all the tokens cost of the enemies_are_dead function


@moechofe

True, that would work in the case where all enemies (living and dead) are in the same table. If yippiez wants to keep their game structured that way, that's a good solution.


Not in pico-8 but I have used the counter approach in the past and it has served me well. It can get messy if you forget to add to the counter when generating enemies dynamically, but if you figure that out, it's clean and simple and works (especially if you are not a great coder, like I me :P).


@kikendo

The trick for a robust object-counting system is to force yourself to use only two functions for creating and deleting the objects, and the functions internally do the count+=1 and the count-=1 respectively. That way you simply can't forget to do it.


@Felice, that's right, +1


@Felice Yes, that's great advice. My problem in the past was, for example, having different things destroying the enemies. But again that's just me doing things wrong :D I did say I am a terrible coder, didn't I?


We all are. Some of us have just been at it long enough to move on to confidently writing more complex terrible code.



[Please log in to post a comment]