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

Dynamic 3D Table

Started by 5tarKaster, 04 June 2013 - 07:54 AM
5tarKaster #1
Posted 04 June 2013 - 09:54 AM
Hi All,
I have a function that is meant to dynamically change the size of a 3D Array or table but it keeps breaking saying that it returns value nil because it is out of bounds. Here is the code for it:


function resize()	  
temp = { }
for h=1, height do
    table.insert( temp , { } )
    for y=1, length do
	    table.insert ( temp[h], { } )
	    for x=1, width do
		    num = 16			   
		    if #blocks <= height then
			    if #blocks[h] <= length then
				    if #blocks[h][y] <= width then
					    num = blocks[h][y][x]
				    end
			    end
		    end		
		    table.insert( temp[h][y] , num )
	    end
    end
end
blocks = temp
end


I know it's not very well commented but the idea is that it is meant to make a new table with the changed dimensions, and then superimpose the data of blocks over the new table and finally overwriting blocks with the new temp table.

The length width and height are changed by one either incremented or decremented but only one at a time.

I'm not sure if I explained it clearly enough, if not let me know and I'll try explain in more detail.

Thanks all, James
Lyqyd #2
Posted 04 June 2013 - 12:35 PM
Split into new topic.

Why not just move the existing tables around?
Bomb Bloke #3
Posted 04 June 2013 - 08:39 PM
Rephrasing your code back to you with comments:

function resize()         
temp = { }  -- "temp" is now a new table we can reference in to.
            -- You would do so by using "temp[h]".
for h=1, height do
    table.insert( temp , { } )  -- We now have a table in the "temp[h]" table we can reference in to.
                                -- You would do so by using "temp[h][y]".
    for y=1, length do
            table.insert ( temp[h], { } )    -- We now have a table in the "temp[h][y]" table we can reference in to.
                                             -- You would do so by using "temp[h][y][x]".
            for x=1, width do
                    num = 16                       
                    if #blocks <= height then
                            if #blocks[h] <= length then
                                    if #blocks[h][y] <= width then
                                              num = blocks[h][y][x]
                                    end
                            end
                    end         
                    table.insert( temp[h][y] , num )  -- Problem! You're adding extra indexes NEXT to
                                                      -- the table in "temp[h][y]"! To put things IN
                                                      -- that table, use "temp[h][y][x] = num"
            end
    end
end
blocks = temp
end
5tarKaster #4
Posted 05 June 2013 - 02:17 AM
Why not just move the existing tables around?

Thats now the second time someone said something similar to that… I don't really understand what you mean by move the tables around?

Although wreaking my brain to bits I came up with this solution that does work… although I doubt its the best way to go around doing this, here it is anyways.


function resize() 
temp = { } -- temp table

-- inserting all the height levels
for h=1, height do table.insert( temp , { } ) end

-- inserting all the lengths
for h=1, height do
  for l=1, length do table.insert( temp[h], { } ) end
end

-- inserting all the width and defaulting them to 0
for h=1, height do
  for l=1, length do
   for w=1, width do table.insert( temp[h][l] , 0 ) end
  end
end

-- if the canvas size is increasing
if #blocks <= height then
  if #blocks[1] <= length then
   if #blocks[1][1] <= width then
    for h=1, #blocks do
	 for l=1, #blocks[1] do
	  for w=1, #blocks[1][1] do
	   -- fill in data from blocks
	   temp[h][l][w] = blocks[h][l][w]
	  end
	 end
    end
   end
  end
end

--if the canvas size is decreasing
if #blocks >= height then
  if #blocks[1] >= length then
   if #blocks[1][1] >= width then
    for h=1, #temp do
	 for l=1, #temp[1] do
	  for w=1, #temp[1][1] do
	   -- fill in data from blocks but not the last value
	   temp[h][l][w] = blocks[h][l][w]
	  end
	 end
    end
   end
  end
end

-- overwrite blocks with the new dimensions
blocks = temp
end
Bomb Bloke #5
Posted 05 June 2013 - 08:50 AM
Ok, but what if one of the table dimensions is being lowered, and one is being raised? This code would return a table of the correct size but with all elements reset to 0 regardless as to what was in them before… Though that may not be a problem in your implementation.

A tip about "if" statements; you can pile multiple conditions into the one check. You can also bodge multiple "if", "while", "for", etc openers onto the one line if you like. This:

if #blocks <= height then
  if #blocks[1] <= length then
   if #blocks[1][1] <= width then
    for h=1, #blocks do
	 for l=1, #blocks[1] do
	  for w=1, #blocks[1][1] do
	   -- fill in data from blocks
	   temp[h][l][w] = blocks[h][l][w]
	  end
	 end
    end
   end
  end
end

… can be condensed to this:

if #blocks <= height and #blocks[1] <= length and #blocks[1][1] <= width then
  for h=1, #blocks do for l=1, #blocks[1] do for w=1, #blocks[1][1] do
    -- fill in data from blocks
    temp[h][l][w] = blocks[h][l][w]
  end end end
end

Whatever you find easier to read is good.

Why not just move the existing tables around?

Thats now the second time someone said something similar to that… I don't really understand what you mean by move the tables around?
Lua tables are very dynamic. You can expand or contract them as need be without affecting the rest of their contents (a feature that's going to teach me all sorts of bad habits - I hate to imagine what the interpreter is doing in the background to make it work). You wouldn't exactly be "moving" the existing tables around, but rather you'd be resizing them.

Spoiler
function resize() 
  -- Presumably a global 3D "blocks" table already exists if this is being called.

  -- No need to declare a "temp" table; we'll edit "blocks" directly.

  if height > #blocks then
    -- Add some faces to the bottom of the table.
    for h=#blocks+1,height do  -- Apparently these values only evaluate on the first iteration of the loop.
                               -- It'll break horribly if I'm wrong about that.
      blocks[h] = {}  -- I don't like "table.insert()". So sue me.
                      -- This does much the same thing but makes it easier to keep track of where it'll go.                       
      for l=1,length do
        blocks[h][l] = {}
        for w=1,width do blocks[h][l][w] = 0 end
      end
    end
  elseif height < #blocks then
    -- Prune faces from the bottom of the table.
    for h=height+1,#blocks do blocks[h] = nil end  -- Yes, deleting whole tables is that easy.
  end

  if length > #blocks[1] then
    -- Add some faces to the back of the table.
    for h=1,height do
      for l=#blocks[1],length do
        blocks[h][l] = {}
        for w=1,width do blocks[h][l][w] = 0 end
      end
    end
  elseif length < #blocks[1] then
    -- Prune faces from the back of the table.
    for h=1,height do
      for l=length+1,#blocks[1] do
        blocks[h][l] = nil
      end
    end
  end

  if width > #blocks[1][1] then
    -- Add some faces to the side of the table.
    for h=1,height do
      for l=1,length do
        for w=#blocks[1][1]+1,width do blocks[h][l][w] = 0 end
      end
    end
  elseif width < #blocks[1][1] then
    -- Prune faces from the side of the table.
    for h=1,height do
      for l=1,length do
        for w=width+1,#blocks[1][1] do blocks[h][l][w] = nil end
      end
    end
  end
end

I haven't actually tested this, but even if I've made a mistake in there you'll hopefully understand what it does.
5tarKaster #6
Posted 06 June 2013 - 03:42 AM
table[index] = { }
seems like a much easier way to do it now that I look at it a bit more…

AND
I am going to keep this projects resize code exactly as it is now… simply because right now it works and I don't want to break it… again… However in the future I will probably use the method Script Kiddie showed because its actually easier to understand for me :D/> I kind of forgot that if you reference an object to null it will be discarded.. in a sense of saying.. I come from a java background don't blame me :)/> and about the condensed code, yes I know you can do it that way but because in lua there is no need to use ';' to end of each line it becomes difficult to follow exactly what is going on where. In that case I prefer the longer code for readability, but they do the exact same thing so it is still helpful.

ANYWAYS!
Thanks Everyone for the help :D/> greatly appreciate it