133 lines
3.4 KiB
Text
133 lines
3.4 KiB
Text
-- Lesson 29: Keeping Data. The finished script, exactly as the lesson ends.
|
|
|
|
dofile("Singe/Framework.singe")
|
|
|
|
local sqlite3 = require("sqlite3")
|
|
|
|
local PERFECT = 1000
|
|
|
|
-- What ships with the game: the stages, read once and closed.
|
|
local stages = {}
|
|
local shipped = sqlite3.open(DIR .. "art/stages.db", sqlite3.OPEN_READONLY)
|
|
|
|
for row in shipped:nrows("SELECT name, waitMin, waitMax FROM stages ORDER BY id") do
|
|
stages[#stages + 1] = row
|
|
end
|
|
shipped:close()
|
|
|
|
-- What the game writes: every run anybody plays, kept in the data directory.
|
|
local runs = sqlite3.open(singeGetDataPath() .. "runs.db")
|
|
|
|
runs:exec([[
|
|
CREATE TABLE IF NOT EXISTS runs (
|
|
id INTEGER PRIMARY KEY,
|
|
stage TEXT NOT NULL,
|
|
score INTEGER NOT NULL,
|
|
at INTEGER NOT NULL
|
|
)
|
|
]])
|
|
|
|
local addRun = runs:prepare("INSERT INTO runs (stage, score, at) VALUES (?, ?, ?)")
|
|
local stageAt = 1
|
|
local state = "attract"
|
|
local goAt = 0
|
|
local score = 0
|
|
local best = {}
|
|
local perStage = {}
|
|
local played = 0
|
|
|
|
|
|
-- The questions, asked when a run is added rather than every frame.
|
|
local function refresh()
|
|
best = {}
|
|
for row in runs:nrows("SELECT stage, score FROM runs ORDER BY score DESC, id LIMIT 5") do
|
|
best[#best + 1] = row
|
|
end
|
|
perStage = {}
|
|
for row in runs:nrows("SELECT stage, MAX(score) AS top, COUNT(*) AS tries FROM runs GROUP BY stage ORDER BY stage") do
|
|
perStage[#perStage + 1] = row
|
|
end
|
|
for count in runs:urows("SELECT COUNT(*) FROM runs") do
|
|
played = count
|
|
end
|
|
end
|
|
|
|
|
|
local function record(stage, points)
|
|
addRun:bind_values(stage, points, os.time())
|
|
addRun:step()
|
|
addRun:reset()
|
|
refresh()
|
|
end
|
|
|
|
|
|
local function play()
|
|
local stage = stages[stageAt]
|
|
|
|
state = "waiting"
|
|
goAt = singeGetTicks() + math.random(stage.waitMin, stage.waitMax)
|
|
score = 0
|
|
end
|
|
|
|
|
|
function onInputPressed(what)
|
|
if what == SWITCH_BUTTON2 and state ~= "waiting" and state ~= "go" then
|
|
stageAt = stageAt % #stages + 1
|
|
return
|
|
end
|
|
if what ~= SWITCH_BUTTON1 then
|
|
return
|
|
end
|
|
if state == "waiting" then
|
|
state = "result"
|
|
score = 0
|
|
record(stages[stageAt].name, score)
|
|
elseif state == "go" then
|
|
score = math.max(0, PERFECT - (singeGetTicks() - goAt))
|
|
state = "result"
|
|
record(stages[stageAt].name, score)
|
|
else
|
|
play()
|
|
end
|
|
end
|
|
|
|
|
|
function onOverlayUpdate()
|
|
if state == "waiting" and singeGetTicks() >= goAt then
|
|
state = "go"
|
|
end
|
|
|
|
overlayClear()
|
|
overlayPrint(2, 0, "REACTION TEST: " .. stages[stageAt].name)
|
|
|
|
if state == "attract" then
|
|
overlayPrint(2, 2, "Button 1 to play, button 2 for another stage.")
|
|
elseif state == "waiting" then
|
|
overlayPrint(2, 2, "Wait for it...")
|
|
elseif state == "go" then
|
|
overlayPrint(2, 2, "NOW!")
|
|
else
|
|
overlayPrint(2, 2, "You scored " .. score .. ". Button 1 plays again.")
|
|
end
|
|
|
|
overlayPrint(2, 4, "Runs kept: " .. played)
|
|
overlayPrint(2, 6, "BEST RUNS")
|
|
for place, row in ipairs(best) do
|
|
overlayPrint(2, 6 + place, string.format("%d. %-8s %4d", place, row.stage, row.score))
|
|
end
|
|
overlayPrint(24, 6, "BY STAGE")
|
|
for line, row in ipairs(perStage) do
|
|
overlayPrint(24, 6 + line, string.format("%-8s best %4d of %d", row.stage, row.top, row.tries))
|
|
end
|
|
|
|
return OVERLAY_UPDATED
|
|
end
|
|
|
|
|
|
function onShutdown()
|
|
addRun:finalize()
|
|
runs:close()
|
|
end
|
|
|
|
|
|
refresh()
|