minetest-quiz/quizzes.lua

294 lines
8.8 KiB
Lua

local minetest, yaml, quiz = minetest, yaml, quiz
-- Set by the mod to store and retrieve information.
-- The information is persisted, so after a server restart the information is still available.
local modstore = quiz.store
local MOD_NAME = quiz.MOD_NAME
local MOD_PATH = quiz.MOD_PATH
local S = quiz.get_translator
local settings = quiz.settings
local logQuiz = quiz.logQuiz
local toBool = dofile(MOD_PATH .. "to_bool.lua")
-- local playerAttrs = dofile(MOD_PATH .. "player_attrs.lua")
local calcType = dofile(MOD_PATH .. "calc_type.lua")
local merge = dofile(MOD_PATH .. "merge_table.lua")
-- local isArrayEqu = dofile(MOD_PATH .. "array.lua").equal
quiz.calcType = calcType
local getSession = quiz.getSession
local Quizzes = {}
-- local quizIdPrefix = MOD_NAME .. ":"
--< Current Quiz Index
-- modstore:get_int("currentQuiz")
-- local currQuiz = 1
-- if currQuiz == nil or currQuiz < 1 then currQuiz = 1 end
-- local quizzes = settings.quiz
local function isInvalidQuiz(quiz)
return not quiz.title or quiz.title == "" or not quiz.answer or quiz.answer == ""
end
quiz.isInvalidQuiz = isInvalidQuiz
local function getLastCheckedTime(playerName)
return getSession(playerName).lastChecked or 0
end
local function setLastCheckedTime(playerName, value)
local session = getSession(playerName)
if value == nil then value = os.time() end
session.lastChecked = value
end
function Quizzes.lastChecked(playerName)
return getLastCheckedTime(playerName)
end
function Quizzes.clearAnswered()
local sessions = quiz.sessions
for _,v in ipairs(sessions) do
if v and v.lastChecked ~= nil then v.lastChecked = 0 end
end
end
local function loadConfig(filename)
if filename == nil then filename = "config.yml" end
local newSettings = yaml.readConfig(MOD_NAME, filename, {"quiz"})
local result = false
if (type(newSettings) == "table") then
for k,v in pairs(newSettings) do
settings[k] = v
end
Quizzes.clearAnswered()
result = true
end
return result
end
quiz.loadConfig = loadConfig
local function saveConfig(filename)
if filename == nil then filename = "config.yml" end
return yaml.writeConfig(settings, filename, MOD_NAME)
end
quiz.saveConfig = saveConfig
local function id(quiz)
local result = quiz.id
if result == nil then
if quiz.type == "calc" then
result = quiz.calc
else
result = minetest.sha1(quiz.title .. quiz.answer)
end
end
return result
end
local function setCurrent(playerName, index)
local quizzes = settings.quiz
local currQuiz = modstore:get_int(playerName .. ":currentQuiz")
if index > #quizzes then index = #quizzes end
if index < 1 then index = 1 end
if index ~= currQuiz then
currQuiz = index
modstore:set_int(playerName ..":currentQuiz", currQuiz)
end
end
local function getCurrent(playerName)
local player = playerName
if type(player) == "string" then
player = minetest.get_player_by_name(player)
else
playerName = player:get_player_name()
end
if not player then return nil, S("@1 may be offline", playerName) end
-- local attrs = player:get_meta()
local quizzes = settings.quiz
local currQuiz = modstore:get_int(playerName .. ":currentQuiz")
if currQuiz == 0 then currQuiz = 1 end
if (not quizzes) then return nil, S("No any quiz defined") end
if (currQuiz > #quizzes) then
setCurrent(playerName, 1)
currQuiz = 1
end
local quiz = quizzes[currQuiz]
while (not quiz.title or not quiz.answer) and currQuiz < #quizzes do
-- skip illegal quiz
currQuiz = currQuiz + 1
quiz = quizzes[currQuiz]
end
if (not quiz.title or not quiz.answer) then return nil, S("invalid quiz") end
-- print('TCL:: ~ file: quizzes.lua ~ line 58 ~ getCurrent', currQuiz, dump(quiz));
if (not quiz) then return nil, S("No such quiz Id: '@1'", currQuiz) end
if settings.skipAnswered and settings.skipAnswered > 0 and quiz.type ~= "calc" then
local index = currQuiz
local session = getSession(playerName)
local answered = session[id(quiz)..":answered"] or 0
while answered >= settings.skipAnswered and index < #quizzes do
index = index+1
quiz = quizzes[currQuiz]
answered = session[id(quiz)..":answered"] or 0
end
setCurrent(playerName, index)
-- print('TCL:: ~ file: quizzes.lua ~ line 81 ~ getCurrent - answered', attrId, answered);
if (answered >= settings.skipAnswered) then
return quiz, S("@1 answered all the questions correctly.", playerName)
end
end
return quiz
end
local function next(playerName)
local quizzes = settings.quiz
local currQuiz = modstore:get_int(playerName .. ":currentQuiz")
if (currQuiz == nil) then
currQuiz = 1
else
currQuiz = currQuiz + 1
if currQuiz > #quizzes then
currQuiz = 1
end
end
setCurrent(playerName, currQuiz)
end
local function incValue(session, key)
local value = session[key] or 0
session[key] = value + 1
end
local function trim(s)
return string.gsub(s, "^%s*(.-)%s*$", "%1") --trim spaces
end
local function check(playerName, answer, quiz)
-- print('TCL:: ~ file: quizzes.lua ~ line 108 ~ check - playerName, answer, quiz', playerName, answer, dump(quiz));
if not quiz and not answer then
-- check whether it's idletime if no provide answer
local idletime = settings.idleInterval
if (idletime) then
idletime = idletime * 60
local lasttime = getLastCheckedTime(playerName)
if lasttime ~= 0 then
lasttime = os.time() - lasttime
-- print('TCL:: ~ file: quizzes.lua ~ line 142 ~ check: lasttime', idletime, lasttime);
if (lasttime <= idletime) then return nil end
end
end
end
local player = playerName
if type(player) == "string" then
player = minetest.get_player_by_name(player)
else
playerName = player:get_player_name()
end
local errmsg = nil
if type(quiz) ~= "table" then
quiz, errmsg = getCurrent(playerName)
end
-- print('TCL:: ~ file: quizzes.lua ~ line 132 ~ check quiz', dump(quiz));
if quiz then
-- local attrs = player:get_meta()
local quizId= id(quiz)
if answer ~= nil then
answer = trim(answer) --trim spaces
local session = getSession(playerName)
local vRealAnswer = quiz.answer
local vType = quiz["type"] or type(vRealAnswer)
if vType == "number" then
answer = tonumber(answer)
elseif vType == "boolean" then
answer = toBool(answer)
elseif vType == "calc" then
if quiz["real_answer"] ~= nil then
vRealAnswer = quiz["real_answer"]
else
vRealAnswer = calcType.execute(quiz["calc"], true)
end
if type(vRealAnswer) == "table" then
vType = "string"
vRealAnswer = "/^" .. vRealAnswer[1] .. "%.%.%.+" .. vRealAnswer[2] .. "/"
else
answer = tonumber(answer)
end
elseif vType == "select" then
answer = table.concat(answer, ",")
if type(vRealAnswer) == "table" then
table.sort(vRealAnswer)
vRealAnswer = table.concat(vRealAnswer, ",")
elseif type(vRealAnswer) == "string" then
local t = string.split(vRealAnswer, ",")
for i=1, #t do
t[i] = tonumber(trim(t[i]))
end
table.sort(t)
vRealAnswer = table.concat(t, ",")
else
vRealAnswer = "" .. vRealAnswer
end
end
local ok = false
if vType == "string" and string.sub(vRealAnswer, 1, 1) == "/" and string.sub(vRealAnswer, -1) == "/" then
vRealAnswer = vRealAnswer:sub(2, #vRealAnswer-1)
ok = answer:find(vRealAnswer) ~= nil
elseif answer == vRealAnswer then
ok = true
end
logQuiz(playerName, quiz, answer, ok)
if ok then
if vType == "calc" then
quiz.calc = nil
end
local answeredName = quizId ..":answered"
incValue(session, answeredName)
-- local answered = playerAttrs.getQuiz(attrs, answeredName)
-- answered = answered + 1
-- playerAttrs.setQuiz(attrs, answeredName, answered)
setLastCheckedTime(playerName, os.time())
next(playerName)
return true
else
local wrongName = quizId .. ":wrong"
incValue(session, wrongName)
-- local wrong = playerAttrs.getQuiz(attrs, wrongName)
-- wrong = wrong + 1
-- playerAttrs.setQuiz(attrs, wrongName, wrong)
return false
end
end
end
return quiz, errmsg
end
local function getTitle(quiz)
if quiz["type"] == "calc" and quiz["calc"] == nil then
local expr = calcType.parse(quiz.answer, quiz.forceInt)
quiz["calc"] = expr
quiz["real_answer"] = calcType.execute(expr, true)
end
local result = string.gsub(quiz.title, "$(%w+)", function (n)
return quiz[n]
end)
return result
end
Quizzes.getCurrent = getCurrent
Quizzes.setCurrent = setCurrent
Quizzes.check = check
Quizzes.next = next
Quizzes.id = id
Quizzes.getTitle = getTitle
return Quizzes