Share Your Code

Another local score framework

Posted by MikeHart, Posted on May 16, 2010, Last updated October 7, 2010

A local score framework.

Suggestions and critics are welcome. To use this in your game, you have 7 easy functions that will interface with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- Create a new scorelist. This has to be done ALWAYS before you load one
scList = score.newList(numberOfEntries)
 
-- Load the scorelist from a given filename
scList:Load(scoreFileName)
 
-- Save the scorelist to a given filename
scList:Save(scoreFileName)
 
-- Check if a points value is inside the highscore list, and if yes return the index. 
-- It will return 0 if it isn't inside the list
index = scList:CheckScore(points)
 
-- Store a new score at the given index with the point value and a name
scList:StoreEntry(index,points,name)
 
-- Return the name at a given index
scoreName = scList:GetName(index)
 
-- Return the point value at a given index
scorePoints = scList:GetPoints(index)

Here is the score.lua file. It uses the JSON lib from this place:

http://www.chipmunkav.com/downloads/otherdownloads.htm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
-- score.lua 
-- Author Michael Hartlef
-- Version 1.0
-- License: MIT
 
module(..., package.seeall)
Json = require("Json")
 
 
--****************************************************************
function newList(hsnum)
--****************************************************************
        scoreList = {}
        scoreList.entrycount = hsnum
        scoreList.list = {}
        local i = 0
        for i = hsnum,1,-1 do
                local ts = {}
                ts.name = "---"
                ts.points = 0
                table.insert(scoreList.list, ts)
        end
        
        
        --****************************************************************
        function scoreList:Load(fn)
        --****************************************************************
                local path = system.pathForFile( fn, system.DocumentsDirectory )
                -- io.open opens a file at path. returns nil if no file found
                local file = io.open( path, "r" )
                if file then
                        -- read all contents of file into a string
                        local contents = file:read( "*a" )
                        scoreList.list = Json.Decode(contents);
                        scoreList.entrycount = #scoreList.list
                        io.close( file )
                end
        end
        
        --****************************************************************
        function scoreList:Save(fn)
        --****************************************************************
                local path = system.pathForFile( fn, system.DocumentsDirectory )
                local file = io.open( path, "w" )
                if file then
                        local contents = Json.Encode(scoreList.list)
                        file:write( contents )
                        io.close( file )
                end
        end
        
        
        --****************************************************************
        function scoreList:CheckScore(p)
        --****************************************************************
                print ("#scoreList.list:"..#scoreList.list)
                local i = 0
                for i = 1,#scoreList.list,1 do
                        local ts = scoreList.list[i]
                        if ts.points < p then
                                return i
                        end
                end
                return 0
        end
        
        --****************************************************************
        function scoreList:StoreEntry(index,points,name)
        --****************************************************************
                local ts = {}
                ts.name = name
                ts.points = points
                table.insert(scoreList.list, index, ts)
                table.remove(scoreList.list,(scoreList.entrycount+1))
        end
        
        --****************************************************************
        function scoreList:Count()
        --****************************************************************
                return scoreList.entrycount
        end
        
        --****************************************************************
        function scoreList:GetName(i)
        --****************************************************************
                local ts = scoreList.list[i]
                return ts.name
        end
        
        --****************************************************************
        function scoreList:GetPoints(i)
        --****************************************************************
                local ts = scoreList.list[i]
                return ts.points
        end
        
        return scoreList
end

Ok, that's it. I hope you like it.

Cheers
Michael

Compatibility: 
Corona 2.0

Replies

amigoni
User offline. Last seen 1 year 12 weeks ago. Offline
Joined: 12 Aug 2010

Do I have to require the file as a module? It can't find it.

amigoni
User offline. Last seen 1 year 12 weeks ago. Offline
Joined: 12 Aug 2010

Nevermind. I was missing quotes for string.

reazle
User offline. Last seen 45 weeks 3 days ago. Offline
Joined: 25 Oct 2010

I've made a topic with a sample code using this this framework because I got a problem when I build the project.
it's here : http://developer.anscamobile.com/forum/2010/10/28/best-score-sample-project-work

mediakitchen
User offline. Last seen 1 year 9 weeks ago. Offline
Joined: 27 Jul 2010

Thanks for sharing this Mike

If I want to use this with the Director Class, can you advise me how to make the functions available in all screens? Do I need to declare some globals in main.lua or is it possible without globals?

Any help appreciated

Thanks

Paul

amigoni
User offline. Last seen 1 year 12 weeks ago. Offline
Joined: 12 Aug 2010

Hi Mike thanks for the code. I am having an issue with storing scores. Instead of the scores being updated they are saved at a different location in the table.

I created an scList of 1000 and only using about 50 of them now scattered around. When I save it just saves to a different spot than indicated. I can't figure out if it's a store issue or a save issue.

amigoni
User offline. Last seen 1 year 12 weeks ago. Offline
Joined: 12 Aug 2010

Ok figured out the issue. It's in the remove function in the storing part. Before it removed the last entry instead of the entry after the one you just made.

I modified the code as shown below

1
2
3
4
5
6
7
8
function scoreList:StoreEntry(index,points,name)
        --****************************************************************
                        local ts = {}
                        ts.name = name
                        ts.points = points
                        table.insert(scoreList.list, index, ts)
                        table.remove(scoreList.list,(index+1)) -- this was scoreList.entrycount +1
        end

mdcapps
User offline. Last seen 5 weeks 10 hours ago. Offline
Joined: 21 Jan 2011

does this work with director class?

when trying to change the score i get this error

1
2
3
4
5
6
Runtime error
        /Users/maxprice/Desktop/crates./level1.lua:110: attempt to index upvalue 'scList' (a number value)
stack traceback:
        [C]: ?
        /Users/maxprice/Desktop/crates./level1.lua:110: in function </Users/maxprice/Desktop/crates./level1.lua:108>
        ?: in function <?:214>

tried without director and the code works fine, so its defo director
thanks

mdcapps
User offline. Last seen 5 weeks 10 hours ago. Offline
Joined: 21 Jan 2011

never mind, fixed it

autolib
User offline. Last seen 13 weeks 6 days ago. Offline
Joined: 19 Jan 2011

How did you get it to work with the Director class? Trying the demo below and I get a runtime error too:

module(..., package.seeall)

io.output():setvbuf('no') -- disable output buffering for console in device!

--Json = require("Json")
local score = require("score")

---------------------------------------------------------------
-- GROUPS
---------------------------------------------------------------

local localGroup = display.newGroup()

local currentScore =0

local scList=0

local bestScore = 0
--
-- ////////////////////
--

local showScore=display.newText( "", 300, 300, nil, 60)
showScore:setTextColor( 255,255,255 )
localGroup:insert(showScore)

local yourScore=display.newText( "your score", 50, 300, nil, 20)
yourScore:setTextColor( 255,255,255 )
localGroup:insert(yourScore)

local showBestScore=display.newText( "", 300, 500, nil, 60)
showBestScore:setTextColor( 255,255,255 )
localGroup:insert(showBestScore)

local yourBestScore=display.newText( "best score", 50, 500, nil, 20)
yourBestScore:setTextColor( 255,255,255 )
localGroup:insert(yourBestScore)

local instruction=display.newText( "cmd+R to relaunch", 50, 700, nil, 20)
instruction:setTextColor( 255,255,255 )
localGroup:insert(instruction)

local buttonScore = display.newImage( "btnBig.png" )
localGroup:insert(buttonScore)
buttonScore.x = 100
buttonScore.y = 100

--****************************************************************
function buttonScore:tap(event)
--****************************************************************
currentScore=currentScore+1
if currentScore>=bestScore then scList:StoreEntry(1,currentScore,"best") end
scList:Save("ants.txt")
bestScore=scList:GetPoints(1)
showScore.text=currentScore
showBestScore.text=bestScore
end

--****************************************************************
local onSystemEvent = function( event )
--****************************************************************
print ("onSystemEvent->"..event.type)
if event.type == "applicationExit" then
--
elseif event.type == "applicationStart" then
print ("applicationStart")
scList = score.newList(1)
scList:Load("ants.txt")
bestScore=scList:GetPoints(1)
print ("bestscore:"..bestScore)

elseif "applicationSuspend" == event.type then
--tSuspend = system.getTimer()
elseif "applicationResume" == event.type then
-- add missing time to tPrevious
--tPrevious = tPrevious + ( system.getTimer() - tSuspend )
end
end

--****************************************************************
local onFrame = function( event )
--****************************************************************
end

function new()

-----------------------------------
-- Initiate variables
-----------------------------------
Runtime:addEventListener( "system", onSystemEvent )
buttonScore:addEventListener("tap", buttonScore)
Runtime:addEventListener( "enterFrame", onFrame )
-----------------------------------
-- MUST return a display.newGroup()
-----------------------------------

return localGroup

end

mdcapps
User offline. Last seen 5 weeks 10 hours ago. Offline
Joined: 21 Jan 2011

1
2
3
4
5
6
7
8
9
10
local localGroup = display.newGroup()
 
local currentScore =0
 
local scList=score.newList(1)   -- changed from scList = 0
sList:Load('score.txt')  
 
 
local bestScore = 0
--

basically, further on in your code when youre trying to save to scList when the score changes, it is saving to '0' so you have to change it to a file name to save to (make sure the file name is the same as it is in your score changing chunk)

max

autolib
User offline. Last seen 13 weeks 6 days ago. Offline
Joined: 19 Jan 2011

Hi Max thanks for the speedy reply but it still does not work

local scList=score.newList(1) -- changed from scList = 0
sList:Load("ants.txt")

The above changes give me the following error:

attempt to index global 'sList' (a nil value)

Do you have a woking example using Director (Possibly where the score is loaded on one page and saved another)?

Thanks

Charlie

mdcapps
User offline. Last seen 5 weeks 10 hours ago. Offline
Joined: 21 Jan 2011

sorry, had a mistype in my post above-- i meant

1
scList:Load("ants.txt")  -- changed from sList:Load('ants.txt')

just missed off the c!

max

autolib
User offline. Last seen 13 weeks 6 days ago. Offline
Joined: 19 Jan 2011

It is now running but it dos not show the best score when it is relaunched. Any ideas?

Charlie

autolib
User offline. Last seen 13 weeks 6 days ago. Offline
Joined: 19 Jan 2011

The score is never being saved.

Charlie

mdcapps
User offline. Last seen 5 weeks 10 hours ago. Offline
Joined: 21 Jan 2011

in my code:

1
2
3
4
5
6
currentScore=currentScore+1
                        if currentScore>=bestScore then scList:StoreEntry(1,currentScore,"best") end
                                scList:Save("ants.txt")
                        bestScore=scList:GetPoints(1)
                        showScore.text=currentScore
                        showBestScore.text=bestScore

the ScList:Save is vital

max

autolib
User offline. Last seen 13 weeks 6 days ago. Offline
Joined: 19 Jan 2011

I have that but still no go. If I do a search in Spotlight for ants.txt the file is not found. Any more ideas?

autolib
User offline. Last seen 13 weeks 6 days ago. Offline
Joined: 19 Jan 2011

Finally go it to work. Thanks for your help.

autolib
User offline. Last seen 13 weeks 6 days ago. Offline
Joined: 19 Jan 2011

Hey Max,

I am having a problem with GetName working

-- code to save a score and bestname

local bestname = "sadie"
scList:StoreEntry(1,hit_bug,bestname)
scList:Save("ants.txt")

-- code to show score and name

function showscore(event)
print ("Load")
scList = score.newList(1)
scList:Load("ants.txt")
bestScore=scList:GetPoints(1)
scorename=sclist:GetName(1)
print ("bestscore:"..bestScore)
print ("bestnames:"..scorename)

Scorename.text=Scorename
showBestScore.text=bestScore
end

The score shows but not the name. An error of nil value is given forcorename=sclist:GetName(1)

Any ideas?

Thanks

alexlens
User offline. Last seen 3 years 7 weeks ago. Offline
Joined: 6 Mar 2011

Hi,

This way to store data is really great ! but i think there's something missing.
There are many case in which we would to enrypt file data. This is just to disallowed users to edit there score file.

It's a common requirement when you share score online. In instance, it's true with the iphone leader board. I have to implement this encryption to deal with leaderboard on iphone.

Does someone have an idea about how to do it with system describe in this topic? Thanks for help.

autolib
User offline. Last seen 13 weeks 6 days ago. Offline
Joined: 19 Jan 2011

Hey Mike any chance you can answer me about the " GetName" issue
(#18) above? Just trying to use your neat library.

Thanks

akviby
User offline. Last seen 8 weeks 6 days ago. Offline
Joined: 20 Jan 2011

When I try to use the save function I get

attempt to index global 'Json' (a boolean value)

Any ideas?

akviby
User offline. Last seen 8 weeks 6 days ago. Offline
Joined: 20 Jan 2011

I solved the issue by changing the top require to require("json") from Json = require("json") and then all Json.Decode to json.decode and Json.Encode to json.encode.

Thanks

borgb
User offline. Last seen 1 day 3 hours ago. Offline
Joined: 18 Jan 2011

Thanks for sharing this Michael, really great!

autolib:

you wrote: scorename=sclist:GetName(1)
needs to be: scorename=scList:GetName(1)

brainofsteel
User offline. Last seen 11 hours 58 min ago. Offline
Joined: 26 Jan 2011

Thanks for this excellent, simple framework, Mike.
I messed with a few other Corona scoring/saving frameworks and tutorials before stumbling across this one, and it's by far the most headache-free implementation of a persistent high-score list. Well done!

davidJames
User offline. Last seen 3 days 2 hours ago. Offline
Joined: 9 Aug 2011

Hi Mike (or anybody else who can help)! I'm stumped on something here. I've integrated the script with Director Class and it works fine if I just stay on one Director screen. But when I try adding a second screen, I can't figure out how to carry over the currentScore from the first screen to the second screen. Anyone with ideas here?

caprica13
User offline. Last seen 2 years 27 weeks ago. Offline
Joined: 17 Feb 2011

Hi Mike.

thanks for sharing.

I need to keep the high scores in a reverse order. In my game, the less is better. The fastest time wins.

Any ideas how to modify your code to accommodate for this ?

thank you!
R.

brainofsteel
User offline. Last seen 11 hours 58 min ago. Offline
Joined: 26 Jan 2011

For anyone that would be interested in seeing an implementation of this out in the wild, I used it to do the High Scores list in
Factor Samurai, Ansca's App of the Week.
When your game ends, a score sheet pops up with your top ten scores of all time for that difficulty, and then if your score made it into the top ten, it is shown blinking on the list.
Also it's worth mentioning that I had messed around with a few other libraries on the code exchange before landing on this one, and for simple scoring I found this one easiest to implement.
Once again, Mike, I thank you.
The app:
http://itunes.apple.com/us/app/factor-samurai/id441919897?mt=8