Add automatic solution testing

This commit is contained in:
Wuzzy 2024-10-21 18:04:42 +02:00
parent f3f14dd7de
commit b24e6b51d3
2 changed files with 112 additions and 7 deletions

View File

@ -7,6 +7,12 @@ local state = "idle"
-- true if recording should automatically stop when reaching LEVEL_COMPLETE state -- true if recording should automatically stop when reaching LEVEL_COMPLETE state
-- and trigger a file save -- and trigger a file save
local autostop = false local autostop = false
-- true if running a full solution test of the core levels
local full_test = false
-- level number of currently tested core level
local full_test_level = 0
local current_replay_time = 0 local current_replay_time = 0
local current_action local current_action
local current_solution local current_solution
@ -52,9 +58,78 @@ action: {
]] ]]
local test_next_core_solution_callback = function()
local level_data = lzr_levels.get_core_level_data()
if not level_data.solutions_path then
-- No solutions path. Nothing to test!
minetest.log("error", "[lzr_solutions] No solutions path")
return false
end
local level_id = full_test_level
local level = level_data[level_id]
if not level then
-- Level does not exist
minetest.log("error", "[lzr_solutions] Core level "..tostring(level_id).." does not exist")
return false
end
if not level.filename_solution then
-- No solution in level. Skip test.
minetest.log("error", "[lzr_solutions] Core level "..tostring(level_id).." doesn't have solution")
return false
end
local full_path = level_data.solutions_path.."/"..level.filename_solution
local solution_file = io.open(full_path, "r")
if solution_file then
local csv = solution_file:read("*a")
local solution = lzr_solutions.csv_to_solution(csv)
if solution then
lzr_solutions.replay_solution(solution)
minetest.log("action", "[lzr_solutions] Playing solution for core level "..level_id)
return true
else
minetest.log("error", "[lzr_solutions] Error in solution CSV file for core level "..level_id)
return false
end
else
minetest.log("error", "[lzr_solutions] Error while loading solution CSV file for core level "..level_id)
return false
end
end
local test_next_core_solution = function()
full_test_level = full_test_level + 1
if full_test_level > lzr_levels.LAST_LEVEL then
return false
end
if full_test_level == 1 then
minetest.log("verbose", "[lzr_solutions] Loading core level "..full_test_level.." ...")
lzr_levels.start_level(full_test_level)
end
return true
end
lzr_levels.register_on_level_start(function()
minetest.log("verbose", "[lzr_solutions] on_level_start event")
if full_test then
test_next_core_solution_callback()
end
end)
lzr_solutions.test_core_solutions = function()
full_test = true
full_test_level = 0
local ok = test_next_core_solution()
if not ok then
full_test = false
end
end
local record_action = function(solution, action) local record_action = function(solution, action)
local new_action = table.copy(action) local new_action = table.copy(action)
local time = math.floor(minetest.get_us_time()/1000) local time = math.floor(minetest.get_us_time()/5000)
new_action.time = time - current_solution_start_time new_action.time = time - current_solution_start_time
table.insert(solution.actions, new_action) table.insert(solution.actions, new_action)
end end
@ -67,7 +142,7 @@ local replay_action = function(player, action)
-- Check if node is the expected node -- Check if node is the expected node
local node = minetest.get_node(action.pos) local node = minetest.get_node(action.pos)
if node.name ~= action.node.name or node.param2 ~= action.node.param2 then if node.name ~= action.node.name or node.param2 ~= action.node.param2 then
minetest.log("error", "[lzr_solutions] Tried to dig node '"..action.node.name.."' (param2="..action.node.param2..") but found '"..node.name.."' (param2="..node.param2..")") minetest.log("error", "[lzr_solutions] Expected to dig node '"..action.node.name.."' (param2="..action.node.param2..") but found '"..node.name.."' (param2="..node.param2..")")
return false return false
end end
minetest.node_dig(action.pos, action.node, player) minetest.node_dig(action.pos, action.node, player)
@ -351,12 +426,16 @@ minetest.register_globalstep(function(dtime)
if not current_solution then if not current_solution then
minetest.log("error", "[lzr_solutions] In 'playing' state but current_solution is nil!") minetest.log("error", "[lzr_solutions] In 'playing' state but current_solution is nil!")
state = "idle" state = "idle"
full_test = false
return return
end end
if current_action > #current_solution.actions then if current_action > #current_solution.actions then
-- Replay is finished, go back to idle state -- Replay is finished
if not full_test then
-- Go back to idle state
current_solution = nil current_solution = nil
state = "idle" state = "idle"
end
return return
end end
local action = current_solution.actions[current_action] local action = current_solution.actions[current_action]
@ -370,6 +449,7 @@ minetest.register_globalstep(function(dtime)
-- Abort replay if replay_action returns false (in case of error) -- Abort replay if replay_action returns false (in case of error)
current_solution = nil current_solution = nil
state = "idle" state = "idle"
full_test = false
end end
current_action = current_action + 1 current_action = current_action + 1
end end
@ -461,11 +541,21 @@ lzr_gamestate.register_on_enter_state(function(new_state)
lzr_solutions.stop_recording_solution() lzr_solutions.stop_recording_solution()
minetest.chat_send_player("singleplayer", S("Recording cancelled.")) minetest.chat_send_player("singleplayer", S("Recording cancelled."))
end end
elseif state == "playing" and full_test then
-- If level was completed during full test, continue with next level
if new_state == lzr_gamestate.LEVEL_COMPLETE then
local ok = test_next_core_solution()
if not ok then
full_test = false
current_solution = nil
state = "idle"
end
end
end end
end) end)
minetest.register_chatcommand("replay_solution", { minetest.register_chatcommand("replay_solution", {
privs = { debug = true }, privs = { debug = true, server = true },
params = "", params = "",
description = S("Replay saved solution for current level, if one exists"), description = S("Replay saved solution for current level, if one exists"),
func = function(name, param) func = function(name, param)
@ -504,3 +594,18 @@ minetest.register_chatcommand("replay_solution", {
end end
end, end,
}) })
minetest.register_chatcommand("test_core_solutions", {
privs = { debug = true, server = true },
params = "",
description = S("Test the solution of all core levels"),
func = function(name, param)
if state == "playing" then
return false, S("Already replaying a solution!")
elseif state == "recording" then
return false, S("Already recording!")
end
lzr_solutions.test_core_solutions()
return true
end,
})

View File

@ -1,2 +1,2 @@
name = lzr_solutions name = lzr_solutions
depends = lzr_hook, lzr_laser, lzr_treasure, lzr_csv, lzr_world depends = lzr_hook, lzr_laser, lzr_treasure, lzr_csv, lzr_world, lzr_levels