tweaked operation costs

redo of code preprocessing
master
rnd 2018-07-23 15:32:46 +02:00
parent db15170554
commit 21158c67eb
3 changed files with 87 additions and 93 deletions

View File

@ -18,4 +18,12 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
---------------------------------------------------------------------- ----------------------------------------------------------------------
GAMEPLAY:
- robot has limited operations available every run ( 1 run per 1 second).
- while using for loops, while loops or function calls it is limited to default 48 such code executions per run
- while using 'physical' operations like move/dig robot has (default) 10 operations available per run. Default costs are
move=2, dig = 6, insert = 2, place = 2, machine.generate = 6, machine.smelt = 6, machine.grind = 6,

View File

@ -115,7 +115,7 @@ end
basic_robot.digcosts = { -- 1 energy = 1 coal basic_robot.digcosts = { -- 1 energy = 1 coal
["default:stone"] = 1/25, ["default:stone"] = 1/25,
["default:cloud"] = 10^8,
} }
@ -433,9 +433,18 @@ basic_robot.commands.grab = function(name,target)
end end
--local minetest_version = minetest.get_version().string;
basic_robot.commands.read_book = function (itemstack) -- itemstack should contain book basic_robot.commands.read_book = function (itemstack) -- itemstack should contain book
local data = itemstack:get_meta():to_table().fields -- 0.4.16 local data;
--local data = minetest.deserialize(itemstack:get_metadata()) -- pre 0.4.16 --if minetest_version == "0.4.16" then
data = itemstack:get_meta():to_table().fields -- 0.4.16
if data and data.text then
data.text = data.text:gsub(string.char(13),string.char(10)) --for unknown reason books sometime? convert \n (10) to CR (13)
end
-- else
-- local data = minetest.deserialize(itemstack:get_metadata()) -- pre 0.4.16
-- end
if data then if data then
return data.title,data.text; return data.title,data.text;
else else
@ -539,6 +548,8 @@ end
local robot_activate_furnace = minetest.registered_nodes["default:furnace"].on_metadata_inventory_put; -- this function will activate furnace local robot_activate_furnace = minetest.registered_nodes["default:furnace"].on_metadata_inventory_put; -- this function will activate furnace
basic_robot.commands.activate = function(name,mode, dir) basic_robot.commands.activate = function(name,mode, dir)
check_operations(name,2,true);
local obj = basic_robot.data[name].obj; local obj = basic_robot.data[name].obj;
local tpos = pos_in_dir(obj, dir); -- position of target block in front local tpos = pos_in_dir(obj, dir); -- position of target block in front
@ -896,7 +907,7 @@ basic_robot.commands.machine = {
-- convert fuel into energy -- convert fuel into energy
generate_power = function(name,input, amount) -- fuel used, if no fuel then amount specifies how much energy builtin generator should produce generate_power = function(name,input, amount) -- fuel used, if no fuel then amount specifies how much energy builtin generator should produce
check_operations(name,1.5, true) check_operations(name,6, true)
if amount and amount>0 then -- attempt to generate power from builtin generator if amount and amount>0 then -- attempt to generate power from builtin generator
local pos = basic_robot.data[name].spawnpos; -- position of spawner block local pos = basic_robot.data[name].spawnpos; -- position of spawner block
@ -943,7 +954,7 @@ basic_robot.commands.machine = {
smelt = function(name,input,amount) -- input material, amount of energy used for smelt smelt = function(name,input,amount) -- input material, amount of energy used for smelt
local energy = 0; -- can only do one step at a run time local energy = 0; -- can only do one step at a run time
check_operations(name,2,true) check_operations(name,6,true)
if string.find(input," ") then return nil, "0: only one item per smelt" end if string.find(input," ") then return nil, "0: only one item per smelt" end
@ -1005,6 +1016,7 @@ basic_robot.commands.machine = {
-- grind -- grind
grind = function(name,input) grind = function(name,input)
--[in] ={fuel cost, out, quantity of material required for processing} --[in] ={fuel cost, out, quantity of material required for processing}
check_operations(name,6,true)
local recipe = basic_robot.technic.grinder_recipes[input]; local recipe = basic_robot.technic.grinder_recipes[input];
if not recipe then return nil, "unknown recipe" end if not recipe then return nil, "unknown recipe" end
local cost = recipe[1]; local output = recipe[2]; local cost = recipe[1]; local output = recipe[2];
@ -1035,6 +1047,7 @@ basic_robot.commands.machine = {
-- compress -- compress
compress = function(name,input) compress = function(name,input)
--[in] ={fuel cost, out, quantity of material required for processing} --[in] ={fuel cost, out, quantity of material required for processing}
check_operations(name,6,true)
local recipe = basic_robot.technic.compressor_recipes[input]; local recipe = basic_robot.technic.compressor_recipes[input];
if not recipe then return nil, "unknown recipe" end if not recipe then return nil, "unknown recipe" end
local cost = recipe[1]; local output = recipe[2]; local cost = recipe[1]; local output = recipe[2];
@ -1061,13 +1074,15 @@ basic_robot.commands.machine = {
end, end,
transfer_power = function(name,amount,target) transfer_power = function(name,amount,target)
check_operations(name,2, true);
local pos = basic_robot.data[name].spawnpos; local pos = basic_robot.data[name].spawnpos;
local data = basic_robot.data[name]; local data = basic_robot.data[name];
local tdata = basic_robot.data[target]; local tdata = basic_robot.data[target];
if not tdata then return nil, "target inactive" end if not tdata then return nil, "target inactive" end
local energy = 0; -- can only do one step at a run time local energy = 0; -- can only do one step at a run time
check_operations(name,0.5, true);
energy = data.menergy or 0; energy = data.menergy or 0;
if amount>energy then return nil,"energy too low" end if amount>energy then return nil,"energy too low" end

143
init.lua
View File

@ -21,11 +21,11 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento
basic_robot.http_api = minetest.request_http_api(); basic_robot.http_api = minetest.request_http_api();
basic_robot.version = "2018/07/22a"; basic_robot.version = "2018/07/23a";
basic_robot.data = {}; -- stores all robot related data basic_robot.data = {}; -- stores all robot related data
--[[ --[[
[name] = { sandbox= .., bytecode = ..., ram = ..., obj = robot object, spawnpos= ..., authlevel = ...} [name] = { sandbox= .., bytecode = ..., ram = ..., obj = robot object, spawnpos= ..., authlevel = ... , t = code execution time}
robot object = object of entity, used to manipulate movements and more robot object = object of entity, used to manipulate movements and more
--]] --]]
basic_robot.ids = {}; -- stores maxid for each player basic_robot.ids = {}; -- stores maxid for each player
@ -52,6 +52,7 @@ function getSandboxEnv (name)
left_up = 11, right_up = 12, forward_up = 13, backward_up = 14 left_up = 11, right_up = 12, forward_up = 13, backward_up = 14
} }
if not basic_robot.data[name].rom then basic_robot.data[name].rom = {} end -- create rom if not yet existing
local env = local env =
{ {
pcall=pcall, pcall=pcall,
@ -398,16 +399,6 @@ function getSandboxEnv (name)
tonumber = tonumber, pairs = pairs, tonumber = tonumber, pairs = pairs,
ipairs = ipairs, error = error, type=type, ipairs = ipairs, error = error, type=type,
--_ccounter = basic_robot.data[name].ccounter, -- counts how many executions of critical spots in script
-- increase_ccounter =
-- function()
-- local _ccounter = basic_robot.data[name].ccounter;
-- if _ccounter > basic_robot.call_limit then
-- error("Execution limit " .. basic_robot.call_limit .. " exceeded");
-- end
-- basic_robot.data[name].ccounter = _ccounter + 1;
-- end,
}; };
-- ROBOT FUNCTIONS: move,dig, place,insert,take,check_inventory,activate,read_node,read_text,write_text -- ROBOT FUNCTIONS: move,dig, place,insert,take,check_inventory,activate,read_node,read_text,write_text
@ -565,7 +556,7 @@ end
local identify_strings = function(code) -- returns list of positions {start,end} of literal strings in lua code local identify_strings = function(code) -- returns list of positions {start,end} of literal strings in lua code
local i = 0; local j; local length = string.len(code); local i = 0; local j; local _; local length = string.len(code);
local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string
local modes = { local modes = {
{"'","'"}, {"'","'"},
@ -616,38 +607,24 @@ is_inside_string = function(strings,pos) -- is position inside one of the string
return strings[mid][1]<=pos and pos<=strings[mid][2] return strings[mid][1]<=pos and pos<=strings[mid][2]
end end
--[[
is_inside_string = function(pos,script)
local i1=string.find (script, "\"", 1);
if not i1 then
return false
end
local i2=0;
local par = 1;
if pos<i1 then
return false
end
while i1 do
i2=string.find(script,"\"",i1+1);
if i2 then
par = 1 - par;
else
return false
end
if par == 0 then
if i1<pos and pos<i2 then
return true
end
end
i1=i2;
end
return false;
end
--]]
-- COMPILATION -- COMPILATION
local find_outside_string = function(script, pattern, pos, strings)
local length = string.len(script)
local found = true;
local i1 = pos;
while found do
found = false
local i2 = string.find(script,pattern,i1);
if i2 then
if not is_inside_string(strings,i2) then return i2 end
found = true;
i1 = i2+1;
end
end
return nil
end
preprocess_code = function(script) -- version 07/22/2018 preprocess_code = function(script) -- version 07/22/2018
--[[ idea: in each local a = function (args) ... end insert counter like: --[[ idea: in each local a = function (args) ... end insert counter like:
@ -655,37 +632,33 @@ preprocess_code = function(script) -- version 07/22/2018
when counter exceeds limit exit with error when counter exceeds limit exit with error
--]] --]]
script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") -- strip comments in fancy way script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") -- strip comments
script="local _c_ = 0; " .. script;
script="_ccounter = 0; " .. script;
local i1
-- process script to insert call counter in every function -- process script to insert call counter in every function
local _increase_ccounter = " if _ccounter > " .. basic_robot.call_limit .. local _increase_ccounter = " _c_ = _c_ + 1; if _c_ > " .. basic_robot.call_limit ..
" then error(\"Execution count \".. _ccounter .. \" exceeded ".. basic_robot.call_limit .. "\") end _ccounter = _ccounter + 1; " " then error(\"Execution count \".. _c_ .. \" exceeded ".. basic_robot.call_limit .. "\") end; "
local i1=0; local i2 = 0; local i1=0; local i2 = 0;
local found = true; local found = true;
local strings = identify_strings(script); local strings = identify_strings(script);
local inserts = {};
while (found) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS while (found) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS
found = false; found = false;
i2 = nil; i2 = nil;
-- i1 = where its looking in current pass, i2 = hit position -- i1 = where its looking in current pass, i2 = hit position
i2=string.find (script, "while%s", i1) -- fix while OK i2=string.find (script, "while%s", i1) -- fix while OK
if i2 then if i2 then
if not is_inside_string(strings,i2) then if not is_inside_string(strings,i2) then
local i21 = i2; local i21 = i2;
i2=string.find(script, "%sdo%s", i2); i2 = find_outside_string(script, "%sdo%s", i2, strings); -- find first do not inside string
if i2 then if i2 then
i2 = i2 + 1 i2 = i2 + 2 -- skip space and position at 'o' in ' do'
script = script.sub(script,1, i2+1) .. _increase_ccounter .. script.sub(script, i2+2); inserts[#inserts+1]= i2;
i1=i21+6; -- after while i1=i21+6; -- after while
found = true; found = true;
end end
@ -694,12 +667,11 @@ preprocess_code = function(script) -- version 07/22/2018
i2=string.find (script, "function", i1) -- fix functions i2=string.find (script, "function", i1) -- fix functions
if i2 then if i2 then
--minetest.chat_send_all("func0")
if not is_inside_string(strings,i2) then if not is_inside_string(strings,i2) then
i2=string.find(script, ")", i2); i2 = find_outside_string(script, ")", i2, strings);
if i2 then if i2 then
script = script.sub(script,1, i2) .. _increase_ccounter .. script.sub(script, i2+1); inserts[#inserts+1]= i2;
i1=i2+string.len(_increase_ccounter); i1=i2;
found = true; found = true;
end end
end end
@ -709,45 +681,42 @@ preprocess_code = function(script) -- version 07/22/2018
i2=string.find (script, "for%s", i1) -- fix for OK i2=string.find (script, "for%s", i1) -- fix for OK
if i2 then if i2 then
if not is_inside_string(strings,i2) then if not is_inside_string(strings,i2) then
i2=string.find(script, "%sdo%s", i2); i2 = find_outside_string(script, "%sdo%s", i2, strings);
if i2 then if i2 then
i2 = i2 + 1 i2 = i2 + 2 -- position at 'o' in ' do'
script = script.sub(script,1, i2+1) .. _increase_ccounter .. script.sub(script, i2+2); inserts[#inserts+1]= i2;
i1=i2+string.len(_increase_ccounter); i1=i2;
found = true; found = true;
end end
end end
end end
i2=string.find (script, "goto%s", i1) -- fix goto OK i2=string.find(script, "goto%s", i1) -- fix goto OK
if i2 then if i2 then
if not is_inside_string(strings,i2) then if not is_inside_string(strings,i2) then
script = script.sub(script,1, i2-1) .. _increase_ccounter .. script.sub(script, i2); inserts[#inserts+1]= i2-1; -- just before goto
i1=i2+string.len(_increase_ccounter)+5; -- insert + skip goto i1=i2+5; -- insert + skip goto
found = true; found = true;
end end
end end
i2=string.find (script, "pause()", i1) -- fix pause ?
if i2 then
if not is_inside_string(strings,i2) then
script = script.sub(script,1, i2-1) .. "_ccounter = 0;" .. script.sub(script,i2)
i1=i2+14+7; -- insert + skip _ccounter & pause
found = true;
end
end
end end
return script -- add inserts
local ret = {}; i1=1;
for i = 1, #inserts do
i2 = inserts[i];
ret[#ret+1] = string.sub(script,i1,i2);
i1 = i2+1;
end
ret[#ret+1] = string.sub(script,i1);
script = table.concat(ret,_increase_ccounter)
return script:gsub("pause()", "_c_ = 0; pause()") -- reset ccounter at pause
end end
local function CompileCode ( script ) local function CompileCode ( script )
--minetest.chat_send_all(script)
--if true then return nil, "" end
local ScriptFunc, CompileError = loadstring( script ) local ScriptFunc, CompileError = loadstring( script )
if CompileError then if CompileError then
return nil, CompileError return nil, CompileError
@ -794,8 +763,8 @@ local function runSandbox( name)
return "Bytecode missing." return "Bytecode missing."
end end
data.ccounter = 0;
data.operations = basic_robot.maxoperations; data.operations = basic_robot.maxoperations;
data.t = os.clock()
setfenv( ScriptFunc, data.sandbox ) setfenv( ScriptFunc, data.sandbox )
@ -803,15 +772,17 @@ local function runSandbox( name)
if cor then -- coroutine! if cor then -- coroutine!
local err,ret local err,ret
ret,err = coroutine.resume(cor) ret,err = coroutine.resume(cor)
data.t = os.clock()-data.t
if err then return err end if err then return err end
return nil return nil
end end
local Result, RuntimeError = pcall( ScriptFunc ) local Result, RuntimeError = pcall( ScriptFunc )
data.t = os.clock()-data.t
if RuntimeError then if RuntimeError then
return RuntimeError return RuntimeError
end end
return nil return nil
end end
@ -1140,7 +1111,7 @@ local spawn_robot = function(pos,node,ttl)
if not data.sandbox then data.sandbox = getSandboxEnv (name) end if not data.sandbox then data.sandbox = getSandboxEnv (name) end
-- actual code run process -- actual code run process
data.ccounter = 0;data.operations = basic_robot.maxoperations; data.operations = basic_robot.maxoperations;
setfenv(data.bytecode, data.sandbox ) setfenv(data.bytecode, data.sandbox )
@ -1183,7 +1154,7 @@ local spawn_robot = function(pos,node,ttl)
if data == nil then if data == nil then
basic_robot.data[name] = {}; basic_robot.data[name] = {};
data = basic_robot.data[name]; data = basic_robot.data[name];
data.rom = {}; --data.rom = {};
end end
data.owner = owner; data.owner = owner;