technic: build your technic machines incode with machine.generate_power, machine.smelt, machine.grind, machine.compress. Requires fuel and materials just like the real thing.
This commit is contained in:
parent
182d81acec
commit
c3b8f19c83
309
commands.lua
309
commands.lua
@ -97,6 +97,29 @@ basic_robot.commands.dig = function(name,dir)
|
||||
|
||||
local spos = obj:get_luaentity().spawnpos;
|
||||
local inv = minetest.get_meta(spos):get_inventory();
|
||||
|
||||
--require coal to dig
|
||||
if nodename == "default:stone" and basic_robot.use_coal then
|
||||
local meta = minetest.get_meta(spos);
|
||||
local fuel = meta:get_int("fuel")-1;
|
||||
if fuel<0 then -- attempt to refuel
|
||||
local stack = ItemStack("default:coal_lump 10");
|
||||
if inv:contains_item("main", stack) then
|
||||
meta:set_int("fuel",50) -- 50 digs with 10 coal
|
||||
inv:remove_item("main", stack)
|
||||
else
|
||||
error("#OUT OF FUEL: please insert 10 coal lumps to dig")
|
||||
basic_robot.data[name].obj:remove();
|
||||
basic_robot.data[name].obj=nil;
|
||||
return
|
||||
end
|
||||
else
|
||||
meta:set_int("fuel",fuel)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
if not inv then return end
|
||||
--inv:add_item("main",ItemStack( nodename ));
|
||||
|
||||
@ -225,7 +248,6 @@ basic_robot.no_teleport_table = {
|
||||
["robot"] = true,
|
||||
}
|
||||
|
||||
-- BUG : doesnt return list!
|
||||
|
||||
basic_robot.commands.pickup = function(r,name)
|
||||
|
||||
@ -285,7 +307,7 @@ basic_robot.commands.write_text = function(name,dir,text)
|
||||
return true
|
||||
end
|
||||
|
||||
basic_robot.commands.place = function(name,nodename, dir)
|
||||
basic_robot.commands.place = function(name,nodename, param2,dir)
|
||||
local obj = basic_robot.data[name].obj;
|
||||
local pos = pos_in_dir(obj, dir)
|
||||
local luaent = obj:get_luaentity();
|
||||
@ -315,8 +337,12 @@ basic_robot.commands.place = function(name,nodename, dir)
|
||||
if placename then
|
||||
minetest.set_node(pos,{name = placename})
|
||||
tick(pos); -- needed for seeds to grow
|
||||
else
|
||||
minetest.set_node(pos,{name = nodename})
|
||||
else -- normal place
|
||||
if param2 then
|
||||
minetest.set_node(pos,{name = nodename, param2 = param2})
|
||||
else
|
||||
minetest.set_node(pos,{name = nodename})
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
@ -480,24 +506,25 @@ basic_robot.commands.activate = function(name,mode, dir)
|
||||
local node = minetest.get_node(tpos);
|
||||
if node.name == "default:furnace" or node.name == "default:furnace_active" then
|
||||
if mode>0 then robot_activate_furnace(tpos) end
|
||||
return
|
||||
return true
|
||||
end
|
||||
|
||||
local table = minetest.registered_nodes[node.name];
|
||||
if table and table.mesecons and table.mesecons.effector then
|
||||
else
|
||||
return
|
||||
return false
|
||||
end -- error
|
||||
|
||||
local effector=table.mesecons.effector;
|
||||
|
||||
if mode > 0 then
|
||||
if not effector.action_on then return end
|
||||
if not effector.action_on then return false end
|
||||
effector.action_on(tpos,node,16)
|
||||
elseif mode<0 then
|
||||
if not effector.action_off then return end
|
||||
if not effector.action_off then return false end
|
||||
effector.action_off(tpos,node,16)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@ -603,19 +630,22 @@ basic_robot.commands.craft = function(item, name)
|
||||
|
||||
local cache = basic_robot.commands.craftcache[name];
|
||||
if not cache then basic_robot.commands.craftcache[name] = {}; cache = basic_robot.commands.craftcache[name] end
|
||||
local itemlist = {};
|
||||
local itemlist = {}; local output = "";
|
||||
if cache.item == item then-- read cache
|
||||
itemlist = cache.itemlist;
|
||||
output = cache.output;
|
||||
else
|
||||
|
||||
local craft = minetest.get_craft_recipe(item);
|
||||
if craft and craft.type == "normal" and craft.items then else return end
|
||||
output = craft.output;
|
||||
local items = craft.items;
|
||||
for _,item in pairs(items) do
|
||||
itemlist[item]=(itemlist[item] or 0)+1;
|
||||
end
|
||||
cache.item = item;
|
||||
cache.itemlist = itemlist;
|
||||
cache.output = output;
|
||||
|
||||
-- loop through robot inventory for those "group" items and see if anything in inventory matches group - then replace
|
||||
-- group name with that item
|
||||
@ -659,7 +689,7 @@ basic_robot.commands.craft = function(item, name)
|
||||
inv:remove_item("main",stack);
|
||||
end
|
||||
|
||||
inv:add_item("main",ItemStack(item))
|
||||
inv:add_item("main",ItemStack(output))
|
||||
return true
|
||||
end
|
||||
|
||||
@ -668,7 +698,6 @@ basic_robot.commands.show_form = function(name, playername, form)
|
||||
minetest.show_formspec(playername, "robot_form".. name, form)
|
||||
end
|
||||
|
||||
|
||||
-- handle robots receiving fields
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if not string.sub(formname,1,10) == "robot_form" then return end
|
||||
@ -676,4 +705,260 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if not basic_robot.data[name] then return end
|
||||
basic_robot.data[name].read_form = fields;
|
||||
basic_robot.data[name].form_sender = player:get_player_name() or "";
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
-- ROBOT TECHNIC
|
||||
-- amount parameter in generate_power, smelt,... is determined by upgrade level
|
||||
-- it specifies how much energy will be generated :
|
||||
|
||||
basic_robot.technic = { -- data cache
|
||||
fuels = {}, --[fuel] = value
|
||||
smelts = {}, -- item = [cooktime, cookeditem, aftercookeditem]
|
||||
|
||||
grinder_recipes = { --[in] ={fuel cost, out, quantity of material required for processing}
|
||||
["default:stone"] = {2,"default:sand",1},
|
||||
["default:cobble"] = {1,"default:gravel",1},
|
||||
["default:gravel"] = {0.5,"default:dirt",1},
|
||||
["default:dirt"] = {0.5,"default:clay_lump 4",1},
|
||||
["es:aikerum_crystal"] ={16,"es:aikerum_dust 2",1}, -- added for es mod
|
||||
["es:ruby_crystal"] = {16,"es:ruby_dust 2",1},
|
||||
["es:emerald_crystal"] = {16,"es:emerald_dust 2",1},
|
||||
["es:purpellium_lump"] = {16,"es:purpellium_dust 2",1},
|
||||
["default:obsidian_shard"] = {199,"default:lava_source",1},
|
||||
["gloopblocks:basalt"] = {1, "default:cobble",1}, -- enable coble farms with gloopblocks mod
|
||||
["default:ice"] = {1, "default:snow 4",1},
|
||||
["darkage:silt_lump"]={1,"darkage:chalk_powder",1},
|
||||
["default:diamond"] = {16, "basic_machines:diamond_dust_33 2", 1},
|
||||
["default:ice"] = {1, "default:snow", 1},
|
||||
["moreores:tin_lump"] = {4,"basic_machines:tin_dust_33 2",1},
|
||||
["default:obsidian_shard"] = {199, "default:lava_source",1},
|
||||
["default:mese_crystal"] = {8, "basic_machines:mese_dust_33 2",1},
|
||||
["moreores:mithril_ingot"] = {16, "basic_machines:mithril_dust_33 2",1},
|
||||
["moreores:silver_ingot"] = {5, "basic_machines:silver_dust_33 2",1},
|
||||
["moreores:tin_ingot"] = {4,"basic_machines:tin_dust_33 2",1},
|
||||
["moreores:mithril_lump"] = {16, "basic_machines:mithril_dust_33 2",1},
|
||||
["default:steel_ingot"] = {4, "basic_machines:iron_dust_33 2",1},
|
||||
["moreores:silver_lump"] = {5, "basic_machines:silver_dust_33 2",1},
|
||||
["default:gold_ingot"] = {6, "basic_machines:gold_dust_33 2", 1},
|
||||
["default:copper_ingot"] = {4, "basic_machines:copper_dust_33 2",1},
|
||||
["default:gold_lump"] = {6, "basic_machines:gold_dust_33 2", 1},
|
||||
["default:iron_lump"] = {4, "basic_machines:iron_dust_33 2",1},
|
||||
["default:copper_lump"] = {4, "basic_machines:copper_dust_33 2",1},
|
||||
},
|
||||
|
||||
compressor_recipes = { --[in] ={fuel cost, out, quantity of material required for processing}
|
||||
["default:snow"] = {1,"default:ice"},
|
||||
["default:coalblock"] = {16,"default:diamond"},
|
||||
},
|
||||
}
|
||||
|
||||
local chk_machine_level = function(inv,level) -- does machine have upgrade to be classified with at least "level"
|
||||
local upg = {"default:diamondblock","default:mese","default:goldblock"};
|
||||
for i = 1,#upg do
|
||||
if not inv:contains_item("main",ItemStack(upg[i].. " " .. level)) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
basic_robot.commands.machine = {
|
||||
|
||||
-- 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
|
||||
|
||||
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 inv = minetest.get_meta(pos):get_inventory();
|
||||
local level = amount*40; -- to generate 1 unit ( coal lump per second ) we need at least upgrade 40
|
||||
if not chk_machine_level(inv, level) then error("generate_power : tried to generate " .. amount .. " energy requires upgrade level at least " .. level .. " (blocks of mese, diamond, gold )") return end
|
||||
local data = basic_robot.data[name];
|
||||
local energy = (data.menergy or 0)+amount;
|
||||
data.menergy = energy;
|
||||
return energy;
|
||||
end
|
||||
|
||||
local energy = 0; -- can only do one step at a run time
|
||||
if basic_robot.maxenergy~=0 then
|
||||
local data = basic_robot.data[name];
|
||||
energy = data.energy;
|
||||
if energy > 0 then data.energy = energy-1 else error("only one generate_power per run step allowed"); return end
|
||||
end
|
||||
|
||||
if string.find(input," ") then return nil, "1: can convert only one item at once" end
|
||||
|
||||
local pos = basic_robot.data[name].spawnpos; -- position of spawner block
|
||||
local inv = minetest.get_meta(pos):get_inventory();
|
||||
local stack = ItemStack(input);
|
||||
if not inv:contains_item("main",stack) then return nil,"2: no input material" end
|
||||
|
||||
-- read energy value of input
|
||||
local add_energy = basic_robot.technic.fuels[input];
|
||||
if not add_energy then -- lookup fuel value
|
||||
local fueladd, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = {stack}})
|
||||
if fueladd.time > 0 then
|
||||
add_energy = fueladd.time;
|
||||
end
|
||||
if add_energy>0 then basic_robot.technic.fuels[input] = add_energy/40 end
|
||||
end
|
||||
|
||||
inv:remove_item("main", stack);
|
||||
|
||||
--add energy
|
||||
local data = basic_robot.data[name]; energy = data.menergy or 0;
|
||||
energy = energy+ add_energy;data.menergy = energy
|
||||
return energy;
|
||||
end,
|
||||
|
||||
-- smelting
|
||||
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
|
||||
if basic_robot.maxenergy~=0 then
|
||||
local data = basic_robot.data[name];
|
||||
energy = data.energy;
|
||||
if energy > 0 then
|
||||
data.energy = energy-1
|
||||
else
|
||||
error("only one smelt per run step allowed"); return
|
||||
end
|
||||
end
|
||||
|
||||
if string.find(input," ") then return nil, "0: only one item per smelt" end
|
||||
|
||||
local pos = basic_robot.data[name].spawnpos; -- position of spawner block
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = minetest.get_meta(pos):get_inventory();
|
||||
|
||||
--read robot energy
|
||||
local cost = 1/40;
|
||||
local smelttimeboost = 1;
|
||||
local level = 1;
|
||||
if amount and amount>0 then
|
||||
level = amount*10; -- 10 level required for 1 of amount
|
||||
if not chk_machine_level(inv,level) then
|
||||
error("3 smelting: need at least level " .. level .. " upgrade for required power " .. amount);
|
||||
return
|
||||
end
|
||||
cost = cost*(1+amount);
|
||||
smelttimeboost = smelttimeboost + amount; -- double speed with amount 1
|
||||
end
|
||||
|
||||
local data = basic_robot.data[name]
|
||||
energy = data.menergy or 0; -- machine energy
|
||||
if energy<=cost then return nil,"1: not enough energy" end
|
||||
|
||||
local stack = ItemStack(input);
|
||||
if not inv:contains_item("main",stack) then return nil, "2: no input materials" end
|
||||
|
||||
local src_time = (data.src_time or 0)+smelttimeboost;
|
||||
|
||||
-- get smelting data
|
||||
local smelts = basic_robot.technic.fuels[input];
|
||||
if not smelts then
|
||||
local cooked, aftercooked;
|
||||
cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = {stack}})
|
||||
if cooked.time>0 then
|
||||
basic_robot.technic.fuels[input] = {cooked.time, cooked.item, aftercooked.items[1]};
|
||||
smelts = basic_robot.technic.fuels[input];
|
||||
end
|
||||
end
|
||||
local cooktime = smelts[1]; local cookeditem = smelts[2]; local aftercookeditem = smelts[3]
|
||||
|
||||
-- is smelting done?
|
||||
data.menergy = energy-cost;
|
||||
if src_time >= cooktime then
|
||||
inv:remove_item("main",stack);
|
||||
inv:add_item("main", ItemStack(aftercookeditem));
|
||||
inv:add_item("main", ItemStack(cookeditem));
|
||||
data.src_time = 0
|
||||
return true
|
||||
else
|
||||
data.src_time = src_time
|
||||
return math.floor(src_time/cooktime*100*100)/100
|
||||
end
|
||||
end,
|
||||
|
||||
-- grind
|
||||
grind = function(name,input)
|
||||
--[in] ={fuel cost, out, quantity of material required for processing}
|
||||
local recipe = basic_robot.technic.grinder_recipes[input];
|
||||
if not recipe then return nil, "unknown recipe" end
|
||||
local cost = recipe[1]; local output = recipe[2];
|
||||
|
||||
local pos = basic_robot.data[name].spawnpos; -- position of spawner block
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = minetest.get_meta(pos):get_inventory();
|
||||
|
||||
--level requirement
|
||||
local level = math.floor((cost-1)/3)
|
||||
|
||||
if not chk_machine_level(inv,level) then error("0: tried to grind " .. input .. " requires upgrade level at least " .. level) return end
|
||||
|
||||
local stack = ItemStack(input);
|
||||
if not inv:contains_item("main",stack) then return nil, "1: missing input material" end
|
||||
|
||||
local data = basic_robot.data[name];
|
||||
local energy = data.menergy or 0;
|
||||
if energy<cost then return nil, "2: low energy " .. energy .. "/" .. cost end
|
||||
data.menergy = energy-cost
|
||||
|
||||
inv:remove_item("main",ItemStack(input))
|
||||
inv:add_item("main",ItemStack(output));
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
-- compress
|
||||
compress = function(name,input)
|
||||
--[in] ={fuel cost, out, quantity of material required for processing}
|
||||
local recipe = basic_robot.technic.compressor_recipes[input];
|
||||
if not recipe then return nil, "unknown recipe" end
|
||||
local cost = recipe[1]; local output = recipe[2];
|
||||
|
||||
local pos = basic_robot.data[name].spawnpos; -- position of spawner block
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = minetest.get_meta(pos):get_inventory();
|
||||
|
||||
--level requirement
|
||||
local level = math.floor(cost/2)
|
||||
if not chk_machine_level(inv,level) then error("tried to compress " .. input .. " requires upgrade level at least " .. level) return end
|
||||
|
||||
local stack = ItemStack(input);
|
||||
if not inv:contains_item("main",stack) then return nil, "1: missing input material" end
|
||||
|
||||
local data = basic_robot.data[name];
|
||||
local energy = data.menergy or 0;
|
||||
if energy<cost then return nil, "2: low energy " .. energy .. "/" .. cost end
|
||||
data.menergy = energy-cost
|
||||
|
||||
inv:remove_item("main",ItemStack(input))
|
||||
inv:add_item("main",ItemStack(output));
|
||||
return true
|
||||
end,
|
||||
|
||||
transfer_power = function(name,amount,target)
|
||||
local pos = basic_robot.data[name].spawnpos;
|
||||
local data = basic_robot.data[name];
|
||||
local tdata = basic_robot.data[target];
|
||||
if not tdata then return nil, "target inactive" end
|
||||
|
||||
local energy = 0; -- can only do one step at a run time
|
||||
if basic_robot.maxenergy~=0 then
|
||||
local data = basic_robot.data[name];
|
||||
energy = data.energy;
|
||||
if energy > 0 then
|
||||
data.energy = energy-1
|
||||
else
|
||||
error("only one transfer per run step allowed"); return
|
||||
end
|
||||
end
|
||||
|
||||
energy = data.menergy or 0;
|
||||
if amount>energy then return nil,"energy too low" end
|
||||
|
||||
if not tdata.menergy then tdata.menergy = 0 end
|
||||
tdata.menergy = tdata.menergy + amount
|
||||
data.energy = energy - amount;
|
||||
|
||||
end,
|
||||
}
|
136
init.lua
136
init.lua
@ -9,6 +9,7 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento
|
||||
["craft_guide:sign_wall"] = true,
|
||||
}
|
||||
basic_robot.maxenergy = 1; -- how much energy available per run, 0 = unlimited
|
||||
basic_robot.use_coal = true; -- does robot require coal to dig stone?
|
||||
----------------------
|
||||
|
||||
|
||||
@ -63,13 +64,13 @@ function getSandboxEnv (name)
|
||||
},
|
||||
|
||||
place = {
|
||||
left = function(nodename) return commands.place(name,nodename, 1) end,
|
||||
right = function(nodename) return commands.place(name,nodename, 2) end,
|
||||
forward = function(nodename) return commands.place(name,nodename, 3) end,
|
||||
backward = function(nodename) return commands.place(name,nodename, 4) end,
|
||||
down = function(nodename) return commands.place(name,nodename, 6) end,
|
||||
up = function(nodename) return commands.place(name,nodename, 5) end,
|
||||
forward_down = function(nodename) return commands.place(name,nodename, 7) end,
|
||||
left = function(nodename, param2) return commands.place(name,nodename, param2, 1) end,
|
||||
right = function(nodename, param2) return commands.place(name,nodename, param2, 2) end,
|
||||
forward = function(nodename, param2) return commands.place(name,nodename, param2, 3) end,
|
||||
backward = function(nodename, param2) return commands.place(name,nodename, param2, 4) end,
|
||||
down = function(nodename, param2) return commands.place(name,nodename, param2, 6) end,
|
||||
up = function(nodename, param2) return commands.place(name,nodename, param2, 5) end,
|
||||
forward_down = function(nodename, param2) return commands.place(name,nodename, param2, 7) end,
|
||||
},
|
||||
|
||||
insert = { -- insert item from robot inventory into another inventory
|
||||
@ -127,6 +128,11 @@ function getSandboxEnv (name)
|
||||
name = function() return name end,
|
||||
viewdir = function() local yaw = basic_robot.data[name].obj:getyaw(); return {x=math.cos(yaw), y = 0, z=math.sin(yaw)} end,
|
||||
|
||||
skin = function(textures)
|
||||
local obj = basic_robot.data[name].obj;
|
||||
obj:set_properties({textures=textures});
|
||||
end,
|
||||
|
||||
listen = function (mode)
|
||||
if mode == 1 then
|
||||
basic_robot.data.listening[name] = true
|
||||
@ -236,6 +242,15 @@ function getSandboxEnv (name)
|
||||
end,
|
||||
|
||||
},
|
||||
|
||||
machine = {-- adds technic like functionality to robots: power generation, smelting, grinding, compressing
|
||||
energy = function() return basic_robot.data[name].menergy or 0 end,
|
||||
generate_power = function(input,amount) return commands.machine.generate_power(name,input, amount) end,
|
||||
smelt = function(input,amount) return commands.machine.smelt(name,input, amount) end,
|
||||
grind = function(input) return commands.machine.grind(name,input) end,
|
||||
compress = function(input) return commands.machine.compress(name,input) end,
|
||||
transfer_power = function(amount,target) return commands.machine.transfer_power(name,amount,target) end,
|
||||
},
|
||||
|
||||
keyboard = {
|
||||
get = function() return commands.keyboard.get(name) end,
|
||||
@ -1057,54 +1072,67 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
|
||||
if fields.help then
|
||||
|
||||
local text = "BASIC LUA SYNTAX\n \nif x==1 then A else B end"..
|
||||
"\nfor i = 1, 5 do something end \nwhile i<6 do A; i=i+1; end\n"..
|
||||
"\narrays: myTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}\n"..
|
||||
"access table entries with myTable1[1] or myTable2.entry1 or myTable2[\"entry1\"]\n \n"..
|
||||
"\n for i = 1, 5 do something end \nwhile i<6 do A; i=i+1; end\n"..
|
||||
"\n arrays: myTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}\n"..
|
||||
" access table entries with myTable1[1] or myTable2.entry1 or myTable2[\"entry1\"]\n \n"..
|
||||
"ROBOT COMMANDS\n \n"..
|
||||
" **MOVEMENT,DIGGING, PLACING, INVENTORY TAKE/INSERT\nmove.direction(), where direction is forward, backward, left,right, up, down)\n"..
|
||||
"forward_down direction only works with dig, place and read_node\n"..
|
||||
"turn.left(), turn.right(), turn.angle(45)\n"..
|
||||
"dig.direction()\nplace.direction(\"default:dirt\")\nread_node.direction() tells you names of nodes\n"..
|
||||
"insert.direction(item, inventory) inserts item from robot inventory to target inventory\n"..
|
||||
"check_inventory.direction(itemname, inventory,index) looks at node and returns false/true, direction can be self,\n"..
|
||||
" if index>0 it returns itemname. if itemname == \"\" it checks if inventory empty\n"..
|
||||
"activate.direction(mode) activates target block\n"..
|
||||
"pickup(r) picks up all items around robot in radius r<8 and returns list or nil\n"..
|
||||
"craft(item) crafts item if required materials are present in inventory\n"..
|
||||
"take.direction(item, inventory) takes item from target inventory into robot inventory\n"..
|
||||
"read_text.direction(stringname,mode) reads text of signs, chests and other blocks, optional stringname for other meta,\n mode 1 read number\n"..
|
||||
"write_text.direction(text,mode) writes text to target block as infotext\n"..
|
||||
" **BOOKS/CODE\ntitle,text=book.read(i) returns title,contents of book at i-th position in library \nbook.write(i,title,text) writes book at i-th position at spawner library\n"..
|
||||
"code.run(text) compiles and runs the code in sandbox\n"..
|
||||
"code.set(text) replaces current bytecode of robot\n"..
|
||||
"find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around robot, or false if none\n"..
|
||||
" **PLAYERS\n"..
|
||||
"find_player(3) finds players in radius 3 around robot and returns list, if none returns nil\n"..
|
||||
"attack(target) attempts to attack target player if nearby \n"..
|
||||
"grab(target) attempt to grab target player if nearby and returns true if succesful \n"..
|
||||
"player.getpos(name) return position of player, player.connected() returns list of players\n"..
|
||||
" **ROBOT\n"..
|
||||
"say(\"hello\") will speak\n"..
|
||||
"self.listen(0/1) (de)attaches chat listener to robot\n"..
|
||||
"speaker, msg = self.listen_msg() retrieves last chat message if robot listens\n"..
|
||||
"self.send_mail(target,mail) sends mail to target robot\n"..
|
||||
"sender,mail = self.read_mail() reads mail, if any\n" ..
|
||||
"self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}\n"..
|
||||
"self.name() returns robot name\n"..
|
||||
"self.spam(0/1) (dis)enable message repeat to all\n"..
|
||||
"self.remove() stops program and removes robot object\n"..
|
||||
"self.reset() resets robot position\n"..
|
||||
"self.spawnpos() returns position of spawner block\n"..
|
||||
"self.viewdir() returns vector of view for robot\n"..
|
||||
"self.fire(speed, pitch,gravity) fires a projectile from robot\n"..
|
||||
"self.fire_pos() returns last hit position\n"..
|
||||
"self.label(text) changes robot label\n"..
|
||||
"self.display_text(text,linesize,size) displays text instead of robot face\n"..
|
||||
"rom is aditional table that can store persistent data, like rom.x=1\n"..
|
||||
" **KEYBOARD : place spawner at coordinates (20i,40j+1,20k) to monitor events\n"..
|
||||
"keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. } for keyboard event\n"..
|
||||
"keyboard.set(pos,type) set key at pos of type 0=air, 1..6, limited to range 10 around\n"..
|
||||
"keyboard.read(pos) return node name at pos\n";
|
||||
"**MOVEMENT,DIGGING, PLACING, INVENTORY TAKE/INSERT\n move.direction(), where direction is forward, backward, left,right, up, down)\n"..
|
||||
" forward_down direction only works with dig, place and read_node\n"..
|
||||
" turn.left(), turn.right(), turn.angle(45)\n"..
|
||||
" dig.direction()\n"..
|
||||
" place.direction(\"default:dirt\", optional orientation param)\n"..
|
||||
" read_node.direction() tells you names of nodes\n"..
|
||||
" insert.direction(item, inventory) inserts item from robot inventory to target inventory\n"..
|
||||
" check_inventory.direction(itemname, inventory,index) looks at node and returns false/true, direction can be self,\n"..
|
||||
" if index>0 it returns itemname. if itemname == \"\" it checks if inventory empty\n"..
|
||||
" activate.direction(mode) activates target block\n"..
|
||||
" pickup(r) picks up all items around robot in radius r<8 and returns list or nil\n"..
|
||||
" craft(item) crafts item if required materials are present in inventory\n"..
|
||||
" take.direction(item, inventory) takes item from target inventory into robot inventory\n"..
|
||||
" read_text.direction(stringname,mode) reads text of signs, chests and other blocks, optional stringname for other meta,\n mode 1 read number\n"..
|
||||
" write_text.direction(text,mode) writes text to target block as infotext\n"..
|
||||
"**BOOKS/CODE\n title,text=book.read(i) returns title,contents of book at i-th position in library \n book.write(i,title,text) writes book at i-th position at spawner library\n"..
|
||||
" code.run(text) compiles and runs the code in sandbox\n"..
|
||||
" code.set(text) replaces current bytecode of robot\n"..
|
||||
" find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around robot, or false if none\n"..
|
||||
"**PLAYERS\n"..
|
||||
" find_player(3) finds players in radius 3 around robot and returns list, if none returns nil\n"..
|
||||
" attack(target) attempts to attack target player if nearby \n"..
|
||||
" grab(target) attempt to grab target player if nearby and returns true if succesful \n"..
|
||||
" player.getpos(name) return position of player, player.connected() returns list of players\n"..
|
||||
"**ROBOT\n"..
|
||||
" say(\"hello\") will speak\n"..
|
||||
" self.listen(0/1) (de)attaches chat listener to robot\n"..
|
||||
" speaker, msg = self.listen_msg() retrieves last chat message if robot listens\n"..
|
||||
" self.send_mail(target,mail) sends mail to target robot\n"..
|
||||
" sender,mail = self.read_mail() reads mail, if any\n" ..
|
||||
" self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}\n"..
|
||||
" self.name() returns robot name\n"..
|
||||
" self.skin(textures) sets robot skin, textures is array of 6 textures\n"..
|
||||
" self.spam(0/1) (dis)enable message repeat to all\n"..
|
||||
" self.remove() stops program and removes robot object\n"..
|
||||
" self.reset() resets robot position\n"..
|
||||
" self.spawnpos() returns position of spawner block\n"..
|
||||
" self.viewdir() returns vector of view for robot\n"..
|
||||
" self.fire(speed, pitch,gravity) fires a projectile from robot\n"..
|
||||
" self.fire_pos() returns last hit position\n"..
|
||||
" self.label(text) changes robot label\n"..
|
||||
" self.display_text(text,linesize,size) displays text instead of robot face\n"..
|
||||
" rom is aditional table that can store persistent data, like rom.x=1\n"..
|
||||
"**KEYBOARD : place spawner at coordinates (20i,40j+1,20k) to monitor events\n"..
|
||||
" keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. } for keyboard event\n"..
|
||||
" keyboard.set(pos,type) set key at pos of type 0=air, 1..6, limited to range 10 around\n"..
|
||||
" keyboard.read(pos) return node name at pos\n"..
|
||||
"**TECHNIC FUNCTIONALITY: namespace 'machine'\n" ..
|
||||
" energy() displays available energy\n"..
|
||||
" generate_power(fuel, amount) attempt to generate power from fuel material, if\n" ..
|
||||
" amount>0 try generate amount of power using builtin generator - this requires\n" ..
|
||||
" 40 gold/mese/diamonblock upgrades for each 1 amount\n"..
|
||||
" smelt(input,amount) works as a furnace, if amount>0 try to use power to smelt -\n" ..
|
||||
" requires 10 upgrades for each 1 amount, energy cost is: 1/40*(1+amount)\n"..
|
||||
" grind(input) - grinds input material, requires upgrades for harder material\n"..
|
||||
" compress(input) requires upgrades - energy intensive process\n" ..
|
||||
" transfer_power(amount,target_robot_name)\n";
|
||||
|
||||
text = minetest.formspec_escape(text);
|
||||
|
||||
|
@ -1,195 +0,0 @@
|
||||
--black box by rnd, 03/18/2017
|
||||
--https://en.wikipedia.org/wiki/Black_Box_(game)
|
||||
|
||||
if not data then
|
||||
m=8;n=8;turn = 0;
|
||||
attempts = 1;
|
||||
|
||||
self.spam(1);t0 = _G.minetest.get_gametime();
|
||||
spawnpos = self.spawnpos()
|
||||
data = {};
|
||||
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
|
||||
|
||||
for i=1,4 do -- put in 4 atoms randomly
|
||||
data[math.random(m)][math.random(n)] = 1
|
||||
end
|
||||
|
||||
render_board = function(mode) -- mode 0 : render without solution, 1: render solution
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if mode == 0 or data[i][j] == 0 then
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
else
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},3)
|
||||
end
|
||||
end end
|
||||
end
|
||||
|
||||
get_dirl = function(dir)
|
||||
local dirl; -- direction left
|
||||
if dir[1] > 0.5 then dirl = {0,-1}
|
||||
elseif dir[1] < -0.5 then dirl = {0,1}
|
||||
elseif dir[2] > 0.5 then dirl = {-1,0}
|
||||
elseif dir[2] < -0.5 then dirl = {1,0}
|
||||
end
|
||||
return dirl
|
||||
end
|
||||
|
||||
read_pos = function(x,z)
|
||||
if x<1 or x>m or z<1 or z>n then return nil end
|
||||
return data[x][z]
|
||||
end
|
||||
|
||||
newdir = function(x,z,dir) -- where will ray go next
|
||||
local retdir = {dir[1],dir[2]};
|
||||
local xf = x+dir[1]; local zf = z+dir[2] -- forward
|
||||
local dirl = get_dirl(dir)
|
||||
|
||||
local nodef = read_pos(xf,zf)
|
||||
local nodel = read_pos(xf + dirl[1],zf + dirl[2])
|
||||
local noder = read_pos(xf - dirl[1],zf - dirl[2])
|
||||
if nodef == 1 then
|
||||
retdir = {0,0} -- ray hit something
|
||||
elseif nodel == 1 and noder ~= 1 then
|
||||
retdir = {-dirl[1],-dirl[2]}
|
||||
elseif nodel ~= 1 and noder == 1 then
|
||||
retdir = {dirl[1],dirl[2]}
|
||||
elseif nodel == 1 and noder == 1 then
|
||||
retdir = {-dir[1],-dir[2]}
|
||||
end
|
||||
return retdir
|
||||
end
|
||||
|
||||
shootray = function(x,z,dir)
|
||||
--say("ray starts " .. x .. " " .. z .. " dir " .. dir[1] .. " " .. dir[2])
|
||||
local xp = x; local zp = z;
|
||||
local dirp = {dir[1],dir[2]};
|
||||
local maxstep = m*n;
|
||||
|
||||
for i = 1,maxstep do
|
||||
dirp = newdir(xp,zp,dirp);
|
||||
if dirp[1]==0 and dirp[2]==0 then return -i end -- hit
|
||||
xp=xp+dirp[1];zp=zp+dirp[2];
|
||||
if xp<1 or xp>m or zp<1 or zp>n then return i,{xp,zp} end -- out
|
||||
end
|
||||
return 0 -- hit
|
||||
end
|
||||
|
||||
count = 0; -- how many letters were used up
|
||||
border_start_ray = function(x,z)
|
||||
local rdir
|
||||
if x==0 then rdir = {1,0}
|
||||
elseif x == m+1 then rdir = {-1,0}
|
||||
elseif z == 0 then rdir = {0,1}
|
||||
elseif z == n+1 then rdir = {0,-1}
|
||||
end
|
||||
if rdir then
|
||||
local result,out = shootray(x,z,rdir);
|
||||
if result >= 0 then
|
||||
|
||||
if out then
|
||||
if out[1]==x and out[2]==z then -- got back where it originated, reflection
|
||||
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},1);
|
||||
else
|
||||
if result<=1 then
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},6); -- immediate bounce off
|
||||
else
|
||||
local nodename = "default:obsidian_letter_"..string.char(97+count) .. "u";
|
||||
_G.minetest.set_node(
|
||||
{x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]},
|
||||
{name = nodename, param2 = 1})
|
||||
_G.minetest.set_node(
|
||||
{x=spawnpos.x+x,y=spawnpos.y+1,z=spawnpos.z+z},
|
||||
{name = nodename, param2 = 1})
|
||||
count = count + 1;
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4);
|
||||
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},4);
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif result<0 then
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3); -- hit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- initial border loop and marking
|
||||
|
||||
--render blue border
|
||||
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+0},5) keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+n+1},5) end
|
||||
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y,z=spawnpos.z+j},5) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y,z=spawnpos.z+j},5) end
|
||||
|
||||
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+0},0) keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+n+1},0) end
|
||||
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y+1,z=spawnpos.z+j},0) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y+1,z=spawnpos.z+j},0) end
|
||||
|
||||
|
||||
z=0 -- bottom
|
||||
for x = 1,m do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
x=m+1 -- right
|
||||
for z = 1,n do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
z=n+1 -- top
|
||||
for x = m,1,-1 do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
x=0 -- left
|
||||
for z = n,1,-1 do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
check_solution = function()
|
||||
for i = 1,m do
|
||||
for j = 1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}) == "basic_robot:buttonFF8080" then -- red
|
||||
if data[i][j]~=1 then return false end
|
||||
else
|
||||
if data[i][j]~=0 then return false end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--render board
|
||||
render_board(0)
|
||||
keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},5)
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
if event.type == 5 then
|
||||
if check_solution() then
|
||||
say("#BLACKBOX : CONGRATULATIONS! " .. event.puncher .. " found all atoms after " .. attempts .. " tries."); self.remove()
|
||||
else
|
||||
say("#BLACKBOX : " .. event.puncher .. " failed to detect atoms after " .. attempts .. " attempts.")
|
||||
attempts = attempts+1
|
||||
end
|
||||
end
|
||||
else -- interior punch
|
||||
nodetype = 2;
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button808080" then
|
||||
nodetype = 3
|
||||
end
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},nodetype);
|
||||
end
|
||||
|
||||
end
|
||||
::END::
|
83
scripts/perm2cycles.lua
Normal file
83
scripts/perm2cycles.lua
Normal file
@ -0,0 +1,83 @@
|
||||
if not perm2cycles then
|
||||
|
||||
perm2cycles = function(perm)
|
||||
local n = #perm;
|
||||
local i = 1; -- already reached number
|
||||
local ret = {};
|
||||
local visited = {};
|
||||
local step = 0;
|
||||
|
||||
while (true) do
|
||||
local cycle = {i}
|
||||
local j=i;
|
||||
|
||||
while (true) do
|
||||
step = step +1
|
||||
if step > 2*n then return {} end
|
||||
j=perm[j];
|
||||
visited[j] = 1;
|
||||
if j and j~=cycle[1] then cycle[#cycle+1]=j else break end
|
||||
end
|
||||
|
||||
i=n+1;
|
||||
for k=1,n do -- find smallest non visited yet
|
||||
if not visited[k] and k<i then i = k end
|
||||
end
|
||||
|
||||
ret[#ret+1] = cycle;
|
||||
if i == n+1 then return ret end
|
||||
end
|
||||
end
|
||||
|
||||
random_permute = function(arr)
|
||||
local n = #arr;
|
||||
for i = n,3,-1 do
|
||||
local j = math.random(i);
|
||||
local tmp = arr[j]; arr[j] = arr[i]; arr[i] = tmp;
|
||||
end
|
||||
end
|
||||
|
||||
get_permutations = function(n)
|
||||
free = {}; --[1] = {stack of free for 1 : head, a1,..., a_head}
|
||||
isfree = {}; -- [i]=1 if item i free, 0 if not
|
||||
current = 1; --{1,..., current element, ... , n}
|
||||
selection = {};
|
||||
local data = free[1]; for i=1,n do data[i]=i isfree[i]=1 end data[0] = n;
|
||||
|
||||
--1. pick free spot from free stack ( if not possible backtrack ) and move on onto next element ( if not possible stay at this one)
|
||||
-- backtrack: current--
|
||||
local data = free[current];
|
||||
if data[0]<1 then -- backtrack
|
||||
isfree[selection[current]] = 1;
|
||||
current = current - 1;
|
||||
if current <=0 then return end -- exhausted all options
|
||||
else
|
||||
local i = data[data[0]]; -- free possibility
|
||||
selection[current] = i; isfree[i] = 0;
|
||||
data[0]=data[0]-1; -- pop the stack
|
||||
--try move forward
|
||||
if current<n then
|
||||
current = current+1;
|
||||
-- get new free spots for new current
|
||||
local data = free[current]; data = {};
|
||||
for i = 1,n do if isfree[i] == 1 then data[#data+1]=i; break end end;
|
||||
data[0]=#data;
|
||||
if data[0] == 0 then -- nothing free, backtrack
|
||||
isfree[selection[current]] = 1;
|
||||
current = current - 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
arr2string = function(arr)
|
||||
return string.gsub(_G.dump(arr),"\n","")
|
||||
end
|
||||
|
||||
local arr = {}; for i =1,10 do arr[i] =i end; random_permute(arr);
|
||||
local cycles = perm2cycles(arr);
|
||||
say("random permutation = " .. arr2string(arr) .. " => cycles = " .. arr2string(cycles) )
|
||||
|
||||
end
|
64
scripts/substring_search.lua
Normal file
64
scripts/substring_search.lua
Normal file
@ -0,0 +1,64 @@
|
||||
-- Rabin–Karp substring s search in string t
|
||||
-- https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm
|
||||
|
||||
|
||||
-- rnd 2017
|
||||
-- ALGORITHM:
|
||||
-- 1.) loop compute hashes of all substrings of t of length |s| using rolling hash idea
|
||||
-- 2.) if some hash matches hash of s check more closely ( waste |s| time here, so this should only occur with probability < O(1)/|s| so expected waste is O(1))
|
||||
|
||||
-- how to do 1) rolling hash: how does hash of string change if you remove first character and add new last character? ... dont need to recompute whole hash!
|
||||
|
||||
-- summary: we end up using O(|t|+|s| + (number of needed hits)*O(1)) time (if you want more than 1 hit..)
|
||||
-- this is big improvement compared to O(|t|*|s|) when doing naive substring search
|
||||
|
||||
-- improvement: we could also precompute all substring hashes of length |s| and then compute hash of some string of same length and
|
||||
-- do quick lookups for that hash ( hash of hash :) )
|
||||
|
||||
if not hash then
|
||||
|
||||
hash = function(s,p)
|
||||
local length = string.len(s);
|
||||
local h = 0 ;
|
||||
for i = 1, length do
|
||||
h=(256*h + string.byte(s,i))%p
|
||||
end
|
||||
return h%p
|
||||
end
|
||||
|
||||
getpower = function(p,k) -- safe computation of power 256^k in mod p arithmetic
|
||||
local r=1; for i = 1,k do r=(256*r) % p end; return r
|
||||
end
|
||||
|
||||
karpin_rabin = function(t,s)
|
||||
local ls = string.len(s);
|
||||
local lt = string.len(t);
|
||||
if lt<ls then return nil end
|
||||
|
||||
local p = 10011;
|
||||
local hs = hash(s,p);
|
||||
local rht = hash(string.sub(t,1,ls),p); -- rolling hash
|
||||
|
||||
if hs == rht then
|
||||
if s == string.sub(t,1,ls) then return 1 end -- match at position 1!
|
||||
end
|
||||
local power256 = getpower(p,ls-1);
|
||||
|
||||
for i = 2,lt-ls+1 do
|
||||
local c1 = string.byte(t,i-1); local c2 = string.byte(t,i+ls-1);
|
||||
rht = (256*(rht - c1*power256)+c2)%p; -- update rolling hash to hash of next substring; this is ok since: a===b then also a*c===b*c
|
||||
if hs == rht then
|
||||
if s == string.sub(t,i,i+ls-1) then return i end -- match at position i!
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local t = "The Rabin–Karp algorithm is inferior for single pattern searching to Knuth–Morris–Pratt algorithm, Boyer–Moore string search algorithm and other faster single pattern string searching algorithms because of its slow worst case behavior. However, it is an algorithm of choice for multiple pattern search.";
|
||||
local s = "inferior";
|
||||
local chk = karpin_rabin(t,s)
|
||||
if chk then say("found '" .. s .. "' in text pos. " .. chk .." : ..." .. string.sub(t,chk,chk+50) .. "...")
|
||||
else say("could not find '"..s.."' in text")
|
||||
end
|
||||
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user