This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
remiX's profile picture

[LUA] [Table help] Removing a table from a table in a loop

Started by remiX, 31 December 2012 - 04:24 AM
remiX #1
Posted 31 December 2012 - 05:24 AM
Hey guys, I've been really bored lately so I'm just messing around making random things.
At the moment I'm making a spaceship game or whatever just for fun (yes I know, there are already some but I need to extend my lua knowledge)..

Ok, so now my problem when I try remove a table within a table (for the shots that you shoot) it does remove it (i think but then errors out).
The way I thought (the only way I knew of how to do it) of how to update the shots separately and properly was using tables,
by adding the X and Y position of the shot in a table.

If I haven't made myself clear, just copy the code and test it and look at the number at the bottom right, that is the count of how many shots have been fired.
Now eventually that number will go into the thousands and I need a way to remove the shot once it's off the screen.

Spoiler

running = true
screenX, screenY = term.getSize()
posX, posY = math.floor(screenX/2), screenY
curX, curY = posX, posY
term.clear()
shot = false
t_shots = {}

function drawSpaceShip()
    term.setCursorPos(posX, posY-4)
    term.clearLine()
    write("  _ ")
    term.setCursorPos(posX, posY-3)
    term.clearLine()
    write(" / \\")
    term.setCursorPos(posX, posY-2)
    term.clearLine()
    write("_| |_")
    term.setCursorPos(posX, posY-1)
    term.clearLine()
    write("|____|")
end

function shoot()
    term.setCursorPos(posX+2, posY - 5)
    write("o")
    shot = true
    table.insert(t_shots, {curX = posX + 2, curY = posY - 5})
end

function update()
    if #t_shots > 0 then
        for i = 1, #t_shots do
            if t_shots[i].curX and t_shots[i].curY then
                term.setCursorPos(t_shots[i].curX, t_shots[i].curY)
                write(" ")
                t_shots[i].curY = t_shots[i].curY - 1
                term.setCursorPos(t_shots[i].curX, t_shots[i].curY)
                write("o")
                if t_shots[i].curY == 0 then
                    t_shots[i].curY = nil
                    t_shots[i].curX = nil
                end
            end
        end
    else
        shot = false
    end
end
timer1 = os.startTimer(0.1)
while running do
    drawSpaceShip()
    term.setCursorPos(screenX - 15, screenY) term.clearLine() write(tostring(shot) .. " - " .. #t_shots)
    e = {os.pullEvent()}
    if e[1] == "key" then
        if e[2] == keys.right and posX + 5 < screenX then
            posX = posX + 1
        elseif e[2] == keys.left and posX > 1 then
            posX = posX - 1
        --[[elseif e[2] == keys.up and posY > 5 then
            posY = posY - 1
        elseif e[2] == keys.down and posY - 1 < screenY then
            posY = posY + 1]]
        elseif e[2] == keys.space then
            shoot()
        end
    elseif e[1] == "char" then
        if e[2] == "x" then
            running = false
        end
    elseif e[1] == "timer" and e[2] == timer1 then
        update()
        timer1 = os.startTimer(0.1)
    end
end

I have only tried this but it errors out on this line: "term.setCursorPos(t_shots.curX, t_shots.curY)"


function update()
    if #t_shots > 0 then
        for i = 1, #t_shots do
            if t_shots[i].curX and t_shots[i].curY then
                term.setCursorPos(t_shots[i].curX, t_shots[i].curY)
                write(" ")
                t_shots[i].curY = t_shots[i].curY - 1
                term.setCursorPos(t_shots[i].curX, t_shots[i].curY)
                write("o")
                if t_shots[i].curY == 0 then
               	 table.remove(t_shots, i) -- I think it does remove the table, but then setting the cursor position errors out
                end
            end
        end
    else
        shot = false
    end
end

Also, now and then I've been getting a Java error. It keeps disappearing before I can take a screenshot of it and I have no clue what is causing it.
GopherAtl #2
Posted 31 December 2012 - 05:45 AM
removing items in an index loop like this doesn't really work, you'll end up skipping items every time you remove one and then crash when it tries to continue past the new end of the loop because the end range for the for loop doesn't update.

One way to fix this is to replace the for loop with a while loop, and manage the index yourself. Example:


local function update(myArray)
  local i=1
  while i<=#myArray do
    --do update stuff

    --check if the current element is 'dead'
    if isElementDead(myArray[i]) then
	  --it is, so delete it
	  table.remove(myArray,i)
    else
	  --it ISN'T, so incremement i.
	  i=i+1
    end
  end
end

Note that when this code removes the current item from the array, it does not increment i. Removing the item at i will move the item at i+1 up to i, if there is one, so the next pass you'll want to look at i again. If i was the last item, then the while check will fail next time and end the loop.
remiX #3
Posted 31 December 2012 - 06:01 AM
Using a while loop definitely helps.

Fixed it with this function:

function update()
    i = 1
    while i <= #t_shots do
        term.setCursorPos(t_shots[i].curX, t_shots[i].curY)
        write(" ")
        t_shots[i].curY = t_shots[i].curY - 1
        term.setCursorPos(t_shots[i].curX, t_shots[i].curY)
        write("o")
        if t_shots[i].curY == 0 then
            --it is, so delete it
            table.remove(t_shots,i)
        end
        i=i+1
    end
end

Thanks :>
GopherAtl #4
Posted 31 December 2012 - 06:45 AM
you'll fail to update an item when you delete one, because you ignored the bit about not incrementing i when you delete an item.

Explanation:

list has 3 items… {"a", "b", "c"}

loop over them, when i==1, you remove "a". The list is now

{"b", "c"}

and then you increment i to 2. "b" should update next, but it's now index 1, but you incremented i to 2, so it'll skip "b" and update "c".
remiX #5
Posted 31 December 2012 - 07:55 AM
I see what you mean, but it's updating fine and removes them fine. When all the 'shots' are gone, it says the amount of values in the table is 0.

It updates every 0.1 seconds so it goes fast
GopherAtl #6
Posted 31 December 2012 - 07:58 AM
In your case it's a minor thing, the worst result is that a few bullets move a bit slower or live a bit longer than they should. Just pointing it out as in other contexts it can be a major problem, for example, if this list you were removing things from in the loop were coroutines, then the skipped coroutines could miss some events entirely.
ChunLing #7
Posted 01 January 2013 - 12:02 AM
I always just use a decrementing loop when I'm going to be removing elements from the table.