How do I save a comma separated list (array? table?) to a file?

I am losing my mind over what seems like it should be so simple.

I want to be able to save a high score for each level.
So I start with:

hiScoreTable = { 10400, 12700, 9800, 11200 }

How can I save this to a file and be able to load it when the player comes back and test like so:

local level = 4
if ( currentScore > hiScoreTable[level] ) then
–new hiScore
end
Please I spent a night trying many different ways but none worked. Somebody save my sanity please!
[import]uid: 22392 topic_id: 21978 reply_id: 321978[/import]

You can encode your hiScoreTable lua table to JSON and save that as a file. You can then load that JSON file and decode it back into a lua table to then check a current score against.

Learn about JSON and file i/o in the api/docs sections of this site. There are some good tutorials on these subjects in the blog section as well. Also, search the code sharing area for some ready-made solutions for saving and loading data that others have graciously shared. [import]uid: 9422 topic_id: 21978 reply_id: 87366[/import]

Yeah I’ve been thru the blog, code exchange, etc. I can save something like hiScoreTable.level1 because it has keys and values to split. When I try to do it as one comma separated table it just doesn’t translate. Don’t know JSON but shouldn’t this be easier than resorting to that? [import]uid: 22392 topic_id: 21978 reply_id: 87369[/import]

I have some spaghetti code that I use to read high scores (just a list of comma separated values) into a table, and to output those values from the table into a text file.

[lua]local function saveData(scores, path)
local tempString = scores[1]
for i=2,13,1 do
tempString = (tempString … “,” … scores[i])
end
tempString = (tempString … “,” … releaseNum … “,” … versionNum … “,” … buildNum )
local file = io.open( path, “w” )
file:write( tempString )
io.close( file )
return true
end

local filePath = system.pathForFile( “hiScores”, system.DocumentsDirectory )

saveData(hiScoreTable, filePath)[/lua]

I can’t remember why I build my string that way, but it works so I’l happy with not touching it! And I have separate release, version, and build values that I save into the high scores file so that I can tell how old it is in case I ever need to convert it to a new format or clear high scores for a given level.

To read the file back, I make sure it exists and then have a similar function.

[lua]local function explode(div,str)
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
– Insert 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

local loadHighScores = function( strFilename )
local path = system.pathForFile( strFilename, system.DocumentsDirectory )
local file = io.open( path, “r” )

if file then
– read all contents of file into a string
local line = file:read( “*a” )

– explode string into entries in a table
local contents = explode(",", line)
io.close( file )
end
return contents
end

local hiScoreTable = loadHighScores(“hiScores”)[/lua]

The explode function is the magic sauce (which I didn’t create) that I use for loading levels (which are huge comma delimited files) and high scores. Once I had the explode function and was comfortable with it, saving scores became as simple as updating values in a table.

After the game, I compare the player scores against the appropriate entry in the hiScoreTable, and if the player score is higher, then I change that value in my hiScoreTable (I also score the date of the score, so I change that too) and then I run saveData(hiScoreTable). [import]uid: 14598 topic_id: 21978 reply_id: 87375[/import]

This week’s Tuesday Tutorial on the blog was reading and writing files. I 2nd the motion to use JSON to encode the table and write it out. Read it in, decode it back to a table, piece of cake.
[import]uid: 19626 topic_id: 21978 reply_id: 87391[/import]

@alanfalcon

Awesome. Thanks for the code.

It wasn’t working until I moved line 27 (‘return contents’) up into the if then statement so you may want to edit that in case anyone tries this down the road.

Thanks again! [import]uid: 22392 topic_id: 21978 reply_id: 87402[/import]

Glad to help such that I could. Sorry about the mis-placed return, of course the contents variable was local so I put that return in the wrong place. Ideally there should also be some code in there for creating a high score file when one doesn’t yet exist.

If there’s an edit option available to me, it’s very well hidden. I don’t know how to change the existing post to correct it, so hopefully people read your reply and see how to fix my mistake.

I don’t doubt though that taking the time to learn JSON is probably the best way to go here, but I can’t help with that since I haven’t taken the time myself. [import]uid: 14598 topic_id: 21978 reply_id: 87405[/import]

Heres the thing with JSON and Lua. You don’t need to know the JSON format at all. You just have to have a Lua table of data, let json.encode do its work and you get a nice plain ASCII string to write out. When you read that string back in and call json.decode you end up with the Lua table you started with.

No futzing, no parsing, practically no thinking. It’s truely simple and amazing [import]uid: 19626 topic_id: 21978 reply_id: 87407[/import]

I took a look at JSON and would’ve used it if alanfalcon’s code didn’t work but I figured 45 lines of code beats loading in a whole library of code with JSON. Maybe that’s really not a big deal, I don’t know.

And as far as creating the file if it doesn’t previously exist, I did this:

local hiScoreTable = loadHighScores(“hiScores.txt”)

if ( hiScoreTable == nil ) then
hiScoreTable = {0,0,0}
saveData( hiScoreTable, filePath )
end

Works for me.

[import]uid: 22392 topic_id: 21978 reply_id: 87411[/import]

I’m not sure why JSON scares so many people. You don’t need to know it at all – just two API calls…

local json = require("json")  
  
function saveTable(t, filename)  
 local path = system.pathForFile( filename, system.DocumentsDirectory)  
 local file = io.open(path, "w")  
 if file then  
 local contents = json.encode(t)  
 file:write( contents )  
 io.close( file )  
 return true  
 else  
 return false  
 end  
end  
  
function loadTable(filename)  
 local path = system.pathForFile( filename, system.DocumentsDirectory)  
 local contents = ""  
 local myTable = {}  
 local file = io.open( path, "r" )  
 if file then  
 -- read all contents of file into a string  
 local contents = file:read( "\*a" )  
 myTable = json.decode(contents);  
 io.close( file )  
 return myTable  
 end  
 return nil  
end  

Then to use:

local gameSettings = {}  
gameSettings.musicOn = true  
gameSettings.soundOn = true  
gameSettings.difficulty = "hard"  
  
saveTable(gameSettings,"gamesettings.json")  
  
-- read it back in:  
  
gameSettings = loadTable("gamesettings.json")  
if gameSettings == nil then  
 -- code to initialize game settings  
 gameSettings.musicOn = true  
 gameSettings.soundOn = true  
 gameSettings.difficulty = "hard"  
end  

Got a table of high scores?

local highScores = { 10000, 5000, 2000, 1000, 500, 200, 150, 5 }  
  
saveTable(highScores, "highScores.json")  

read it back in?

highScores = loadTable("highScores.json")  
if highScores == nil then   
 -- no file found, init it to an empty table.  
 highScores = {}  
end  

[import]uid: 19626 topic_id: 21978 reply_id: 87497[/import]

@robmiracle

Any reason why this would be preferred over the other solution?

Seems the other way can do the same thing without having to require a larger library. [import]uid: 22392 topic_id: 21978 reply_id: 87501[/import]

Sure. The code above works great for a comma delimited set of numbers. Wanna save strings instead? What if there is a comma embedded in the string? Does it handle booleans? UTF-8 characters? Or… key-value pairs?

The JSON library is around 600 lines of code after you remove the comments. Yea, that’s more than 45, but it’s also not a pig either.

You can save pretty much any table and get the exact came table back.
[import]uid: 19626 topic_id: 21978 reply_id: 87509[/import]

Awesome. Thanks for the code and explanation.
Makes perfect sense now.

And I can easily switch to JSON if I need to. [import]uid: 22392 topic_id: 21978 reply_id: 87513[/import]

Thanks for the explanation. Rob. I’ve been slowly coming around to the fact that I should be broadening my horizons, but it’s been more a germ of an idea than something I’ve spent any effort on.

For me, I came to Corona specifically because I don’t have a programming background, and Lua is just so simple to grasp, even though I’m still learning it as I go. When I hear JSON, that holds no meaning to me, it’s greek, same with SQL or pretty much any other acronym. For the most part, when I read those my eyes glaze over because so many people do have experience with them that they hardly ever think to stop and explain what it is or why I would want to use it. But your explanation makes perfect sense, so thank you again. [import]uid: 14598 topic_id: 21978 reply_id: 87535[/import]

I guess that if you were to google JSON and try reading some of those webites, it would scare the bajeebies out of you (It scares me and I’m a very experienced programmer). Frankly I’m not sure I could write a JSON file from scratch, way too syntaxy for me.

But using json.decode and .encode, both here an in PHP makes a great way to exchange information with a webserver. And because it does a really good job of encoding Lua table data, it’s the perfect way to avoid doing your own data parsing.

My idea of this not being scary is that you don’t need to know JSON, just two Corona API calls and you don’t have to think about **HOW** the data is stored. [import]uid: 19626 topic_id: 21978 reply_id: 87544[/import]

Thanks for the nice explanation and code examples, robmiracle.

My first reply to this thread was probably misleading. A few months ago if someone had told *me* to “learn JSON” just to save some data my own eyes would have glazed over with visions of spending weeks studying an arcane subject.

I should have mentioned that there’s almost nothing to it once you take a quick look at a couple bits from the docs and tutorials. And now robmiracle’s succinct posts make that point clear.
[import]uid: 9422 topic_id: 21978 reply_id: 87547[/import]

Thank you, the JSON is definitely the way to go [import]uid: 133556 topic_id: 21978 reply_id: 100138[/import]

Ive played abit with json lately and its nice to use for loading info from a file and into my app… but then I started with trying to save things and it changes how it is.

say I have this in a json file:

{ 1:200, 2:“b”, 3:“G”, 4:4, 5:5, 6:6, 7:7}

now it loads great and I can check any of the varibles I want by using a number like table[2] whichic would then show the letter b.

but if I save that file it looks like this afterwards:
{“1”:200,“2”:“b”,“3”:“G”,“4”:4,“5”:5,“6”:6,“7”:7}

and now I cant use a number like before but have to use “1” intead or I have to “convert” the table into a new one in code. This is ok for something simple like this but I want the saved file to look like the loaded file so I dont have to worry about things like this…

any solution to this?

[import]uid: 17969 topic_id: 21978 reply_id: 100286[/import]

… [import]uid: 9328 topic_id: 21978 reply_id: 105371[/import]

I had the exact same problem borgb. I assume it’s an issue on Ansca’s part but what I did was use the tostring() command. Or you could use this function: https://developer.anscamobile.com/forum/2012/04/29/json-encoding-differently-it-decodes#comment-104270.

Either way you should at least have a working game. [import]uid: 63320 topic_id: 21978 reply_id: 105386[/import]