Page 1 of 1

[solved] is there a better way to code this?

Posted: Thu Jul 05, 2018 7:48 pm
by icekiller8002
i am trying to remove blocks in these three coordinates. this is the best working solution i came come up with:

Code: Select all

for x,b in ipairs(blocks) do
  if (b.tx == 99 or b.tx == 101 or b.tx == 103) and b.ty == 11 then
    table.remove(blocks,x)
  end
end
for y,c in ipairs(blocks) do
  if c.tx == 101 and c.ty == 11 then
    table.remove(blocks,y)
  end
end
for z,d in ipairs(blocks) do
  if d.tx == 103 and d.ty == 11 then
    table.remove(blocks,z)
  end
end
i have no clue how to code a better working version of this. i tried something like this:

Code: Select all

if (b.tx == 99 or b.tx == 101 or b.tx == 103) and b.ty == 11 then
however, it won't work for some odd reason. i know my solution looks trash coding-wise, but other shortcuts just don't work. i have no clue why either. ¯\_(ツ)_/¯

Re: is there a better way to code this?

Posted: Thu Jul 05, 2018 8:31 pm
by joedono
You've got to be careful removing elements from a table while you're looping through it. If you remove the 2nd block in blocks, then the rest will all rearrange themselves, and your 3rd block will become your 2nd. Then your loop will try to get the 3rd block in the new blocks table, but will give you what used to be the 4th block instead. I get around this by putting all my activeBlocks in a temporary table.

Code: Select all

local activeBlocks = {};
for x,b in ipairs(blocks) do
  if (b.tx ~= 99 and b.tx ~= 101 and b.tx ~= 103) or b.ty ~= 11 then
    table.insert(activeBlocks, b)
  end
end
blocks = activeBlocks
Maybe there's a better way than this. I'm not sure.

Re: is there a better way to code this?

Posted: Thu Jul 05, 2018 11:19 pm
by pgimeno
There are two other ways of getting around that. The simplest is to just loop backwards, so that the renumbering of the elements doesn't affect you:

Code: Select all

for x = #blocks, 1, -1 do
  local b = blocks[x]
  ...
end
The other one is more complicated. The idea is to increment only when you skip the current element and not when you remove an element (because the next element takes the current number and you will need to check it too). When you remove, update the length if you keep it in a variable. You need to use something other than a for loop because the length of the array is going to change as you go. Something like this:

Code: Select all

local x = 1
local len = #blocks
while x <= len do
  if (blocks[x] must be removed) then
    table.remove(blocks, x)
    len = len - 1
  else
    x = x + 1
  end
end
It's useful in cases where the removal order matters.

Re: is there a better way to code this?

Posted: Fri Jul 06, 2018 3:57 am
by ivan
I think more generally, the question is, can multiple blocks occupy the same coordinate?

If "yes" then you probably don't want to reference them using coordinates.

If the answer is "no" then you need to research how hashing works.
This way you can access objects directly:

Code: Select all

function removeBlock(x, y, z)
  local index = hash3(x, y, z)
  blocks[index] = nil
end
To illustrate how hashing works you could use strings:

Code: Select all

function hash3(x, y, z)
  return x..','..y..','..z
end
It's usually more efficient to use math though.
One basic approach is to use shifting to pack the index into a single hex.
x,y,z must be integers within the range 0-255.

Code: Select all

function hash3(x, y, z)
  assert(x >= 0 and y >= 0 and z >= 0, "out of bounds")
  assert(x <= 255 and y <= 255 and z <= 255, "out of bounds")
  return x*0x10000 + y*0x100 + z
end
This is basically the code for packing a 3-component color (RGB) into a hex.
Of course there are more sophisticated algorithms out there that can (depending on your needs) pack coords better.