Converting my code structure array property issue

I have this kind of structure in my Corona code I am trying to convert but in the print line it errors with nill index value? What is the issue? The key parts are the Tile = {} andTile[counter] = {X=x, Y=y, Terrain=terrain, Cost=cost, VP=vp, Feature=feature, Owner=owner}. Then I should be able to reference that structure like;

What am I doing wrong?

function Map:draw()
print(self.Tile[1].Terrain)
end

Full class code below(Map is a class that has an array of Tiles and each tile has a property set):

Map = class()

function Map:init(FileName)
    Tile = {}
    
    local File = io.open(FileName) 
    local counter = 1
    if File then -- nil if no file found
        for Lines in File:lines() do
            FileLine = Lines
            local thistile = split(FileLine, ",")

            local x = tonumber (thistile[1])
            local y = tonumber (thistile[2])
            local terrain = thistile[3]
            local cost = tonumber (thistile[4])
            local vp = thistile[5]
            local feature = thistile[6]
            local owner = thistile[7]
            --local rivern = thistile[8]
            --local rivere = thistile[9] 
            --local rivers = thistile[10]
            --local riverw = thistile[11]
            --local roadn = thistile[12]
            --local roade = thistile[13]
            --local roads = thistile[14]
            --local roadw = thistile[15]

            --self.Tile[x][y] = Tile()
            --self.Tile[counter].Terrain = terrain
            ----self.Tile[counter].Image = sprite("Dropbox:desert")
            --self.Tile[counter].Cost = cost
            --self.Tile[counter].VP = vp
            --self.Tile[counter].Feature = feature
            --self.Tile[counter].Owner = owner
            --self.Tile[x][y].RiverN = rivern
            --self.Tile[x][y].RiverE = rivere
            --self.Tile[x][y].RiverS = rivers
            --self.Tile[x][y].RiverW = riverw
            --self.Tile[x][y].RoadN = roadn
            --self.Tile[x][y].RoadE = roade
            --self.Tile[x][y].RoadN = roadn
            --self.Tile[x][y].RoadW = roadw
            
            Tile[counter] = {X=x, Y=y, Terrain=terrain, Cost=cost, VP=vp, Feature=feature, Owner=owner}
            counter = counter + 1
        end
        
        io.close(file)
    end
end

function Map:draw()
    -- Codea does not automatically call this method
    print(self.Tile[1].Terrain)
end

function Map:touched(touch)
    -- Codea does not automatically call this method
end

function split(str, div)
    if (div=='') then 
        return false
    end

    local pos,arr = 0,{}

    -- for each divider found
    for st,sp in function() return string.find(str,div,pos,true) end do
        -- Attach chars left of current divider
        table.insert(arr,string.sub(str,pos,st-1)) 
        pos = sp + 1 -- Jump past current divider
    end
    
    -- Attach chars right of last divider
    table.insert(arr,string.sub(str,pos)) 
    return arr
end

Please put 3 ~ above and below your code for proper formatting, otherwise it’s a big mess.

Like this: ~~~

A nil index error would be… I think something like trying to do table[nil]. nil is an invalid table index.

thanks, fixed the code format. So why is my Map.Tile[1].Terrain variable set to nil when I populate it with this line that worked just find in Corona? I believe this is standard lua(been awhile since i created it):

Tile[counter] = {X=x, Y=y, Terrain=terrain, Cost=cost, VP=vp, Feature=feature, Owner=owner}

@Gib I stripped out your commented code and the class information just to get a working example of your code. I replaced you data from a file with just a string. I’m only using one iteration so I set counter to 1 and didn’t bother to increment it. I printed out the Tile table for the 1 iteration and it looks like the correct info is in the table using your code. Is my example similar to what you’re trying to do.


function setup()

    FileLine="123,234,terrain-1,300,vp-1,feature-1,owner-1"
    
    Tile={}
    counter=1
    local thistile = split(FileLine, ",")
    local x = tonumber (thistile[1])
    local y = tonumber (thistile[2])
    local terrain = thistile[3]
    local cost = tonumber (thistile[4])
    local vp = thistile[5]
    local feature = thistile[6]
    local owner = thistile[7]
    Tile[counter] = {X=x, Y=y, Terrain=terrain, Cost=cost, VP=vp, Feature=feature, Owner=owner}
        
    print(Tile[1].X)
    print(Tile[1].Y)
    print(Tile[1].Terrain)
    print(Tile[1].Cost)
    print(Tile[1].VP)
    print(Tile[1].Feature)
    print(Tile[1].Owner)
end

function split(str, div) 
    if (div=='') then 
        return false 
    end
    local pos,arr = 0,{}
    -- for each divider found
    for st,sp in function() return string.find(str,div,pos,true) end do
    -- Attach chars left of current divider
    table.insert(arr,string.sub(str,pos,st-1)) 
    pos = sp + 1 -- Jump past current divider
    end
    -- Attach chars right of last divider
    table.insert(arr,string.sub(str,pos)) 
    return arr
end

Here’s the Main:

function setup()
    print("Hi")    
    local file = "Dropbox:map1"
    BattleMap = Map(file)
    parameter.watch("counter")
    parameter.watch("FileName")
end

function draw()
    background(0,0,0)
    BattleMap:draw()
end

Good idea on the single iteration. Is your setup function in your main when you tested this because its supposed to be a class and not in the main:setup function.

I’ll take out the filename pass(local file = “Dropbox:map1”) in my main:setup to eliminate a layer (dont know if that is how you point to a file?)

@Gib I took out the class code just to make it easy on me to try an see what was wrong with your code. You’ll keep it as a class for your code. From what I see so far, your code is working. Without having all your code, that makes it harder to figure out what’s not working.

I got a single iteration working. Is my file reading the issue? See where in my main:setup i set the path:filename - is that right for map1(a text file) that IS in my dropbox in the codea folder(even though codea doesnt show it)?

Then if that is right, what about my local File = io.open(FileName) in the Map:Init function?

Appreciate your guys help.

@Gib What is the format of the file you’re reading. Does it contain multiple occurances of 7 items seperated by a comma similar to what I put in the test string I used. If I can create a file that’s similar, then I can use your code to read it and maybe see what’s happening.

yes this is the data in map1.txt that i have in the codea sprites dropbox.

1,1,grass,1,0,none,russian
1,2,desert,2,1,none,german

Thanks,
Gib

Aha! Found it. You’re trying to index self.Tile[1], when you set it to Tile[1]. Just add self when creating the tiles and it should be fixed.

ok after editing my last post, the forum format brings up the carriage return…
so after the word russian the 1,2,desert,2,1,none,german is the 2nd line

@Gib Missed my post. This is the code it should be:

Map = class()

function Map:init(FileName)
    self.Tile = {} -- Change: added self.

    local File = io.open(FileName) 
    local counter = 1
    if File then -- nil if no file found
        for Lines in File:lines() do
            FileLine = Lines
            local thistile = split(FileLine, ",")

            local x = tonumber (thistile[1])
            local y = tonumber (thistile[2])
            local terrain = thistile[3]
            local cost = tonumber (thistile[4])
            local vp = thistile[5]
            local feature = thistile[6]
            local owner = thistile[7]
            --local rivern = thistile[8]
            --local rivere = thistile[9] 
            --local rivers = thistile[10]
            --local riverw = thistile[11]
            --local roadn = thistile[12]
            --local roade = thistile[13]
            --local roads = thistile[14]
            --local roadw = thistile[15]

            --self.Tile[x][y] = Tile()
            --self.Tile[counter].Terrain = terrain
            ----self.Tile[counter].Image = sprite("Dropbox:desert")
            --self.Tile[counter].Cost = cost
            --self.Tile[counter].VP = vp
            --self.Tile[counter].Feature = feature
            --self.Tile[counter].Owner = owner
            --self.Tile[x][y].RiverN = rivern
            --self.Tile[x][y].RiverE = rivere
            --self.Tile[x][y].RiverS = rivers
            --self.Tile[x][y].RiverW = riverw
            --self.Tile[x][y].RoadN = roadn
            --self.Tile[x][y].RoadE = roade
            --self.Tile[x][y].RoadN = roadn
            --self.Tile[x][y].RoadW = roadw

            self.Tile[counter] = {X=x, Y=y, Terrain=terrain, Cost=cost, VP=vp, Feature=feature, Owner=owner} -- Change: added self.
            counter = counter + 1
        end

        io.close(file)
    end
end

function Map:draw()
    -- Codea does not automatically call this method
    print(self.Tile[1].Terrain)
end

function Map:touched(touch)
    -- Codea does not automatically call this method
end

function split(str, div)
    if (div=='') then 
        return false
    end

    local pos,arr = 0,{}

    -- for each divider found
    for st,sp in function() return string.find(str,div,pos,true) end do
        -- Attach chars left of current divider
        table.insert(arr,string.sub(str,pos,st-1)) 
        pos = sp + 1 -- Jump past current divider
    end

    -- Attach chars right of last divider
    table.insert(arr,string.sub(str,pos)) 
    return arr
end

Here is main and map latest version with your changes. It still fails to print the values but i get a new error: error: [string “Map = class()…”]:9: attempt to index local ‘File’ (a nil value)

--Main:

function setup()
    file = "Dropbox:map1.txt"
    BattleMap = Map(file)
end

function draw()
    background(0,0,0)
end



--Map:
Map = class()

function Map:init(FileName)
    self.Tile = {}
    counter = 1
    local File = io.open(FileName) 

    for Lines in File:lines() do
        FileLine = Lines
        print(FileLine)
        local thistile = split(FileLine, ",")

        self.Tile[counter] = {}
            
        local x = tonumber (thistile[1])
        local y = tonumber (thistile[2])
        local terrain = thistile[3]
        local cost = tonumber (thistile[4])
        local vp = thistile[5]
        local feature = thistile[6]
        local owner = thistile[7]
            
        self.Tile[counter] = {X=x, Y=y, Terrain=terrain, Cost=cost, VP=vp, Feature=feature, Owner=owner}

        print(self.Tile[1].X)
        print(self.Tile[1].Y)
        print(self.Tile[1].Terrain)
        print(self.Tile[1].Cost)
        print(self.Tile[1].VP)
        print(self.Tile[1].Feature)
        print(self.Tile[1].Owner)

        counter = counter + 1
    end
    io.close(File)
end

function Map:draw()
    -- Codea does not automatically call this method
end

function Map:touched(touch)
    -- Codea does not automatically call this method
end

function split(str, div)
    if (div=='') then 
        return false
    end

    local pos,arr = 0,{}

    -- for each divider found
    for st,sp in function() return string.find(str,div,pos,true) end do
        -- Attach chars left of current divider
        table.insert(arr,string.sub(str,pos,st-1)) 
        pos = sp + 1 -- Jump past current divider
    end
    
    -- Attach chars right of last divider
    table.insert(arr,string.sub(str,pos)) 
    return arr
end

got it!

--main: 

function setup()
    file = os.getenv("HOME").."/Documents/Dropbox.spritepack/map1.txt"
    BattleMap = Map(file)
end

function draw()
    background(0,0,0)
    --BattleMap:draw()
end
Map = class()

function Map:init(file)
    self.Tile = {}
    counter = 1
    io.input(io.open(file,"r"))

    for FileLine in io.lines() do
        print(i)
        local thistile = split(FileLine, ",")
        self.Tile[counter] = {}
            
        local x = tonumber (thistile[1])
        local y = tonumber (thistile[2])
        local terrain = thistile[3]
        local cost = tonumber (thistile[4])
        local vp = thistile[5]
        local feature = thistile[6]
        local owner = thistile[7]
            --local rivern = thistile[8]
            --local rivere = thistile[9] 
            --local rivers = thistile[10]
            --local riverw = thistile[11]
            --local roadn = thistile[12]
            --local roade = thistile[13]
            --local roads = thistile[14]
            --local roadw = thistile[15]

            --self.Tile[x][y] = Tile()
            --self.Tile[counter].Terrain = terrain
            ----self.Tile[counter].Image = sprite("Dropbox:desert")
            --self.Tile[counter].Cost = cost
            --self.Tile[counter].VP = vp
            --self.Tile[counter].Feature = feature
            --self.Tile[counter].Owner = owner
            --self.Tile[x][y].RiverN = rivern
            --self.Tile[x][y].RiverE = rivere
            --self.Tile[x][y].RiverS = rivers
            --self.Tile[x][y].RiverW = riverw
            --self.Tile[x][y].RoadN = roadn
            --self.Tile[x][y].RoadE = roade
            --self.Tile[x][y].RoadN = roadn
            --self.Tile[x][y].RoadW = roadw
            
        self.Tile[counter] = {X=x, Y=y, Terrain=terrain, Cost=cost, VP=vp, Feature=feature, Owner=owner}

        print("Hey im inside your map class!")
        print(self.Tile[1].X)
        print(self.Tile[1].Y)
        print(self.Tile[1].Terrain)
        print(self.Tile[1].Cost)
        print(self.Tile[1].VP)
        print(self.Tile[1].Feature)
        print(self.Tile[1].Owner)
        counter = counter + 1
    end
end

function Map:draw()
    -- Codea does not automatically call this method
end

function Map:touched(touch)
    -- Codea does not automatically call this method
end

function split(str, div)
    if (div=='') then 
        return false
    end

    local pos,arr = 0,{}

    -- for each divider found
    for st,sp in function() return string.find(str,div,pos,true) end do
        -- Attach chars left of current divider
        table.insert(arr,string.sub(str,pos,st-1)) 
        pos = sp + 1 -- Jump past current divider
    end
    
    -- Attach chars right of last divider
    table.insert(arr,string.sub(str,pos)) 
    return arr
end

--draw horizontal lines of the grid
--for y=1,MapHeight,1 do
--    local line = display.newLine(1, y*SquareSize, SquareSize*MapWidth, y*SquareSize)
--    line:setColor( 255, 0, 0, 128)
--    line.width = 2
--    GameBoard:insert(line)
--end

--draw vertical lines of the grid
--for x=1,MapWidth,1 do
--    local line = display.newLine(x*SquareSize, 1, x*SquareSize, SquareSize*MapHeight)
--    line:setColor( 255, 0, 0, 128)
--    line.width = 2
--    GameBoard:insert(line)
--end

ok, so the main thing i had to fix was

file = os.getenv(“HOME”)…“/Documents/Dropbox.spritepack/map1.txt”

So how come this basic progamming method is so hard to find? Is there a complete manual on how to code in Codea or did I just miss it somewhere?

I understand Apple is way beyond Draconian/Control Freakyness when it comes to be able to browse the file system (unless you got something like iExplorer) from withing the ipad itself. I’ve been told that this prevents viruses/contributes to overall protection and security is that right?

If I may make a suggestion for the developers of Codea?.. simplify this for us(in Codea). I found no documentation on that HOME…thing, only happened to find it in all the posts(great forum thankfully). Don’t get me wrong, loving Codea but my god this was a MF to figure out. Course I am way out of practice and this is all new(ios programming) but I would think that since my spirtes are loaded just by doing sprite(Dropbox:“spritename.png”) then you should make file opening the same. Too many flamming hoops for me lol.

If you keep it in a similar format like sprites then you could do this to open and read in the contents of a file:

MyFile(Dropbox:“filename.txt”)
for lines in MyFile do
–blah
end

Do you understand what I mean by keeping it the same? Would be beautiful and elegant, and in line with the design concept of sprites.

Anyway, thanks you guys for your help! Now on to getting this to draw…

Gib

@Gib I think the problem with file io is that Codea is written to be a game/graphics language. There isn’t any true reason for file io. There is next to no file io documentation because it’s not used. Any documentation is just little bits here and there that people have shared.

@dave1707 I am not sure I understand what you mean by that since loading graphics is file io. Also a language without file io?? How could you do anything? map levels like I am doing depend on it. is Codea not for serious game development?

@Gib Cargo-Bot was said to be coded all on the iPad. So I would say it’s a serious game development language. But for some reason, file io doesn’t seem to be important. I’m not sure how they got everything on the iPad for the game, but I don’t think it was through file io.

EDIT: see this link http://twolivesleft.com/CargoBot/

@Gib Yes, loading sprites and stuff in file IO. But that’s handled by Codea. The file IO part is basically you, in your own code, reading or writing plain files. Codea is a legitimate game programming suite, and was designed with graphics in mind. Not things like a file manager, but games or toys.

Also, the reason it wouldn’t be Dropbox:filename.txt is because say, you didn’t want to be restricted to Codea’s files, say, you wanted to reach an entirely new area on the file system. Then Dropbox:filename.txt would be holding you back to Codea’s files.