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.

master
rnd1 2017-04-27 09:22:17 +02:00
parent 182d81acec
commit c3b8f19c83
5 changed files with 526 additions and 261 deletions

View File

@ -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
View File

@ -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);

View File

@ -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
View 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

View File

@ -0,0 +1,64 @@
-- RabinKarp 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 RabinKarp algorithm is inferior for single pattern searching to KnuthMorrisPratt algorithm, BoyerMoore 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