merge upstream

master
Milan 2019-01-31 15:59:39 +01:00
commit fd136303c5
332 changed files with 7773 additions and 423 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
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

@ -21,18 +21,17 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc
local yaw = obj:getyaw();
local pos = obj:getpos();
if dir == 1 then -- left
if dir == 1 then -- left
yaw = yaw + pi/2;
elseif dir == 2 then --right
yaw = yaw - pi/2;
elseif dir == 3 then -- forward
elseif dir == 4 then
yaw = yaw+pi; -- backward
elseif dir == 4 then -- backward
yaw = yaw+pi;
elseif dir == 5 then -- up
pos.y=pos.y+1
elseif dir == 6 then -- down
pos.y=pos.y-1
elseif dir == 7 then -- left_down
yaw = yaw + pi/2;pos.y=pos.y-1
elseif dir == 8 then -- right_down
@ -40,8 +39,7 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc
elseif dir == 9 then -- forward_down
pos.y=pos.y-1
elseif dir == 10 then -- backward_down
yaw = yaw + pi; pos.y=pos.y-1
yaw = yaw + pi;pos.y=pos.y-1
elseif dir == 11 then -- left_up
yaw = yaw + pi/2;pos.y=pos.y+1
elseif dir == 12 then -- right_up
@ -49,12 +47,12 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc
elseif dir == 13 then -- forward_up
pos.y=pos.y+1
elseif dir == 14 then -- backward_up
yaw = yaw + pi; pos.y=pos.y+1
yaw = yaw + pi;pos.y=pos.y+1
end
if dir ~= 5 and dir ~= 6 then
pos.x = pos.x+math.cos(yaw)
pos.z = pos.z+math.sin(yaw)
pos.x = pos.x - math.sin(yaw) -- math.cos(yaw+pi/2)
pos.z = pos.z + math.cos(yaw) -- math.sin(yaw+pi/2)
end
return pos
@ -68,7 +66,7 @@ local check_operations = function(name, amount, quit)
data.operations = operations
else
if quit then
error("robot out of available operations in one step."); return false
error("Robot out of available operations in one step (1s). View available operations with self.operations() and check help to see how much operations each action requires."); return false
end
return false
end
@ -77,9 +75,11 @@ end
basic_robot.commands.move = function(name,dir)
check_operations(name,2,true)
local obj = basic_robot.data[name].obj;
local pos = pos_in_dir(obj, dir)
-- can move through walkable nodes
if minetest.registered_nodes[minetest.get_node(pos).name].walkable then return end
-- up; no levitation!
@ -90,7 +90,7 @@ basic_robot.commands.move = function(name,dir)
obj:moveto(pos, true)
-- sit and stand up for model - doenst work for overwriten obj export
-- sit and stand up for model - doesnt work for overwriten obj export
-- if dir == 5 then-- up
-- obj:set_animation({x=0,y=0})
-- elseif dir == 6 then -- down
@ -113,14 +113,14 @@ end
basic_robot.digcosts = { -- 1 energy = 1 coal
["default:stone"] = 1/25,
["default:cloud"] = 10^8,
}
basic_robot.commands.dig = function(name,dir)
local energy = 0;
check_operations(name,2,true)
check_operations(name,6,true)
local obj = basic_robot.data[name].obj;
local pos = pos_in_dir(obj, dir)
@ -168,6 +168,8 @@ end
basic_robot.commands.insert_item = function(name,item, inventory,dir)
check_operations(name,2,true)
local obj = basic_robot.data[name].obj;
local tpos = pos_in_dir(obj, dir); -- position of target block
local luaent = obj:get_luaentity();
@ -210,6 +212,8 @@ basic_robot.commands.insert_item = function(name,item, inventory,dir)
end
basic_robot.commands.take_item = function(name,item, inventory,dir)
check_operations(name,2,true)
local obj = basic_robot.data[name].obj;
local tpos = pos_in_dir(obj, dir); -- position of target block
local luaent = obj:get_luaentity();
@ -280,7 +284,8 @@ basic_robot.no_teleport_table = {
basic_robot.commands.pickup = function(r,name)
if r>8 then return false end
check_operations(name,4,true)
local pos = basic_robot.data[name].obj:getpos();
local spos = basic_robot.data[name].spawnpos; -- position of spawner block
local meta = minetest.get_meta(spos);
@ -337,6 +342,8 @@ basic_robot.commands.write_text = function(name,dir,text)
end
basic_robot.commands.place = function(name,nodename, param2,dir)
check_operations(name,2,true)
local obj = basic_robot.data[name].obj;
local pos = pos_in_dir(obj, dir)
local luaent = obj:get_luaentity();
@ -380,7 +387,7 @@ end
basic_robot.commands.attack = function(name, target) -- attack range 4, damage 5
local energy = 0;
check_operations(name,2,true);
check_operations(name,4,true);
local reach = 4;
local damage = 5;
@ -424,9 +431,18 @@ basic_robot.commands.grab = function(name,target)
end
--local minetest_version = minetest.get_version().string;
basic_robot.commands.read_book = function (itemstack) -- itemstack should contain book
local data = itemstack:get_meta():to_table().fields -- 0.4.16
--local data = minetest.deserialize(itemstack:get_metadata()) -- pre 0.4.16
local data;
--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
return data.title,data.text;
else
@ -441,10 +457,11 @@ basic_robot.commands.write_book = function(name,title,text) -- returns itemstack
local data = {}
if title == "" or not title then title = "program book "..minetest.get_gametime() end
data.title = title
data.title = title or ""
data.text = text or ""
data.text_len = #data.text
data.page = 1
data.description = title or ""
data.page_max = math.ceil((#data.text:gsub("[^\n]", "") + 1) / lpp)
data.owner = name
--local data_str = minetest.serialize(data) -- pre 0.4.16
@ -468,15 +485,16 @@ basic_robot.give_drops = function(nodename, inv) -- gives apropriate drops when
if max_items==0 then -- just drop all the items (taking the rarity into consideration)
max_items = #table.drop.items or 0;
end
max_items = math.random(max_items) -- return random number of items
local drop = table.drop;
local i = 0;
for k,v in pairs(drop.items) do
if i > max_items then break end; i=i+1;
if i > max_items then break end;
i=i+1;
local rare = v.rarity or 1;
if math.random(1, rare)==1 then
if rare>0 and math.random(1, rare)==1 then
dropname = v.items[math.random(1,#v.items)]; -- pick item randomly from list
inv:add_item("main",dropname);
end
end
else
@ -493,6 +511,7 @@ local render_text = function(text,linesize)
local count = math.floor(string.len(text)/linesize)+1;
local width = 18; local height = 24;
local tex = "";
local ret = {};
local y = 0; local x=0;
for i=1,string.len(text) do
local cb = string.byte(text,i);
@ -501,27 +520,30 @@ local render_text = function(text,linesize)
y=y+1; x=0
else
c = string.format("%03d",cb)..".png"
tex = tex .. ":" .. (x*width) .. "," .. (y*height) .. "=" .. c;
ret[#ret+1] = ":" .. (x*width) .. "," .. (y*height) .. "=" .. c;
--tex = tex .. ":" .. (x*width) .. "," .. (y*height) .. "=" .. c;
if x==linesize-1 then y=y+1 x=0 else x=x+1 end
end
end
local background = "(black_screen.png^[resize:"..(linesize*width).. "x".. (linesize*height) ..")";
tex = "([combine:"..(linesize*width).."x"..(linesize*height)..tex..")";
tex = background .. "^" .. tex;
return tex;
--tex = "([combine:"..(linesize*width).."x"..(linesize*height)..tex..")";
return background .. "^" .."([combine:"..(linesize*width).."x"..(linesize*height)..table.concat(ret,"")..")";
end
basic_robot.commands.display_text = function(obj,text,linesize,size)
if not linesize or linesize<1 then linesize = 20 end
if not linesize or linesize<1 then linesize = 20 elseif linesize>40 then linesize = 40 end
if size and size<=0 then size = 1 end
if string.len(text)>linesize*linesize then text = string.sub(text,1,linesize*linesize) end
local tex = render_text(text,linesize);
if not size then return tex end
if string.len(tex)<60000 then
obj:set_properties({textures={"arrow.png","basic_machine_side.png",tex,"basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"},visual_size = {x=size,y=size}})
if string.len(tex)<=1600 then
obj:set_properties({
textures = {"topface.png","legs.png",tex,"face-back.png","left-hand.png","right-hand.png"},
--textures={"arrow.png","basic_machine_side.png",tex,"basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"},
visual_size = {x=size,y=size}})
else
self.label("error: string too long")
end
@ -529,6 +551,8 @@ end
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)
check_operations(name,2,true);
local obj = basic_robot.data[name].obj;
local tpos = pos_in_dir(obj, dir); -- position of target block in front
@ -734,13 +758,16 @@ basic_robot.commands.keyboard = {
basic_robot.commands.craftcache = {};
basic_robot.commands.craft = function(item, mode, idx, name)
basic_robot.commands.craft = function(item, mode, idx,amount, name)
amount = amount and tonumber(amount) or 1;
if amount<0 then amount = 1 end
if not item then return false end
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 output = "";
if cache.item == item then-- read cache
if cache.item == item and cache.idx == idx then -- read cache
itemlist = cache.itemlist;
output = cache.output;
else
@ -759,6 +786,7 @@ basic_robot.commands.craft = function(item, mode, idx, name)
itemlist[item]=(itemlist[item] or 0)+1;
end
cache.item = item;
cache.idx = idx;
cache.itemlist = itemlist;
cache.output = output;
@ -797,12 +825,12 @@ basic_robot.commands.craft = function(item, mode, idx, name)
local inv = minetest.get_meta(pos):get_inventory();
for item,quantity in pairs(itemlist) do
local stack = ItemStack(item .. " " .. quantity);
local stack = ItemStack(item .. " " .. quantity*amount);
if not inv:contains_item("main",stack) then return false end
end
for item,quantity in pairs(itemlist) do
local stack = ItemStack(item .. " " .. quantity);
local stack = ItemStack(item .. " " .. quantity*amount);
inv:remove_item("main",stack);
end
@ -866,7 +894,7 @@ basic_robot.technic = { -- data cache
compressor_recipes = { --[in] ={fuel cost, out, quantity of material required for processing}
["default:snow"] = {1,"default:ice"},
["default:coalblock"] = {16,"default:diamond"},
["default:coalblock"] = {41,"default:diamond"}, -- to smelt diamond dust to diamond need 25 coal + 16 for grinder
},
}
@ -885,7 +913,7 @@ 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
check_operations(name,1.5, true)
check_operations(name,6, true)
if amount and amount>0 then -- attempt to generate power from builtin generator
local pos = basic_robot.data[name].spawnpos; -- position of spawner block
@ -932,7 +960,7 @@ basic_robot.commands.machine = {
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
check_operations(name,2,true)
check_operations(name,6,true)
if string.find(input," ") then return nil, "0: only one item per smelt" end
@ -994,6 +1022,7 @@ basic_robot.commands.machine = {
-- grind
grind = function(name,input)
--[in] ={fuel cost, out, quantity of material required for processing}
check_operations(name,6,true)
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];
@ -1024,6 +1053,7 @@ basic_robot.commands.machine = {
-- compress
compress = function(name,input)
--[in] ={fuel cost, out, quantity of material required for processing}
check_operations(name,6,true)
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];
@ -1050,13 +1080,15 @@ basic_robot.commands.machine = {
end,
transfer_power = function(name,amount,target)
check_operations(name,2, true);
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
check_operations(name,0.5, true);
energy = data.menergy or 0;
if amount>energy then return nil,"energy too low" end
@ -1209,10 +1241,10 @@ end
local cmd_get_player = function(data,pname) -- return player for further manipulation
local player = minetest.get_player_by_name(pname)
if not player then return end
if not player then error("player does not exist"); return end
local spos = data.spawnpos;
local ppos = player:getpos();
if not is_same_block(ppos,spos) then return end
if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end
return player
end
@ -1221,7 +1253,7 @@ local cmd_get_player_inv = function(data,pname)
if not player then return end
local spos = data.spawnpos;
local ppos = player:getpos();
if not is_same_block(ppos,spos) then return end
if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end
return player:get_inventory();
end
@ -1320,7 +1352,9 @@ basic_robot.commands.puzzle = {
if minetest.is_protected(pos,data.owner) then return end
if not is_same_block(pos,spos) then return end
if minetest.get_node(pos).name == "basic_robot:spawner" then return end
return minetest.get_meta(pos)
local meta = minetest.get_meta(pos);
if not meta then error("get_meta in puzzle returned nil"); return end
return meta
end,
get_gametime = function() return minetest.get_gametime() end,
@ -1354,3 +1388,106 @@ basic_robot.commands.puzzle = {
return true
end,
}
-- VIRTUAL PLAYER --
local Vplayer = {};
function Vplayer:new(name) -- constructor
if not basic_robot.data[name].obj then return end -- only make it for existing robot
if basic_robot.virtual_players[name] then return end -- already exists
local o = {}
setmetatable(o, self)
self.__index = self
o.obj = basic_robot.data[name].obj;
o.data = basic_robot.data[name];
local spawnpos = o.data.spawnpos;
local meta = minetest.get_meta(spawnpos); if not meta then return end
o.inv = meta:get_inventory();
basic_robot.virtual_players[name] = o;
end
-- functions
function Vplayer:getpos() return self.obj:getpos() end
function Vplayer:remove() end
function Vplayer:setpos() end
function Vplayer:move_to() end
function Vplayer:punch() end
function Vplayer:rightlick() end
function Vplayer:get_hp() return 20 end
function Vplayer:set_hp() return 20 end
function Vplayer:get_inventory() return self.inv end
function Vplayer:get_wield_list() return "main" end
function Vplayer:get_wield_index() return 1 end
function Vplayer:get_wielded_item() return self.inv:get_stack("main", 1) end
function Vplayer:set_wielded_item() end
function Vplayer:set_armor_groups() end
function Vplayer:get_armor_groups() return {fleshy = 100} end
function Vplayer:set_animation() end
function Vplayer:get_animation() end
function Vplayer:set_attach() end
function Vplayer:get_attach() end
function Vplayer:set_detach() end
function Vplayer:set_bone_position() end
function Vplayer:get_bone_position() end
function Vplayer:set_properties() end
function Vplayer:get_properties() end
function Vplayer:is_player() return true end
function Vplayer:get_nametag_attributes() end
function Vplayer:set_nametag_attributes() end
function Vplayer:set_velocity() end
function Vplayer:get_velocity() end
function Vplayer:set_acceleration() end
function Vplayer:get_acceleration() end
function Vplayer:set_yaw() end
function Vplayer:get_yaw() end
function Vplayer:set_texture_mod() end
function Vplayer:get_luaentity() end
function Vplayer:get_player_name() return self.data.name end
function Vplayer:get_player_velocity() return {x=0,y=0,z=0} end
function Vplayer:get_look_dir() return {x=1,y=0,z=0} end
function Vplayer:get_look_vertical() return 0 end
function Vplayer:get_look_horizontal() return 0 end
function Vplayer:set_look_vertical() end
function Vplayer:set_look_horizontal() end
function Vplayer:get_breath() return 1 end
function Vplayer:set_breath() end
function Vplayer:set_attribute() end
function Vplayer:get_attribute() end
function Vplayer:set_inventory_formspec() end
function Vplayer:get_inventory_formspec() return "" end
function Vplayer:get_player_control() return {} end
function Vplayer:get_player_control_bits() return 0 end
function Vplayer:set_physics_override() end
function Vplayer:get_physics_override() return {} end
function Vplayer:hud_add() end
function Vplayer:hud_remove() end
function Vplayer:hud_change() end
function Vplayer:hud_get() end
function Vplayer:hud_set_flags() end
function Vplayer:hud_get_flags() return {} end
function Vplayer:hud_set_hotbar_itemcount() end
function Vplayer:hud_get_hotbar_itemcount() return 0 end
function Vplayer:hud_set_hotbar_image() end
function Vplayer:hud_get_hotbar_image() return "" end
function Vplayer:hud_set_hotbar_selected_image() end
function Vplayer:hud_get_hotbar_selected_image() return "" end
function Vplayer:set_sky() end
function Vplayer:get_sky() end
function Vplayer:set_clouds() end
function Vplayer:get_clouds() end
function Vplayer:override_day_night_ratio() end
function Vplayer:get_day_night_ratio() end
function Vplayer:set_local_animation() end
function Vplayer:get_local_animation() end
function Vplayer:set_eye_offset() end
function Vplayer:get_eye_offset() end
-- code for act borrowed from: https://github.com/minetest-mods/pipeworks/blob/fa4817136c8d1e62dafd6ab694821cba255b5206/wielder.lua, line 372

669
init.lua
View File

@ -2,33 +2,45 @@
basic_robot = {};
---- SETTINGS ------
basic_robot.call_limit = 48; -- how many execution calls per script run allowed
------ SETTINGS --------
basic_robot.call_limit = {50,200,1500,10^9}; -- how many execution calls per script run allowed, for auth levels 0,1,2 (normal, robot, puzzle, admin)
basic_robot.entry_count = 2 -- how many robots ordinary player can have
basic_robot.advanced_count = 16 -- how many robots player with robot privs can have
basic_robot.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches
basic_robot.password = "password"; -- IMPORTANT: change it before running mod, password used for authentifications
basic_robot.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running mod, password used for authentifications
basic_robot.admin_bot_pos = {x=0,y=1,z=0} -- position of admin robot spawner that will be run automatically on server start
basic_robot.maxoperations = 10; -- how many operations (dig, place,move,...,generate energy,..) available per run, 0 = unlimited
basic_robot.dig_require_energy = true; -- does robot require energy to dig stone?
basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses
["craft_guide:sign_wall"] = true,
["basic_machines:battery_0"] = true,
["basic_machines:battery_1"] = true,
["basic_machines:battery_2"] = true,
["basic_machines:generator"] = true,
}
basic_robot.maxoperations = 2; -- how many operations (dig, generate energy,..) available per run, 0 = unlimited
basic_robot.dig_require_energy = true; -- does robot require energy to dig?
----------------------
----- END OF SETTINGS ------
basic_robot.http_api = minetest.request_http_api();
basic_robot.version = "2017/12/18a";
basic_robot.version = "2019/01/13a";
basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management
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
--]]
basic_robot.ids = {}; -- stores maxid for each player
--[name] = {id = .., maxid = .. }, current id for robot controller, how many robot ids player can use
basic_robot.virtual_players = {}; -- this way robot can interact with the world as "player" TODO
basic_robot.data.listening = {}; -- which robots listen to chat
dofile(minetest.get_modpath("basic_robot").."/robogui.lua") -- gui stuff
dofile(minetest.get_modpath("basic_robot").."/commands.lua")
local check_code, preprocess_code,is_inside_string;
@ -47,6 +59,7 @@ function getSandboxEnv (name)
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 =
{
pcall=pcall,
@ -73,14 +86,20 @@ function getSandboxEnv (name)
return commands.pickup(r, name);
end,
craft = function(item, idx,mode)
return commands.craft(item, mode, idx, name)
craft = function(item, idx,mode, amount)
return commands.craft(item, mode, idx, amount, name)
end,
pause = function() -- pause coroutine
if not basic_robot.data[name].cor then error("you must start program with '--coroutine' to use pause()") return end
coroutine.yield()
end,
self = {
pos = function() return basic_robot.data[name].obj:getpos() end,
spawnpos = function() local pos = basic_robot.data[name].spawnpos; return {x=pos.x,y=pos.y,z=pos.z} end,
name = function() return name end,
operations = function() return basic_robot.data[name].operations end,
viewdir = function() local yaw = basic_robot.data[name].obj:getyaw(); return {x=math.cos(yaw), y = 0, z=math.sin(yaw)} end,
set_properties = function(properties)
@ -152,7 +171,7 @@ function getSandboxEnv (name)
fire = function(speed, pitch,gravity, texture, is_entity) -- experimental: fires an projectile
local obj = basic_robot.data[name].obj;
local pos = obj:getpos();
local yaw = obj:getyaw();
local yaw = obj:getyaw()+ math.pi/2;
pitch = pitch*math.pi/180
local velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)};
-- fire particle
@ -174,6 +193,7 @@ function getSandboxEnv (name)
local obj = minetest.add_entity(pos, "basic_robot:projectile");
if not obj then return end
obj:setvelocity(velocity);
obj:set_properties({textures = {texture or "default_furnace_fire_fg.png"}})
obj:setacceleration({x=0,y=-gravity,z=0});
local luaent = obj:get_luaentity();
luaent.name = name;
@ -189,7 +209,7 @@ function getSandboxEnv (name)
label = function(text)
local obj = basic_robot.data[name].obj;
obj:set_properties({nametag = text}); -- "[" .. name .. "] " ..
obj:set_properties({nametag = text or ""}); -- "[" .. name .. "] " ..
end,
display_text = function(text,linesize,size)
@ -290,14 +310,15 @@ function getSandboxEnv (name)
grab = function(target) return basic_robot.commands.grab(name,target) end,
say = function(text, owneronly)
if not basic_robot.data[name].quiet_mode and not owneronly then
say = function(text, pname)
if not basic_robot.data[name].quiet_mode and not pname then
minetest.chat_send_all("<robot ".. name .. "> " .. text)
if not basic_robot.data[name].allow_spam then
basic_robot.data[name].quiet_mode=true
end
else
minetest.chat_send_player(basic_robot.data[name].owner,"<robot ".. name .. "> " .. text)
if not pname then pname = basic_robot.data[name].owner end
minetest.chat_send_player(pname,"<robot ".. name .. "> " .. text) -- send chat only to player pname
end
end,
@ -336,44 +357,39 @@ function getSandboxEnv (name)
return
end
end,
run = function(script)
if basic_robot.data[name].authlevel < 3 then
local err = check_code(script);
script = preprocess_code(script);
if err then
minetest.chat_send_player(name,"#ROBOT CODE CHECK ERROR : " .. err)
return
end
end
local ScriptFunc, CompileError = loadstring( script )
if CompileError then
minetest.chat_send_player(name, "#code.run: compile error " .. CompileError )
return false
end
setfenv( ScriptFunc, basic_robot.data[name].sandbox )
local Result, RuntimeError = pcall( ScriptFunc );
if RuntimeError then
minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError )
return false
end
return true
end
},
rom = basic_robot.data[name].rom,
string = {
byte = string.byte, char = string.char,
find = string.find,
format = string.format, gsub = string.gsub,
gsub = string.gsub,
gmatch = string.gmatch,
len = string.len, lower = string.lower,
upper = string.upper, rep = string.rep,
reverse = string.reverse, sub = string.sub,
format = function(...)
local out = string.format(...)
if string.len(out) > 1024 then
error("result string longer than 1024")
return
end
return out
end,
concat = function(strings, sep)
local length = 0;
for i = 1,#strings do
length = length + string.len(strings[i])
if length > 1024 then
error("result string longer than 1024")
return
end
end
return table.concat(strings,sep or "")
end,
},
math = {
abs = math.abs, acos = math.acos,
@ -392,13 +408,6 @@ function getSandboxEnv (name)
sqrt = math.sqrt, tan = math.tan,
tanh = math.tanh,
},
table = {
concat = table.concat,
insert = table.insert,
maxn = table.maxn,
remove = table.remove,
sort = table.sort,
},
os = {
clock = os.clock,
difftime = os.difftime,
@ -412,16 +421,6 @@ function getSandboxEnv (name)
tonumber = tonumber, pairs = pairs,
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
@ -478,6 +477,43 @@ function getSandboxEnv (name)
end
if authlevel>=1 then -- robot privs
env.table = {
concat = table.concat,
insert = table.insert,
maxn = table.maxn,
remove = table.remove,
sort = table.sort,
}
env.code.run = function(script)
if basic_robot.data[name].authlevel < 3 then
local err = check_code(script);
script = preprocess_code(script, basic_robot.call_limit[basic_robot.data[name].authlevel+1]);
if err then
minetest.chat_send_player(name,"#ROBOT CODE CHECK ERROR : " .. err)
return
end
end
local ScriptFunc, CompileError = loadstring( script )
if CompileError then
minetest.chat_send_player(name, "#code.run: compile error " .. CompileError )
return false
end
setfenv( ScriptFunc, basic_robot.data[name].sandbox )
local Result, RuntimeError = pcall( ScriptFunc );
if RuntimeError then
minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError )
return false
end
return true
end
env.self.read_form = function()
local fields = basic_robot.data[name].read_form;
local sender = basic_robot.data[name].form_sender;
@ -524,8 +560,6 @@ function getSandboxEnv (name)
end
--special sandbox for admin
if authlevel<3 then -- is admin?
env._G = env;
@ -541,131 +575,162 @@ end
-- code checker
check_code = function(code)
--"while ", "for ", "do ","goto ",
local bad_code = {"repeat", "until", "_ccounter", "_G", "while%(", "while{", "pcall","\\\""}
--"while ", "for ", "do ","goto ",
local bad_code = {"repeat", "until", "_c_", "_G", "while%(", "while{", "pcall","%.%."} --,"\\\"", "%[=*%[","--[["}
for _, v in pairs(bad_code) do
if string.find(code, v) then
return v .. " is not allowed!";
end
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;
local identify_strings = function(code) -- returns list of positions {start,end} of literal strings in lua code
if pos<i1 then
return false
end
while i1 do
i2=string.find(script,"\"",i1+1);
if i2 then
par = 1 - par;
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 modes = {
{"'","'"},
{"\"","\""},
{"%[=*%[","%]=*%]"}
}
local ret = {}
while i < length do
i=i+1
local jmin = length+1;
if mode == 0 then -- not yet inside string
for k=1,#modes do
j = string.find(code,modes[k][1],i);
if j and j<jmin then -- pick closest one
jmin = j
mode = k
end
end
if mode ~= 0 then -- found something
j=jmin
ret[#ret+1] = {jmin}
end
if not j then break end -- found nothing
else
return false
end
if par == 0 then
if i1<pos and pos<i2 then
return true
_,j = string.find(code,modes[mode][2],i); -- search for closing pair
if not j then break end
if (mode~=2 or string.sub(code,j-1,j-1) ~= "\\") then -- not (" and \")
ret[#ret][2] = j
mode = 0
end
end
i1=i2;
i=j -- move to next position
end
return false;
if mode~= 0 then ret[#ret][2] = length end
return ret
end
is_inside_string = function(strings,pos) -- is position inside one of the strings?
local low = 1; local high = #strings;
if high == 0 then return false end
local mid = high;
while high>low+1 do
mid = math.floor((low+high)/2)
if pos<strings[mid][1] then high = mid else low = mid end
end
if pos>strings[low][2] then mid = high else mid = low end
return strings[mid][1]<=pos and pos<=strings[mid][2]
end
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
-- COMPILATION
preprocess_code = function(script)
--todo: 2018/12 this suddenly stopped working, wtf??
preprocess_code = function(script, call_limit) -- version 07/24/2018
--[[ idea: in each local a = function (args) ... end insert counter like:
local a = function (args) counter() ... end
local a = function (args) counter_check_code ... end
when counter exceeds limit exit with error
--]]
script="_ccounter = 0; " .. script;
script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") -- strip comments
script="_c_ = 0; " .. script;
-- process script to insert call counter in every function
local _increase_ccounter = " _c_ = _c_ + 1; if _c_ > " .. call_limit ..
" then _G.error(\"Execution count \".. _c_ .. \" exceeded ".. call_limit .. "\") end; "
local i1 -- process script to insert call counter in every function
local _increase_ccounter = " if _ccounter > " .. basic_robot.call_limit ..
" then error(\"Execution count \".. _ccounter .. \" exceeded ".. basic_robot.call_limit .. "\") end _ccounter = _ccounter + 1; "
local i1=0; local i2 = 0;
local i1=0; local i2 = 0;
local found = true;
while (found) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS
found = false;
i2 = nil;
local strings = identify_strings(script);
-- i1 = where its looking
local inserts = {};
local constructs = {
{"while%s", "%sdo%s", 2, 6}, -- numbers: insertion pos = i2+2, after skip to i1 = i12+6
{"function", ")", 0, 8},
{"for%s", "%sdo%s", 2, 4},
{"goto%s", nil , -1, 5},
}
for i = 1,#constructs do
i1 = 0; found = true
while (found) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS
i2=string.find (script, "while ", i1) -- fix while OK
if i2 then
if not is_inside_string(i2,script) then
found = false;
i2=find_outside_string(script, constructs[i][1], i1, strings) -- first part of construct
if i2 then
local i21 = i2;
i2=string.find(script, "do", i2);
if i2 then
script = script.sub(script,1, i2+1) .. _increase_ccounter .. script.sub(script, i2+2);
i1=i21+6; -- after while
found = true;
if constructs[i][2] then
i2 = find_outside_string(script, constructs[i][2], i2, strings); -- second part of construct ( if any )
if i2 then
inserts[#inserts+1]= i2+constructs[i][3]; -- move to last position of construct[i][2]
found = true;
end
else
inserts[#inserts+1]= i2+constructs[i][3]
found = true -- 1 part construct
end
if found then
i1=i21+constructs[i][4]; -- skip to after constructs[i][1]
end
end
end
i2=string.find (script, "function", i1) -- fix functions
if i2 then
--minetest.chat_send_all("func0")
if not is_inside_string(i2,script) then
i2=string.find(script, ")", i2);
if i2 then
script = script.sub(script,1, i2) .. _increase_ccounter .. script.sub(script, i2+1);
i1=i2+string.len(_increase_ccounter);
found = true;
end
end
end
i2=string.find (script, "for ", i1) -- fix for OK
if i2 then
if not is_inside_string(i2,script) then
i2=string.find(script, "do", i2);
if i2 then
script = script.sub(script,1, i2+1) .. _increase_ccounter .. script.sub(script, i2+2);
i1=i2+string.len(_increase_ccounter);
found = true;
end
end
end
i2=string.find (script, "goto ", i1) -- fix goto OK
if i2 then
if not is_inside_string(i2,script) then
script = script.sub(script,1, i2-1) .. _increase_ccounter .. script.sub(script, i2);
i1=i2+string.len(_increase_ccounter)+5; -- insert + skip goto
found = true;
end
end
--minetest.chat_send_all("code rem " .. string.sub(script,i1))
end
return script
table.sort(inserts)
-- 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
local function CompileCode ( script )
--minetest.chat_send_all(script)
--if true then return nil, "" end
local ScriptFunc, CompileError = loadstring( script )
if CompileError then
return nil, CompileError
@ -678,17 +743,30 @@ local function initSandbox (name)
end
local function setCode( name, script ) -- to run script: 1. initSandbox 2. setCode 3. runSandbox
local err;
if basic_robot.data[name].authlevel<3 then -- not admin
local cor = false;
if string.sub(script,1,11) == "--coroutine" then cor = true end
local authlevel = basic_robot.data[name].authlevel;
if authlevel<3 then -- not admin
err = check_code(script);
script = preprocess_code(script);
script = preprocess_code(script,basic_robot.call_limit[authlevel+1]);
elseif cor then
script = preprocess_code(script, basic_robot.call_limit[authlevel+1]); -- coroutines need ccounter reset or 'infinite loops' fail after limit
end
if err then return err end
local bytecode, err = CompileCode ( script );
if err then return err end
basic_robot.data[name].bytecode = bytecode;
if cor then -- create coroutine if requested
basic_robot.data[name].cor = coroutine.create(bytecode)
else
basic_robot.data[name].cor = nil
end
return nil
end
@ -702,16 +780,26 @@ local function runSandbox( name)
return "Bytecode missing."
end
data.ccounter = 0;
data.operations = basic_robot.maxoperations;
data.t = os.clock()
setfenv( ScriptFunc, data.sandbox )
local cor = data.cor;
if cor then -- coroutine!
local err,ret
ret,err = coroutine.resume(cor)
data.t = os.clock()-data.t
if err then return err end
return nil
end
local Result, RuntimeError = pcall( ScriptFunc )
data.t = os.clock()-data.t
if RuntimeError then
return RuntimeError
end
return nil
end
@ -720,7 +808,7 @@ end
local function setupid(owner)
local privs = minetest.get_player_privs(owner); if not privs then return end
local maxid = basic_robot.entry_count;
if privs.robot then maxid = basic_robot.advanced_count end -- max id's per user
if privs.robot or privs.puzzle then maxid = basic_robot.advanced_count end -- max id's per user
basic_robot.ids[owner] = {id = 1, maxid = maxid}; --active id for remove control
end
@ -836,7 +924,7 @@ minetest.register_entity("basic_robot:robot",{
--textures={"character.png"},
visual="cube",
textures={"arrow.png","basic_machine_side.png","face.png","basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"},
textures={"topface.png","legs.png","left-hand.png","right-hand.png","face.png","face-back.png"},
visual_size={x=1,y=1},
running = 0, -- does it run code or is it idle?
@ -904,16 +992,13 @@ minetest.register_entity("basic_robot:robot",{
end
self.running = 0; -- stop execution
if string.find(err,"stack overflow") then -- remove stupid player privs and spawner, ban player ip
if string.find(err,"stack overflow") then
local name = self.name;
local pos = basic_robot.data[name].spawnpos;
minetest.set_node(pos, {name = "air"});
local privs = core.get_player_privs(self.owner);privs.interact = false;
core.set_player_privs(self.owner, privs); minetest.auth_reload()
minetest.ban_player(self.owner)
--local privs = core.get_player_privs(self.owner);privs.interact = false;
--core.set_player_privs(self.owner, privs); minetest.auth_reload()
minetest.kick_player(self.owner, "#basic_robot: stack overflow")
end
local name = self.name;
@ -994,7 +1079,7 @@ local spawn_robot = function(pos,node,ttl)
local sec_hash = minetest.get_password_hash("",data.authlevel.. owner .. basic_robot.password)
if meta:get_string("sec_hash")~= sec_hash then
minetest.chat_send_all("#ROBOT: " .. name .. " is using fake auth level. dig and place again.")
minetest.chat_send_player(owner,"#ROBOT: " .. name .. " is using fake auth level. dig and place again.")
return
end
@ -1021,7 +1106,7 @@ local spawn_robot = function(pos,node,ttl)
if data.authlevel<3 then -- not admin
err = check_code(script);
script = preprocess_code(script);
script = preprocess_code(script, basic_robot.call_limit[data.authlevel+1]);
end
if err then
meta:set_string("infotext","#CODE CHECK ERROR : " .. err);
@ -1040,7 +1125,7 @@ local spawn_robot = function(pos,node,ttl)
if not data.sandbox then data.sandbox = getSandboxEnv (name) end
-- actual code run process
data.ccounter = 0;data.operations = basic_robot.maxoperations;
data.operations = basic_robot.maxoperations;
setfenv(data.bytecode, data.sandbox )
@ -1074,7 +1159,7 @@ local spawn_robot = function(pos,node,ttl)
local sec_hash = minetest.get_password_hash("",luaent.authlevel.. owner .. basic_robot.password)
if meta:get_string("sec_hash")~= sec_hash then
minetest.chat_send_all("#ROBOT: " .. name .. " is using fake auth level. dig and place again.")
minetest.chat_send_player(owner,"#ROBOT: " .. name .. " is using fake auth level. dig and place again.")
obj:remove();
return
end
@ -1083,7 +1168,7 @@ local spawn_robot = function(pos,node,ttl)
if data == nil then
basic_robot.data[name] = {};
data = basic_robot.data[name];
data.rom = {};
--data.rom = {};
end
data.owner = owner;
@ -1105,6 +1190,16 @@ local spawn_robot = function(pos,node,ttl)
self.running = 1
end
--admin robot that starts automatically after server start
minetest.after(10, function()
local admin_bot_pos = basic_robot.admin_bot_pos;
minetest.forceload_block(admin_bot_pos,true) -- load map position
spawn_robot(admin_bot_pos,nil,1)
print("[BASIC_ROBOT] admin robot at " .. admin_bot_pos.x .. " " .. admin_bot_pos.y .. " " .. admin_bot_pos.z .. " started.")
end)
local despawn_robot = function(pos)
local meta = minetest.get_meta(pos);
@ -1150,113 +1245,6 @@ local despawn_robot = function(pos)
end
-- GUI
-- robogui GUI START ==================================================
robogui = {}; -- a simple table of entries: [guiName] = {getForm = ... , show = ... , response = ... , guidata = ...}
robogui.register = function(def)
robogui[def.guiName] = {getForm = def.getForm, show = def.show, response = def.response, guidata = def.guidata or {}}
end
minetest.register_on_player_receive_fields(
function(player, formname, fields)
local gui = robogui[formname];
if gui then gui.response(player,formname,fields) end
end
)
-- robogui GUI END ====================================================
--- DEMO of simple form registration, all in one place, clean and tidy
-- adapted for use with basic_robot
-- if not basic_gui then
-- basic_gui = _G.basic_gui; minetest = _G.minetest;
-- basic_gui.register({
-- guiName = "mainWindow", -- formname
-- getForm = function(form_id, update) -- actual form design
-- local gui = basic_gui["mainWindow"];
-- local formdata = gui.guidata[form_id]
-- if not formdata then -- init
-- gui.guidata[form_id] = {}; formdata = gui.guidata[form_id]
-- formdata.sel_tab = 1;
-- formdata.text = "default";
-- formdata.form = "";
-- end
-- if not update then return formdata.form end
-- local sel_tab = formdata.sel_tab;
-- local text = formdata.text;
-- formdata.form = "size[8,9]"..
-- "label[0,0;basic_gui_DEMO, form_id " .. form_id .. ", tab " .. sel_tab .. "]"..
-- "button[0,1;2,1;gui_button;CLICK ME]"..
-- "textarea[0.25,2;2,1;gui_textarea;text;" .. text .. "]"..
-- "tabheader[0,0;tabs;tab1,table demo,tab3;".. sel_tab .. ";true;true]"..
-- "list[current_player;main;0,5;8,4;]";
-- if sel_tab == 2 then
-- formdata.form = "size[12,6.5;true]" ..
-- "tablecolumns[color;tree;text,width=32;text]" ..
-- "tableoptions[background=#00000000;border=false]" ..
-- "field[0.3,0.1;10.2,1;search_string;;" .. minetest.formspec_escape(text) .. "]" ..
-- "field_close_on_enter[search_string;false]" ..
-- "button[10.2,-0.2;2,1;search;" .. "Search" .. "]" ..
-- "table[0,0.8;12,4.5;list_settings;"..
-- "#FFFF00,1,TEST A,,"..
-- "#FFFF00,2,TEST A,,"..
-- ",3,test a,value A,"..
-- "#FFFF00,1,TEST B,,"..
-- ",2,test b,value B,"
-- end
-- formdata.info = "This information comes with the form post"; -- extra data set
-- end,
-- show = function(player_name,update) -- this is used to show form to user
-- local formname = "mainWindow";
-- local form_id = player_name; -- each player has his own window!
-- local gui = basic_gui[formname];
-- local formdata = gui.guidata[form_id]; -- all form data for this id gets stored here
-- if update then gui.getForm(form_id,true); formdata = gui.guidata[form_id]; end
-- minetest.show_formspec(player_name, "mainWindow", formdata.form)
-- end,
-- response = function(player,formname, fields) -- this handles response
-- local player_name = player:get_player_name();
-- local form_id = player_name;
-- local gui = basic_gui[formname];
-- local formdata = gui.guidata[form_id]; --gui.guidata[form_id];
-- if not formdata then say("err") return end --error!
-- if fields.gui_textarea then
-- formdata.text = fields.gui_textarea or ""
-- end
-- if fields.tabs then
-- formdata.sel_tab = tonumber(fields.tabs) or 1;
-- gui.show(player_name,true) -- update and show form
-- else
-- local form = "size[5,5]" ..
-- "label[0,0;you interacted with demo form, fields : " ..
-- _G.minetest.formspec_escape(_G.dump(fields)) .. "]"..
-- "label[0,4;" .. formdata.info .. "]"
-- _G.minetest.show_formspec(player_name,"basic_response", form);
-- end
-- end,
-- })
-- end
--process forms from spawner
local on_receive_robot_form = function(pos, formname, fields, sender)
@ -1265,13 +1253,15 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
if minetest.is_protected(pos,name) then return end
if fields.OK then
local meta = minetest.get_meta(pos);
if fields.code then
local code = fields.code or "";
if string.len(code) > 64000 then
minetest.chat_send_all("#ROBOT: " .. name .. " is spamming with long text.") return
end
if meta:get_int("admin") == 1 then
if meta:get_int("authlevel") > 1 and name ~= meta:get_string("owner")then
local privs = minetest.get_player_privs(name); -- only admin can edit admin robot code
if not privs.privs then
return
@ -1317,103 +1307,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
end
if fields.help then ----- INGAME HELP ------
local text = "BASIC LUA SYNTAX\n \nif x==1 then A else B end"..
"\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\n move.direction(), where direction is forward, backward, left,right, up, down)\n"..
" left_down, ..., backward_down, left_up, ..., backward_up\n"..
" boost(v) sets robot velocity, -6<v<6, if v = 0 then stop\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,idx,mode) crafts item if required materials are present in inventory. mode = 1 returns recipe, optional recipe idx\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,pos) finds players in radius 3 around robot(position) 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.set_properties({textures=.., visual=..,visual_size=.., , ) sets visual appearance\n"..
" set_animation(anim_start,anim_end,anim_speed,anim_stand_start) set mesh animation \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, texture, is_entity) 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, if no size return tex\n"..
" self.sound(sample,volume, opt. pos) plays sound named 'sample' at robot location (opt. pos)\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,7-15,16-271, limited to range 10 around\n"..
" keyboard.read(pos) return node name at pos\n"..
"**TECHNIC FUNCTIONALITY: namespace 'machine'. most functions return true or nil, error\n" ..
" energy() displays available energy\n"..
" generate_power(fuel, amount) = energy, attempt to generate power from fuel material,\n" ..
" if 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) = progress/true. works as a furnace, if amount>0 try to\n" ..
" use power to smelt - requires 10 upgrades for each 1 amount, energy cost is:\n"..
" 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"..
"**CRYPTOGRAPHY: namespace 'crypto'\n"..
" encrypt(input,password) returns encrypted text, password is any string \n"..
" decrypt(input,password) attempts to decrypt encrypted text\n"..
" scramble(input,randomseed,sgn) (de)permutes text randomly according to sgn = -1,1\n"..
" basic_hash(input,n) returns simple mod hash from string input within range 0...n-1\n"..
"**PUZZLE: namespace 'puzzle' - need puzzle priv\n"..
" set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers\n"..
" check_triggers(pname) check if player is close to any trigger and run that trigger\n"..
" set_node(pos,node) - set any node, limited to current protector mapblock & get_node(pos)\n"..
" get_player(pname) return player objRef in current mapblock\n"..
" chat_send_player(pname, text) \n"..
" get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes/players in current mapblock\n"..
" get_meta(pos) - return meta of target position\n"..
" get_gametime() - return current gametime\n"..
" ItemStack(itemname) returns ItemRef to be used with inventory\n"..
" count_objects(pos,radius)\n"..
" pdata contains puzzle data like .triggers and .gamedata\n"..
" add_particle(def)\n"
text = minetest.formspec_escape(text);
--local form = "size [8,7] textarea[0,0;8.5,8.5;help;HELP;".. text.."]"
--textlist[X,Y;W,H;name;listelem 1,listelem 2,...,listelem n]
local list = "";
for word in string.gmatch(text, "(.-)\r?\n+") do list = list .. word .. ", " end
local form = "size [10,8] textlist[-0.25,-0.25;10.25,8.5;help;" .. list .. "]"
minetest.show_formspec(sender:get_player_name(), "robot_help", form);
robogui["robot_help"].show(name)
return
end
@ -1505,8 +1399,10 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
end
end
-- handle form: when rightclicking robot entity, remote controller
minetest.register_on_player_receive_fields(
function(player, formname, fields)
@ -1537,6 +1433,9 @@ minetest.register_on_player_receive_fields(
local name = string.sub(formname, string.len(robot_formname)+1); -- robot name
if fields.OK and fields.code then
local item = player:get_wielded_item(); --set_wielded_item(item)
if string.len(fields.code) > 1000 then
minetest.chat_send_player(player:get_player_name(),"#ROBOT: text too long") return
end
item:set_metadata(fields.code);
player:set_wielded_item(item);
if fields.id then
@ -1678,6 +1577,10 @@ minetest.register_on_player_receive_fields(
local data = itemstack:get_meta():to_table().fields -- 0.4.16, old minetest.deserialize(itemstack:get_metadata())
if not data then data = {} end
local text = fields.book or "";
if string.len(text) > 64000 then
local sender = player:get_player_name();
minetest.chat_send_all("#ROBOT: " .. sender .. " is spamming with long text.") return
end
data.text = text or ""
data.title = fields.title or ""
data.text_len = #data.text
@ -1715,13 +1618,15 @@ end
-- handle chats
minetest.register_on_chat_message(
function(name, message)
local hidden = false;
if string.sub(message,1,1) == "\\" then hidden = true; message = string.sub(message,2) end
local listeners = basic_robot.data.listening;
for pname,_ in pairs(listeners) do
local data = basic_robot.data[pname];
data.listen_msg = message;
data.listen_speaker = name;
end
return false
return hidden
end
)
@ -1834,7 +1739,7 @@ end
minetest.register_craftitem("basic_robot:control", {
description = "Robot remote control",
inventory_image = "control.png",
groups = {book = 1, not_in_creative_inventory = 1},
groups = {book = 1}, --not_in_creative_inventory = 1
stack_max = 1,
on_secondary_use = function(itemstack, user, pointed_thing)
@ -1951,7 +1856,7 @@ minetest.register_entity(
on_step = function(self, dtime)
local vel = self.object:getvelocity();
if (self.oldvel.x~=0 and vel.x==0) or (self.oldvel.y~=0 and vel.y==0) or (self.oldvel.z~=0 and vel.z==0) then
if (self.oldvel.x~=0 and vel.x==0) or (self.oldvel.y~=0 and vel.y==0) or (self.oldvel.z~=0 and vel.z==0) then -- hit
local data = basic_robot.data[self.name];
if data then
data.fire_pos = self.object:getpos();
@ -1997,4 +1902,6 @@ minetest.register_craft({
minetest.register_privilege("robot", "increased number of allowed active robots")
minetest.register_privilege("puzzle", "allow player to use puzzle. namespace in robots")
minetest.register_privilege("puzzle", "allow player to use puzzle. namespace in robots")
print('[MOD]'.. " basic_robot " .. basic_robot.version .. " loaded.")

350
robogui.lua Normal file
View File

@ -0,0 +1,350 @@
local robogui = basic_robot.gui;
-- GUI
-- robogui GUI START ==================================================
-- a simple table of entries: [guiName] = {getForm = ... , show = ... , response = ... , guidata = ...}
robogui.register = function(def)
robogui[def.guiName] = {getForm = def.getForm, show = def.show, response = def.response, guidata = def.guidata or {}}
end
minetest.register_on_player_receive_fields(
function(player, formname, fields)
local gui = robogui[formname];
if gui then gui.response(player,formname,fields) end
end
)
-- robogui GUI END ====================================================
--- DEMO of simple form registration, all in one place, clean and tidy
-- adapted for use with basic_robot
-- if not basic_gui then
-- basic_gui = _G.basic_gui; minetest = _G.minetest;
-- basic_gui.register({
-- guiName = "mainWindow", -- formname
-- getForm = function(form_id, update) -- actual form design
-- local gui = basic_gui["mainWindow"];
-- local formdata = gui.guidata[form_id]
-- if not formdata then -- init
-- gui.guidata[form_id] = {}; formdata = gui.guidata[form_id]
-- formdata.sel_tab = 1;
-- formdata.text = "default";
-- formdata.form = "";
-- end
-- if not update then return formdata.form end
-- local sel_tab = formdata.sel_tab;
-- local text = formdata.text;
-- formdata.form = "size[8,9]"..
-- "label[0,0;basic_gui_DEMO, form_id " .. form_id .. ", tab " .. sel_tab .. "]"..
-- "button[0,1;2,1;gui_button;CLICK ME]"..
-- "textarea[0.25,2;2,1;gui_textarea;text;" .. text .. "]"..
-- "tabheader[0,0;tabs;tab1,table demo,tab3;".. sel_tab .. ";true;true]"..
-- "list[current_player;main;0,5;8,4;]";
-- if sel_tab == 2 then
-- formdata.form = "size[12,6.5;true]" ..
-- "tablecolumns[color;tree;text,width=32;text]" ..
-- "tableoptions[background=#00000000;border=false]" ..
-- "field[0.3,0.1;10.2,1;search_string;;" .. minetest.formspec_escape(text) .. "]" ..
-- "field_close_on_enter[search_string;false]" ..
-- "button[10.2,-0.2;2,1;search;" .. "Search" .. "]" ..
-- "table[0,0.8;12,4.5;list_settings;"..
-- "#FFFF00,1,TEST A,,"..
-- "#FFFF00,2,TEST A,,"..
-- ",3,test a,value A,"..
-- "#FFFF00,1,TEST B,,"..
-- ",2,test b,value B,"
-- end
-- formdata.info = "This information comes with the form post"; -- extra data set
-- end,
-- show = function(player_name,update) -- this is used to show form to user
-- local formname = "mainWindow";
-- local form_id = player_name; -- each player has his own window!
-- local gui = basic_gui[formname];
-- local formdata = gui.guidata[form_id]; -- all form data for this id gets stored here
-- if update then gui.getForm(form_id,true); formdata = gui.guidata[form_id]; end
-- minetest.show_formspec(player_name, "mainWindow", formdata.form)
-- end,
-- response = function(player,formname, fields) -- this handles response
-- local player_name = player:get_player_name();
-- local form_id = player_name;
-- local gui = basic_gui[formname];
-- local formdata = gui.guidata[form_id]; --gui.guidata[form_id];
-- if not formdata then say("err") return end --error!
-- if fields.gui_textarea then
-- formdata.text = fields.gui_textarea or ""
-- end
-- if fields.tabs then
-- formdata.sel_tab = tonumber(fields.tabs) or 1;
-- gui.show(player_name,true) -- update and show form
-- else
-- local form = "size[5,5]" ..
-- "label[0,0;you interacted with demo form, fields : " ..
-- _G.minetest.formspec_escape(_G.dump(fields)) .. "]"..
-- "label[0,4;" .. formdata.info .. "]"
-- _G.minetest.show_formspec(player_name,"basic_response", form);
-- end
-- end,
-- })
-- end
local help_address = {}; -- array containing current page name for player
local help_pages = {
["main"] = {
" === ROBOT HELP - MAIN SCREEN === ","",
"[Commands reference] display list of robot commands",
"[Lua basics] short introduction to lua","",
"INSTRUCTIONS: double click links marked with []",
"------------------------------------------","",
"basic_robot version " .. basic_robot.version,
"(c) 2016 rnd",
},
["Lua basics"] = {
"back to [main] menu",
"BASIC LUA SYNTAX","",
" IF CONDITIONAL: if x==1 then A else B end",
" FOR LOOP: for i = 1, 5 do something end",
" WHILE LOOP: while i<6 do A; i=i+1; end",
' ARRAYS: myTable1 = {1,2,3}, myTable2 = {["entry1"]=5, ["entry2"]=1}',
' access table entries with myTable1[1] or myTable2.entry1 or',
' myTable2["entry1"]',
" FUNCTIONS: f = function(x) return 2*x end, call like f(3)",
" STRINGS: name = \"rnd\" or name = [[rnd ]] (multiline string)",
" string.concat({string1,string2,...}, separator) returns",
" concatenated string with maxlength is 1024",
},
["Commands reference"] = {
"back to [main] menu",
"ROBOT COMMANDS","",
" 1. [MOVEMENT DIGGING PLACING NODE SENSING]",
" 2. [TAKE INSERT AND INVENTORY]",
" 3. [BOOKS CODE TEXT WRITE OR READ]",
" 4. [PLAYERS]",
" 5. [ROBOT SPEAK LABEL APPEARANCE OTHER]",
" 6. [KEYBOARD AND USER INTERACTIONS]",
" 7. [TECHNIC FUNCTIONALITY]",
" 8. [CRYPTOGRAPHY]",
" 9. [PUZZLE]",
},
["MOVEMENT DIGGING PLACING NODE SENSING"] = {
"back to [Commands reference]",
"MOVEMENT,DIGGING, PLACING, NODE SENSING","",
" move.direction(), where direction is: left, right, forward, backward,",
" up, down, left_down, right_down, forward_down, backward_down,",
" left_up, right_up, forward_up, backward_up",
" boost(v) sets robot velocity, -6<v<6, if v = 0 then stop",
" turn.left(), turn.right(), turn.angle(45)",
" dig.direction()",
" place.direction(\"default:dirt\", optional orientation param)",
" read_node.direction() tells you names of nodes",
},
["TAKE INSERT AND INVENTORY"] = {
"back to [Commands reference]",
"TAKE INSERT AND INVENTORY","",
" insert.direction(item, inventory) inserts item from robot inventory to",
" target inventory",
" check_inventory.direction(itemname, inventory,index) looks at node and ",
" returns false/true, direction can be self, if index>0 it returns itemname.",
" if itemname == \"\" it checks if inventory empty",
" activate.direction(mode) activates target block",
" pickup(r) picks up all items around robot in radius r<8 and returns list",
" or nil",
" craft(item,idx,mode) crafts item if required materials are present in",
" inventory, mode = 1 returns recipe, optional recipe idx",
" take.direction(item, inventory) takes item from target inventory into",
" robot inventory",
},
["BOOKS CODE TEXT WRITE OR READ"] = {
"back to [Commands reference]",
"BOOKS CODE TEXT WRITE OR READ","",
" title,text=book.read(i) returns title,contents of book at i-th position in",
" library",
" book.write(i,title,text) writes book at i-th position at spawner library",
" code.run(text) compiles and runs the code in sandbox (privs only)",
" code.set(text) replaces current bytecode of robot",
" find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around",
" robot, or false if none found",
" read_text.direction(stringname,mode) reads text of signs, chests and",
" other blocks, optional stringname for other meta, mode 1 to read number",
" write_text.direction(text,mode) writes text to target block as infotext",
},
["PLAYERS"] = {
"back to [Commands reference]",
"PLAYERS","",
" find_player(3,pos) finds players in radius 3 around robot(position) and",
" returns list of found player names, if none returns nil",
" attack(target) attempts to attack target player if nearby",
" grab(target) attempt to grab target player if nearby and returns",
" true if succesful",
" player.getpos(name) return position of player, player.connected()",
" returns list of connected players names",
},
["ROBOT SPEAK LABEL APPEARANCE OTHER"] = {
"back to [Commands reference]",
"ROBOT","",
" say(\"hello\") will speak",
" self.listen(0/1) (de)attaches chat listener to robot",
" speaker, msg = self.listen_msg() retrieves last chat message if robot",
" has listener attached",
" self.send_mail(target,mail) sends mail to target robot",
" sender,mail = self.read_mail() reads mail, if any",
" self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}",
" self.name() returns robot name",
" self.operations() returns remaining robot operations",
" self.set_properties({textures=.., visual=..,visual_size=.., , ) sets visual",
" appearance",
" self.set_animation(anim_start,anim_end,anim_speed,anim_stand_start)",
" set mesh,animation",
" self.spam(0/1) (dis)enable message repeat to all",
" self.remove() stops program and removes robot object",
" self.reset() resets robot position",
" self.spawnpos() returns position of spawner block",
" self.viewdir() returns vector of view for robot",
" self.fire(speed, pitch,gravity, texture, is_entity) fires a projectile",
" from robot. if is_entity false (default) it fires particle.",
" self.fire_pos() returns last hit position",
" self.label(text) changes robot label",
" self.display_text(text,linesize,size) displays text instead of robot face,",
" if no size just return texture string",
" self.sound(sample,volume, opt. pos) plays sound named 'sample' at",
" robot, location (optional pos)",
" rom is aditional table that can store persistent data, like rom.x=1",
},
["KEYBOARD AND USER INTERACTIONS"] = {
"back to [Commands reference]",
"KEYBOARD","",
" EVENTS : place spawner at coordinates (r*i,2*r*j+1,r*k) to monitor",
" events. value of r is ".. basic_robot.radius,
" keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. }",
" for keyboard event",
" keyboard.set(pos,type) set key at pos of type 0=air,1-6,7-15,16-271,",
" limited to range 10 around spawner",
" keyboard.read(pos) return node name at pos",
},
["TECHNIC FUNCTIONALITY"] = {
"back to [Commands reference]",
"TECHNIC FUNCTIONALITY","",
" All commands are in namespace 'machine', for example machine.energy()",
" most functions return: ok, error = true or nil, error",
" To use some commands fully robot must be upgraded. 1 upgrade is",
" goldblock+meseblock+diamonblock.",
" energy() displays available energy",
" generate_power(fuel, amount) = energy, attempt to generate power",
" from fuel material. If amount>0 try generate amount of power",
" using builtin generator - this requires 40 upgrades for each",
" 1 amount",
" smelt(input,amount) = progress/true. works as a furnace, if amount>0",
" try to use power to smelt - requires 10 upgrades for each 1 amount,",
" energy cost of smelt is: 1/40*(1+amount)",
" grind(input) - grinds input material, requires upgrades for harder",
" materials",
" compress(input) - requires upgrades - energy intensive process",
" transfer_power(amount,target_robot_name)",
},
["CRYPTOGRAPHY"] = {
"back to [Commands reference]",
"CRYPTOGRAPHY","",
" namespace 'crypto'",
" encrypt(input,password) returns encrypted text, password is any string",
" decrypt(input,password) attempts to decrypt encrypted text",
" scramble(input,randomseed,sgn) (de)permutes text randomly according",
" to sgn = -1,1",
" basic_hash(input,n) returns simple mod hash from string input within",
" range 0...n-1",
},
["PUZZLE"] = {
"back to [Commands reference]",
"PUZZLE","",
" namespace 'puzzle' - need puzzle priv",
" set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers",
" check_triggers(pname) check if player is close to any trigger and run",
" that trigger",
" set_node(pos,node) - set any node, limited to current protector",
" region",
" get_player(pname) return player objRef in current protector region",
" chat_send_player(pname, text)",
" get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes",
" /players in current mapblock",
" get_meta(pos) - return meta of target position",
" get_gametime() - return current gametime",
" ItemStack(itemname) returns ItemRef to be used with inventory",
" count_objects(pos,radius)",
" pdata contains puzzle data like .triggers and .gamedata",
" add_particle(def)"
}
}
for k,v in pairs(help_pages) do
local pages = help_pages[k]; for i = 1,#pages do pages[i] = minetest.formspec_escape(pages[i]) end
end
local robot_show_help = function(pname) --formname: robot_help
local address = help_address[pname] or "main";
--minetest.chat_send_all("D DISPLAY HELP for ".. address )
local pages = help_pages[address];
local content = table.concat(pages,",")
local size = 9; local vsize = 8.75;
local form = "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]";
--minetest.chat_send_all("D " .. form)
minetest.show_formspec(pname, "robot_help", form)
return
end
robogui["robot_help"] = {
response = function(player,formname,fields)
local name = player:get_player_name()
local fsel = fields.wiki;
if fsel and string.sub(fsel,1,3) == "DCL" then
local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line
local address = help_address[name] or "main";
local pages = help_pages[address];
local link = string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]")
if help_pages[link] then
help_address[name] = link;
robot_show_help(name)
end
end
end,
getForm = function(player_name)
end,
show = robot_show_help,
};

242
scripts/command_robot.lua Normal file
View File

@ -0,0 +1,242 @@
--COMMAND ROBOT by rnd, v2, adapted for skyblock
if not s then
self.listen(1)
s=1;_G.minetest.forceload_block(self.pos(),true)
self.spam(1)
users = {["rnd"]=3,["rnd1"]=3,["Giorge"]=1,["quater"]=1,["UltimateNoob"]=1,["reandh3"]=1,["karo"]=1,["Fedon"]=1,["DS"]=2,
["Arcelmi"]=1,["Gregorro"]=1,Mokk = 1, Evandro010 = 1}
cmdlvl = {["kill"]=2,["tp"]=3,["heal"]=1, ["rename"]=1,["jump"]=1,["givediamond"]=3, ["msg"]=1,["calc"]=0, ["acalc"]=3,["run"]=3, ["shutdown"] = 3,["sayhi"]=0, ["day"] = 1};
tpr = {};
cmds = {
help =
{
level = 0,
run = function(param)
local arg = param[2];
if not arg then
say(colorize("red","OPEN INVENTORY AND READ 'Quests'. YOU GET REWARD AND BETTER STUFF FOR COMPLETING QUESTS. Say /spawn to get to spawn area."))
return
end
if arg and cmds[arg] then
local text = cmds[arg]["docs"];
if text then say(text) end
else
--say(colorize("red","commands") .. colorize("LawnGreen",": 0 status, 2 kill $name, 3 tp $name1 $name2, 1 heal $name, 1 day, 1 rename $name $newname, 3 givediamond $name, 1 msg $name $message, 0 sayhi, 0 calc $formula, 3 acalc $formula, 3 run $expr"))
local msg = ""
for k,v in pairs(cmds) do
msg = msg .. v.level .. " " .. k .. ", "
end
say(colorize("red","commands") .. " " .. colorize("LawnGreen", msg))
end
end,
docs = "Displays available commands. 'help command' shows help about particular command"
},
status =
{
level = 0,
run = function(param)
local usr = param[2] or speaker;
local id = _G.skyblock.players[usr].id;
local pos = _G.skyblock.get_island_pos(id)
minetest.chat_send_all(minetest.colorize("yellow",
usr .. " data : permission level " .. (users[usr] or 0).. ", island " .. id .." (at " .. pos.x .. " " .. pos.z .. "), skyblock level " .. _G.skyblock.players[usr].level
))
end,
docs = "status name, Show target(speaker if none) level, which determines access privilege."
},
kill =
{
level = 2,
run = function(param)
local name = param[2]; if not name then return end
local player = _G.minetest.get_player_by_name(name);
if player then
if (users[name] or 0)<=(users[speaker] or 0) then player:set_hp(0) end
end
end,
docs = "kill name; kills target player",
},
tp =
{
level = 2,
run = function(param)
local player1 = _G.minetest.get_player_by_name(param[2] or "");
local player2 = _G.minetest.get_player_by_name(param[3] or "");
if player1 and player2 then if (users[param[2]] or 0)<=(users[speaker] or 0) then player1:setpos(player2:getpos()) end end
end,
docs = "tp name1 name2; teleports player name2 to name2",
},
tpr =
{
level = 0,
run = function(param)
local name = param[2] or "";
local player1 = _G.minetest.get_player_by_name(name);
if player1 then tpr = {speaker, name} else return end
_G.minetest.chat_send_player(name,minetest.colorize("yellow","#TELEPORT REQUEST: say tpy to teleport " .. speaker .. " to you."))
end,
docs = "tpr name; request teleport to target player",
},
tpy =
{
level = 0,
run = function(param)
if speaker == tpr[2] then
local player1 = _G.minetest.get_player_by_name(tpr[1] or "");
local player2 = _G.minetest.get_player_by_name(tpr[2] or "");
if player1 and player2 then else return end
player1:setpos(player2:getpos())
_G.minetest.chat_send_player(tpr[2],minetest.colorize("yellow","#teleporting " .. tpr[1] .. " to you."))
tpr = {}
end
end,
docs = "tpy; allow player who sent teleport request to teleport to you.",
},
calc =
{
level = 0,
run = function(param)
local formula = param[2] or "";
if not string.find(formula,"%a") then
result = 0;
code.run("result = "..formula);
result = tonumber(result)
if result then say(result) else say("error in formula") end
else
say("dont use any letters in formula")
end
end,
docs = "calculate expression",
},
day =
{
level = 1,
run = function() minetest.set_timeofday(0.25) end,
docs = "set time to day"
},
sayhi =
{
level = 0,
run = function()
local players = _G.minetest.get_connected_players();local msg = "";
for _,player in pairs(players) do
local name = player:get_player_name();
local color = string.format("#%x",math.random(2^24)-1)
if name~=speaker then msg = msg..colorize(color , " hi " .. name) .."," end
end
_G.minetest.chat_send_all("<"..speaker..">" .. string.sub(msg,1,-2))
end,
docs = "say hi to all the other players"
},
msg = {
level = 2,
run = function(param)
local text = string.sub(msg, string.len(param[1] or "")+string.len(param[2] or "") + 3)
local form = "size [8,2] textarea[0.,0;8.75,3.75;book;MESSAGE from " .. speaker .. ";" .. _G.minetest.formspec_escape(text or "") .. "]"
_G.minetest.show_formspec(param[2], "robot_msg", form);
end,
docs = "msg name message, displays message to target player",
},
plist = {
level = 0,
run = function()
local p = {};
for k,v in pairs(minetest.get_connected_players()) do
local name = v:get_player_name()
local pdata = _G.skyblock.players[name]
p[#p+1] = name..", level " .. pdata.level .. "+" .. pdata.completed .. "/" .. pdata.total
end
local text = table.concat(p,"\n")
local form = "size [8,5] textarea[0.,0;8.75,6.75;book;PLAYERS;" .. _G.minetest.formspec_escape(text or "") .. "]"
_G.minetest.show_formspec(speaker, "robot_msg", form);
end,
docs = "plist, displays player list and their levels",
},
run = {
level = 3,
run = function(param)
local expr = string.sub(msg,5);
--say("running " .. expr)
code.run(expr);
end,
docs = "run lua code",
},
}
self.label(colorize("red","\nCMD ROBOT"))
end
speaker, msg = self.listen_msg();
if msg then
local words = {};
for word in string.gmatch(msg,"%S+") do words[#words+1]=word end -- extract words
local level = users[speaker] or 0;
local cmd = words[1];
cmdlevel = cmdlvl[cmd] or 0;
if level < cmdlevel then
say("You need to be level " .. cmdlevel .. " to use " .. words[1])
else
if cmds[cmd] then
cmds[cmd].run(words)
elseif words[1]=="heal" and words[2] then
local player = _G.minetest.get_player_by_name(words[2]);
if player then player:set_hp(20) end
elseif words[1]=="rename" then
local player = _G.minetest.get_player_by_name(words[2]);
if player then if ((users[words[2]] or 0)<=level) and (level>=3 or words[2]~=speaker) then player:set_nametag_attributes({text = words[3] or words[2]}) end end
elseif words[1]=="robot"then
local player = _G.minetest.get_player_by_name(words[2])
if player then
player:set_properties({visual = "cube"});
player:set_properties({textures={"arrow.png^[transformR90","basic_machine_side.png","basic_machine_side.png","basic_machine_side.png","face.png","basic_machine_side.png"}})
player:set_properties({collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}})
end
elseif words[1] == "mese" then
local player = _G.minetest.get_player_by_name(words[2])
if player then
player:set_properties({visual = "mesh",textures = {"zmobs_mese_monster.png"},mesh = "zmobs_mese_monster.x",visual_size = {x=1,y=1}})
end
elseif words[1] == "givediamond" then
local player = _G.minetest.get_player_by_name(words[2])
local pos = player:getpos();
_G.minetest.add_item(pos,_G.ItemStack("default:diamond"))
elseif words[1] == "acalc" then
local formula = words[2] or "";
if not string.find(formula,"_G") then
result = 0;
code.run("result = "..formula);
result = tonumber(result)
if result then say(result) else say("error in formula") end
end
elseif words[1] == "shutdown" then
_G.minetest.request_shutdown("maintenance, come back",true)
elseif words[1] == "web" then
local text = string.sub(msg,5);
local f = _G.io.open("H:\\sfk\\files\\index.html", "w")
f:write(text);f:close()
end
end
end

90
scripts/copy_paste.lua Normal file
View File

@ -0,0 +1,90 @@
-- COPY PASTE ROBOT by rnd: c1 c2 r = markers, c = copy p = paste
if not paste then
_G.minetest.forceload_block(self.pos(),true)
paste = {};
round = function(x)
if x>0 then
return math.floor(x+0.5)
else
return -math.floor(-x+0.5)
end
end
data = {};
display_marker = function(pos,label)
minetest.add_particle(
{
pos = pos,
expirationtime = 10,
velocity = {x=0, y=0,z=0},
size = 18,
texture = label..".png",
acceleration = {x=0,y=0,z=0},
collisiondetection = true,
collision_removal = true,
})
end
self.listen(1)
self.label("COPY-PASTE MASTER v1.2 gold edition. commands: c1 c2 r c p")
end
speaker, msg = self.listen_msg()
if speaker == "rnd" then
local player = _G.minetest.get_player_by_name(speaker);
local p = player:getpos(); p.x = round(p.x); p.y=round(p.y); p.z = round(p.z);
if p.y<0 then p.y = p.y +1 end -- needed cause of minetest bug
if msg == "c1" then
paste.src1 = {x=p.x,y=p.y,z=p.z};say("marker 1 set at " .. p.x .. " " .. p.y .. " " .. p.z)
display_marker(p,"099") -- c
elseif msg == "c2" then
paste.src2 = {x=p.x,y=p.y,z=p.z};say("marker 2 set at " .. p.x .. " " .. p.y .. " " .. p.z)
display_marker(p,"099") -- c
elseif msg == "r" then
paste.ref = {x=p.x,y=p.y,z=p.z};say("reference set at " .. p.x .. " " .. p.y .. " " .. p.z)
display_marker(p,"114") -- r
elseif msg == "c" then -- copy
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
local count = 0; data = {};
for i = x1,x2 do
for j = y1,y2 do
for k = z1,z2 do
local node = _G.minetest.get_node({x=i,y=j,z=k});
if node.name ~= "air" then
if not data[i] then data[i]= {} end
if not data[i][j] then data[i][j]= {} end
data[i][j][k] = {node, _G.minetest.get_meta({x=i,y=j,z=k}):to_table()}
count = count +1;
end
end
end
end
say(count .. " nodes copied ");
elseif msg == "p" then -- paste
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
local count = 0; p.x = p.x-paste.ref.x; p.y = p.y-paste.ref.y; p.z = p.z-paste.ref.z;
for i = x1,x2 do
for j = y1,y2 do
for k = z1,z2 do
local pdata;
if data[i] and data[i][j] and data[i][j][k] then
pdata = data[i][j][k]
end
if pdata then
count = count + 1
_G.minetest.set_node({x=i+p.x,y=j+p.y,z=k+p.z}, pdata[1]);
_G.minetest.get_meta({x=i+p.x,y=j+p.y,z=k+p.z}):from_table(pdata[2])
end
end
end
end
say(count .. " nodes pasted ");
end
end

108
scripts/craft_guide.lua Normal file
View File

@ -0,0 +1,108 @@
-- ROBOT craft guide by rnd, 2017
if not list then
tname = "rnd";
list = {};
tmplist = _G.minetest.registered_items;
for k,v in pairs(tmplist) do
local texture = v.inventory_image or "";
if texture=="" and v.tiles then texture = v.tiles[1] or "" end
if (not v.groups.not_in_craft_guide or v.groups.not_in_craft_guide == 0) and type(texture)=="string" and texture~="" then
list[#list+1] = {_G.minetest.formspec_escape(k),_G.minetest.formspec_escape(v.description),_G.minetest.formspec_escape(texture)}; -- v.inventory_image, k, v.description
end
end
idx = 1; n = 35; row = 6; size = 1.25;
filter = "" item = "" recipeid = 1
filterlist = {}; for i = 1,#list do filterlist[i] = i end
get_texture = function(ritem)
local v = _G.minetest.registered_items[ritem]; if not v then return "" end
local texture = v.inventory_image or "";
if texture=="" and v.tiles then texture = v.tiles[1] or "" end
if type(texture)~="string" then return "" end
return texture
end
get_form = function()
local form = "size[7.5,8.5]";
local x,y,i; local idxt = idx+n; if idxt > #filterlist then idxt = #filterlist end
for i = idx, idxt do
local id = filterlist[i];
if list[id] and list[id][3] then
x = ((i-idx) % row)
y = (i-idx-x)/row;
form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. list[id][3] ..";".."item"..";".. list[id][1] .."]"
end
end
form = form .. "textarea[0.25,0;2,0.75;filter;filter;"..filter .. "]" .. "button[2.,0;1,0.5;search;search]"..
"button[5.5,0;1,0.5;prev;PREV]" .. "button[6.5,0;1,0.5;next;NEXT]" .. "label[4,0;".. idx .. "-"..idxt .. "/" .. #filterlist.."]";
return form
end
get_recipe = function()
local form = "size[7.5,8.5]";
local recipes = _G.minetest.get_all_craft_recipes(item); if not recipes then return end;
local recipe = recipes[recipeid]; if not recipe then return end
local items = recipe.items
local x,y,i;
for i = 0, 8 do
local ritem = items[i+1] or ""; local sritem = "";
local j = string.find(ritem,":"); if j then sritem = string.sub(ritem,j+1) end; --ritem = _G.minetest.formspec_escape(ritem);
x = (i % 3)
y = (i-x)/3;
form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. get_texture(ritem) ..";".."item"..";".. sritem .."]"
end
form = form .. "textarea[0.25,0;2,0.75;recipeid;recipeid ".. #recipes .. ";"..recipeid .. "]" .. "button[2.,0;1,0.5;go;go]"..
"label[3,0;" .. item .. "]" .. "button[6.5,0;1,0.5;back;BACK]" ;
return form
end
s=0
end
if s==0 then
local p = find_player(4); s = 1
if p then
self.show_form(p[1],get_form())
else
self.remove()
end
end
sender,fields = self.read_form()
if sender then
if fields.search then
filter = fields.filter or ""
filterlist = {};
for i = 1,#list do
if string.find(list[i][1],filter) then filterlist[#filterlist+1] = i end
end
idx=1;self.show_form(sender,get_form())
elseif fields.prev then
idx = idx - n; if idx<1 then idx =#filterlist-n end
self.show_form(sender,get_form())
elseif fields.next then
idx = idx+n; if idx > #filterlist then idx = 1 end
self.show_form(sender,get_form())
elseif fields.back then
self.show_form(sender,get_form())
elseif fields.recipeid then
recipeid = tonumber(fields.recipeid) or 1;
self.show_form(sender,get_recipe())
elseif fields.item then
item = fields.item;
local recipes = _G.minetest.get_all_craft_recipes(item);
local count = 0; if recipes then count = #recipes end
if count>0 then
recipeid = 1
self.show_form(sender,get_recipe() or "")
end
elseif fields.quit then
self.remove()
end
end

15
scripts/farm_walker.lua Normal file
View File

@ -0,0 +1,15 @@
if not init then
init = true
angle = 90
walk = {["default:dirt"] = 1}
stop = {["wool:white"] = 1}
end
node = read_node.forward_down()
if walk[node] then
move.forward()
elseif stop[node] then
self.reset(); angle = 90
else
turn.angle(angle);move.forward(); turn.angle(angle); angle = - angle
end

146
scripts/games/CTF_bot.lua Normal file
View File

@ -0,0 +1,146 @@
-- simple ctf robot, rnd
--instructions: build game arena and place blue/red buttons as flags. edit flag positions below
--you must register 'keyboard' events by placing robot with same 'id' as robot running this code at 'event register' positions - default (32*i,64*j+1, 32*k)
if not ctf then
_G.minetest.forceload_block(self.pos(),true)
ctf = {
[1] = {state = 1, flagnode = "basic_robot:button8080FF", pos = {x=-18,y=502,z=110}, name = "blue", owner = "", score = 0}, -- team[1]
[2] = {state = 1, flagnode = "basic_robot:buttonFF8080", pos = {x=-18,y=502,z=80}, name = "red", owner = "", score = 0}, -- team[2]
}
teams = {} -- example : {["rnd"] = {1,0, player, health points at start}}; -- team, ownership of flag
maxscore = 3;
t = 0
teamid = 1; -- team selector when joining
gamestate = 0;
self.listen(1)
self.spam(1)
get_id = function(pos)
local range = 1000;
return pos.x + range*pos.y+range^2*pos.z
end
flag_id = {}; for i = 1,#ctf do flag_id[get_id(ctf[i].pos)] = i end
render_flags = function()
for i = 1,#ctf do minetest.set_node(ctf[i].pos, {name = ctf[i].flagnode}) end
end
end
if gamestate == 0 then -- welcome
say(colorize("red","#CAPTURE THE FLAG GAME. say '\\join' to join game. to start game one of joined players says '\\start'"))
gamestate = 1
elseif gamestate == 1 then
speaker,msg = self.listen_msg()
if msg == "join" then
local pl = minetest.get_player_by_name(speaker);
teams[speaker] = {teamid, 0, pl,20};pl:set_hp(20)
local msg1 = ""; local msg2 = ""
for k,v in pairs(teams) do
if v[1] == 1 then msg1 = msg1 .. k .. " " elseif v[1] == 2 then msg2 = msg2 .. k .. " " end
end
say(colorize("yellow","#CTF : " .. speaker .. " joined team " .. ctf[teamid].name .. ". TEAM " .. ctf[1].name .. ": " .. msg1 .. ", TEAM " .. ctf[2].name .. ": " .. msg2))
teamid = 3-teamid; -- 1,2
elseif msg == "start" then -- game start
if teams[speaker] then
gamestate = 2
keyboard.get() -- clear keyboard buffer
say(colorize("red","#CTF GAME STARTED. GET ENEMY FLAG AND BRING IT BACK TO YOUR FLAG. DONT LET YOUR HEALTH GO BELOW 3 HEARTS OR YOU ARE OUT."))
for k,_ in pairs(teams) do -- teleport players
local data = teams[k];data[3]:setpos( ctf[data[1]].pos )
end
render_flags()
end
end
elseif gamestate == 2 then
speaker,msg = self.listen_msg()
if msg == "score" then
local msg1 = ""; local msg2 = ""
for k,v in pairs(teams) do
if v[1] == 1 then msg1 = msg1 .. k .. " " elseif v[1] == 2 then msg2 = msg2 .. k .. " " end
end
say(colorize("yellow","SCORE " .. ctf[1].score .. "/" .. ctf[2].score) .."\n" .. colorize("yellow","TEAM " .. ctf[1].name .. ": " .. msg1 .. ", TEAM " .. ctf[2].name .. ": " .. msg2))
end
-- check player health
for k,v in pairs(teams) do
local hp = teams[k][3]:get_hp();
if not hp or hp<6 then -- teams[k][4]
local cflag = teams[k][2];
if cflag>0 then -- drop flag
ctf[cflag].state = 1
ctf[cflag].owner = ""
minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode})
say(colorize("red", "#CTF " .. k .. " dropped " .. ctf[cflag].name .. " flag!"))
end
if not hp then -- player left
say(colorize("yellow", "#CTF " .. k .. " left the game!"))
teams[k] = nil
else -- reset player
say(colorize("yellow", "#CTF " .. k .. " resetted!"))
v[2] = 0 -- player has no flag
v[3]:set_hp(20)
v[3]:setpos( ctf[v[1]].pos )
end
end
end
event = keyboard.get()
if event and teams[event.puncher] then
--say(serialize(event))
local punch_id = get_id({x=event.x,y=event.y,z=event.z});
local flag = flag_id[punch_id];
if flag then
local state = ctf[flag].state
local puncher = event.puncher;
if state == 1 then -- flag is here, ready to be taken or capture of enemy flag
if teams[puncher][1] ~= flag then -- take
say(colorize("red","#CTF " .. puncher .. " has taken " .. ctf[flag].name .. " flag !"))
ctf[flag].state = 2;
ctf[flag].owner = puncher;
teams[puncher][2] = flag;
minetest.set_node(ctf[flag].pos, {name = "basic_robot:buttonFFFF80"})
else -- capture?
if teams[puncher][2] > 0 then
local cflag = teams[puncher][2] -- puncher has this flag
local data = ctf[cflag];
local team = teams[puncher][1];
ctf[team].score = ctf[team].score + 1
ctf[team].owner = ""
ctf[cflag].state = 1; -- reset captured flag state
minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode})
teams[puncher][2] = 0
say(colorize("orange","#CTF " .. puncher .. " has captured " .. data.name .. " flag! Team " .. ctf[team].name .. " has score " .. ctf[team].score ))
if ctf[team].score == maxscore then
say(colorize("yellow","#CTF: TEAM " .. ctf[team].name .. " WINS! "))
gamestate = 3;t=5; -- intermission, duration 5
--reset
teams = {}
for i=1,#ctf do ctf[i].state = 1 ctf[i].score = 0 ctf[i].owner = "" end
end
end
end
end
end
--say(serialize(event))
end
elseif gamestate == 3 then -- intermission
if t>0 then t=t-1 else gamestate = 0 end
end

View File

@ -0,0 +1,91 @@
if not s then
-- init
bots = {[4] = {}, [5] = {}}; -- [type] = {{1,1,10}, {3,2,10}}; -- {x,y,hp}
arena = {}; --[x][z] = {type, idx}
for i = -10,10 do arena[i] = {} for j=-10,10 do arena[i][j] = {0,0} end end
centerpos = self.spawnpos(); centerpos.y = centerpos.y+2
TYPE = 4; -- 4,5 defines which bots are on the move/attack
DIR = 1
s=0
t=0
-- load user progs
_,script1 = book.read(1);_,script2 = book.read(2);
prog1, _ = _G.loadstring( script1 ); prog2, _ = _G.loadstring( script2 );
spawn_bot = function (x,z,type)
if arena[x] and arena[x][z] and arena[x][z][1] == 0 then
keyboard.set({x=centerpos.x+x,y=centerpos.y,z=centerpos.z+z},type)
table.insert(bots[type],{x,z,10})
arena[x][z] = {type,#bots[type]}
else
return false
end
end
move_bot = function (i,dx,dz)
local bot = bots[TYPE][i];if not bot then return false end
if math.abs(dx)>1 or math.abs(dz)>1 then return false end
local x1=bot[1]+dx; local z1=bot[2]+dz;
if math.abs(x1)>10 or math.abs(z1)>10 then return false end
if arena[x1] and arena[x1][z1] and arena[x1][z1][1] == 0 then else return false end
keyboard.set({x=centerpos.x+bot[1],y=centerpos.y,z=centerpos.z+bot[2]},0);
keyboard.set({x=centerpos.x+x1,y=centerpos.y,z=centerpos.z+z1},TYPE);
arena[bot[1]][bot[2]] = {0,0}
arena[x1][z1] = {TYPE,i}
bot[1]=x1;bot[2]=z1;
end
attack_bot = function(i,dx,dz)
local bot = bots[TYPE][i];if not bot then return false end
if math.abs(dx)>1 or math.abs(dz)>1 then return false end
local x1=bot[1]+dx; local z1=bot[2]+dz;
if math.abs(x1)>10 or math.abs(z1)>10 then return false end
if arena[x1] and arena[x1][z1] and arena[x1][z1][1] == 0 then return false end
local type = arena[x1][z1][1]; local idx = arena[x1][z1][2];
local tbot = bots[type][idx];
if not tbot then return false end
tbot[3]=tbot[3]-5;
if tbot[3]<=0 then
keyboard.set({x=centerpos.x+tbot[1],y=centerpos.y,z=centerpos.z+tbot[2]},0);
table.remove(bots[type],idx);
arena[x1][z1] = {0,0}
end
end
read_arena = function(x,z)
local data = arena[x][z];
if not data then return end
return {data[1],data[2]};
end
read_bots = function (type, idx)
local data = bots[type][idx];
if not data then return end
return {data[1],data[2],data[3]}
end
end
if t%10 == 0 then
spawn_bot(0,-10,4)
spawn_bot(0,10,5)
end
t=t+1
self.label(#bots[4] .. " " .. #bots[5])
-- PROGRAM RULES:
-- not allowed to modify api code: TYPE, bots,t,s, spawn_bot, move_bot, attack_bot, read_arena, read_bots
-- only allowed to move bot or attack, but not to dig/place
TYPE = 4+(t%2);
DIR = - DIR
if TYPE == 5 then
_G.setfenv(prog1, _G.basic_robot.data[self.name()].sandbox )
_,err = pcall(prog1)
else
_G.setfenv(prog2, _G.basic_robot.data[self.name()].sandbox )
_,err = pcall(prog2)
end
if err then say(err) self.remove() end

View File

@ -0,0 +1,189 @@
if not data then
m=10;n=10;
players = {};
paused = true
turn = 2;
turntimeout = 5
shorttimeout = 1
shortround = false
turnmessage = ""
t = 0;
SIGNUP = 0; GAME = 1; INTERMISSION = 2
state = SIGNUP
t0 = _G.minetest.get_gametime();spawnpos = self.spawnpos() -- place mines
data = {};
init_game = function()
abilitypoints = {0,0}; -- points to use ability, step outside ring to use them in following round
shortround = false
data = {}; minescount = 32
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
minescount = 0;
for i = 1,m do for j = 1,n do -- render game
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
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
end end
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},4) -- safe start spot
end
get_mine_count = function(i,j)
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
for k = -1,1 do for l = -1,1 do
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
end end
return count
end
chk_mines = function()
local count = minescount;
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" and data[i] and data[i][j]==1 then
count=count-1
end
end end
return count
end
near_chat = function(msg)
end
greeting = function()
_G.minetest.chat_send_all(colorize("red",
"#BATTLE MINESWEEPER : two player battle in minesweeper. say join to play.\nRules: "..
"1. each player has 5 second turn to make a move 2. if you dont make move you lose\n"..
"3. if you make move in other player turn you lose. 4. if you hit bomb or mark bomb falsely you lose\n"..
"5. by playing fast you get ability charge. when you collect 10 you can step out of ring when your round ends and opponent will only have 2s to play his turn."
))
end
player_lost = function ()
for i=1,#players do
local player = _G.minetest.get_player_by_name(players[i]);
if player then player:setpos({x=spawnpos.x,y=spawnpos.y+10,z=spawnpos.z}) end
end
_G.minetest.sound_play("electric_zap",{pos=spawnpos, max_hear_distance = 100})
state = INTERMISSION; t = 0
end
function change_turn()
shortround = false -- reset ability if activated
if turn == 1 then
_G.minetest.sound_play("default_break_glass",{pos=spawnpos, max_hear_distance = 100})
else
_G.minetest.sound_play("default_cool_lava",{pos=spawnpos, max_hear_distance = 100})
end
if paused == false then
say(players[turn] .. " lost : didn't make a move fast enough ");
player_lost()
else
local player = minetest.get_player_by_name(players[turn])
local ppos = player:getpos()
local x = ppos.x - spawnpos.x;local y = ppos.y - spawnpos.y;local z = ppos.z - spawnpos.z;
local points = abilitypoints[turn]
if x<1 or x>m or z<1 or z>n then -- outside area
local points = abilitypoints[turn]
if points>=10 then -- do we have enough points?
shortround = true -- next round will be shorter, 2s
_G.minetest.sound_play("grinder",{pos=spawnpos, max_hear_distance = 100})
abilitypoints[turn] = abilitypoints[turn]-10
end
end
if turn == 1 then turn = 2 else turn = 1 end
turnmessage = "turn " .. turn .. " " .. players[turn] .. ",charge " .. abilitypoints[turn] .. "\n"
self.label(turnmessage)
t=0
paused = false
end
end
init_game()
greeting()
self.listen(1)
end
if state == SIGNUP then
speaker,msg = self.listen_msg()
if speaker then
if msg == "join" then
players[#players+1] = speaker;
local plist = ""; for i=1,#players do plist = plist .. players[i] .. ", " end
_G.minetest.chat_send_all("BATTLE MINESWEEPER, current players : " .. plist)
if #players >= 2 then
state = GAME
change_turn();
keyboard.get(); t=0;
for i = 1, #players do
local player = _G.minetest.get_player_by_name(players[i]);
if player then player:setpos({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z}) end
end
_G.minetest.chat_send_all(colorize("red","BATTLE MINESWEEPER " .. m .. "x" ..n .. " with " .. minescount .. " mines.\n" .. players[turn] .. " its your move!"))
init_game()
end
end
end
elseif state == GAME then
t = t + 1;
if (t>turntimeout) or (shortround and t>shorttimeout) then -- change of turn
change_turn()
else
self.label(turnmessage .. " " .. (turntimeout-t+1))
end
event = keyboard.get();
if event and event.type == 2 and not paused then
if event.puncher == players[turn] then
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
local points = abilitypoints[turn]
if x<1 or x>m or z<1 or z>n then -- outside area
else
local ppos = player.getpos(event.puncher)
points = points + math.max(turntimeout-t-2,0); if points>40 then points = 40 end
abilitypoints[turn] = points
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
if data[x] and data[x][z] == 1 then
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
keyboard.set({x=event.x,y=event.y,z=event.z},2)
else
keyboard.set({x=event.x,y=event.y,z=event.z},3)
end
else
say(event.puncher .. " lost : marked a bomb where it was none! ");
player_lost()
end
else
if data[x] and data[x][z]==1 then
_G.minetest.sound_play("tnt_boom",{pos=spawnpos, max_hear_distance = 100})
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3)
say(event.puncher .. " lost : punched a bomb! ");
player_lost()
else
local count = get_mine_count(x,z);
if count == 0 then keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4)
else keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},7+count) end
end
end
end
paused = true
else
say(event.puncher .. " lost : played out of his/her turn"); player_lost()
end
end
elseif state == INTERMISSION then
t=t+1; if t> 15 then state = SIGNUP;players = {}; paused = true; greeting() end
end

View File

@ -0,0 +1,208 @@
--black box by rnd, 03/18/2017
--https://en.wikipedia.org/wiki/Black_Box_(game)
if not data then
m=16;n=16;
atoms = 32
attempts = 1;turn = 0;
spawnpos = self.spawnpos(); spawnpos.x = spawnpos.x-m/2; spawnpos.y = spawnpos.y+2; spawnpos.z = spawnpos.z-n/2
local players = find_player(5,spawnpos);
if not player then self.remove() else pname = players[1] end
self.spam(1);t0 = _G.minetest.get_gametime();
data = {};
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
for i=1,atoms do -- put in atoms randomly
data[math.random(m)][math.random(n)] = 1
end
atoms = 0
for i = 1,m do for j = 1,n do if data[i][j]==1 then atoms = atoms + 1 end end 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 = "basic_robot:button_"..(65+count);
_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},4)
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z-1},5)
self.label("BLACKBOX with " .. atoms .. " atoms")
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 == 4 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
elseif event.type == 5 then
say("#BLACKBOX : DISPLAYING SOLUTION",pname)
render_board(1)
self.remove()
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::

View File

@ -0,0 +1,38 @@
-- rnd 2017
if not s then
s=0
player0 ="";
reward = "default:gold_ingot 6"
price = "default:gold_ingot";
self.spam(1)
end
if s==0 then
local player = find_player(5);
if player then
player=player[1]
if player~=player0 then
self.label("Hello " .. player .. ". Please insert one gold ingot in chest to play.\nYou need to roll 6 on dice to win 6 gold.")
player0 = player
end
else
self.label(colorize("red","Come and win 6 gold!"))
end
if check_inventory.forward(price) then
take.forward("default:gold_ingot");
self.label("Thank you for your gold. rolling the dice!")
s=1
end
elseif s==1 then
roll = math.random(6);
if roll == 6 then
self.label("#YOU WIN!")
say("#WE HAVE A WINNER! get 6 gold in chest!")
insert.forward(reward)
s=2
else
self.label(":( you rolled " .. roll.. ". Put gold in to try again.")
s=0
end
elseif s==2 then
if not check_inventory.forward(reward) then s=0 self.label("Please insert one gold to continue playing") end
end

View File

@ -0,0 +1,60 @@
-- CONNECT, coded in 20 minutes by rnd
if not data then
m=8;n=8;turn = 0; num = 4;
self.spam(1);t0 = _G.minetest.get_gametime();
spawnpos = self.spawnpos() -- place mines
state = 0; -- 0 signup 1 game
players = {};
data = {};
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
for i = 1,m do for j = 1,n do -- render game
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
end end
get_count_in_dir = function(dir,x,y)
local r = num; -- num=4? in a row
local snode = data[x][y];local count = 1;
for j = 1,2 do
for i = 1,r-1 do
local x1 = x + dir[1]*i;local y1 = y + dir[2]*i;
if not data[x1] or not data[x1][y1] then break end; if data[x1][y1]~= snode then break end
count = count +1
end
dir[1]=-dir[1];dir[2]=-dir[2];
end
return count
end
get_count = function(x,y)
local c1 = get_count_in_dir({0,1},x,y); local c2 = get_count_in_dir({1,0},x,y)
local c3 = get_count_in_dir({1,1},x,y); local c4 = get_count_in_dir({1,-1},x,y)
if c2>c1 then c1 = c2 end; if c3>c1 then c1 = c3 end; if c4>c1 then c1 = c4 end
return c1
end
self.label("CONNECT 4 : GREEN starts play. 2 players punch to join game.")
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
elseif event.type == 2 then --if event.type == 2 then
if state == 0 then
if #players<2 then players[#players+1] = event.puncher
else state = 1 end
if #players==2 then state = 1 end
end
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4+turn);
data[x][z] = 4+turn;
if get_count(x,z) == num then say("CONGRATULATIONS! " .. event.puncher .. " has "..num .. " in a row"); self.remove(); goto END end
turn = 1-turn
if state == 1 then
local msg = ""; if turn == 0 then msg = "GREEN " else msg = "BLUE" end
self.label(msg .. " : " .. players[turn+1])
end
end
end
::END::

View File

@ -0,0 +1,95 @@
if not init then
init = true
generate_random_string = function(n,m)
local ret = {};
for i = 1,n do ret[i]=string.char(math.random(m)+96) end --24
return table.concat(ret)
end
get_similiarity = function(text1,text2)
local n = string.len(text1);
if string.len(text2)~=n then return 0 end
local ret = 0;
for i = 1,n do
if string.sub(text1,i,i) == string.sub(text2,i,i) then ret = ret + 1 end
end
return ret
end
get_form = function()
local n = #passlist;
local frm = "label[0,0;" .. intro_msg .. "] " .. "label[0,8.5;" .. msg .. "] "
for i = 1,10 do
frm = frm .. "button[0,".. (i)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] "
end
for i = 11,n do
frm = frm .. "button[2,".. (i-11+1)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] "
end
local form = "size[6," .. 9 .. "]" .. frm;
return form
end
_G.math.randomseed(os.time())
intro_msg = minetest.colorize("lawngreen","FALLOUT PASSWORD HACKING GAME\nmatch is both position and character.")
msg = "" --TEST\nTEST\nTEST";
passlist = {}; passdict = {}
n = 20; -- how many options
count = 0;
while count< n do
local pass = generate_random_string(5,5); -- password length, charset size
if not passdict[pass] then passlist[#passlist+1] = pass; passdict[pass] = true; count = count + 1 end
end
correct = math.random(n)
guesses = 0
max_guesses = 4
rom.data = {};
if not rom.data then rom.data = {} end
self.spam(1)
local players = find_player(4);
if not players then say("#fallout hacking game: no players") self.remove() end
pname = players[1];
say("#fallout hacking game, player " .. pname)
--if rom.data[pname] then say("password is locked out!") self.remove() end
self.show_form(pname,get_form())
self.read_form()
end
sender,fields = self.read_form()
if sender and sender == pname then -- form event
local pl = _G.minetest.get_player_by_name(pname);
if pl then
local selected = 0
for k,_ in pairs(fields) do if k~="quit" then selected = tonumber(k) break end end
if selected>0 then
guesses = guesses + 1
if selected == correct then
say("password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.")
self.show_form(pname, "size[1,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]")
self.remove()
--correct: do something with player
else
msg = msg .. " " .. minetest.colorize("yellow",guesses .. ". " .. passlist[selected]) .. " (" .. get_similiarity(passlist[correct], passlist[selected]) .. " match)"
self.show_form(pname, get_form())
end
if guesses>=max_guesses then
msg = minetest.colorize("red","A C C E S S D E N I E D!")
self.show_form(pname, get_form())
say("too many false guesses. password locked out!") rom.data[pname] = 1; self.remove()
end
end
if fields.quit then self.remove() end
end
end

View File

@ -0,0 +1,97 @@
-- 'hacking' game from Fallout, by rnd
if not init then
init = true
generate_random_string = function(n,m)
local ret = {};
for i = 1,n do ret[i]=string.char(math.random(m)+96) end --24
return table.concat(ret)
end
get_similiarity = function(text1,text2)
local n = string.len(text1);
if string.len(text2)~=n then return 0 end
local ret = 0;
for i = 1,n do
if string.sub(text1,i,i) == string.sub(text2,i,i) then ret = ret + 1 end
end
return ret
end
get_form = function()
local n = #passlist;
local frm = "label[0,0;" .. intro_msg .. "] " .. "label[0,8.5;" .. msg .. "] "
for i = 1,10 do
frm = frm .. "button[0,".. (i)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] "
end
for i = 11,n do
frm = frm .. "button[2,".. (i-11+1)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] "
end
local form = "size[4," .. 9 .. "]" .. frm;
return form
end
_G.math.randomseed(os.time())
intro_msg = minetest.colorize("lawngreen","FALLOUT PASSWORD HACKING GAME\nmatch is both position and character.")
msg = "" --TEST\nTEST\nTEST";
passlist = {}; passdict = {}
n = 20; -- how many options
count = 0;
while count< n do
local pass = generate_random_string(4,5); -- password length, charset size
if not passdict[pass] then passlist[#passlist+1] = pass; passdict[pass] = true; count = count + 1 end
end
correct = math.random(n)
guesses = 0
max_guesses = 4
rom.data = {};
if not rom.data then rom.data = {} end
self.spam(1)
local players = find_player(4);
if not players then say("#fallout hacking game: no players") self.remove() end
pname = players[1];
minetest.chat_send_player(pname,"#fallout hacking game, player " .. pname)
--if rom.data[pname] then say("password is locked out!") self.remove() end
self.show_form(pname,get_form())
self.read_form()
end
sender,fields = self.read_form()
if sender and sender == pname then -- form event
local pl = _G.minetest.get_player_by_name(pname);
if pl then
local selected = 0
for k,_ in pairs(fields) do if k~="quit" then selected = tonumber(k) break end end
if selected>0 then
guesses = guesses + 1
if selected == correct then
minetest.chat_send_player(pname,"password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.")
self.show_form(pname, "size[2,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]")
self.remove()
--correct: do something with player
else
if guesses == 3 then msg = msg .. "\n" end
msg = msg .. " " .. minetest.colorize("yellow",guesses .. ". " .. passlist[selected]) .. " (" .. get_similiarity(passlist[correct], passlist[selected]) .. " match)"
self.show_form(pname, get_form())
end
if guesses>=max_guesses then
msg = minetest.colorize("red","A C C E S S D E N I E D!")
self.show_form(pname, get_form())
minetest.chat_send_player(pname,"too many false guesses. password locked out!") rom.data[pname] = 1; self.remove()
end
end
if fields.quit then self.remove() end
end
end

View File

@ -0,0 +1,106 @@
--HIDE AND SEEK game robot, by rnd
if not gamemaster then
timeout = 10;
gamemaster = "rnd"
player_list = {};
_G.minetest.chat_send_all("# HIDE AND SEEK .. say #hide to join play")
s=0;t=0; count = 0;
_G.minetest.forceload_block(self.pos(),true)
self.listen(1); self.label(colorize("yellow","HIDE&SEEK"))
end
speaker,msg = self.listen_msg();
if s==0 then
if msg =="#hide" then
player_list[speaker]={};
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. speaker .. " joined the game")
local player = _G.minetest.get_player_by_name(speaker);
if player then
player:setpos({x=0,y=5,z=0});player:set_properties({nametag_color = "0x0"})
end
end
if msg == "#start" and speaker == gamemaster then s = 0.5 _G.minetest.chat_send_all("# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!") end
elseif s==0.5 then
t=t+1
if t==timeout then
t=0;s = 1; count = 0;
for pname,_ in pairs(player_list) do
local player = _G.minetest.get_player_by_name(pname);
if player then
player_list[pname].hp = player:get_hp();
player_list[pname].pos = player:getpos()
player_list[pname].t = 0;
count = count+1
end
end
if count == 1 then
gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.")
else
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS. You are out if: 1.your health changes, 2. leave spawn. If stay in same area for too long or you will be exposed."))
end
end
elseif s==1 then
players = _G.minetest.get_connected_players();
count = 0;
for _,player in pairs(players) do
local name = player:get_player_name();
local data = player_list[name];
if data then
count=count+1
local pos = player:getpos();
local dist = math.max(math.abs(pos.x),math.abs(pos.y),math.abs(pos.z));
if dist>50 or (not _G.minetest.get_player_by_name(name)) then
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " )
player:set_properties({nametag_color = "white"})
player_list[name] = nil;
end
if data.hp ~= player:get_hp() then
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" )
player:set_properties({nametag_color = "white"})
player_list[name] = nil;
end
--expose campers
local p = data.pos;
dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z));
--say( name .. " dist " .. dist .. " t " .. data.t)
if dist<8 then
data.t = data.t+1;
if not data.camp then
if data.t>15 and not data.camp then
_G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed")
data.camp = true
end
elseif data.t>=20 then
pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z);
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z)
data.camp = false; data.t = 0
end
else
data.t = 0; data.pos = player:getpos(); data.camp = false
end
end
end
self.label(count)
if count<=1 then
if count==1 then
for name,_ in pairs(player_list) do
player0=_G.minetest.get_player_by_name(name)
_G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******"))
player0:set_properties({nametag_color = "white"})
gamemaster = false;
end
else
_G.minetest.chat_send_all("# HIDE AND SEEK: no players left")
gamemaster = false;
end
end
end

View File

@ -0,0 +1,151 @@
--HIDE AND SEEK game robot
if not gamemaster then
timeout = 30;
gamemaster = "rnd"
gamepos = {x=0,y=5,z=0}
player_list = {};
s=0;t=0; count = 0;
prize = ""
get_players = function()
local msg = "";
for name,_ in pairs(player_list) do
msg = msg .. " " .. name
end
return msg
end
init_game = function()
local msg = get_players();
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK : hide from everyone else who is playing. Winner gets DIAMONDS\nsay join to join play. say #start to start game."..
" players: " .. msg))
s=0;t=0;
end
init_game()
_G.minetest.forceload_block(self.pos(),true)
self.listen(1); self.label(colorize("yellow","HIDE&SEEK"))
end
speaker,msg = self.listen_msg();
if s==0 then
t = t +1
if t%30 == 0 then
init_game();
end
if msg =="join" then
player_list[speaker]={};
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK: " .. speaker .. " joined the game"));
_G.minetest.chat_send_all("players: " .. get_players())
local player = _G.minetest.get_player_by_name(speaker);
count = count + 1
if player then
player:setpos(gamepos);player:set_properties({nametag_color = "0x0"})
player:set_hp(20)
local inv = player:get_inventory();inv:set_list("main",{})
end
end
if msg == "#start" and count>1 then s = 0.5 _G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!")) end
elseif s==0.5 then
t=t+1
if t==timeout then
t=0;s = 1; count = 0;
for pname,_ in pairs(player_list) do
local player = _G.minetest.get_player_by_name(pname);
if player then
player_list[pname].hp = player:get_hp();
player_list[pname].pos = player:getpos()
player_list[pname].t = 0;
count = count+1
end
end
if count == 1 then
gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.")
else
prize = "default:diamond " .. (count-1);
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS."..
"You are out if: 1.your health changes, 2. leave game area. If stay in same area for too long or you will be exposed."))
_G.minetest.chat_send_all(colorize("red","# WINNER WILL GET " .. prize))
end
end
elseif s==1 then
players = _G.minetest.get_connected_players();
count = 0;
for _,player in pairs(players) do
local name = player:get_player_name();
local data = player_list[name];
if data then
count=count+1
local pos = player:getpos();
local dist = math.max(math.abs(pos.x-gamepos.x),math.abs(pos.y-gamepos.y),math.abs(pos.z-gamepos.z));
if dist>100 or (not _G.minetest.get_player_by_name(name)) then
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " )
player:set_properties({nametag_color = "white"})
player_list[name] = nil;
_G.minetest.chat_send_all("remaining players: " .. get_players())
end
if data.hp ~= player:get_hp() then
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" )
player:set_properties({nametag_color = "white"})
player_list[name] = nil;
_G.minetest.chat_send_all("remaining players: " .. get_players())
player:setpos({x=0,y=5,z=0})
end
--expose campers
local p = data.pos;
dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z));
--say( name .. " dist " .. dist .. " t " .. data.t)
if dist<8 then
data.t = data.t+1;
if not data.camp then
if data.t>25 and not data.camp then
_G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed")
data.camp = true
end
elseif data.t>=30 then
pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z);
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z)
data.camp = false; data.t = 0
end
else
data.t = 0; data.pos = player:getpos(); data.camp = false
end
end
end
self.label(count)
if count<=1 then
if count==1 then
for name,_ in pairs(player_list) do
local player0=_G.minetest.get_player_by_name(name)
if player0 then
_G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******"))
local inv = player0:get_inventory();
inv:add_item("main",_G.ItemStack(prize))
player0:set_properties({nametag_color = "white"})
player0:setpos({x=0,y=5,z=0})
end
s=2
end
else
_G.minetest.chat_send_all("# HIDE AND SEEK: no players left")
s=2
end
end
elseif s==2 then
player_list = {}
init_game()
end

View File

@ -0,0 +1,138 @@
-- maze generation by rnd
-- http://en.wikipedia.org/wiki/Maze_generation_algorithm#Depth-first_search, recursive backtracker
-- representation of node coordinate (row,coloumn)=(i,j) -> (i-1)*n+j, i=1..n, j=1...m
-- representation of walls: below node k --> k, left of node k --> k+m.n
-- good overview of maze generation algorithms using javascript/html5
-- http://www.jamisbuck.org/presentations/rubyconf2011/index.html#recursive-backtracker
-- helper functions
--stack in lua
local stack={};
function stack.push(s,e) s[#s+1]=e end
function stack.pop(s) local r = s[#s];s[#s]=nil;return r end
--function table2string(s) local r = ""; for i,v in pairs(s) do r = r.. " ["..i.."]=".. v ; end return r end
function maze_deep_first_search(m,n,start,seed) -- returns a table of strings representing line renders
local steps,maxsteps; steps= 0; maxsteps = 999999;
local maze = {}
maze.m = m; maze.n = n;
maze.unvisited = {};maze.stack = {}; maze.walls = {};
maze.free = maze.m*maze.n;
local i,j,k
local nb,wall -- unvisited neighbbors, walls
--init structures
for i=1,maze.m do
for j =1,maze.n do
k=(i-1)*maze.n+j;maze.unvisited[k]=true -- initially all cells unvisited
maze.walls[k]=true;maze.walls[k+maze.n*maze.m]=true; -- walls are there
end
end
_G.math.randomseed(seed)
maze.current = start
maze.unvisited [ maze.current ] = false;
maze.free = maze.free-1; maze.stack[1+#maze.stack] = maze.current
while maze.free>0 and steps<maxsteps do -- main loop
steps=steps+1
-- check current node neighbors
k=maze.current
j = k % maze.n;i=math.ceil(k/maze.n); -- get coords back from index
if j==0 then j = maze.n end
--print("coords current node "..k .. " = " .. i .. " " ..j)
nb={};wall={}-- check unvisited neighbors & wall removals
if i>1 then -- down
k=(i-2)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k+maze.n;nb[#nb+1]=k end
end
if i<maze.m then -- up
k=(i)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k;nb[#nb+1]=k end
end
if j<maze.n then --right
k=(i-1)*maze.n+j+1; if maze.unvisited[k] then wall[#wall+1]=k+maze.n*maze.m; nb[#nb+1]=k end
end
if j>1 then --left
k=(i-1)*maze.n+j-1; if maze.unvisited[k] then wall[#wall+1]=k+1+maze.n*maze.m;nb[#nb+1]=k end
end
--print(" unvisited neighbors " .. table2string(nb))
if (#nb)>0 then -- if unvisited neighbors, choose random one as next current node
stack.push(maze.stack,maze.current) -- remember previous current node
k=math.random(#nb); -- pick random unvisited neighbor
maze.walls[wall[k]]=false; -- remove wall
--print(" removed wall ".. wall[k])
k=nb[k];
maze.current = k; -- new current cell
maze.unvisited[k]=false; maze.free = maze.free-1 -- one less unvisited
--print("new explore " .. k);
elseif (#maze.stack)~=0 then -- no unvisited neighbors, backtrack using stack
maze.current = stack.pop(maze.stack)
--print("backtrack to "..maze.current)
else -- even stack is empty, just pick random unvisited cell
k = math.random(maze.free); j=1;
for i =1,maze.m*maze.n do
if maze.unvisited[i] then
if j==k then k=i; break end -- pick node
j=j+1
end
end
--print(" stack empty, random pick " ..k)
maze.current=k;maze.unvisited[k]=false; maze.free = maze.free -1;
end
end -- of do
-- render maze with chars, row by row
maze.ret = {};
local hor;local vert;
local wall = "1"
for i=1,maze.m do
hor="";vert="";
k= (i-1)*maze.n;
-- horizontal
for j = 1, maze.n do
k=k+1;
-- if maze.walls[k+maze.n*maze.m] then vert=vert.."X." else vert=vert.. "0." end
-- if maze.walls[k] then hor=hor.."XX" else hor=hor.."X0" end
if maze.walls[k+maze.n*maze.m] then vert=vert..wall.."0" else vert=vert.. "00" end
if maze.walls[k] then hor=hor..wall..wall else hor=hor..wall.."0" end
end
maze.ret[1+#maze.ret]=hor..wall;maze.ret[1+#maze.ret]=vert..wall;
end
maze.ret[1+#maze.ret] = string.rep(wall,2*maze.n+1)
return maze.ret
end
-- RUN PROGRAM
local maze=maze_deep_first_search(10,30,1,2015)
--for _,v in pairs(maze) do print(v) end
make_maze = function(m,n,start,seed)
local pos = self.spawnpos();pos.y=pos.y+1
local p
local maze=maze_deep_first_search(m,n,start,seed) -- m,n,start,seed
local i,j,k;local p = {x=pos.x,y=pos.y,z=pos.z};
for i,v in pairs(maze) do
p.x = pos.x+i
for k = 1,string.len(v) do
p.z=pos.z+k
if string.sub(v,k,k)=="1" then
minetest.set_node(p,{name="default:brick"})
else minetest.set_node(p,{name="air"})
end
end
end
end
make_maze(10,10,1,1)
self.remove()

View File

@ -0,0 +1,89 @@
if not init then
msg =
"'Mensch argere Dich nicht' is a German board game (but not a German-style board game), developed by Josef Friedrich Schmidt in 1907/1908.\n"..
" The players throw a die in turn and can advance any of their pieces in the game by the thrown number of dots on the dice.\n" ..
"Throwing a six means bringing a piece into the game (by placing one from the 'out' area onto the 'start' field) and throwing the dice again. If\n" ..
"a piece is on the 'start' field and there are still pieces in the 'out' area, it must be moved as soon as possible. If a piece cannot be\n".. "brought into the game then any other piece in the game must be moved by the thrown number, if that is possible. Pay attention that throwing\n".." dice continuously without moving is forbidden and by each dice throw you have to make a move.\n" ..
"Pieces can jump over other pieces, and throw out pieces from other players (into that player's 'out' area) if they land on them. A player\n".. "cannot throw out his own pieces though, he can advance further than the last field in the 'home' row. A player can be thrown out if he is on\n"..
"his 'start' field.\n" ..
"Variation which is played by most players: A player who has no pieces in the game has 3 tries to throw a six"
self.label(msg)
init = true;
state = 1; -- game on
step = 0;
punchstate = 1; -- first punch
punchpos = {}
pos = self.spawnpos()
dice = 0
spawns = {{2,2,"basic_robot:buttonFF8080"},{2,11,"basic_robot:button8080FF"},{11,11,"basic_robot:button80FF80"},{11,2,"basic_robot:buttonFFFF80"}}
for i = 1,12 do for j = 1,12 do
minetest.swap_node({x=pos.x+i,y=pos.y+1,z=pos.z+j},{name = "air"})
end end
for k = 1,#spawns do
for i = 0,1 do for j = 0,1 do
minetest.swap_node({x=pos.x+i+spawns[k][1],y=pos.y+1,z=pos.z+j+spawns[k][2]},{name = spawns[k][3]})
end end
end
keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7)
end
if state == 0 then
elseif state == 1 then
event = keyboard.get();
if event then
x = event.x-pos.x; y = event.y-pos.y; z = event.z-pos.z
--say("type " .. event.type .. " pos " .. x .. " " .. y .. " " .. z)
if x == 7 and y == 1 and z == 7 then
_G.math.randomseed(os.time())
dice = math.random(6);
keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7+dice)
step = step + 1;
msg = colorize("red","<Mensch argere dich nicht>") .. " STEP " .. step .. ": " ..event.puncher .. " threw dice = " .. dice;
minetest.chat_send_all(msg)
self.label(msg)
punchstate = 1
elseif punchstate == 1 then
if y == 1 and event.type ~= 2 and event.type<7 then
punchpos = 2; minetest.chat_send_player(event.puncher,colorize("red","<Mensch argere dich nicht>") .. " punch place on board where to move ")
punchpos = {x=event.x,y=event.y,z=event.z}
punchstate = 2
end
elseif punchstate == 2 then
if y == 0 and event.type ~= 2 then
if x<2 or x>12 or z<2 or z>12 then
else
local nodename = minetest.get_node(punchpos).name;
minetest.swap_node({x=event.x, y = event.y+1, z=event.z},{name = nodename})
minetest.swap_node(punchpos,{name = "air"})
punchstate = 1; dice = 0
minetest.add_particle(
{
pos = punchpos,
expirationtime = 15,
velocity = {x=0, y=0,z=0},
size = 18,
texture = "default_apple.png",
acceleration = {x=0,y=0,z=0},
collisiondetection = true,
collision_removal = true,
}
)
msg = colorize("red","<Mensch argere dich nicht>") .. " " .. event.puncher .. " moved.";
minetest.chat_send_all(msg)
self.label(msg)
end
end
end
end
end

View File

@ -0,0 +1,83 @@
-- minesweeper
if not data then
m=24;n=22; minescount = m*n/5;
reward = 30;
if not find_player(4) then error("minesweeper: no players near") end
self.spam(1)
t0 = _G.minetest.get_gametime();
data = {}; spawnpos = self.spawnpos() -- place mines
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
minescount = 0;
for i = 1,m do for j = 1,n do -- render game
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
puzzle.set_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},{name = "basic_robot:button808080"})
end
end end
puzzle.set_node({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},{name = "basic_robot:button80FF80"})
get_mine_count = function(i,j)
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
for k = -1,1 do for l = -1,1 do
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
end end
return count
end
chk_mines = function()
local count = minescount;
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" and data[i] and data[i][j]==1 then
count=count-1
end
end end
return count
end
say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ")
self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).")
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 x == 0 and z == 1 then
local count = chk_mines();
if count == 0 then
t0 = _G.minetest.get_gametime() - t0;
say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s")
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward
else
reward = reward*(1-(count/minescount))^(1.5); reward = math.floor(reward);
say("FAIL! " .. count .. " mines remaining. You get " .. reward .. " gold for found mines")
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward
end
self.remove()
end
else --if event.type == 2 then
local ppos = player.getpos(event.puncher)
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:button808080"})
else
puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonFF8080"})
end
else
if data[x] and data[x][z]==1 then
say("boom! "..event.puncher .. " is dead ");puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:buttonFF8080"});
local player_ = puzzle.get_player(event.puncher);
player_:setpos({x=spawnpos.x-1,y=spawnpos.y+1,z=spawnpos.z-1});
self.remove()
else
local count = get_mine_count(x,z);
if count == 0 then puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button80FF80"})
else puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button"..count}) end
end
end
end
end

264
scripts/games/nonogram.lua Normal file
View File

@ -0,0 +1,264 @@
-- nonogram game, created in 1hr 40 min by rnd
-- INIT
if not grid then
n=6
solved = false -- do we render solution or blank?
-- _G.math.randomseed(3)
self.spam(1)
function get_score_from_string(score)
--say(score)
local scores = {};
local j=1; --j k l
for i=0,5 do -- 0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999
j = string.find(score," ", j+1);
local k = string.find(score," ", j+1);
local l = string.find(score," ", k+1);
if i==5 then l = string.len(score)+1 end
scores[i] = {string.sub(score,j+1,k-1),tonumber(string.sub(score,k+1,l-1))};
j=l
end
return scores
end
if not rom.score then _,rom.score = book.read(1) end
if not rom.score then rom.score = "0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999" end
highscore = get_score_from_string(rom.score)
--self.label(string.gsub(_G.dump(highscore), "\n",""))
function get_score_string(scores)
local out = ""
for i = 0,5 do
out = out .. i .. " " ..
scores[i][1] .. " " ..
scores[i][2] .. " "
end
return out
end
t0 = _G.minetest.get_gametime()
local intro ="numbers at beginning of each row (coloumn) tell how many\nred blocks are together in each row ( coloumn )." ..
"\npunch gray blocks to toggle them and reveal hidden red blocks.\npunch green to check solution. If you give up punch blue.";
self.label(intro)
grid = {}
spawnpos = self.spawnpos();
offsetx = 10 - math.ceil(n/2); offsetz = math.floor(n/2);
spawnpos.x = spawnpos.x - offsetx; spawnpos.z = spawnpos.z - offsetz;
spawnpos.y = spawnpos.y+3
for i=1,n do
grid[i]={};
for j=1,n do
grid[i][j]=math.random(2)-1
end
end
getcounts = function(grid)
local rowdata = {};
for i=1,n do
rowdata[i]={}; local data = rowdata[i];
local s=0;local c=0;
for j = 1, n do
if s == 0 and grid[i][j]==1 then s=1;c=0 end
if s == 1 then
if grid[i][j]==1 then
c=c+1
if j == n then data[#data+1]=c end
else
data[#data+1]=c; s=0
end
end
end
end
local coldata = {};
for j=1,n do
coldata[j]={}; local data = coldata[j];
local s=0;local c=0;
for i = 1, n do
if s == 0 and grid[i][j]==1 then s=1;c=0 end
if s == 1 then
if grid[i][j]==1 then
c=c+1
if i == n then data[#data+1]=c end
else
data[#data+1]=c; s=0
end
end
end
end
return rowdata,coldata
end
read_field = function()
local grid = {};
for i = 1, n do
grid[i]={};
for j = 1,n do
local typ = keyboard.read({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+i});
if typ == "basic_robot:button808080" then grid[i][j] = 0 else grid[i][j] = 1 end
end
end
return grid
end
rowdata,coldata = getcounts(grid)
check_solution = function()
local rdata,cdata;
rdata,cdata = getcounts(read_field())
for i = 1,#rdata do
if #rdata[i]~=#rowdata[i] then return false end
for j = 1, #rdata[i] do
if rdata[i][j]~=rowdata[i][j] then return false end
end
end
for i = 1,#cdata do
if #cdata[i]~=#coldata[i] then return false end
for j = 1, #rdata[i] do
if cdata[i][j]~=coldata[i][j] then return false end
end
end
return true
end
get_difficulty = function()
local easy = 0;
for k = 1, n do
local sum=0
for i = 1,#rowdata[k]-1 do
sum = sum + rowdata[k][i]+1;
end
if #rowdata[k]>0 then sum = sum + rowdata[k][#rowdata[k]] else sum = n end
if sum == n then easy = easy + 1 end
end
for k = 1, n do
local sum=0
for i = 1,#coldata[k]-1 do
sum = sum + coldata[k][i]+1;
end
if #coldata[k]>0 then sum = sum + coldata[k][#coldata[k]] else sum = n end
if sum == n then easy = easy + 1 end
end
easy = 5-easy;
if easy < 0 then easy = 0 end
return easy
end
-- render game
for i=1,n do
for j =1,n do
keyboard.set({x=spawnpos.x-n+j,y=spawnpos.y,z=spawnpos.z+i},0) -- clear
keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+2*n-i+1},0) -- clear
local typ;
if grid[j][i]==0 then typ = 2 else typ = 3 end
if not solved then typ = 2 end
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) --board
end
end
--render counts rows
for i=1,n do
length = #rowdata[i]
for k = 1,length do
keyboard.set({x=spawnpos.x-length+k,y=spawnpos.y,z=spawnpos.z+i},rowdata[i][k]+7)
end
end
--render counts coloumns
for j=1,n do
length = #coldata[j]
for k = 1,length do
keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+k+n},coldata[j][k]+7)
end
end
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},4) -- game check button
keyboard.set({x=spawnpos.x+2,y=spawnpos.y,z=spawnpos.z},5) -- game check button
local players = find_player(4,spawnpos)
if not players then error("minesweeper: no players near") end
local pname = players[1];
--self.label()
--self.label(string.gsub(_G.dump(read_field()),"\n","") )
difficulty = get_difficulty()
reward = 0; limit = 0;
if difficulty == 5 then limit = 120 reward = 10
elseif difficulty == 4 then limit = 115 reward = 9 -- 60s
elseif difficulty == 3 then limit = 100 reward = 8
elseif difficulty == 2 then limit = 80 reward = 7
elseif difficulty <= 1 then limit = 70 reward = 6
end
minetest.chat_send_player(pname, "nonogram difficulty " .. difficulty .. ". you will get " .. reward .. " gold if you solve it in faster than " .. limit .."s" ..
". Current record " .. highscore[difficulty][2] .. " by " .. highscore[difficulty][1])
end
event = keyboard.get()
if event then
if event.y == spawnpos.y and event.z == spawnpos.z then
if event.x == spawnpos.x+1 then -- check solution
if check_solution() then
t = _G.minetest.get_gametime(); t = t- t0;
local msg = "";
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},2)
msg = n .. "x" .. n .. " nonogram (difficuly " .. difficulty .. ") solved by " .. event.puncher .. " in " .. t .. " seconds. "
if t < limit then
msg = msg .. " He gets " .. reward .. " gold for quick solve.";
else
reward = reward*2*(1-2*(t-limit)/limit)/2; if reward<0 then reward = 0 end
reward = math.floor(reward);
msg = msg .. " Your time was more than " .. limit .. ", you get " .. reward .. " gold ";
end
-- highscore
if t<highscore[difficulty][2] then
say("nonogram: new record " .. t .. " s ! old record " .. highscore[difficulty][2] .. "s by " .. highscore[difficulty][1])
highscore[difficulty] = {event.puncher, t}
rom.score = get_score_string(highscore)
book.write(1,"scores", rom.score)
end
if reward>0 then
local player = _G.minetest.get_player_by_name(event.puncher);
if player then
local inv = player:get_inventory();
inv:add_item("main",_G.ItemStack("default:gold_ingot " .. reward))
end
end
minetest.chat_send_player(event.puncher,msg)
self.remove()
else self.label("FAIL") end
elseif event.x == spawnpos.x+2 then -- solve
minetest.chat_send_player(event.puncher,"you gave up on game, displaying solution")
for i=1,n do
for j =1,n do
local typ;
if grid[j][i]==0 then typ = 2 else typ = 3 end
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ)
end
end
self.remove()
end
else
local i = event.x-spawnpos.x;local j = event.z-spawnpos.z;
if i>0 and i<=n and j>0 and j<=n then
local typ = keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j});
local newtyp;
if typ == "basic_robot:button808080" then newtyp = 3
else newtyp = 2
end
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},newtyp);
end
end
end

View File

@ -0,0 +1,112 @@
-- paint canvas by rnd, 2018
if not init then
colors = {
"black","blue","brown","cyan","dark_green","dark_grey","green","grey",
"magenta","orange","pink","red","violet","white","yellow"
}
invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end
color = 1;
size = 16;
init = true
local ent = _G.basic_robot.data[self.name()].obj:get_luaentity();
ent.timestep = 0.5
players = find_player(5); if not players then self.remove() end
player = _G.minetest.get_player_by_name(players[1])
self.label("-> " .. players[1])
spos = self.spawnpos(); spos.y=spos.y+1;
canvasn = "wool:white"
reset_canvas = function()
for i = 1, size do
for j = 1, size do
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn})
end
end
end
reset_canvas()
save_image = function()
local ret = {};
for i = 1, size do
for j = 1, size do
local nname = string.sub(minetest.get_node({x=spos.x +i , y = spos.y + j, z = spos.z }).name,6)
local pcolor = invcolors[nname] or 1;
ret[#ret+1]= string.char(96+pcolor)
end
end
return table.concat(ret,"")
end
load_image = function(image)
if not image then return end
local ret = {}; local k = 0;
for i = 1, size do
for j = 1, size do
k=k+1;
local pcolor = colors[string.byte(image,k)-96] or "black";
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "wool:"..pcolor})
end
end
end
--draw buttons
for i = 1,#colors do
minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]})
end
minetest.set_node({x=spos.x +1 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_83"})
minetest.set_node({x=spos.x +2 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_76"})
vn = {x=0,y=0,z=1};
T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z};
get_intersect = function(vn, T0, p, v)
local a = (T0.x-p.x)*vn.x + (T0.y-p.y)*vn.y + (T0.z-p.z)*vn.z;
local b = vn.x*v.x + vn.y*v.y + vn.z*v.z
if b<=0 then return nil end
if a<=0 then return nil end
local t = a / b
return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t}
end
end
if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui'
local v = player:get_look_dir();
local p = player:get_pos(); p.y = p.y + 1.5
local c = get_intersect(vn,T0,p,v);
if c then
local x = c.x - T0.x; local y = c.y - T0.y
if x>0 and x<size and y>-2 and y<size then
if y>0 then -- above: painting
c.z = c.z+0.5
minetest.set_node(c, {name = "wool:" .. colors[color]})
elseif y>-1 then -- color selection
x = 1+math.floor(x)
if colors[x] then
color = x;
self.label(colors[x])
end
else -- save,load button row
x = 1+math.floor(x)
if x==1 then
self.label("SAVED.")
book.write(1,"ROBOT_IMAGE",save_image())
elseif x==2 then
local _,image = book.read(1)
load_image(image);
self.label("LOADED.")
end
end
end
end
end

View File

@ -0,0 +1,101 @@
-- sliding unscramble game by rnd, made in 20 minutes
if not init then
reward = "default:gold_ingot"
size = 3;
init = true
spos = self.spawnpos(); spos.y = spos.y + 1
board = {};
local players = find_player(4);
if not players then say("#sliding puzzle game: no players") self.remove() end
name = players[1];
minetest.chat_send_player(name, "#SLIDING GAME: try to sort numbers in increasing order, starting from top left")
create_board = function(n)
local k = 0;
local ret = scramble(n*n, os.time())
for i = 1,n do
board[i]={};
for j = 1,n do
k=k+1
board[i][j]=7+ret[k] -- 7 numbers, 82 letters
end
end
board[math.random(n)][math.random(n)] = 0
end
render_board = function()
local n = #board;
for i = 1,n do
for j = 1,n do
keyboard.set({x=spos.x+i, y = spos.y, z=spos.z+j}, board[i][j])
end
end
end
check_score = function() -- check how many places are increasing in order, starting top left
local n = #board;
local cmax = 0;
local score = 0;
for j = n,1,-1 do
for i = 1,n do
local b = board[i][j];
if b==0 or b<cmax then return score else score = score +1 cmax = b end
end
end
end
find_hole = function(i,j)
if board[i][j] == 0 then return i,j end
if i>1 and board[i-1][j] == 0 then return i-1,j end
if i<#board and board[i+1][j] == 0 then return i+1,j end
if j>1 and board[i][j-1] == 0 then return i,j-1 end
if j<#board and board[i][j+1] == 0 then return i,j+1 end
return nil
end
scramble = function(n,seed)
_G.math.randomseed(seed);
local ret = {}; for i = 1,n do ret[i]=i end
for j = n,2,-1 do
local k = math.random(j);
if k~=j then
local tmp = ret[k]; ret[k] = ret[j]; ret[j] = tmp
end
end
return ret
end
create_board(size)
render_board()
end
event = keyboard.get();
if event and event.y == spos.y then
local x = event.x-spos.x;
local z = event.z-spos.z;
if x<1 or x>size or z<1 or z>size then
else
local i,j = find_hole(x,z);
if i then
local tmp = board[x][z];
keyboard.set({x=spos.x+x, y = spos.y, z=spos.z+z}, board[i][j])
board[x][z] = board[i][j]
board[i][j] = tmp;
keyboard.set({x=spos.x+i, y = spos.y, z=spos.z+j}, tmp)
end
local score = check_score()
self.label("score : " .. score)
if score >= size*size-2 then
minetest.chat_send_player(name, "CONGRATULATIONS! YOU SOLVED PUZZLE. REWARD WAS DROPPED ON TOP OF ROBOT.")
pos = self.pos(); pos.y = pos.y+2;
minetest.add_item(pos, _G.ItemStack(reward))
self.remove()
end
end
end

View File

@ -0,0 +1,189 @@
-- SOKOBAN GAME, by rnd, robots port
if not sokoban then
sokoban = {};
local players = find_player(8);
if not players then error("sokoban: no player near") end
name = players[1];
-- self.show_form(name,
-- "size[2,1.25]"..
-- "label[0,0;SELECT LEVEL 1-90]"..
-- "field[0.25,1;1,1;LVL;LEVEL;1]"..
-- "button_exit[1.25,0.75;1,1;OK;OK]"
-- )
state = 1 -- will wait for form receive otherwise game play
self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks")
player_ = puzzle.get_player(name); -- get player entity - player must be present in area
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}) -- reset player
self.spam(1)
sokoban.push_time = 0
sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0;
imax = 0; jmax = 0
sokoban.load=0;sokoban.playername =""; sokoban.pos = {};
SOKOBAN_WALL = "moreblocks:cactus_brick"
SOKOBAN_FLOOR = "default:silver_sandstone"
SOKOBAN_GOAL = "default:aspen_tree"
SOKOBAN_BOX = "basic_robot:buttonFFFFFF"
load_level = function(lvl)
local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1;
sokoban.pos = pos;
sokoban.playername = name
if lvl == nil then return end
if lvl <0 or lvl >89 then return end
local file = _G.io.open(minetest.get_modpath("basic_robot").."\\scripts\\sokoban.txt","r")
if not file then return end
local str = ""; local s; local p = {x=pos.x,y=pos.y,z=pos.z}; local i,j;i=0;
local lvl_found = false
while str~= nil do
str = file:read("*line");
if str~=nil and str =="; "..lvl then lvl_found=true break end
end
if not lvl_found then file:close();return end
sokoban.blocks = 0;sokoban.level = lvl+1; sokoban.moves=0;
imax=0; jmax = 0;
while str~= nil do
str = file:read("*line");
if str~=nil then
if string.sub(str,1,1)==";" then
imax=i;
file:close();
player_:set_physics_override({jump=0})
player_:set_eye_offset({x=0,y=20,z=0},{x=0,y=0,z=0});
return
end
i=i+1;
if string.len(str)>jmax then jmax = string.len(str) end -- determine max dimensions
for j = 1,string.len(str) do
p.x=pos.x+i;p.y=pos.y; p.z=pos.z+j; s=string.sub(str,j,j);
p.y=p.y-1;
if puzzle.get_node(p).name~=SOKOBAN_FLOOR then puzzle.set_node(p,{name=SOKOBAN_FLOOR}); end -- clear floor
p.y=p.y+1;
if s==" " and puzzle.get_node(p).name~="air" then puzzle.set_node(p,{name="air"}) end
if s=="#" and puzzle.get_node(p).name~=SOKOBAN_WALL then puzzle.set_node(p,{name=SOKOBAN_WALL}) end
if s=="$" then puzzle.set_node(p,{name=SOKOBAN_BOX});sokoban.blocks=sokoban.blocks+1 end
if s=="." then p.y=p.y-1;puzzle.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;puzzle.set_node(p,{name="air"}) end
--starting position
if s=="@" then
player_:setpos({x=p.x,y=p.y-0.5,z=p.z}); -- move player to start position
--p.y=p.y-1;puzzle.set_node(p,{name="default:glass"});
puzzle.set_node(p,{name="air"})
p.y=p.y+1;puzzle.set_node(p,{name="air"})
--p.y=p.y+2;puzzle.set_node(p,{name="default:ladder"})
end
if s~="@" then p.y = pos.y+2;puzzle.set_node(p,{name="air"}); -- ceiling default:obsidian_glass
else --p.y=pos.y+2;puzzle.set_node(p,{name="default:ladder"})
end -- roof above to block jumps
end
end
end
file:close();
end
clear_game = function()
local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1;
for i = 1, 20 do
for j = 1,20 do
local node = minetest.get_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}).name
if node ~= "default:silver_sandstone" then minetest.set_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}, {name = "default:silver_sandstone"}) end
node = minetest.get_node({x=pos.x+i,y=pos.y,z=pos.z+j}).name
if node ~= "air" then minetest.set_node({x=pos.x+i,y=pos.y,z=pos.z+j}, {name = "air"}) end
end
end
end
end
if state == 1 then
clear_game()
load_level(20)
state = 0
self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks")
else
local ppos = player_:getpos()
if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then minetest.chat_send_player(name,colorize("red", "SOKOBAN: " .. name .. " QUITS ! "));
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}); self.remove() end
event = keyboard.get();
if event then
local pname = event.puncher
if pname ~= name then goto quit end
local pos = {x=event.x, y = event.y, z = event.z};
local p=player.getpos(pname);local q={x=pos.x,y=pos.y,z=pos.z}
p.x=p.x-q.x;p.y=p.y-q.y;p.z=p.z-q.z
if math.abs(p.y+0.5)>0 then goto quit end
if math.abs(p.x)>math.abs(p.z) then -- determine push direction
if p.z<-0.5 or p.z>0.5 or math.abs(p.x)>1.5 then goto quit end
if p.x+q.x>q.x then q.x= q.x-1
else q.x = q.x+1
end
else
if p.x<-0.5 or p.x>0.5 or math.abs(p.z)>1.5 then goto quit end
if p.z+q.z>q.z then q.z= q.z-1
else q.z = q.z+1
end
end
if minetest.get_node(q).name=="air" then -- push crate
sokoban.moves = sokoban.moves+1
local old_infotext = minetest.get_meta(pos):get_string("infotext");
minetest.set_node(pos,{name="air"})
minetest.set_node(q,{name=SOKOBAN_BOX})
minetest.sound_play("default_dig_dig_immediate", {pos=q,gain=1.0,max_hear_distance = 24,}) -- sound of pushing
local meta = minetest.get_meta(q);
q.y=q.y-1;
if minetest.get_node(q).name==SOKOBAN_GOAL then
if old_infotext~="GOAL REACHED" then
sokoban.blocks = sokoban.blocks -1;
end
meta:set_string("infotext", "GOAL REACHED")
else
if old_infotext=="GOAL REACHED" then
sokoban.blocks = sokoban.blocks +1
end
--meta:set_string("infotext", "push crate on top of goal block")
end
end
if sokoban.blocks~=0 then -- how many blocks left
--say("move " .. sokoban.moves .. " : " ..sokoban.blocks .. " crates left ");
else
say("games: ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves.");
player_:set_physics_override({jump=1})
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})
local player = _G.minetest.get_player_by_name(event.puncher);
if player then
local inv = player:get_inventory();
inv:add_item("main",_G.ItemStack("skyblock:sokoban 4 "))
end
local i,j;
for i = 1,imax do
for j=1,jmax do
minetest.set_node({x= sokoban.pos.x+i,y=sokoban.pos.y,z=sokoban.pos.z+j}, {name = "air"}); -- clear level
end
end
sokoban.playername = ""; sokoban.level = 1
end
::quit::
end
end

View File

@ -0,0 +1,101 @@
--[[
SWITCHING GAME by rnd, 2018
lights:
0110
switches, each one toggles certain lights like: s1 1001 (toggles light with 1)
PROBLEM:
hit switches in correct order to turn on all lights
GENERATE RANDOM CHALLENGE:
start with all lights on and apply random sequence of switches
TODO: instead of simply 0/1 switches have ones that advance +1 mod p (p can be say 3 or more)
REMARKS: application of 2 different switches is commutative ( obvious, since just x->x+1 mod p)
--]]
if not init then
init = true
numlights = 2;
numswitches = 2;
states = 10;
lights = {}; -- states of lights, initialy 1,1,...,1
for i = 1, numlights do lights[i] = 0 end
switches = {}
--switches = {{1,0,0,1},{1,1,1,1}};
make_random_switches = function(lights, switches,count)
for i = 1, count do
switches[i] = {};
local switch = switches[i];
for j = 1, #lights do switch[j] = math.random(states)-1 end
end
end
make_random_switches(lights,switches, numswitches)
pos = self.spawnpos(); pos.x = pos.x + 1;-- pos.z = pos.z + 1
apply_switch = function(switches,lights,idx)
local switch = switches[idx];
for i = 1, #switch do
local state = lights[i] + switch[i];
if state >= states then state = state - states end
lights[i] = state
end
end
randomize = function(switches, lights, steps) -- randomize lights
for i = 1, steps do
local idx = math.random(#switches);
apply_switch(switches,lights,idx);
end
end
render_lights = function() for i = 1, #lights do keyboard.set({x=pos.x+i-1,y=pos.y+1, z=pos.z}, 7+lights[i]) end end
render_switches = function(mode)
if mode then
for i = 1, #switches do keyboard.set({x=pos.x+i-1,y=pos.y, z=pos.z}, 1+i) end
else
for i = 1, #switches do keyboard.set({x=pos.x+i-1,y=pos.y, z=pos.z}, 0) end
end
end
check_lights = function()
for i = 1, #lights do if lights[i] ~= 0 then return false end end
return true
end
step = 0
randomize(switches,lights, math.min((#switches)^states,10000))
if check_lights() then randomize(switches,lights, #switches + states) end
render_lights(); render_switches(true)
self.label("GOAL OF GAME: punch buttons with numbers in correct order to turn all blocks to 0")
--self.label(serialize(switches))
end
event = keyboard.get()
if event then
local idx = event.x-pos.x+1;
if event.y==pos.y and idx>=1 and idx <= #switches then
apply_switch(switches, lights, idx)
render_lights()
step = step + 1
if check_lights() then
self.label("DONE IN " .. step .. " STEPS !")
render_switches(false)
else
self.label("STEP " .. step)
end
end
end

View File

@ -0,0 +1,85 @@
-- rnd 2017
-- instructions: put 7 buttons around bot(top one left empty)
-- clockwise: empty, green, yellow,blue, red, blue,yellow,green.
-- those buttons serve as controls
if not s then
name = self.name();
direction = 1;
s=0;
self.label("TANK ROBOT. control with colored buttons")
user=find_player(4); if user then user = user[1] end
speed = 7 + math.random(7);turn.angle(math.random(360));
pitch = 0
gravity = 1+math.random(2);
if user then
say("TANK ROBOT, ready. ".. user .. " in control")
else
say("no player found nearby. deactivating"); self.remove()
s=-1
end
pos = self.spawnpos();
end
ppos = player.getpos(user); ppos.x=ppos.x-pos.x;ppos.y=ppos.y-pos.y;ppos.z=ppos.z-pos.z;
if ppos.x^2+ppos.y^2+ppos.z^2>10 then
local obj = _G.minetest.get_player_by_name(user);
if obj then say("deserter " .. user .. " killed for abandoning the tank!") obj:set_hp(0) end
self.remove()
else
local obj = _G.minetest.get_player_by_name(user);
if obj then
if obj:get_hp() == 0 then
say("TANK DESTROYED!")
self.remove()
end
end
end
if s == 0 then
event = keyboard.get();
if event and event.puncher==user then
--self.label(event.x-pos.x .. " " .. event.y-pos.y .. " " .. event.z-pos.z .. " T " .. event.type)
event.x = event.x-pos.x;event.y = event.y-pos.y;event.z = event.z-pos.z;
if event.x == 0 and event.y == 0 and event.z == 1 then
self.fire(speed, pitch,gravity)
s=1;self.label("BOOM")
_G.minetest.sound_play("tnt_explode",{pos = self.pos(), max_hear_distance = 256, gain = 1})
elseif event.x == direction*1 and event.y == 0 and event.z == direction*1 then
turn.angle(2)
elseif event.x == -1*direction and event.y == 0 and event.z == 1*direction then
turn.angle(-2)
elseif event.x == 1*direction and event.y == 0 and event.z == 0 then
turn.angle(40)
elseif event.x == -1*direction and event.y == 0 and event.z == 0 then
turn.angle(-40)
elseif event.x == 1*direction and event.y == 0 and event.z == -1*direction then
pitch = pitch + 5; if pitch> 85 then pitch = 85 end
self.label("pitch " .. pitch)
elseif event.x == -1*direction and event.y == 0 and event.z == -1*direction then
pitch = pitch - 5; if pitch<-10 then pitch = -10 end
self.label("pitch " .. pitch)
end
end
end
if s == 1 then
local pos = self.fire_pos();
if pos then
self.label("HIT")
msg = "";
_G.minetest.sound_play("tnt_explode",{pos = pos, max_hear_distance = 256, gain = 1})
local objs=_G.minetest.get_objects_inside_radius(pos, 4);
for _,obj in pairs(objs) do
if obj:is_player() then
obj:set_hp(0)
msg = msg .. obj:get_player_name() .. " is dead, "
end
end
s = 0
if msg~="" then say(msg) end
end
end

View File

@ -0,0 +1,112 @@
-- paint canvas by rnd, 2018
if not init then
colors = {
"black","blue","brown","cyan","dark_green","dark_grey","green","grey",
"magenta","orange","pink","red","violet","white","yellow"
}
invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end
color = 1;
size = 16;
init = true
local ent = _G.basic_robot.data[self.name()].obj:get_luaentity();
ent.timestep = 0.5
players = find_player(5); if not players then self.remove() end
player = _G.minetest.get_player_by_name(players[1])
self.label("-> " .. players[1])
spos = self.spawnpos(); spos.y=spos.y+1;
canvasn = "wool:white"
reset_canvas = function()
for i = 1, size do
for j = 1, size do
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn})
end
end
end
reset_canvas()
save_image = function()
local ret = {};
for i = 1, size do
for j = 1, size do
local nname = string.sub(minetest.get_node({x=spos.x +i , y = spos.y + j, z = spos.z }).name,6)
local pcolor = invcolors[nname] or 1;
ret[#ret+1]= string.char(96+pcolor)
end
end
return table.concat(ret,"")
end
load_image = function(image)
if not image then return end
local ret = {}; local k = 0;
for i = 1, size do
for j = 1, size do
k=k+1;
local pcolor = colors[string.byte(image,k)-96] or "black";
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "wool:"..pcolor})
end
end
end
--draw buttons
for i = 1,#colors do
minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]})
end
minetest.set_node({x=spos.x +1 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_83"})
minetest.set_node({x=spos.x +2 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_76"})
vn = {x=0,y=0,z=1};
T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z};
get_intersect = function(vn, T0, p, v)
local a = (T0.x-p.x)*vn.x + (T0.y-p.y)*vn.y + (T0.z-p.z)*vn.z;
local b = vn.x*v.x + vn.y*v.y + vn.z*v.z
if b<=0 then return nil end
if a<=0 then return nil end
local t = a / b
return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t}
end
end
if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui'
local v = player:get_look_dir();
local p = player:get_pos(); p.y = p.y + 1.5
local c = get_intersect(vn,T0,p,v);
if c then
local x = c.x - T0.x; local y = c.y - T0.y
if x>0 and x<size and y>-2 and y<size then
if y>0 then -- above: painting
c.z = c.z+0.5
minetest.set_node(c, {name = "wool:" .. colors[color]})
elseif y>-1 then -- color selection
x = 1+math.floor(x)
if colors[x] then
color = x;
self.label(colors[x])
end
else -- save,load button row
x = 1+math.floor(x)
if x==1 then
self.label("SAVED.")
book.write(1,"ROBOT_IMAGE",save_image())
elseif x==2 then
local _,image = book.read(1)
load_image(image);
self.label("LOADED.")
end
end
end
end
end

View File

@ -0,0 +1,58 @@
-- painting import from minetest 'painting mod' to robot canvas by rnd
-- stand near image and run "get_texture()" command in remote control
if not init then
self.label("PAINTING IMPORTER")
pname = "rnd"
player = minetest.get_player_by_name(pname)
get_texture = function()
local pos = player:get_pos(); local radius = 2
local objs = minetest.get_objects_inside_radius(pos, radius)
local obj = {};
local ret = {};
for i=1,#objs do
if not objs[i]:is_player() then obj = objs[i] break end
end
if obj then
local tex = obj:get_properties().textures
local out = tex[1] or ""
if string.sub(out,1,9) == "[combine:" then
local pcolors = {"black","blue","brown","cyan","darkgreen","darkgrey","green","grey",
"magenta","orange","pink","red","violet","white","yellow"}
local ipcolors = {}; for i = 1,#pcolors do ipcolors[pcolors[i]] = i end
local ret = {};
local i =0; local j = 1; local k = 0; local size = 16;
--ret[1] = {}
for word in out:gmatch("=(%a+)%.png") do
ret[#ret+1] = string.char(96 + (ipcolors[word] or 1))
end
local rret = {};
for i = 1, size do rret[i] = {} for j = 1,size do rret[i][j] = 0 end end
k = 0 -- rotate 90 right
for j = 1,size do
for i = size,1,-1 do
k = k + 1
rret[size-i+1][size-j+1] = ret[k]
end
end
ret = {}; for i = 1, size do for j = 1, size do ret[#ret+1]= rret[i][j] end end -- write back
out = table.concat(ret,"")
book.write(1,"IMPORTED_PAINTING", out)
minetest.chat_send_player(pname, "PAINTING FOUND, saved in robot library in book 1.")
end
else return "empty"
end
end
init = true
end

108
scripts/gui/craft_guide.lua Normal file
View File

@ -0,0 +1,108 @@
-- ROBOT craft guide by rnd, 2017
if not list then
tname = "rnd";
list = {};
tmplist = _G.minetest.registered_items;
for k,v in pairs(tmplist) do
local texture = v.inventory_image or "";
if texture=="" and v.tiles then texture = v.tiles[1] or "" end
if (not v.groups.not_in_craft_guide or v.groups.not_in_craft_guide == 0) and type(texture)=="string" and texture~="" then
list[#list+1] = {_G.minetest.formspec_escape(k),_G.minetest.formspec_escape(v.description),_G.minetest.formspec_escape(texture)}; -- v.inventory_image, k, v.description
end
end
idx = 1; n = 35; row = 6; size = 1.25;
filter = "" item = "" recipeid = 1
filterlist = {}; for i = 1,#list do filterlist[i] = i end
get_texture = function(ritem)
local v = _G.minetest.registered_items[ritem]; if not v then return "" end
local texture = v.inventory_image or "";
if texture=="" and v.tiles then texture = v.tiles[1] or "" end
if type(texture)~="string" then return "" end
return texture
end
get_form = function()
local form = "size[7.5,8.5]";
local x,y,i; local idxt = idx+n; if idxt > #filterlist then idxt = #filterlist end
for i = idx, idxt do
local id = filterlist[i];
if list[id] and list[id][3] then
x = ((i-idx) % row)
y = (i-idx-x)/row;
form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. list[id][3] ..";".."item"..";".. list[id][1] .."]"
end
end
form = form .. "textarea[0.25,0;2,0.75;filter;filter;"..filter .. "]" .. "button[2.,0;1,0.5;search;search]"..
"button[5.5,0;1,0.5;prev;PREV]" .. "button[6.5,0;1,0.5;next;NEXT]" .. "label[4,0;".. idx .. "-"..idxt .. "/" .. #filterlist.."]";
return form
end
get_recipe = function()
local form = "size[7.5,8.5]";
local recipes = _G.minetest.get_all_craft_recipes(item); if not recipes then return end;
local recipe = recipes[recipeid]; if not recipe then return end
local items = recipe.items
local x,y,i;
for i = 0, 8 do
local ritem = items[i+1] or ""; local sritem = "";
local j = string.find(ritem,":"); if j then sritem = string.sub(ritem,j+1) end; --ritem = _G.minetest.formspec_escape(ritem);
x = (i % 3)
y = (i-x)/3;
form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. get_texture(ritem) ..";".."item"..";".. sritem .."]"
end
form = form .. "textarea[0.25,0;2,0.75;recipeid;recipeid ".. #recipes .. ";"..recipeid .. "]" .. "button[2.,0;1,0.5;go;go]"..
"label[3,0;" .. item .. "]" .. "button[6.5,0;1,0.5;back;BACK]" ;
return form
end
s=0
end
if s==0 then
local p = find_player(4); s = 1
if p then
self.show_form(p[1],get_form())
else
self.remove()
end
end
sender,fields = self.read_form()
if sender then
if fields.search then
filter = fields.filter or ""
filterlist = {};
for i = 1,#list do
if string.find(list[i][1],filter) then filterlist[#filterlist+1] = i end
end
idx=1;self.show_form(sender,get_form())
elseif fields.prev then
idx = idx - n; if idx<1 then idx =#filterlist-n end
self.show_form(sender,get_form())
elseif fields.next then
idx = idx+n; if idx > #filterlist then idx = 1 end
self.show_form(sender,get_form())
elseif fields.back then
self.show_form(sender,get_form())
elseif fields.recipeid then
recipeid = tonumber(fields.recipeid) or 1;
self.show_form(sender,get_recipe())
elseif fields.item then
item = fields.item;
local recipes = _G.minetest.get_all_craft_recipes(item);
local count = 0; if recipes then count = #recipes end
if count>0 then
recipeid = 1
self.show_form(sender,get_recipe() or "")
end
elseif fields.quit then
self.remove()
end
end

View File

@ -0,0 +1,57 @@
-- file 'manager' by rnd
if not init then
fmver = "2018/12/09"
local players = find_player(4);
if not players then self.remove() end
pname = players[1];
size = 8;
vsize = 6.5;
path = "/";
pathlist = {}
folderlist = {};
filelist = {};
render_page = function()
local foldlist = minetest.get_dir_list(path,true) -- only folders
if foldlist then folderlist = foldlist else folderlist = {} end
for i = 1,#foldlist do foldlist[i] = "*"..foldlist[i] end
foldlist[#foldlist+1] = "*.."
local fillist = minetest.get_dir_list(path,false)
if fillist then filelist = fillist else filelist = {} end
local content = table.concat(folderlist,",") .. ",-------------------," .. table.concat(filelist,",")
return "size[" .. size .. "," .. size .. "] label[0,-0.25;ROBOT FILE MANAGER " .. fmver .. " by rnd\nPATH " .. minetest.formspec_escape(path) .. "] textlist[-0.25,0.75;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]";
end
page = {}
self.show_form(pname,render_page())
init = true
self.read_form()
end
sender,fields = self.read_form()
if sender then
local fsel = fields.wiki;
if fsel and string.sub(fsel,1,3) == "DCL" then
local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line
local fold = folderlist[sel];
if fold and string.sub(fold,1,1) == "*" then
if fold == "*.." then -- go back
if #pathlist>0 then
local i = string.len(pathlist[#pathlist]);
if i>0 then
pathlist[#pathlist] = nil
path = string.sub(path,1,-i-2);
end
end
else
pathlist[#pathlist+1] = string.sub(fold,2)
path = path .. "/".. pathlist[#pathlist]
end
self.show_form(pname,render_page())
end
end
--self.label(fsel);
end

View File

@ -0,0 +1,57 @@
-- gui demo by rnd
-- 2 player cooperative editing of image with 2 colors
if not init then
_G.basic_robot.data[self.name()].obj:get_luaentity().timestep = 0.25
init = true
name = "rnd"
otherrobotname = "rnd2" -- on other robot do name of this robot
drawcolor = 3; -- other robot has reversed colors
otherdrawcolor = 4
color = {"black","white","blue","green"}
data = {};
n = 20;
for i = 1,n do
data[i]={};
--local y = math.floor(f(i));
for j = 1,n do
data[i][j] = 1--(n-j>y) and 2 or 1
end
end
get_form = function()
local form = "size[10,10] "; ret = {};
for i = 1,n do
for j = 1,n do
ret[#ret+1] = "image_button["..((i-1)*0.5)..","..((j-1)*0.5)..";0.7,0.63;wool_"..color[data[i][j]]..".png;"..((i-1)*n+j-1) .. ";] "
end
end
return form .. table.concat(ret,"")
end
self.show_form(name,get_form())
self.read_form()
end
sender,mail = self.read_mail()
if mail then
local x = mail[1]; local y = mail[2];
if data[x][y]==1 then data[x][y] = otherdrawcolor else data[x][y] = 1 end
self.show_form(name,get_form())
end
sender,fields = self.read_form()
if fields then
if fields.quit then self.remove() end
local sel = 0;
for k,v in pairs(fields) do
if k ~="quit" then sel = tonumber(k); break end
end
local x = 1+math.floor(sel/n); local y = 1+sel % n;
if data[x][y]==1 then data[x][y] = drawcolor else data[x][y] = 1 end
self.send_mail(otherrobotname,{x,y})
self.show_form(name,get_form())
end

View File

@ -0,0 +1,94 @@
if not init then init = true
deposit = function(pname)
_, text = book.read(1);
local data = deserialize(text) or {};
local player = _G.minetest.get_player_by_name(pname)
local inv = player:get_inventory();
local pstack = inv:get_stack("main", 1);
local iname = pstack:to_string()
item, count = _G.string.match(iname,"(%S+)%s?(%d*)");
item = item or "";if item == "" then return end
count = tonumber(count) or 1;
--say("item " .. item .. ", count " .. count)
data[pname] = data[pname] or {};
local pdata = data[pname];
pdata[item] = pdata[item] or {};
local t = minetest.get_gametime()
pdata[item][1] = (pdata[item][1] or 0) + count
pdata[item][2] = t;
inv:set_stack("main", 1, _G.ItemStack(""))
book.write(1,"",serialize(data))
local form = "size [5,5] label[0,0; You deposited " .. item .. "( " .. count .. " pieces)]"
self.show_form(pname, form)
--say(pname .. " deposited " .. item .. "( " .. count .. " pieces) ")
end
check = function(pname)
_, text = book.read(1);
local data = deserialize(text) or {};
data[pname] = data[pname] or {};
--say(serialize(data[pname]))
return serialize(data[pname])
end
withdraw = function(pname)
_, text = book.read(1);
local data = deserialize(text) or {};
data[pname] = data[pname] or {};
local player = _G.minetest.get_player_by_name(pname)
local inv = player:get_inventory();
local pdata = data[pname]
local t0 = minetest.get_gametime();
for k,v in pairs(pdata) do
local t = t0 - v[2]; -- time since last deposit
local a = (1+interests/100)^t;
self.label("deposited time " .. t .. ", deposited quantity " .. v[1] .. ", new quantity : " .. math.floor(a*tonumber(v[1])) )
inv:add_item("main", _G.ItemStack(k .. " " .. math.floor(a*tonumber(v[1]))) )
end
data[pname] = nil;
book.write(1,"",serialize(data))
end
function show(pname)
local itemlist = check(pname)
local form = "size [5,5] button[0,0;2,1;DEPOSIT;DEPOSIT] button[0,1;2,1;WITHDRAW;WITHDRAW]" ..
"textarea[0,2.5;6,3;STORAGE;YOUR DEPOSITS;" .. minetest.formspec_escape(itemlist) .. "]"
self.show_form(pname, form)
end
interests = 5; -- interest rate per second (demo)
local players = find_player(4)
if not players then self.remove() end
show(players[1])
end
sender,fields = self.read_form()
if sender then
if fields.DEPOSIT then
deposit(sender)
show(sender)
elseif fields.WITHDRAW then
withdraw(sender)
show(sender)
end
--say(sender .. " clicked " .. serialize(fields))
end

View File

@ -0,0 +1,46 @@
-- gui demo by rnd
if not init then
init = true
name = "rnd"
color = {"white","black"}
data = {};
n = 20;
f = function(x) return 7*(1+math.sin(x/2)) end
for i = 1,n do
data[i]={};
local y = math.floor(f(i));
for j = 1,n do
data[i][j] = (n-j>y) and 2 or 1
end
end
get_form = function()
local form = "size[10,10] "; ret = {};
for i = 1,n do
for j = 1,n do
ret[#ret+1] = "image_button["..((i-1)*0.5)..","..((j-1)*0.5)..";0.7,0.63;wool_"..color[data[i][j]]..".png;"..((i-1)*n+j-1) .. ";] "
end
end
return form .. table.concat(ret,"")
end
self.show_form(name,get_form())
self.read_form()
end
sender,fields = self.read_form()
if fields then
if fields.quit then self.remove() end
local sel = 0;
for k,v in pairs(fields) do
if k ~="quit" then sel = tonumber(k); break end
end
local x = 1+math.floor(sel/n); local y = 1+sel % n;
data[x][y] = 3 - data[x][y]
--self.label(x .. " " .. y)
self.show_form(name,get_form())
end

68
scripts/gui/wiki.lua Normal file
View File

@ -0,0 +1,68 @@
-- ROBOT WIKI by rnd
-- to do: ability for multiple links in 1 line
if not init then
_G.basic_robot.data[self.name()].obj:get_luaentity().timestep = 0.1
local players = find_player(4);
if not players then self.remove() end
pname = players[1];
size = 8;
vsize = 8;
linesize = 60; -- break up longer lines
wiki = { -- example of wiki pages
["MAIN PAGE"] =
{
"-- WIKI CONTENTS -- ", "",
"double click link marked with [] or press enter while selected.","",
"[Viewing wiki]",
"[Editing wiki]"
},
["Viewing wiki"] = {
"back to [MAIN PAGE]","",
" ** Viewing wiki",
"double click link marked with [] or press enter while selected."
},
["Editing wiki"] = {
"back to [MAIN PAGE]","",
" ** Editing wiki",
"Edit wiki table and write in entries"
}
}
for k,v in pairs(wiki) do
local pages = wiki[k]; for i = 1,#pages do pages[i] = minetest.formspec_escape(pages[i]) end
end
current = "MAIN PAGE";
render_page = function()
local content = table.concat(wiki[current],",")
return "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]";
end
page = {}
self.show_form(pname,render_page())
init = true
end
sender,fields = self.read_form()
if sender then
local fsel = fields.wiki;
if fsel and string.sub(fsel,1,3) == "DCL" then
local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line
local address = current or "main";
local pages = wiki[address];
local link = _G.string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]")
if wiki[link] then
current = link;
self.show_form(pname,render_page())
--robot_show_help(name)
end
end
end

View File

@ -0,0 +1,44 @@
-- HOW TO DOWNLOAD WEBPAGES/FILES USING ROBOT
if not fetch then
fetch = _G.basic_robot.http_api.fetch;
-- WARNING: this is run outside pcall and can crash server if errors!
result = function(res) -- res.data is string containing result
if not res.succeeded then self.label("#ERROR: data couldn't be downloaded :\n" .. minetest.serialize(res) ) return end
if res.data then self.label(res.data) end
end
fetch({url = "http://185.85.149.248/FILES/minetest/README.txt", timeout = 30}, result)
end
--[[
from https://github.com/minetest/minetest/blob/master/doc/lua_api.txt :
`HTTPRequest` definition
------------------------
Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`.
{
url = "http://example.org",
timeout = 10,
-- ^ Timeout for connection in seconds. Default is 3 seconds.
post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"},
-- ^ Optional, if specified a POST request with post_data is performed.
-- ^ Accepts both a string and a table. If a table is specified, encodes
-- ^ table as x-www-form-urlencoded key-value pairs.
-- ^ If post_data ist not specified, a GET request is performed instead.
user_agent = "ExampleUserAgent",
-- ^ Optional, if specified replaces the default minetest user agent with
-- ^ given string.
extra_headers = { "Accept-Language: en-us", "Accept-Charset: utf-8" },
-- ^ Optional, if specified adds additional headers to the HTTP request.
-- ^ You must make sure that the header strings follow HTTP specification
-- ^ ("Key: Value").
multipart = boolean
-- ^ Optional, if true performs a multipart HTTP request.
-- ^ Default is false.
}
--]]

View File

@ -0,0 +1,68 @@
// listen to web request and pass it to minetest or back to web, rnd 2018
// INSTRUCTIONS. url options:
// 1./mtmsg/msg will store msg as message received from minetest ( minetest_message). note that msg cant contain spaces or newlines
// 2./getwebmsg/ will reply = IP + ' ' + webmessage
// 3./webmsg/msg will store message as webmessage
// 4./getmtmsg will reply with minetest_message
// NOTES: 1. avoids the need to deal with POST nastyness and complications like
// https://stackoverflow.com/questions/4295782/how-do-you-extract-post-data-in-node-js
const http = require('http');
const hostname = '192.168.0.10' //write address of your router (it will be accessible from internet then if you open firewall for nodejs process)
const port = 80;
var webreq = "" // message from web
var mtreq = "" // message from mt
// take request from web and pass it to minetest
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
var msg = req.url;
if (msg == '/favicon.ico') return // prevent passing this as request
var pos = msg.indexOf("/",1); // gets the 2nd / in /part1/part2/...
var cmd = msg.substring(1,pos);
var response = ""
var ip = req.connection.remoteAddress;
switch(cmd)
{
case "mtmsg":
response = msg.substring(pos+1);
mtreq = response
break
case "getmtmsg":
response = mtreq; mtreq = ''
break
case "getwebmsg":
response = webreq; webreq = ''
break
case "webmsg":
webreq = ip + ' ' + msg.substring(pos+1);
response = 'request received: ' + webreq + '\nuse /getmtmsg to view response from minetest'
break
default:
response = 'INSTRUCTIONS. url options:\n'+
'1./mtmsg/msg will store msg as message received from minetest ( minetest_message). note that msg cant contain spaces or newlines\n'+
'2./getwebmsg/ will reply = IP + " " + webmessage\n'+
'3./webmsg/msg will store message as webmessage\n'+
'4./getmtmsg will reply with minetest_message\n'
}
if (msg!='' && cmd != 'getwebmsg') console.log('ip ' + ip + ', msg ' + msg)
res.write(response); res.end()
return
});
// make server listen
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

View File

@ -0,0 +1,71 @@
-- webcommands : access url like: 192.168.0.10/hello_world
--[[ instructions:
1.download nodejs server from
https://nodejs.org/dist/v10.14.2/node-v10.14.2-win-x86.zip
2. run nodejs server using run.bat
:loop
node $path/minetest_webcommands.js
goto loop
3. run robot and type 'http://192.168.0.10/webmsg/hello this is a test' into browser
(you need to write your router address here, i.e. ip accessible from internet OR lan address)
--]]
if not fetch then
address = "192.168.0.10";
fetch = _G.basic_robot.http_api.fetch;
state = 0 -- ready to fetch new command
-- WARNING: this is run outside pcall and can crash server if errors!
result = function(res) -- res.data is string containing result
state = 0
if not res.succeeded then self.label("#ERROR: data couldn't be downloaded :\n" .. minetest.serialize(res) ) return end
if res.data == "" then return end
local req = res.data; req = string.gsub(req,"%%20"," ")
if res.data then
self.label(os.date("%X") ..', cmd : ' .. req)
local i = string.find(req," !")
if i then
local cmd = string.sub(req,i+2)
if cmd == "players" then
local players = minetest.get_connected_players();
out = {};
for i = 1,#players do out[i] = players[i]:get_player_name() end
MT2web("online players : " .. table.concat(out,", "))
else
run_commmand(cmd)
end
end
end
end
admin = minetest.setting_get("name")
run_commmand = function(message)
local cmd, param = _G.string.match(message, "([^ ]+) *(.*)")
if not param then
param = ""
end
local cmd_def = minetest.chatcommands[cmd]
if cmd_def then
cmd_def.func(admin, param)
else
minetest.chat_send_all(admin..": "..message)
end
end
MT2web = function(message)
message = string.gsub(message," ","%%20") -- NOTE: if you send request that has 'space' in it there will be error 400!
fetch({url = "http://".. address .. "/mtmsg/"..message, timeout = 5}, result)
end
MT2web("minetest robot started and listening.")
self.listen(1)
end
if state == 0 then
fetch({url = "http://"..address.."/getwebmsg/", timeout = 5}, result)
state = 1
end

207
scripts/math/enigma.lua Normal file
View File

@ -0,0 +1,207 @@
--ENIGMA emulator by rnd
-- programming ~30 mins, total 3hrs 30 minutes with debugging - cause of youtube video with missing
-- key detail - reflector!
-- REFERENCES:
-- 1. https://en.wikipedia.org/wiki/Enigma_machine
-- 2. http://users.telenet.be/d.rijmenants/en/enigmatech.htm#reflector
-- 3. https://www.youtube.com/watch?src_vid=V4V2bpZlqx8&v=G2_Q9FoD-oQ
-- default settings
settings = {}
settings.set = function(reflector, plugboard, rotors)
settings.reflector = reflector or 2017
settings.plugboard = plugboard or 2017
settings.rotors = {}
if rotors then
table.insert(settings.rotors, rotors[1])
table.insert(settings.rotors, rotors[2])
table.insert(settings.rotors, rotors[3])
table.insert(settings.rotors, rotors[4])
else
table.insert(settings.rotors, 0)
table.insert(settings.rotors, 0)
table.insert(settings.rotors, 0)
table.insert(settings.rotors, 2018)
table.insert(settings.rotors, 2017)
table.insert(settings.rotors, 2020)
end
--if not enigma_encrypt then
if true then
scramble = function(input,password,sgn) -- permutes text randomly, nice after touch to stream cypher to prevent block analysis
_G.math.randomseed(password);
local n = #input;
local permute = {}
for i = 1, n do permute[i] = i end --input:sub(i, i)
for i = n,2,-1 do
local j = math.random(i-1);
local tmp = permute[j];
permute[j] = permute[i]; permute[i] = tmp;
end
local out = {};
if sgn>0 then -- unscramble
for i = 1,n do out[permute[i]] = string.sub(input,i,i) end
else -- scramble
for i = 1,n do out[i] = string.sub(input,permute[i],permute[i]) end
end
return table.concat(out,"")
end
local permutation = function(n,password,sgn) -- create random permutation of numbers 1,...,n
_G.math.randomseed(password);
local permute = {}
for i = 1, n do permute[i] = i end
for i = n,2,-1 do
local j = math.random(i-1);
local tmp = permute[j];
permute[j] = permute[i]; permute[i] = tmp;
end
return permute;
end
-- produces permutation U such that U^2 = 1; modified fisher-yates shuffle by rnd
local reflector = function(n,password)
_G.math.randomseed(password)
local permute = {}
local used = {};
for i = 1, n do permute[i] = i end
local rem = n;
for i = n,2,-1 do
if not used[i] then
local j = math.random(rem);
-- now we need to find j-th unused idx
local k = 1; local l = 0; -- k position, l how many we tried
while l < j do
if not used[k] then l=l+1; end
k=k+1
end
j=k-1;
used[i]=true; used[j] = true;
local tmp = permute[j];
permute[j] = permute[i]; permute[i] = tmp;
rem = rem - 2;
end
end
return permute;
end
local inverse_table = function(tbl)
local ret = {};
for i = 1,#tbl do
ret[ tbl[i] ] = i;
end
return ret;
end
local rotors = {}; -- { permutation, work index }}
local invrotors = {}; -- reversed tables
-- SETUP REFLECTOR, ROTORS AND PLUGBOARD!
local enigma_charcount = 127-32+1; -- n = 96
local enigma_charstart = 32;
local reflector = reflector(enigma_charcount,settings.reflector); -- this is permutation U such that U^2 = id --
local plugboard = permutation(enigma_charcount,settings.plugboard,1); -- setup plugboard for enigma machine
local invplugboard = inverse_table(plugboard);
for i = 1,3 do rotors[i] = {rotor = permutation(enigma_charcount,settings.rotors[3 + i],1), idx = 0} end -- set up 3 rotors together with their indices
for i = 1,3 do invrotors[i] = {rotor = inverse_table(rotors[i].rotor)} end -- set up 3 rotors together with their indices
-- how many possible setups:
--[[
n = charcount;
rotors positions: n^3
plugboard wiring : n!
reflector wiring: n! / (2^n * (n/2)!)
TOTAL: (n!)^2*n^3 / ( 2^n * (n/2)! ) ~ 6.4 * 10^77
rotor positions & plugboard wiring: (n!)*n^3 ~ 4.8 * 10^57 (n=43)
--]]
-- END OF SETUP
local rotate_rotor = function(i)
local carry = 1;
for j = i,1,-1 do
local idx = rotors[j].idx;
idx = idx + 1;
if idx>=enigma_charcount then
carry = 1;
else
carry = 0;
end
rotors[j].idx = idx % enigma_charcount;
if carry == 0 then break end
end
end
local enigma_encrypt_char = function(x) -- x : 1 .. enigma_charcount
-- E = P.R1.R2.R3.U.R3^-1.R2^-1.R1^-1.P^-1, P = plugboard, R = rotor, U = reflector
x = plugboard[x];
for i = 1,3 do
local idx = rotors[i].idx;
x = rotors[i].rotor[((x+idx-1) % enigma_charcount)+1];
end
x = reflector[x];
for i = 3,1,-1 do
local idx = rotors[i].idx;
x = invrotors[i].rotor[x];
x = ((x-1-idx) % enigma_charcount)+1
end
x = invplugboard[x];
-- apply rotation to rotor - and subsequent rotors if necessary
rotate_rotor(3)
return x;
end
--enigma_encrypt = function(input)
settings.encrypt = function(input)
-- rotor settings!
rotors[1].idx = settings.rotors[1]
rotors[2].idx = settings.rotors[2]
rotors[3].idx = settings.rotors[3]
local ret = "";
for i = 1,#input do
local c = string.byte(input,i) - enigma_charstart +1;
--say(i .. " : " .. c)
if c>=1 and c<=enigma_charcount then
c = enigma_encrypt_char(c);
end
ret = ret .. string.char(enigma_charstart+c-1);
end
return ret
end
settings.decrypt = settings.encrypt
end
end
msg = self.listen_msg()
if msg then
msg = minetest.strip_colors(msg)
local mark = string.find(msg,"@e") -- delimiter in chat
if mark then
msg = string.sub(msg,mark+2);
msg = minetest.colorize("yellow",enigma_encrypt(msg))
say(minetest.colorize("red","#decrypted : ") .. msg)
end
end
msg = self.sent_msg()
if msg then
local msg = enigma_encrypt(msg);
say("@e" .. msg,true)
-- minetest.show_formspec("encrypted_text", "size[4,4] textarea[0,-0.25;5,5;text;;".. "@e" .. minetest.formspec_escape(msg) .. "]")
end

View File

@ -0,0 +1,92 @@
-- robot can construct classic fractals like menger sponge, jerusalem cube, sierpinski triangles,..
-- use: build a pattern at position 1,1,1 relative to robot. when run robot will analyse pattern and construct fractal
if not init then
minetest.forceload_block(self.pos(),true)
init = true; local spos = self.spawnpos();
offsets = {["default:dirt"] = 0, ["default:wood"] = -1, ["default:cobble"] = 1}
read_form = function(fractal) -- read shape from world
local form = {}; local i = 0;
local spos = self.spawnpos(); spos.x = spos.x+1;spos.y = spos.y+1;spos.z = spos.z+1;
local nx = 0; local ny = 0; local nz = 0;
fractal.form = {}
for x = 0,fractal.nx-1 do
for y = 0,fractal.ny-1 do
for z = 0,fractal.nz-1 do
local node = _G.minetest.get_node({x=spos.x+x,y=spos.y+y,z=spos.z+z}).name;
local offset = offsets[node] or 0;
if node~= "air" then
form[i] = {x,y,z,offset}; i=i+1
if nx<x then nx = x end
if ny<y then ny = y end
if nz<z then nz = z end
end
end
end
end
form[0] = nil;
fractal.form = form;
fractal.nx = nx+1; fractal.ny = ny+1; fractal.nz = nz+1;
end
iterate = function(fractal)
local m = #sponge;
local nx = fractal.nx;
for j = 1, #sponge do
local scoords = sponge[j];
for i = 1, #(fractal.form) do
local coords = (fractal.form)[i];
sponge[#sponge+1] = {scoords[1] + (coords[1]+coords[4]/nx)*sidex, scoords[2] + coords[2]*sidey, scoords[3] + coords[3]*sidez};
end
end
sidex = sidex * fractal.nx;
sidey = sidey * fractal.ny;
sidez = sidez * fractal.nz;
end
make = function(fractal, iter)
sidex = 1;sidey = 1;sidez = 1;
sponge = {{0,0,0}};
for i = 1, iter do
iterate(fractal)
end
end
build = function(sponge)
local grid = 2^2;
local spos = self.spawnpos(); spos.x = spos.x+1;spos.y = spos.y+1;spos.z = spos.z+1;
for j = 1, #sponge do
local scoords = sponge[j];
--local color = (math.floor(scoords[1]/grid) + math.floor(scoords[3]/grid)) % 2;
local nodename = "default:stonebrick"
--if color == 0 then nodename = "default:goldblock" else nodename = "default:diamondblock" end
minetest.swap_node({x=spos.x+scoords[1],y=spos.y+scoords[2],z=spos.z+scoords[3]},{name = nodename})
end
end
clear = function()
local grid = 2^2;
local spos = self.spawnpos(); spos.x = spos.x+1;spos.y = spos.y+1;spos.z = spos.z+1;
for j = 1, #sponge do
local scoords = sponge[j];
minetest.swap_node({x=spos.x+scoords[1],y=spos.y+scoords[2],z=spos.z+scoords[3]},{name = "air"})
end
end
fractal0 = { nx = 5, ny = 5, nz = 5, form = {} }
read_form(fractal0)
self.label("form count: " .. 1+#fractal0.form .. " dim " .. fractal0.nx .. " " .. fractal0.ny .. " " .. fractal0.nz)
make(fractal0,3);
build(sponge)
end
--self.remove()

View File

@ -0,0 +1,184 @@
if not number then
number = {};
number.base = 10; -- what base: 2-10
number.data = {};
number.size = -1;
function number:tostring()
local ret = ""
--say("tostring size " .. self.size)
for i = self.size,1,-1 do ret = ret .. (self.data[i] or "X").."" end
return ret
end
function number:new(data)
local o = {};_G.setmetatable(o, self); o.data = {};
for i = 1,#data do o.data[i] = data[i] end -- do copy otherwise it just saves reference
o.size = #data;
self.__index = self; return o
end
number.add = function (lhs, rhs,res)
local n1 = lhs.size;local n2 = rhs.size;local n = math.max(n1,n2);
local carry=0, sum; local base = lhs.base;
local out = false;
if not res then res = number:new({}) out = true end
local data = res.data
for i = 1,n do
sum = (lhs.data[i] or 0)+(rhs.data[i] or 0)+carry;
if carry>0 then carry = 0 end
if sum>=base then data[i]=sum-base; carry = 1 else data[i] = sum end
end
if carry>0 then data[n+1]=1 res.size = n+1 else res.size = n end
if out then return res end
end
number.__add = add;
function number:set(m)
local data = self.data;
local mdata = m.data;
for i=1,#mdata do
data[i]=mdata[i];
end
self.size = m.size;
end
-- 'slow' long multiply
number.multiply = function (lhs, rhs, res)
local n1 = lhs.size;local n2 = rhs.size;local n = n1+n2;
--say("multiply sizes " .. n1 .. "," .. n2)
local out = false;
if not res then res = number:new({}); out = true end;
res.size = n1+n2-1;
res.data = {} -- if data not cleared it will interfere with result!
local data = res.data;
local c,prod,carry = 0; local base = lhs.base;
for i=1,n1 do
carry = 0;
c = lhs.data[i] or 0;
for j = 1,n2 do -- multiply with i-th digit and add to result
prod = (data[i+j-1] or 0)+c*(rhs.data[j] or 0)+carry;
carry = math.floor(prod / base);
prod = prod % base;
data[i+j-1] = (prod)%base;
end
if carry>0 then data[i+n2] = (data[i+n2] or 0)+ carry ;if res.size<i+n2 then res.size = i+n2 end end
end
if out then return res end
end
--TO DO
number.karatsuba_multiply = function(lhs,rhs,res)
local n1 = lhs.size;local n2 = rhs.size;
local n0 = math.max(n1,n2);
if n0< 2 then -- normal multiply for "normal" sized numbers
number.multiply(lhs, rhs, res)
return
end
n0 = math.floor(n0/2);
local n = n1+n2;
local a1 = number:new({});
local tdata = a1.data; local sdata = lhs.data;
for i = 1,n0 do tdata[i] = sdata[i] or 0 end; a1.size = n0;
local a2 = number:new({}); tdata = a2.data;
for i = n0+1,n1 do tdata[i-n0] = sdata[i] or 0 end; a2.size = n1-n0;
local b1 = number:new({});
local tdata = b1.data; sdata = rhs.data;
for i = 1,n0 do tdata[i] = sdata[i] or 0 end; b1.size = n0;
local b2 = number:new({}); tdata = b2.data;
for i = n0+1,n1 do tdata[i-n0] = sdata[i] or 0 end; b2.size = n1-n0;
--say("a1 " .. a1:tostring())
--say("a2 " .. a2:tostring())
--say("b1 " .. b1:tostring())
--say("b2 " .. b2:tostring())
local A = number:new({}); number.karatsuba_multiply(a1,b1,A);
local B = number:new({}); number.karatsuba_multiply(a2,b2,B);
local C = number:new({}); number.karatsuba_multiply(a1+a2,b1+b2,C);
--== C-A-B
--TODO minus, reassemble together..
end
karatsuba_multiply_test = function()
--local in2 = number:new({2,1,1})
--local in1 = number:new({2,1,1})
local res = number:new({});
local in1 = number:new({3,1,4}); --413
local in2 = number:new({7,2,5}); -- 527
number.karatsuba_multiply(in1,in2,res)
end
karatsuba_multiply_test()
multiply_test = function()
--local in2 = number:new({2,1,1})
--local in1 = number:new({2,1,1})
local res = number:new({});
local in1 = number:new({4})
number.multiply(in1,in1,res)
say("mult check 1 " .. res:tostring())
--say("mult check 2 " .. number.multiply(in1,in2):tostring())
end
--multiply_test()
number.__mul = number.multiply;
number.power = function(n,power_) -- calculate high powers efficiently - number of steps is log_2(power)
local power = power_;
local input = number:new(n.data);
local out = number:new({});
local count = 0;
local r; local powerplan = {}; -- 0: just square a->a^2, 1 = square and multiply a-> a*a^2
while (power>0) do
r=power%2; powerplan[#powerplan+1] = r; power = (power-r)/2
end
for i = #powerplan-1,1,-1 do
number.multiply(input,input,out);
if powerplan[i] == 1 then
input,out = out, input;
number.multiply(input,n,out); count = count + 2
else count = count + 1;
end
input,out = out, input;
end
return input
end
split = function(s,k)
local ret = "";
local j=1,length; length = string.len(s)/k
for i = 1, length do
j = (i-1)*k+1;
ret = ret .. string.sub(s,j,j+k-1) .. "\n"
end
--say("j " .. j)
if j>1 then j = j+k end
ret = ret .. string.sub(s,j)
return ret
end
self.spam(1)
-- little endian ! lower bits first ..
--n = number:new({7,1,0,2}); local power = 2017;
--self.label(split(n:tostring().."^"..power .. " = " .. number.power(n,power):tostring(),100))
--2017^2017 = 3906...
end

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,65 @@
-- PINE TREE HARVEST by rnd1
if not harvest then
harvest = {};
harvest.s = -1 -- -1 idle, 0 expect tree
harvest.tree = "default:pine_tree";harvest.wood = "default:wood";harvest.sapling = "default:pine_sapling";
harvest.step = function()
local s=harvest.s;
if s == 0 then -- did we bump into tree
local node = read_node.forward();
if node == harvest.tree then
dig.forward(); move.forward(); harvest.s = 1 -- found tree, moving in
self.label("im digging up tree ")
end
elseif s == 1 then -- climbing up
dig.up(); move.up(); place.down(harvest.wood);
local node = read_node.up();
if node ~= harvest.tree then
harvest.s = 2 -- top
self.label("i reached top of tree")
end
elseif s == 2 then -- going down
local node = read_node.down();
if node == harvest.wood then
dig.down(); move.down()
self.label("im going back down")
else
pickup(8);
move.backward();place.forward(harvest.sapling);move.forward();
harvest.s = -1 -- idle
self.label("i finished cutting tree")
end
end
end
end
--harvest walk init
if not angle then
sender,mail = self.read_mail(); if sender == "rnd1" then harvest.s = tonumber(mail) or 0 end
wall = "default:cobble";
angle = 90
end
if harvest.s~=-1 then
harvest.step()
elseif harvest.s==-1 then
node = read_node.forward();
if node==harvest.tree then
harvest.s = 0;
self.label("i found tree")
else
self.label("im walking")
if not move.forward() then
if node == wall then
self.label("i hit wall")
turn.angle(angle);
if move.forward() then
move.forward();move.forward();turn.angle(angle); angle = -angle
else
turn.angle(-angle);angle = - angle
end
end
end
end
end
self.send_mail("rnd1",harvest.s) -- remember state in case of reactivation

View File

@ -0,0 +1,59 @@
N = 30; -- length of program (without ])
desired_frequencies = {
[">"] = 10,
["<"] = 10,
["-"]=10,
["+"]=20,
["."]=10,
[","]=10,
["["]=10, -- matching "]" will be inserted automatically!
}
matching_parenthesis = "]";
routine_lower = 1; routine_higher = 5; -- specify range, how many characters in routine [.....]
generate_selections = function(desired_frequency)
local sum = 0
for k,v in pairs(desired_frequency) do sum = sum + v end
local isum = 0; local count = 0; local selections = {}
for k,v in pairs(desired_frequency) do count = count +1 isum = isum + desired_frequency[k]/sum; selections[count] = {isum,k} end
return selections
end
choose = function(selections, rnd)
local low, mid, high;
low = 1; high = #selections; mid = math.floor((low+high)/2)
local step = 0;
while high-low>1 and step < 20 do
step = step + 1
if rnd <= selections[mid][1] then high = mid else low = mid end
mid = math.floor((low+high)/2)
end
return selections[mid][2]
end
generate_program = function(desired_frequencies,N, routine_lower, routine_higher)
local selections = generate_selections(desired_frequencies);
local ret = {};
local count = 0
local stack = {};
for count = 1, N do
local choice = choose(selections, math.random());
if choice == "[" then
local i = count + math.random(routine_lower,routine_higher)
if i > N then i = N end
stack[#stack+1] = i;
end
ret[count] = choice
end
for i = 1,#stack do local j = stack[i] ret[j]=ret[j]..matching_parenthesis end
return table.concat(ret)
end
say(generate_program(desired_frequencies,N, routine_lower, routine_higher))
self.remove()

View File

@ -0,0 +1,66 @@
-- BRAINFUCK interpreter by rnd, 2017
-- https://en.wikipedia.org/wiki/Brainfuck
if not ram then
prog = "+++.>++++++.<[->+<]>."
ramsize = 10
maxsteps = 100; step=0; -- for RUN state only
n = string.len(prog);ram = {};for i = 1, ramsize do ram[i]=0 end -- init ram
pointer = 1 -- ram pointer
instruction = 1 -- instruction pointer
self.spam(1)
RUNNING = 1; END = 2; RUN = 3;
state = RUNNING
get_ram = function() msg = "" for i = 1,ramsize do msg = msg .. ram[i] .. "," end return msg end
cmdset = {
[">"] = function() pointer = pointer + 1; if pointer > ramsize then pointer = 1 end end,
["<"] = function() pointer = pointer - 1; if pointer > ramsize then pointer = 1 end end,
["+"] = function() ram[pointer]=ram[pointer]+1 end,
["-"] = function() ram[pointer]=ram[pointer]-1 end,
["."] = function() say(ram[pointer]) end,
[","] = function() ram[pointer] = tonumber(read_text.forward("infotext") or "") or 0 end,
["["] = function()
if ram[pointer] == 0 then
local lvl = 0
for j = instruction, n, 1 do
if string.sub(prog,j,j) == "]" then lvl = lvl - 1 if lvl == 0 then
self.label("JMP " .. j ) instruction = j return
end end
if string.sub(prog,j,j) == "[" then lvl = lvl + 1 end
end
end
end,
["]"] = function()
if ram[pointer] ~= 0 then
local lvl = 0
for j = instruction, 1, -1 do
if string.sub(prog,j,j) == "]" then lvl = lvl - 1 end
if string.sub(prog,j,j) == "[" then lvl = lvl + 1 if lvl == 0 then
self.label("JMP " .. j ) instruction = j return
end end
end
end
end,
}
end
-- EXECUTION
if state == RUNNING then
c = string.sub(prog,instruction,instruction) or "";
if c and cmdset[c] then cmdset[c]() end
self.label("ins ptr " .. instruction .. ", ram ptr " .. pointer .. ": " .. ram[pointer] .. "\n" .. string.sub(prog, instruction).."\n"..get_ram())
instruction = instruction + 1; if instruction > n then state = END end
-- RUN THROUGH
elseif state == RUN then
while (step<maxsteps) do
step = step + 1
c = string.sub(prog,instruction,instruction) or "";
if c and cmdset[c] then cmdset[c]() end
instruction = instruction + 1; if instruction > n then self.label("ram : " .. get_ram()) step = maxsteps state = END end
end
end

View File

@ -0,0 +1,53 @@
function identify_strings(code) -- returns list of positions {start,end} of literal strings in lua code
local i = 0; local j; local length = string.len(code);
local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string
local modes = {
{"'","'"},
{"\"","\""},
{"%[=*%[","%]=*%]"}
}
local ret = {}
while i < length do
i=i+1
local jmin = length+1;
if mode == 0 then -- not yet inside string
for k=1,#modes do
j = string.find(code,modes[k][1],i);
if j and j<jmin then -- pick closest one
jmin = j
mode = k
end
end
if mode ~= 0 then -- found something
j=jmin
ret[#ret+1] = {jmin}
end
if not j then break end -- found nothing
else
_,j = string.find(code,modes[mode][2],i); -- search for closing pair
if not j then break end
if (mode~=2 or string.sub(code,j-1,j-1) ~= "\\") then -- not (" and \")
ret[#ret][2] = j
mode = 0
end
end
i=j -- move to next position
end
if mode~= 0 then ret[#ret][2] = length end
return ret
end
identify_strings_test = function()
local code =
[[code; a = "text \"text\" more text"; b = 'some more text'; c = [==[ text ]==] ]]
local strings = identify_strings(code);
say(minetest.serialize(strings))
for i =1,#strings do
say(i ..": " .. string.sub(code, strings[i][1],strings[i][2]))
end
end
identify_strings_test()
self.remove()

View File

@ -0,0 +1,114 @@
-- demo bot
if not s then
-- move.forward();--turn.angle(180)
s=0;t=0
mag = 3;
boot = function()
self.display_text("RND technologies\n(inc) 2016\n\n\nmemchk "..(t*1024).. "kb ",16,mag)
t=t+1; if t == 8 then s = 0.5 t = 0 end
end
boot_memchk = function()
self.display_text("RND technologies\n(inc) 2016\n\n ".. (8*1024).. "kb READY",16,mag)
t=t+1; if t==3 then t=0 s = 1 end
end
os_load = function()
if t == 0 then self.display_text("============\nrndos v2.5\n============\n\nloading...\nkernel v12.2 ",12,mag) end
t=t+1; if t == 7 then s = 2 t = 0 end
end
main_menu = function()
if t==0 then self.display_text("MAIN MENU \n\n1) ip lister\n2) kick player\n3) teleport\n4) give fly\n5) kill\n6) turn off\n\n0 to return here from app",16,mag) end
text = read_text.backward();
if text and text~="" then
write_text.backward("")
if text == "1" then s = 3 t = 0
elseif text == "2" then s = 4 t = 0
elseif text == "3" then s=5 t =0
elseif text == "4" then s=6 t =0
elseif text == "5" then s=7 t =0
elseif text == "6" then self.remove()
end
end
end
ip_lister = function()
if t%5 == 0 then
players = _G.minetest.get_connected_players(); msg = "IP LISTER\n\n";
for _, player in pairs(players) do
local name = player:get_player_name(); ip = _G.minetest.get_player_ip(name) or "";
msg = msg .. name .. " " .. ip .. "\n";
end
self.display_text(msg,30,mag)
t=0
end
t=t+1
if read_text.backward() == "0" then s=2 t=0 end
end
act_on_player = function(mode)
if t==0 then
msg = get_player_list()
local txt = {[1]="KICK WHO?\n", [2] = "TELEPORT WHO HERE?\n", [3] = "GIVE FLY TO WHOM?\n", [4] = "KILL WHO?\n"}
text = txt[mode] or "";
self.display_text(text..msg,30,mag) t=1
end
text = read_text.backward();
if text then
if text=="0" then s=2 t=0 else
write_text.backward("");
if mode ==1 then
_G.minetest.kick_player(player_list[tonumber(text)] or "");
elseif mode ==2 then
player =_G.minetest.get_player_by_name(player_list[tonumber(text)] or "");
if player then player:setpos(self.spawnpos()) end
elseif mode ==3 then
player=_G.minetest.get_player_by_name(player_list[tonumber(text)] or "");
if player then player:set_physics_override({gravity=0.1}) end
elseif mode ==4 then
player=_G.minetest.get_player_by_name(player_list[tonumber(text)] or "");
if player then player:set_hp(0) end
end
end
end
end
player_list = {}
get_player_list = function()
local players = _G.minetest.get_connected_players(); local msg = ""; local i=0;
for _, player in pairs(players) do
local name = player:get_player_name();
i=i+1;msg = msg .. i ..") " .. name .. "\n";
player_list[i]=name;
end
return msg
end
end
self.label(s)
if s == 0 then
boot()
elseif s==0.5 then
boot_memchk()
elseif s==1 then
os_load()
elseif s==2 then
main_menu()
elseif s==3 then
ip_lister()
elseif s==4 then
act_on_player(1)
elseif s==5 then
act_on_player(2)
elseif s==6 then
act_on_player(3)
elseif s==7 then
act_on_player(4)
end

View File

@ -0,0 +1,271 @@
-- rnd 2017
-- call stack, subroutines, recursive subroutines, if jumps, loops, variables
if not cmd then
prog = "\n"..
"<fd M(loop) f NNOT(air)[<] G(loop)";
pause = 0
self.label(prog)
build = {};
cmd = {
["f"] = function() move.forward() end,
["b"] = function() move.backward() end,
["l"] = function() move.left() end,
["r"] = function() move.right() end,
["u"] = function() move.up() end,
["d"] = function() move.down() end,
["p"] = function() place.forward_down("default:dirt") end,
["P"] = function() place.forward("default:dirt") end,
["F"] = function() dig.forward() end,
["L"] = function() dig.left() end,
["R"] = function() dig.right() end,
["U"] = function() dig.up() end,
["D"] = function() dig.down() end,
["<"] = function() turn.left() end,
[">"] = function() turn.right() end,
};
build.debug =true;
build.stackdepth = 16;
build.ignored = {["]"]=true,["["]=true};
-- RESERVED: Ex(y)[A] : if x == y do A end, N(node)[A] : if read_node.forward()==node then do A end
-- G(label) .. GOTO GR(label) .. CALL SUBROUTINE +a(b),-a(b),=a(b)
set_routine = function(i)
local jr = string.find(prog,"%[",i+1)
if not jr then say("error, missing [ after position " .. i+1); self.remove() end
build.count = get_var(string.sub(prog,i+1,jr-1)) or 1
local kr = string.find(prog,"]",jr+1);
if not kr then say("error, missing ] after position " .. jr+1); self.remove() end
return jr,kr --returns routine limits jr,kr
end
error_msg = function(i)
local callstack = "";
for _,v in pairs(build.callstack) do
callstack = callstack.. call_marks[v].. ",";
end
return "ERROR: " ..string.sub(prog,i) .. "\nCALL STACK: " .. callstack
end
run_routine = function(i,jr,kr)
local count = build.count;
if count > 0 then
if i == kr then
count = count - 1 i = jr+1
end
--say(" i " .. i .. " kr " .. kr)
if count > 0 then
c=string.sub(prog,i,i)
if c == "G" then i=go_to(i); build.state = 0 else -- exit routine
if cmd[c] then cmd[c]() else
if not ignored[c] then
self.label("run routine: invalid instruction at position " .. i .. "\n" .. error_msg(i));
self.debug = false; build.state = -1
end
end
end
end
else
i=kr-- exit,jump to next instruction
build.state = 0
if build.debug then self.label("["..build.state .. "]" .. i .. "\nROUTINE EXIT") end
end
build.count = count
return i -- return next execution address
end
push_callstack = function(val) -- addresses where to continue after function ends
if #build.callstack > build.stackdepth then say("error: stack depth limit " .. build.stackdepth .. " exceeded "); self.remove() end
build.callstack[#build.callstack+1] = val;
end
pop_callstack = function()
local val = build.callstack[#build.callstack];
build.callstack[#build.callstack] = nil;
return val
end
go_to = function(i)
local j = string.find(prog,"%(",i+1); local k = string.find(prog,"%)",j+1)
local call = false;
if string.sub(prog,i+1,j-1) == "R" then call = true push_callstack(k) end -- function call, save call exit address to return here
local target = string.sub(prog,j+1,k-1);
if target == "" then -- marking end of routine
i = pop_callstack();
if build.debug then self.label("["..build.state .. "]" .. i .. "\nEXITING ROUTINE to " .. i .. ", stack level " .. #build.callstack) end
else
i = go_to_mark[target]-1;
if call == false then
if build.debug then self.label("["..build.state .. "]" .. i .. "\nGOTO " .. target .. "=".. i ) end
else
if build.debug then self.label("["..build.state .. "]" .. i .. "\nCALL SUBROUTINE " .. target .. "=".. i .. "\ncall stack = " .. string.gsub(_G.dump(build.callstack),"\n","")) end
end
end
return i;
end
-- scan for go_to markers
go_to_mark = {};
call_marks = {}; -- OPTIONAL for nicer error messages
scan_go_to = function()
local i = 0;
while ( i < string.len(prog)) do
i=i+1;
if string.sub(prog,i,i+1) == "M(" then
local j = string.find(prog,"%(",i+1)
local k = string.find(prog,"%)",j+1)
local name = string.sub(prog,j+1,k-1);
prog = string.sub(prog,1,i-1)..string.sub(prog,k+1)
go_to_mark[name] = i;
end
end
i=0 -- OPTIONAL call marks scanning
while ( i < string.len(prog)) do
i=i+1;
if string.sub(prog,i,i+2) == "GR(" then
local j = string.find(prog,"%(",i+1)
local k = string.find(prog,"%)",j+1)
local name = string.sub(prog,j+1,k-1);
call_marks[k] = name;
end
end
end
get_var = function(s)
local ns = tonumber(s);
if _G.tostring(ns)==s then return ns else return build.var[s] end
end
prog = string.gsub(prog, " ", "") -- remove all spaces
prog = string.gsub(prog, "\n", "") -- remove all newlines
scan_go_to()
build.n=string.len(prog)
build.i=1;
build.count = 1;
build.state = 0;
build.callstack = {};
build.var = {};
self.spam(1)
end
build.run = function()
local i=build.i; -- get current execution address
local jr,kr -- routine execution boundaries
local jc, kc
--i=i+1; if i>build.n then i = 1 end end
c=string.sub(prog,i,i)
if build.state == 0 then
if c == "R" then
jr,kr=set_routine(i); i = jr; -- set up execution point i=jr
build.state = 1
elseif c == "=" then
jc = string.find(prog,"%(",i+1)
kc = string.find(prog,"%)",jc+1)
local var1 = string.sub(prog,i+1,jc-1);
local var2 = get_var(string.sub(prog,jc+1,kc-1));
build.var[var1]=var2
i=kc
elseif c == "+" then
jc = string.find(prog,"%(",i+1)
kc = string.find(prog,"%)",jc+1)
local var1 = string.sub(prog,i+1,jc-1);
local var2 = get_var(string.sub(prog,jc+1,kc-1));
build.var[var1]=build.var[var1]+var2
i=kc
elseif c == "-" then
jc = string.find(prog,"%(",i+1)
kc = string.find(prog,"%)",jc+1)
local var1 = string.sub(prog,i+1,jc-1);
local var2 = get_var(string.sub(prog,jc+1,kc-1));
build.var[var1]=build.var[var1]-var2
i=kc
elseif c == "E" then
jc = string.find(prog,"%(",i+1)
kc = string.find(prog,"%)",jc+1)
local INOT = string.sub(prog,i+1,i+3) == "NOT";
local trigger; local var1; local var2;
if INOT then
var1 = get_var(string.sub(prog,i+4,jc-1));
var2 = get_var(string.sub(prog,jc+4,kc-1));
else
var1 = get_var(string.sub(prog,i+1,jc-1));
var2 = get_var(string.sub(prog,jc+1,kc-1));
end
trigger = (var1 == var2)
if (not INOT and trigger) or (INOT and not trigger)then
i=kc;
jr,kr=set_routine(i);i=jr
build.state = 1
else
kc = string.find(prog,"]",kc+1)
i = kc
end
elseif c == "N" then
jc = string.find(prog,"%(",i+1)
kc = string.find(prog,"%)",jc+1)
local node = string.sub(prog,jc+1,kc-1) or "air";
local INOT = string.sub(prog,i+1,jc-1) == "NOT";
local trigger;
trigger = read_node.forward() == node;
if (not INOT and trigger) or (INOT and not trigger)then
i=kc;
jr,kr=set_routine(i); i = jr
build.state = 1
else
kc = string.find(prog,"]",kc+1)
i = kc
end
elseif c == "G" then
i=go_to(i);
elseif c == "C" then
jc = string.find(prog,"%(",i+1)
kc = string.find(prog,"%)",jc+1)
var = string.sub(prog,jc+1,kc-1);
i = kc
self.label(var .. "=" .. get_var(var))
else
if cmd[c] then cmd[c]() else
if not build.ignored[c] then
self.label("run main: invalid instruction at position " .. i.. "\n" .. error_msg(i));
build.state = -1; self.debug = false;
end
end
end
elseif build.state == 1 then -- routine
jr = build.jr; kr = build.kr;
i=run_routine(i,jr,kr)
end
i=i+1; if i>build.n then i = 1 end
build.i = i -- update execution address
build.jr = jr;
build.kr = kr;
end
if build.debug then self.label("["..build.state .. "]" .. build.i .. "\n" .. string.sub(prog,build.i)) end
build.run()

View File

@ -0,0 +1,50 @@
--rnd 2017
if not logdata then
self.label("chatlog bot");
_G.minetest.forceload_block(self.pos(),true)
n = 500; -- store so many messsages before repeating
maxresults = 100 -- display at most 'this' result
logdata = {}; -- circular array to hold messages
idx = 1;
insert_log = function(logdata,text) -- store new message
idx = idx +1;
if idx > n then idx = 1 end
logdata[idx] = text;
end
retrieve_log = function(logdata,count,filter) -- return last k messages, with filter only selected messages
local k = 0;
local i=idx; local j=0; local ret = {}
for j = 1,n do
if not logdata[i] then break end
if filter and not string.find(logdata[i], filter) then
else
ret[#ret+1] = logdata[i]
k=k+1
if k>=count then break end -- enough results
end
i=i-1; if i < 1 then i = n end
end
return table.concat(ret,"\n")
end
self.listen(1)
end
speaker, msg = self.listen_msg()
if msg then
if string.sub(msg,1,4) == "?log" then
local j = string.find(msg," ",7); -- find first argument
local k;local text;
if j then k = tonumber(string.sub(msg,6,j-1)) else k = tonumber(string.sub(msg,6)) end -- if there was first argument find second
k = k or maxresults;
if j then text = retrieve_log(logdata,k,string.sub(msg,j+1)) else text = retrieve_log(logdata,k) end
local form = "size[8,8]".. "textarea[0.,0;11.,9.5;text;chatlog;".. text .. "]"
self.show_form(speaker, form)
else
insert_log(logdata, os.date("%X") .. " " .. speaker .. "> " .. msg)
end
end

View File

@ -0,0 +1,42 @@
-- with current mods there are 4 registered chat responses so we add 5th
-- CHANGE COLOR OF CHAT FOR CERTAIN PLAYERS
if not rom.color_chat_messages then rom.color_chat_messages = 1+#minetest.registered_on_chat_messages end
colors = {"cyan", "LawnGreen"}
chatgroup = {}; -- players in here will see chat without colors
--say("chat " .. rom.chat_messages)
minetest.registered_on_chat_messages[rom.color_chat_messages] =
function(name,message)
if message == "nocolor" then
chatgroup[name] = not chatgroup[name]
minetest.chat_send_all("colored chat display " .. (chatgroup[name] and "DISABLED" or "ENABLED") .. " for " .. name)
return false
else
--message = os.date("%X") .. " " .. name .." <> " .. message;
local newmessage = "["..name .."] " .. message;
local player = minetest.get_player_by_name(name);
local pos1 = player:get_pos();
for _,player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local pos2 = player:get_pos();
local dist = math.sqrt((pos2.x-pos1.x)^2+(pos2.y-pos1.y)^2+ (pos2.z-pos1.z)^2)
local length = string.len(name);
local color = 1; -- default
if (chatgroup[name] or dist>32 or dist == 0) then color = 0 end
if string.find(message,string.lower(name)) then color = 2 end
if color == 0 then
minetest.chat_send_player(name, newmessage)
else
minetest.chat_send_player(name, minetest.colorize(colors[color], newmessage))
end
end
end
return true
end
self.remove()

View File

@ -0,0 +1,335 @@
if not init then
rom.best_player = nil
init = true
depth = 4; -- how many moves does random player evaluate
error_rate = 0.25; -- players make wrong decision with this probability
generation = 10; -- how many times we repeat
steps = 100; -- how many steps each generation
bestc = 0; -- how many times was new best player picked
mode = 2; -- 1, evolution!, 2 play game
-- game pay offs
rules = {
{
{2., 2.}, -- first player cooperate, second player cooperate: +2,+2
{-1, 3}, -- first player cooperate, second player cheat: -1,+3
},
{
{3,-1}, -- first player cheats, second player cooperate
{0,0}, -- first player cheats, second player cheat
}
};
copytable = function(tab)
if type(tab)~="table" then return tab end
local ret = {};
for k,v in pairs(tab) do ret[k] = copytable(v) end return
ret
end
copycat = {
rules = {
{0}, -- initial: 0 = cooperate
{0,1}, -- after one move : cooperate if other cooperated last turn, cheat if other cheated last turn
},
-- encode decision sequence in binary: 01110 = dec 8+4+2=14
memory = 1, -- how many moves back player consideres?
moves ={}, -- opponent moves
mood = 0, -- probability that player will cheat if he had intention to cooperate
moody = 0.5, -- by how much cheat/cooperate change mood
points = 0,
name = "copycat"
}
cheater = {
rules = {
{1},
},
memory = 0, -- how many moves back player consideres?
moves ={}, -- opponent moves
mood = 0,
moody = 0.5,
points = 0,
name = "cheater"
}
realplayer = {
rules = {
{0},
},
memory = 0, -- how many moves back player consideres?
moves ={}, -- opponent moves
mood = 0,
moody = 0.,
points = 0,
name = "real player",
real = true,
out = 0
}
create_random_player = function(memory)
local rules = {};
for i = 1, memory+1 do
rules[i] = {};
for j = 1,2^(i-1) do
rules[i][j] = math.random(2)-1
end
end
return {rules = rules, memory = memory, moves = {}, mood = 0, moody = math.random(), points = 0, name = "randomplayer"}
end
-- player makes next move according to his memory of moves so far
play = function(player)
if player.real then return player.out end -- real player
local moves = player.moves;
local n = #moves;
if n > player.memory then n = player.memory end -- there are many moves, examine only what we can
local rules = player.rules[n+1]
local state = bin2dec(player.moves,n); -- examine last n moves
--say("n " .. n .. " state " .. state)
return rules[state+1]
end
group_play = function(playrs) -- each randomplayer plays with every other player in lower group
local n = #playrs;
local m = 10; -- play m games with each opponent, random pair order
for i = 1,10 do
for j = 11,20 do -- i plays with j, randomized order
playrs[i].moves = {}; playrs[j].moves = {}; -- reset remembered moves before paired match!
playrs[i].mood = 0;
for k = 1,m do
if math.random(2) == 1 then
interact(playrs[i],playrs[j])
else
interact(playrs[j],playrs[i])
end
end
end
end
end
sort_players = function(pl)
table.sort(pl,
function(p1,p2) return p1.points<p2.points end
) -- sorts in ascending order of points
end
genetics = function(playrs)
local population = {}
for i = 11,20 do -- pick out only "randomplayer"s
population[#population+1] = playrs[i] --copytable()
end
sort_players(population); -- sort from worst to best
for i = 1,5 do -- replace 5 worst players with new random players
local points2 = population[#population-i+1].points; -- points of i-th best player
if points2 then
local points1 = population[i].points;
if points1<points2 or (points1==points2 and math.random(2) == 1) then --
population[i] = create_random_player(depth);
--if i == 1 then say(serialize(population[i].rules)) end
population[i].points = 0 --points2;
population[i].name = "randomplayer"
end
end
end
--say("best random points " .. population[#population].points)
for i = 11,20 do -- pick out only "randomplayer"s
playrs[i] = population[i-10]
end
end
-- paired interaction: player1 plays with player2, then player2 plays with player1
interact = function(player1,player2)
local res1 = play(player1);
local mood = player1.mood;
if not player1.real and math.random(1000)<=1000*error_rate then res1=1-res1 end
if res1 == 0 and math.random()<= mood then res1 = 1 end
local moves = player2.moves; moves[#moves+1 ] = res1;
--say("1 moves : " .. serialize(moves) .. " res1 " .. res1)
local res2 = play(player2);
mood = player2.mood;
if math.random(1000)<=1000*error_rate then res2=1-res2 end
if res2 == 0 and math.random()<= mood then res2 = 1 end
moves = player1.moves; moves[#moves+1] = res2;
--say("2 moves : " .. serialize(moves) .. " res2 " .. res2)
local res = rules[res1+1][res2+1];
player1.points = player1.points + res[1];
player2.points = player2.points + res[2];
if res2 == 0 then -- player2 cooperated, mood change
if res1==1 then
mood = mood + player2.moody;
else
mood = mood - player2.moody;
end
if mood>1 then mood = 1 elseif mood<0 then mood = 0 end
player2.mood = mood
end
if res1 == 0 then -- mood change for player1
mood = player1.mood;
if res2==1 then
mood = mood + player1.moody;
else
mood = mood - player1.moody;
end
if mood>1 then mood = 1 elseif mood<0 then mood = 0 end
player1.mood = mood
end
end
dec2bin = function(input)
local ret = {};
if input == 0 then return {0} end
while input~=0 do
local r=input%2; input = (input -r)/2
ret[#ret+1] = r;
end
local n = #ret;
local rret = {}
for i = 1, n do
rret[i]=ret[n-i+1]
end
return rret
end
bin2dec = function(bin, length) -- length= how many last elements we take
if length == 0 then return 0 end
if not length then length = #bin end
local offset = #bin - length; if offset<0 then offset = 0 end
local ret = 0;
for i = 1,#bin-offset do
ret = 2*ret + bin[i+offset]
end
return ret
end
get_results = function(players)
local ret = {} for i=1,#players do ret[i] = players[i].name .. " " .. players[i].points .. "M " .. players[i].mood .. "(" .. players[i].moody .. ")" end return table.concat(ret,"\n")
end
players = {} -- start with 5 cheaters, 5 copycats and 10 randomplayers
if mode == 1 then
for i = 1,5 do players[i] = copytable(cheater) end
for i = 1,5 do players[5+i] = copytable(copycat) end
for i = 1,10 do players[10+i] = create_random_player(depth) end -- last 10 players are random
age = 0
rom.best_player = nil;
elseif mode == 2 then
players = {copytable(realplayer), copytable( create_random_player(4) ) } ;
self.listen(1)
end
--players[20] = copytable(rom.best_player) or create_random_player(depth) -- add best player from before
--players[20].name = "randomplayer"
end
if mode == 1 then
local bestpoints = 0
for k = 1, generation do -- repeat experiment generation*
--rom.best_player = nil
for i = 1,#players do players[i].points = 0 end
for j = 1, steps do -- several steps to see who is best long term on average
--for i = 1,#players do players[i].points = 0 end
group_play(players)
end
bestpoints = 0
genetics(players) -- remove 5 worst randomplayers & replace them by new randoms
local population = {}
bestrandom = 0
for i = 11,20 do
if players[i].points >= bestrandom then bestrandom = players[i].points end
population[#population+1] = players[i]
end
--say("randomplayer population size " .. #population)
sort_players(population);
if rom.best_player then
lastbest = rom.best_player.points
if bestrandom >= lastbest then -- PICK NEW BEST
bestc = bestc+1
rom.best_player = copytable(population[#population]); -- remember best randomplayer for next experiment
end
else
rom.best_player = copytable(population[#population]); -- remember best randomplayer for next experiment
end
end
age = age + generation
--display results!
msg = ""
msg = msg .. "Planet Earth (galactical name Halfwits), age " .. age .. ", error_rate " .. error_rate .. ", steps " .. steps .. ", generations " .. generation .. ":\n"
msg = msg .."\nlast round\n"
--sort_players(players)
bestpoints = 0
for i =1,#players do
if players[i].points>bestpoints then bestpoints = players[i].points end
msg = msg .. players[i].name .. ": " .. players[i].points .. " M " .. players[i].mood .. "(" .. players[i].moody .. ")\n"
end
local rules = rom.best_player.rules;
msg = msg .. "BEST: " .. bestpoints .. "\n\n## alltime best random player no. " .. bestc .. ", points " .. rom.best_player.points .. " moody " .. rom.best_player.moody ..
"\ncurrent best random player/max current score: ".. math.floor(100*bestrandom/bestpoints*100)/100 .. "% \nrules " .. serialize(rules) .. " )\n";
local msg1 = "{" .. rules[1][1] .. "}\n"
for i = 2,#rules do
local rule = rules[i];
msg1 = msg1.. "{"
for j = 1,#rule do
local rule_string = table.concat(dec2bin(j-1),"");
rule_string = string.rep("0",i-string.len(rule_string)-1) .. rule_string;
msg1 = msg1 .. rule_string .."="..rule[j]..","
end
msg1 = msg1 .. "}\n"
end
self.label(msg .. msg1)
elseif mode == 2 then -- play vs player
speaker,msg = self.listen_msg();
if msg then
if msg == "0" or msg == "1" then
local nextmove = tonumber(msg);
if nextmove == 1 and players[1].out == 1 then
say("look buddy, we dont like cheaters around here. dont cheat twice in a row")
else
players[1].out = nextmove
interact(players[1], players[2])
say("input " .. players[1].out .. ", BOT MOVES: " .. serialize(players[1].moves) .. " BOT MOOD " .. players[2].mood .. "(" .. players[2].moody .. ") SCORE: you " .. players[1].points .. " bot " .. players[2].points)
end
end
end
end

View File

@ -0,0 +1,153 @@
-- rnd's robot swarm assembly algorithm 2017
-- https://www.youtube.com/watch?v=xK54Bu9HFRw&feature=youtu.be&list=PLC7119C2D50BEA077
-- notes:
-- 1. limitation: there must be room for diagonal move
-- this is not big limitation: assume bots are circles of radius 1, then to allow diagonal movement
-- just spread them by factor sqrt(2)~1.4 initially
-- 2. initial random placement(not part of move algorithm): due to collision some bots may occupy same place
if not pos then
n=50; m = 500;
stuck = m;
state = 0;
step = 0
pos = {}; tpos = {};
-- INIT
for i = 1, m do
--local r = i % n;local c = (i-r)/n;pos[i]={n-c,r+1}; -- regular rectangle shape
pos[i]={math.random(n),math.random(n)};
--tpos[i]={math.random(n),math.random(n)}; -- random shape
local r = i % n;local c = (i-r)/n;tpos[i]={c+1,r+1}; -- regular rectangle shape
end
doswap = true -- use closest swap or not?
-- initially swap ids so that i-th bot is closest to i-th target
permute2closest = function()
-- swap bot i with one closest to i-th target
free = {}; for i = 1, m do free[i] = i end -- list of available ids for swapping
local opos = {};
for i=1,m do opos[i] = {pos[i][1],pos[i][2]} end
closest = {};
for i = 1,m do
-- find closest bot to i-th point
local dmin = 2*n;
local jmin = -1;
local tp = tpos[i];
for j = 1,#free do
local p = opos[free[j]];
local d = math.sqrt((p[1]-tp[1])^2+(p[2]-tp[2])^2);
if d< dmin then dmin = d; jmin = j end
end
if jmin>0 then
local newj = free[jmin];
pos[i] = {opos[newj][1], opos[newj][2]}; -- reassign id
table.remove(free,jmin);
end
end
end
if doswap then
permute2closest()
else
for i=1,m do pos[i] = opos[i] end -- just copy positions
end
data = {};
for i = 1,n do data[i]={}; for j=1,n do data[i][j] = {0,0,1} end end -- 0/1 present, id, move status?
for i = 1,#pos do data[pos[i][1]][pos[i][2]] = {1,i,1} end -- 1=present,i = id, 1=move status
step_move = function()
local count = 0;
for i = 1, #pos do
local p = pos[i];
local tp = tpos[i];
local x = tp[1]-p[1];
local y = tp[2]-p[2];
local d = math.sqrt(x^2+y^2);
if d~=0 then
x=x/d;y=y/d
x=p[1]+x;y=p[2]+y;
x=math.floor(x+0.5);y=math.floor(y+0.5);
if data[x][y][1]==0 then -- target is empty
data[p[1]][p[2]][1] = 0; data[x][y][1] = 1
pos[i]={x,y}; data[x][y][2] = i; data[x][y][3] = 1;
end
else
data[p[1]][p[2]][3] = 0 -- already at position
count = count +1
end
end
return m-count -- how many missaligned
end
render = function()
out = "";
for i = 1,n do
for j= 1,n do
if data[i][j][1]==1 then
local id = data[i][j][2]; id = id % 10;
if data[i][j][3] == 0 then
out = out .. id
else
out = out .. "S" -- didnt move last step
end
else
out = out .. "_" -- empty
end
end
out = out .. "\n"
end
return out
end
s=1
self.listen(1)
end
speaker,msg = self.listen_msg()
if speaker == "rnd" then
if msg == "p" then
say("permute2closest()")
permute2closest()
end
end
if s == 1 then
step = step + 1
local c = step_move();
--state = how many times stuck count was constant; if more than 3x then perhaps it stabilized?
-- stuck = how many robots not yet in position
if c<stuck then stuck = c else state = state + 1; if state > 3 then state = 0 s = 2 end end
self.label(render().. "\nleft " .. stuck .. "="..(100*stuck/m) .. "%")
if stuck == 0 then say("*** COMPLETED! in " .. step .." steps ***") s = 3 end
elseif s == 2 then
-- do swaps of stuck ones..
for i = 1, #pos do
local p = {pos[i][1], pos[i][2]};
local tp = tpos[i];
local x = tp[1]-p[1];
local y = tp[2]-p[2];
local d = math.sqrt(x^2+y^2);
if d~=0 then
x=x/d;y=y/d
x=p[1]+x;y=p[2]+y;
x=math.floor(x+0.5);y=math.floor(y+0.5); -- see whats going on in attempted move direction
if data[x][y][1]==1 then -- there is obstruction, do id swap
local x1,y1;
x1 = x; y1 = y;
local idb = data[x][y][2]; -- blocker id, stuck robot id is i
pos[i]={x,y}; -- new position for id-i is position of blocker
pos[idb] = {p[1],p[2]}; -- new position for blocker is position of stuck robot
-- reset stuck status
data[x][y][3]=1;data[p[1]][y][p[2]]=1;
end
end
end
s=1
end
--TO DO: if robots stuck do permute2closest again

View File

@ -0,0 +1,132 @@
-- rnd 2017
if not data then
m=50;n=50; minescount = m*n/14;
t0 = _G.minetest.get_gametime();
rom.data = {}; rom.rooms = {} -- so we dont make new tables everytime
data = rom.data; spawnpos = self.spawnpos();
rooms = rom.rooms;
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
get_mine_count = function(i,j)
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
for k = -1,1 do for l = -1,1 do
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
end end
return count
end
-- generate level data
for i = 1,m do rooms[i]={}; for j = 1,n do
if get_mine_count(i,j) > 0 or (data[i] and data[i][j] == 1) then
rooms[i][j] = 1
else
rooms[i][j] = 0
end
end end
-- find passages
for i = 2,m-1 do for j = 2,n-1 do
if rooms[i][j] == 0 then
local A11 = rooms[i-1][j-1]; local A21 = rooms[i][j-1];local A31 = rooms[i+1][j-1];
local A12 = rooms[i-1][j]; local A32 = rooms[i+1][j];
local A13 = rooms[i-1][j+1]; local A23 = rooms[i][j+1];local A33 = rooms[i+1][j+1];
if (A12~=1 and A32~=1 and A21 == 1 and A23 == 1) or
(A12==1 and A32==1 and A21 ~= 1 and A23 ~= 1)
then
rooms[i][j] = 2; -- passage
end
end
end end
read_room = function(i,j)
if i<1 or i > m then return nil end
if j<1 or j > n then return nil end
return rooms[i][j]
end
render_rooms = function()
for i = 1,m do for j = 1,n do
local tile = rooms[i][j];
if tile == 0 then
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "air"})
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+j}, {name = "air"})
elseif tile == 1 then
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "basic_robot:buttonFFFFFF"})
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+j}, {name = "basic_robot:buttonFFFFFF"})
elseif tile == 2 then -- passage, insert 1 door in it
--determine direction
local dir = {0,0}
if read_room(i+1,j) == 2 then dir = {1,0}
elseif read_room(i-1,j) == 2 then dir = {-1,0}
elseif read_room(i,j+1) == 2 then dir = {0,1}
elseif read_room(i,j-1) == 2 then dir = {0,-1}
elseif read_room(i-1,j) ~= 0 or read_room(i+1,j) ~= 0 then dir = {0,1}
else dir = {1,0}
end
local k1 = 0; local k2 = 0;
for k = 1, 10 do
if read_room(i+dir[1]*k,j+dir[2]*k)~= 2 then k1 = k-1 break
else
if rooms[i+dir[1]*k] then rooms[i+dir[1]*k][j+dir[2]*k] = 0 end
_G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y,z=spawnpos.z+j+dir[2]*k}, {name = "air"})
end
end
for k = 1, 10 do
if read_room(i-dir[1]*k,j-dir[2]*k)~= 2 then k2 = -(k-1) break
else
if rooms[i+dir[1]*k] then rooms[i+dir[1]*k][j+dir[2]*k] = 0 end
_G.minetest.swap_node({x=spawnpos.x+i-dir[1]*k,y=spawnpos.y,z=spawnpos.z+j-dir[2]*k}, {name = "air"})
end
end
local k = math.floor((k1+k2)/2);
--place door
local param = 1
if dir[1]~=0 then param = 2 end
_G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y+1,z=spawnpos.z+j+dir[2]*k}, {name = "air"})
if param == 1 then
_G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y,z=spawnpos.z+j+dir[2]*k}, {name = "doors:door_wood_a", param2 = 2})
else
_G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y,z=spawnpos.z+j+dir[2]*k}, {name = "doors:door_wood_a", param2 = 1})
end
elseif tile == 3 then
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "default:stonebrick"})
end
end end
end
render_rooms()
fill_room = function(x,y, roomIdx) -- room index: 1,2,3,... will be written as -1,-2,.. in rooms
local tile = rooms[i][j];
if tile ~= 0 then return false end
rooms[i][j] = -roomIdx;
local stk = {{i,j}}; -- stack to place border tiles
local tmpstk = {}; -- temporary stack
local free = true; -- are there any free room tiles
while free do
-- loop all stack tiles
for i=1,#stk do
local p = stk[i];
tile = rooms[p[1]][p[2]];
end
end
end
end
self.remove()

View File

@ -0,0 +1,70 @@
-- rnd 2017
if not data then
data = {FUEL = 1.8,TV=0,T=0,P=0,E=0;}
generate_nuclear_power = function(CONTROL,COOLING)
if COOLING>1 then COOLING = 1 elseif COOLING<0 then COOLING = 0 end
if CONTROL>1 then CONTROL = 1 elseif CONTROL<0 then CONTROL = 0 end
--data = ...
local FUEL = data.FUEL;
local TV = data.TV;
local T = data.T;
local P = data.P;
local E = data.E;
-- reactor specifications
local TVmax = 10000;
local FUELC = 2; -- critical mass for fuel
local DECAYRATE = 1-0.01; -- how much fuel decays per time step
local COOLINGCOEF = 1; -- how efficient is cooling per 1 unit of power (how many degrees are cooled)
local PCOEF = 1; -- how efficiently temperature is converted to power
local TGV = FUEL/(1-(FUEL/FUELC)^2);
if TGV>TVmax then TGV = TVmax end; if FUEL>FUELC then TGV = TVmax end -- basic temperature generation speed generated in core
TV = TV + TGV* CONTROL - P*COOLING*COOLINGCOEF; -- temperature change speed
T = T + TV ;
P = 0.5*P + T*PCOEF; P = P - P*COOLING -- produced power
FUEL = FUEL*DECAYRATE;
if P<0 then P = 0 end if T<0 then T = 0 end E=E+P;
data.FUEL = FUEL;
data.T = T;
data.TV = TV;
data.P = P;
data.E = E
return E, P, T, FUEL, TV
end
render = function(data) -- data should be normalized [0,1]
local tname = "pix.png";
local obj = _G.basic_robot.data[self.name()].obj;
local n = 150; local m = n;
local length = #data; if length == 0 then return end
local hsize = 1; local wsize=hsize;
local tex = "[combine:"..(wsize*m).."x"..(hsize*n);
for i = 1,length do
j=math.floor((1-data[i])*m);
local ix = math.floor((i/length)*n)
tex = tex .. ":"..(ix*wsize).."," .. (j*hsize) .. "="..tname
end
obj:set_properties({visual = "sprite",textures = {tex}})
end
tdata = {};
COOLING = 0.03
end
-- generate_nuclear_power(CONTROL, COOLING)
-- CONTROL = 0.20; -- control rods; 1 = no control, between 0 and 1
-- COOLING = 0.03; -- how much power assigned for cooling, in ratio of output power P; between 0 and 1
E,P,T,FUEL,TV = generate_nuclear_power(0.2,COOLING)
-- cooling strategy
if TV < 0 then COOLING = 0.0 elseif T>90 then COOLING = 0.03 else COOLING = 0 end
tdata[#tdata+1]=math.min(T/100,1);
render(tdata)
self.label( "T " .. T .. "\nTV " .. TV .. "\nP ".. P .. "\nFUEL " .. FUEL .. "\nTOTAL ENERGY PRODUCED " .. E )

View File

@ -0,0 +1,507 @@
-- REDSTONE EMULATOR & EDITOR
--v 06/28/2018a
if not init then
local players = find_player(5);
if not players then
name = "";
else
name = players[1]
player_ = puzzle.get_player(name)
local inv = player_:get_inventory();
inv:set_stack("main", 8, puzzle.ItemStack("basic_robot:control 1 0 \"@\"")) -- add controller in players inventory
--add items for building
inv:set_stack("main", 1, puzzle.ItemStack("default:pick_diamond"))
inv:set_stack("main", 2, puzzle.ItemStack("basic_robot:button_273 999")) -- switch 9 = 273/274
inv:set_stack("main", 3, puzzle.ItemStack("basic_robot:button_275 999")) -- button 7 = 275/276
inv:set_stack("main", 4, puzzle.ItemStack("basic_robot:button_277 999")) -- equalizer 61 = 277
inv:set_stack("main", 5, puzzle.ItemStack("basic_robot:button_278 999")) -- setter 15 = 278
inv:set_stack("main", 6, puzzle.ItemStack("basic_robot:button_279 999")) -- piston 171 = 279
inv:set_stack("main", 7, puzzle.ItemStack("basic_robot:button_282 999")) -- delayer 232 = 282
inv:set_stack("main", 9, puzzle.ItemStack("basic_robot:button_281 999")) -- NOT 33 = 281
inv:set_stack("main", 10, puzzle.ItemStack("basic_robot:button_280 999")) -- diode 175 = 280
inv:set_stack("main", 11, puzzle.ItemStack("basic_robot:button_283 999")) -- platform 22 = 283
inv:set_stack("main", 12, puzzle.ItemStack("basic_robot:button_284 999")) -- giver 23 150/284
inv:set_stack("main", 13, puzzle.ItemStack("basic_robot:button_285 999")) -- checker 24 151/285
local round = math.floor; protector_position = function(pos) local r = 32;local ry = 2*r; return {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry,z=round(pos.z/r+0.5)*r}; end
local spawnblock = protector_position(self.spawnpos())
local meta = puzzle.get_meta(spawnblock);
meta:set_string("shares", name) -- add player to protection!
puzzle.chat_send_player(name,colorize("yellow","#EDITOR: if you need any blocks get them by using 'give me' in craft guide. you can now use controller to make links from pointed at blocks. In addition hold SHIFT to display infos. Reset block links by selecting block 2x"))
end
init = true
self.spam(1)
self.label(colorize("orange","REDSTONE EMULATOR/EDITOR"))
-- 1. EMULATOR CODE
TTL = 16 -- signal propagates so many steps before dissipate
--self.label(colorize("red","REDSTONE")..colorize("yellow","EMULATOR"))
opcount = 0;
-- DEFINITIONS OF BLOCKS THAT CAN BE ACTIVATED
toggle_button_action = function(mode,pos,ttl) -- SIMPLE TOGGLE BUTTONS - SWITCH
if not ttl or ttl <=0 then return end
if mode == 1 then -- turn on
puzzle.set_node(pos,{name = "basic_robot:button_274"})
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
else -- turn off
puzzle.set_node(pos,{name = "basic_robot:button_273"})
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
end
end
button_action = function(mode,pos,ttl) -- SIMPLE ON BUTTON, TOGGLES BACK OFF after 1s
if not ttl or ttl <=0 then return end
if mode == 0 then return end
puzzle.set_node(pos,{name = "basic_robot:button_276"})
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
minetest.after(1, function()
puzzle.set_node(pos,{name = "basic_robot:button_275"})
for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
end)
for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
end
giver_action = function(mode,pos,ttl) -- GIVER: give block below it to player and activate targets
local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name;
if nodename == "air" then return end
local objects = minetest.get_objects_inside_radius(pos, 5);local player1;
for _,obj in pairs(objects) do if obj:is_player() then player1 = obj; break end end
if player1 then
player1:get_inventory():add_item("main", puzzle.ItemStack(nodename))
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
end
end
checker_action = function(mode,pos,ttl) -- CHECKER: check if player has block below it, then remove block from player and activate targets
local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name;
if nodename == air then return end
local objects = minetest.get_objects_inside_radius(pos, 5);local player1;
for _,obj in pairs(objects) do if obj:is_player() then player1 = obj; break end end
if player1 then
local inv = player1:get_inventory();
if inv:contains_item("main", puzzle.ItemStack(nodename)) then
inv:remove_item("main",puzzle.ItemStack(nodename))
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
end
end
end
equalizer_action = function(mode,pos,ttl) -- CHECK NODES AT TARGET1,TARGET2. IF EQUAL ACTIVATE TARGET3,TARGET4,...
if not ttl or ttl <=0 then return end
if mode == 0 then return end
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}).name
local node2 = puzzle.get_node({x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}).name
if node1==node2 then
for i = 3,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
else
for i = 3,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
end
end
delayer_action = function(mode,pos,ttl) -- DELAY FORWARD SIGNAL, delay determined by distance of target1 from delayer ( in seconds)
if not ttl or ttl <=0 then return end
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
local pos1 = {x=meta:get_int("x1"),y=meta:get_int("y1"),z=meta:get_int("z1")}
local delay = math.sqrt(pos1.x^2+pos1.y^2+pos1.z^2);
if delay > 0 then
minetest.after(delay, function()
if mode == 1 then
for i = 2,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
else
for i = 2,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
end
end)
end
end
diode_action = function(mode,pos,ttl) -- ONLY pass through ON signal
if not ttl or ttl <=0 then return end
if mode ~= 1 then return end
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
end
not_action = function(mode,pos,ttl) -- negate signal: 0 <-> 1
if not ttl or ttl <=0 then return end
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
for i = 1,n do activate(1-mode,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end
end
setter_action = function(mode,pos,ttl) -- SETS NODES IN TARGET AREA TO PRESELECTED NODE
if not ttl or ttl <=0 then return end
if mode == 0 then return end
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
if n ~= 3 then say("#setter: error, needs to be set with 3 links"); return end
local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z})
local pos1 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}
local pos2 = {x=meta:get_int("x3")+pos.x,y=meta:get_int("y3")+pos.y,z=meta:get_int("z3")+pos.z}
if pos1.x>pos2.x then pos1.x,pos2.x = pos2.x,pos1.x end
if pos1.y>pos2.y then pos1.y,pos2.y = pos2.y,pos1.y end
if pos1.z>pos2.z then pos1.z,pos2.z = pos2.z,pos1.z end
local size = (pos2.x-pos1.x+1)*(pos2.y-pos1.y+1)*(pos2.z-pos1.z+1)
if size > 27 then say("#setter: target area too large, more than 27 blocks!"); return end
for x = pos1.x,pos2.x do
for y = pos1.y,pos2.y do
for z = pos1.z,pos2.z do
puzzle.set_node({x=x,y=y,z=z},node1)
end
end
end
end
local piston_displaceable_nodes = {["air"] = 1,["default:water_flowing"] = 1}
piston_action = function(mode,pos,ttl) -- PUSH NODE AT TARGET1 AWAY FROM PISTON
if not ttl or ttl <=0 then return end
--if mode == 0 then return end
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
if n < 1 or n>2 then say("#piston: error, needs to be set with at least link and most two"); return end
local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}
-- determine direction
local dir = {x=pos1.x-pos.x, y= pos1.y-pos.y, z= pos1.z-pos.z};
local dirabs = {x=math.abs(dir.x), y= math.abs(dir.y), z= math.abs(dir.z)};
local dirmax = math.max(dirabs.x,dirabs.y,dirabs.z);
if dirabs.x == dirmax then dir = { x = dir.x>0 and 1 or -1, y = 0,z = 0 }
elseif dirabs.y == dirmax then dir = { x = 0, y = dir.y>0 and 1 or -1, z=0}
else dir = {x = 0, y = 0, z = dir.z>0 and 1 or -1}
end
local pos2 = {x=pos1.x+dir.x,y=pos1.y+dir.y,z=pos1.z+dir.z};
if mode == 0 then pos1,pos2 = pos2,pos1 end
local node1 = puzzle.get_node(pos1)
if node1.name == "air" then return end
if piston_displaceable_nodes[puzzle.get_node(pos2).name] then
puzzle.set_node(pos2, node1)
puzzle.set_node(pos1, {name = "air"})
minetest.check_for_falling(pos2)
self.sound("doors_door_open",1,pos)
end
end
platform_action = function(mode,pos,ttl) -- SPAWN MOVING PLATFORM
if mode~=1 then return end
local meta = puzzle.get_meta(pos);
if not meta then return end
local n = meta:get_int("n");
if n ~= 2 then say("#platform: error, needs to be set with 2 targets"); return end
local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}
local pos2 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}
-- determine direction
local dir = {x=pos2.x-pos1.x, y= pos2.y-pos1.y, z= pos2.z-pos1.z};
local obj = minetest.add_entity(pos1, "basic_robot:projectile");
if not obj then return end
obj:setvelocity(dir);
--obj:setacceleration({x=0,y=-gravity,z=0});
local luaent = obj:get_luaentity();
luaent.name = name;
luaent.spawnpos = pos1;
local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name;
local tiles = minetest.registered_nodes[nodename].tiles; tiles = tiles or {};
local texture = tiles[1] or "default_stone";
obj:set_properties({visual = "cube",textures = {texture,texture,texture,texture,texture,texture},visual_size = {x=1,y=1},
collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}})
end
-- HOW TO ACTIVATE TARGET ELEMENT - adds mesecons/basic machines compatibility
activate = function(mode, pos, ttl)
if not ttl or ttl <=0 then return end
local nodename = puzzle.get_node(pos).name;
local active_element = active_elements[nodename];
opcount = opcount + 1
if opcount > 64 then say("#puzzle error: opcount 64 exceeded. too many active connections."); error("#puzzle: abort") end
if active_element then
active_element(mode,pos,ttl-1)
else -- try mesecons activate
local nodename = puzzle.get_node(pos).name
local table = minetest.registered_nodes[nodename];
if table and table.mesecons then else return end
local effector=table.mesecons.effector;
if mode == 1 then
if effector.action_on then
effector.action_on(pos,node,ttl)
end
else
if effector.action_off then
effector.action_off(pos,node,ttl)
end
end
end
end
-- THESE REACT WHEN PUNCHED
interactive_elements = {
[275] = {button_action,1}, -- BUTTON, 1 means it activates(ON) on punch
[273] = {toggle_button_action,1}, -- TOGGLE BUTTON_OFF
[274] = {toggle_button_action,0}, -- TOGGLE BUTTON_ON, 0 means it deactivates
[284] = {giver_action,0}, -- GIVER: give player item below it when activated and activate targets after that
[285] = {checker_action,0}, -- CHECKER: check if player has block below it in inventory, remove it and activate targets after that
}
-- THESE CAN BE ACTIVATED WITH SIGNAL
active_elements = {
["basic_robot:button_275"] = button_action, -- BUTTON, what action to do on activate
["basic_robot:button_273"] = toggle_button_action, -- TOGGLE BUTTON_OFF
["basic_robot:button_274"] = toggle_button_action, -- TOGGLE BUTTON_ON
["basic_robot:button_278"] = setter_action, -- SETTER
["basic_robot:button_277"] = equalizer_action, -- EQUALIZER
["basic_robot:button_279"] = piston_action, -- PISTON
["basic_robot:button_283"] = platform_action, -- PLATFORM
["basic_robot:button_282"] = delayer_action, -- DELAYER
["basic_robot:button_280"] = diode_action, -- DIODE
["basic_robot:button_281"] = not_action, -- NOT
}
-- EDITOR CODE --
edit = {};
edit.state = 1; -- tool state
edit.source = {}; edit.sourcenode = ""; -- selected source
-- blocks that can be activated
edit.active_elements = {
["basic_robot:button_275"] = "button: now select one or more targets", -- button
["basic_robot:button_273"] = "switch: now select one or more targets", -- switch OFF
["basic_robot:button_274"] = "switch: now select one or more targets", -- switch ON
["basic_robot:button_278"] = "setter: target1 defines what material wall will use, target2/3 defines where wall will be", -- setter
["basic_robot:button_277"] = "equalizer: target1 and target2 are for comparison, other targets are activated", -- equalizer
["basic_robot:button_279"] = "piston: push block at target1 in direction away from piston", -- equalizer
["basic_robot:button_283"] = "platform: select target1 to set origin, target2 for direction", -- PLATFORM
["basic_robot:button_282"] = "delayer: distance from delayer to target1 determines delay", -- delayer
["basic_robot:button_280"] = "diode: only pass through ON signal", -- DIODE
["basic_robot:button_281"] = "NOT gate: negates the signal", -- NOT
["basic_robot:button_284"] = "GIVER: give player item below it when activated and activate targets after that",
["basic_robot:button_285"] = "CHECKER: check if player has block below it in inventory, remove it and activate targets after that",
}
linker_use = function(pos)
if not pos then return end
--say(serialize(player_:get_player_control()))
if edit.state < 0 then -- link edit mode!
local meta = puzzle.get_meta(edit.source);
local i = -edit.state;
meta:set_int("x" ..i, pos.x-edit.source.x); meta:set_int("y" ..i, pos.y-edit.source.y); meta:set_int("z" ..i, pos.z-edit.source.z)
puzzle.chat_send_player(name, colorize("red", "EDIT ".. " target " .. i .. " changed"))
edit.state = 1
goto display_particle
end
if player_:get_player_control().sneak then -- SHOW LINKS
local meta = puzzle.get_meta(pos);
local n = meta:get_int("n");
local nodename = puzzle.get_node(pos).name;
local active_element = edit.active_elements[nodename]
if active_element and edit.source.x == pos.x and edit.source.y == pos.y and edit.source.z == pos.z then -- gui with more info
local form = "size[4,"..(0.75*n).."] label[0,-0.25; "..active_element .."]" ;
for i = 1,n do -- add targets as lines
form = form ..
"button[0,".. (0.75*i-0.5) .. ";1,1;".."S"..i..";" .. "show " .. i .. "]"..
"button_exit[1,".. (0.75*i-0.5) .. ";1,1;".."E"..i..";" .. "edit " .. "]" ..
"button_exit[2,".. (0.75*i-0.5) .. ";1,1;".."D"..i..";" .. "delete " .. "]"..
"label[3,".. (0.75*i-0.25) .. "; " .. meta:get_int("x"..i) .. " " .. meta:get_int("y"..i) .. " " .. meta:get_int("z"..i) .."]"
end
self.show_form(name,form);
edit.state = 3;
return
end
edit.source = {x=pos.x,y=pos.y, z=pos.z};
edit.state = 1
if not active_element then return end
local i = string.find(active_element,":");
if not i then return end
puzzle.chat_send_player(name,colorize("red","#INFO ".. string.sub(active_element,1,i-1) ..":") .." has " .. n .. " targets. Select again for more info.")
meta:set_string("infotext",string.sub(active_element,1,i-1)) -- write name of element on it!
for i = 1, n do
minetest.add_particle(
{
pos = {x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},
expirationtime = 5,
velocity = {x=0, y=0,z=0},
size = 18,
texture = "010.png",
acceleration = {x=0,y=0,z=0},
collisiondetection = true,
collision_removal = true,
}
)
end
return
end
if edit.state == 1 then -- SET SOURCE
local nodename = puzzle.get_node(pos).name;
local active_element = edit.active_elements[nodename]
if not active_element then puzzle.chat_send_player(name,colorize("red","#ERROR linker:").. " source must be valid element like switch"); return end
edit.source = {x=pos.x,y=pos.y, z=pos.z};
sourcenode = nodename;
puzzle.chat_send_player(name, colorize("yellow","SETUP " ..edit.state .. ": ").. active_element)
edit.state = 2
else -- SET TARGET
local meta = puzzle.get_meta(edit.source);
local n = meta:get_int("n");
if edit.state == 2 and pos.x == edit.source.x and pos.y == edit.source.y and pos.z == edit.source.z then -- RESET LINK FOR SOURCE
local meta = puzzle.get_meta(pos);meta:set_int("n",0) -- reset links
puzzle.chat_send_player(name, colorize("red", "SETUP " .. edit.state .. ":") .. " resetted links for selected source.")
edit.state = 1;return
else
n=n+1;
meta:set_int("x"..n, pos.x-edit.source.x);meta:set_int("y"..n, pos.y-edit.source.y);meta:set_int("z"..n, pos.z-edit.source.z) -- relative to source!
meta:set_int("n",n)
puzzle.chat_send_player(name, colorize("red", "SETUP "..edit.state .. ":") .. " added target #" .. n)
edit.state = 1
end
end
-- display
::display_particle::
minetest.add_particle(
{
pos = pos,
expirationtime = 5,
velocity = {x=0, y=0,z=0},
size = 18,
texture = "009.png",
acceleration = {x=0,y=0,z=0},
collisiondetection = true,
collision_removal = true,
}
)
end
tools = {
["basic_robot:control"] = linker_use
}
------ END OF EDIT PROGRAM
end
opcount = 0
event = keyboard.get() -- handle keyboard
if event then
if event.type == 0 then -- EDITING
if event.puncher == name then -- players in protection can edit -- not minetest.is_protected({x=event.x,y=event.y,z=event.z},event.puncher)
local wield_item = player_:get_wielded_item():get_name()
local tool = tools[wield_item]
if tool then tool({x=event.x,y=event.y,z=event.z}) end
end
else -- EMULATOR
local typ = event.type;
local interactive_element = interactive_elements[typ]
if interactive_element then
interactive_element[1](interactive_element[2],{x=event.x,y=event.y,z=event.z},TTL)
self.sound("doors_glass_door_open",1,{x=event.x,y=event.y,z=event.z})
end
end
end
sender,fields = self.read_form() -- handle gui for editing
if sender then
edit.state = 1
for k,_ in pairs(fields) do
local c = string.sub(k,1,1);
local i = tonumber(string.sub(k,2)) or 1;
if c == "S" then
local meta = puzzle.get_meta(edit.source);
minetest.add_particle(
{
pos = {x=meta:get_int("x"..i)+edit.source.x,y=meta:get_int("y"..i)+edit.source.y,z=meta:get_int("z"..i)+edit.source.z},
expirationtime = 5,
velocity = {x=0, y=0,z=0},
size = 18,
texture = "010.png",
acceleration = {x=0,y=0,z=0},
collisiondetection = true,
collision_removal = true,
}
)
elseif c == "E" then
edit.state = -i;
puzzle.chat_send_player(name, colorize("yellow", "#EDIT: select target " .. i));
elseif c == "D" then
local meta = puzzle.get_meta(edit.source);
local n = meta:get_int("n")
if n > 0 then
for j = i,n-1 do
meta:set_int("x"..j, meta:get_int("x"..(j+1)))
meta:set_int("y"..j, meta:get_int("y"..(j+1)))
meta:set_int("z"..j, meta:get_int("z"..(j+1)))
end
meta:set_int("n",n-1)
end
puzzle.chat_send_player(name, colorize("red", "#EDIT: target " .. i .. " deleted"));
end
--say(serialize(fields))
end
end

View File

@ -0,0 +1,266 @@
-- cheat trust game by rnd, made in 60 minutes
-- http://ncase.me/trust/
--[[
TO DO:
hierarchies, they determine who plays with who (boss plays only with his direct underlings)
fakers: they give fake +points
--]]
if not init then
init = true
if not find_player(6) then error("#TRUST GAME: no players near") end
rules = {
{
{2., 2.}, -- first player cooperate, second player cooperate: +2,+2
{-1, 3}, -- first player cooperate, second player cheat: -1,+3
},
{
{3,-1}, -- first player cheats, second player cooperate
{0,0}, -- first player cheats, second player cheat
}
};
error_rate = 0.0; -- 10% probability we take wrong decision
grudger =
{
name = "grudger",
opponent = {}, -- list of opponent moves from previous games,
state = 1, -- internal state, for grudger it means how he plays next move
reset = function(self)
self.state = 1; self.opponent = {};
end,
game = function(self) -- how will we play next move
if self.state == 1 then
local opp = self.opponent;
if #opp>0 and opp[#opp] == 2 then self.state = 2 end -- you cheat me once and we are done, pardner.
end
return self.state
end,
err = error_rate, -- probability we make different decision
}
cheater =
{
name = "cheater",
opponent = {}, -- list of opponent moves from previous games,
reset = function(self)
self.opponent = {};
end,
game = function(self)
return 2
end,
err = error_rate, -- probability we make different decision
}
cooperator =
{
name = "cooperator",
opponent = {},
reset = function(self)
self.opponent = {};
end,
game = function(self)
return 1
end,
err = error_rate, -- probability we make different decision
}
copycat =
{
name = "copycat",
opponent = {},
reset = function(self)
self.opponent = {};
end,
game = function(self)
local opp = self.opponent;
if #opp>0 then return opp[#opp] else return 1 end -- i do to you what you did to me last move
end,
err = error_rate, -- probability we make different decision
}
fcopycat =
{
name = "forgiving copycat",
opponent = {},
reset = function(self)
self.opponent = {}; self.state = 0; self.cheat = 0;
end,
state = 0,
cheat = 0,
game = function(self)
local opp = self.opponent;
if #opp>0 then
if opp[#opp] == 2 then -- you cheat me
if self.state == 1 then self.cheat = self.cheat+ 1 else self.state = 1 end -- cheat in a row
else
self.state = 0
self.cheat = 0
end
if self.cheat >= 1 then -- fk you
return 2
else -- you cheated me less than 2x, its still cool
return 1
end
else -- first time
return 1
end
end,
err = error_rate, -- probability we make different decision
}
detective =
{
name = "detective",
opponent = {},
moves = {1,2,1,1}, -- starting 4 moves
step = 0, -- what move we played so far
state = 0,
reset = function(self)
self.state = 0; self.step = 0; self.opponent = {};
end,
game = function(self) -- how will we play next move
local st = self.step+1;
self.step = st;
local opp = self.opponent;
if st < 5 then
if self.state == 0 and opp[#opp] == 2 then self.state = 1 end -- i caught you cheating!
return self.moves[st]
end
if self.state == 0 then -- exploiter
return 2
else -- copycat
if #opp>0 then return opp[#opp] end -- copycat
end
return self.state
end,
err = error_rate, -- probability we make different decision
}
-- internal functions
Player = {}
function Player:new (o)
ret = {}; _G.setmetatable(ret, self); self.__index = self
for k,v in pairs(o) do ret[k] = v end
ret.points = 0
return ret
end
gamestep = function(player1,player2)
local res1 = player1:game(); if math.random(1000)<=1000*player1.err then res1 = 3-res1 end
local opponent = player2.opponent;
opponent[#opponent+1] = res1; -- player2 remembers player1 move
local res2 = player2:game(); if math.random(1000)<=1000*player2.err then res2 = 3-res2 end
opponent = player1.opponent;
opponent[#opponent+1] = res2; -- player1 remembers player2 move
local res = rules[res1][res2];
player1.points = player1.points + res[1];
player2.points = player2.points + res[2];
return res1,res2 -- return what players did
end
paired_match = function(player1,player2, rounds)
player1:reset();player2:reset()
for i = 1, rounds do
gamestep(player1,player2)
end
end
sort_players = function(players)
table.sort(players,
function(p1,p2) return p1.points<p2.points end
) -- sorts in ascending order of points
end
group_match = function(players,elimination) -- each player plays once with every other player
local n = #players;
local m = 10; -- play m games with each opponent
for i = 2,n do
for j = 1,i-1 do
if math.random(2) == 1 then
paired_match(players[i],players[j],m)
else
paired_match(players[j],players[i],m)
end
end
end
if elimination then
sort_players(players)
for i = 1,5 do -- replace 5 worse players with clones of 5 best players
local points2 = players[#players-i+1].points;
if points2 then
local points1 = players[i].points;
if points1<points2 or (points1==points2 and math.random(2) == 1) then
players[i] = Player:new( players[#players-i+1] );
players[i].points = points2;
end
end
end
end
end
get_results = function(players)
local msg = colorize("red","player results\n")
for j = 1, #players do -- display final results
msg = msg .. j .. ". " .. players[j].name .. ": points " .. players[j].points .. "\n"
end
return msg
end
-- SIMULATION START
self.spam(1)
_G.math.randomseed(os.time())
players = {Player:new(grudger), Player:new(cheater), Player:new(cooperator), Player:new(copycat),Player:new(detective)} --,Player:new(fcopycat)}
M = 10000; -- do 1000 matches, make average later
for i = 1, M do
group_match(players) -- SIMPLE GROUP MATCH, everyone play 1x vs everyone else (random player ordering in single match)
end
for i =1,#players do players[i].points = players[i].points/M end
-- players = {}
-- for i = 1, 5 do players[#players+1 ] = Player:new(grudger) end
-- for i = 1, 5 do players[#players+1 ] = Player:new(cheater) end
-- for i = 1, 5 do players[#players+1 ] = Player:new(cooperator) end
-- for i = 1, 5 do players[#players+1 ] = Player:new(copycat) end
-- for i = 1, 5 do players[#players+1 ] = Player:new(detective) end
-- group_match(players)
-- for i = 1, 10 do-- play 10 matches with elimination
-- group_match(players,true)
-- end
sort_players(players)
self.label("10000 rounds + averaged, error rate = " .. error_rate .."\n" .. get_results(players) .. "comment: under 6.2% error copycats more succesful, but above 6.2% cheaters. at 50% error everyone same")
players = nil; -- clear up
--STEP BY STEP DUAL VS 2 PLAYERS
-- players = {Player:new(grudger),Player:new(detective)};
-- step = 0;
-- say("#game start")
-- for i = 1, 10 do
-- step = step +1
-- local res1,res2;
-- res1,res2 = gamestep(players[1],players[2])
-- say("step " .. step .. ": " .. players[1].name .. " " .. res1 .. " -> " .. players[1].points .. " VS " .. players[2].name .. " " .. res2 .. " -> " .. players[2].points);
-- end
end

View File

@ -0,0 +1,28 @@
if not cmd then
cmd = {
["f"] = function() move.forward() end,
["b"] = function() move.backward() end,
["l"] = function() move.left() end,
["r"] = function() move.right() end,
["u"] = function() move.up() end,
["d"] = function() move.down() end,
["a"] = function() activate.forward(1) end,
["<"] = function() turn.left() end,
[">"] = function() turn.right() end,
}
i=0;
prog = read_text.right(); s=0
prog = string.gsub(prog,"%s","");
--say(prog)
self.label("RUNNING PROGRAM: " .. prog);n=string.len(prog);
if string.sub(prog,1,1) == " " then self.label("WRITE A PROGRAM FIRST!") s=1 end
end
if s == 0 then
i=i+1; if i > n then self.label("PROGRAM ENDED");s=1 end;
if s == 0 then
c=string.sub(prog,i,i)
if cmd[c] then cmd[c]() else self.label("INVALID PROGRAM INSTRUCTION : " .. c) s=1 end
end
end

62
scripts/spawn_quiz.lua Normal file
View File

@ -0,0 +1,62 @@
if not s then
s=0
t=0
option = {"A","B","C","D","E"}
generate_question = function()
local a = math.random(10)+0;
local b = math.random(10)+0;
local c = math.random(20)-10;
local d = a*b+c;
msg = "To get out solve the math problem\n";
msg = msg .. colorize("LawnGreen",a.." * "..b.." + "..c .. " = ?\n\n")
problem = a.."*"..b.."+"..c .. " = ?";
correct = math.random(5);
local frm = "";
for i =1,5 do
local offset = 0;
if i~=correct then offset = math.random(10)-5; if offset == 0 then offset = -1 end end
frm = frm .. "button_exit[".. -0.1+(i-1)*1.25 ..",0.75;1.25,1;" .. i .. ";".. d + offset .. "]"
end
local form = "size[6,1.25]" .. "label[0.05,-0.3;".. msg.."] "..frm .. "button_exit[4.9,-0.25;1.2,1;cancel;cancel]";
return form, correct
end
selection = 1;
question = "";
problem = "";
end
if t%4 == 0 then
t = 0; form,selection = generate_question();
for _,obj in pairs(_G.minetest.get_objects_inside_radius({x=2,y=2,z=0}, 1)) do
if obj:is_player() then
local pname = obj:get_player_name();
self.show_form(pname,form)
end
end
end
t=t+1;
sender,fields = self.read_form()
if sender then
player = _G.minetest.get_player_by_name(sender);
if player then
answer = 0;
for i = 1,5 do if fields[_G.tostring(i)] then answer = i end end
if answer == correct then
player:setpos({x=0,y=2,z=3})
--inv = player:get_inventory(); inv:add_item("main", "default:apple")
--_G.minetest.chat_send_player(sender,"<MATH ROBOT> congratulations, here is an apple.")
elseif answer ~= 0 then
player:setpos({x=0,y=-6,z=-1})
say(sender .. " failed to solve the problem " .. problem)
self.show_form(sender, "size[1.25,0.5] label[0,0; WRONG]")
end
end
end

112
scripts/tree_harvest.lua Normal file
View File

@ -0,0 +1,112 @@
-- rnd, 2017
if not tree then
tree = {};
wood = "default:tree";
support = "default:tree";
leaves = "default:leaves";
sapling = "default:sapling";
tree.s = 0 -- searching
tree.st = 0
end
tree_step = function()
if tree.s == 0 then -- search
node = read_node.forward()
if node == wood then tree.s = 1 end
elseif tree.s==1 then -- found
dig.forward();
move.forward();
tree.s=2
elseif tree.s==2 then -- dig up
node = read_node.up()
dig.up()
move.up()
place.down(support)
if node ~= wood then
tree.s=3 tree.st = 0
end
elseif tree.s==3 then -- on top
if tree.st == 0 then
move.up();
turn.right();place.forward_down(support);
dig.forward()
turn.angle(180); place.forward_down(support);
tree.st=1
elseif tree.st == 1 then
dig.forward(); move.forward();
tree.st=2
elseif tree.st == 2 then
turn.left(); dig.forward(); turn.angle(180)
tree.st=3
elseif tree.st == 3 then
dig.forward(); turn.right(); tree.st = 4
elseif tree.st == 4 then
dig.down();move.forward(); move.forward(); tree.st = 5
elseif tree.st == 5 then
turn.left(); dig.forward(); turn.angle(180)
tree.st=6
elseif tree.st == 6 then
dig.forward(); turn.right(); tree.st = 7
elseif tree.st == 7 then
dig.down();move.forward();
turn.right();
tree.st = 0; tree.s = 4
end
elseif tree.s == 4 then -- going down
node = read_node.down()
if node == wood then
dig.down();
move.down();
else
pickup(8); move.forward();
place.backward(sapling)
tree.s=5
end
end
end
-- walk around
if not s then
wall = "basic_robot:buttonFFFFFF";
s=0
if rom.tree and rom.s then
tree.s = rom.tree.s; tree.st = rom.tree.st
s = rom.s;
rom.s = nil;
else
rom.tree = {}
end;
angle = 90
end
if s==0 then -- walk
if not move.forward() then
node = read_node.forward();
if node == wood then
s = 1
else
turn.angle(angle); node = read_node.forward()
if node == wall then
turn.angle(180);move.forward();turn.angle(-angle)
else
move.forward(); turn.angle(angle)angle=-angle;
end
end
end
elseif s==1 then
tree_step();
if tree.s == 5 then s = 0; tree.s = 0 end
end
rom.s = s;rom.tree.s = tree.s; rom.tree.st = tree.st -- remember whats it doing
--self.label(s .. " " .. tree.s .. " " .. tree.st)

46
scripts/utils/chatlog.lua Normal file
View File

@ -0,0 +1,46 @@
--rnd 2017
if not logdata then
self.label("chatlog bot");
_G.minetest.forceload_block(self.pos(),true)
n = 250;
idx = 1;
logdata = {};
insert = function(text) -- insert new message
idx = idx +1;
if idx > n then idx = 1 end
logdata[idx] = text;
end
last = function(k,filter) -- return last k messages
if k > n then k = 30 end
local i,j,ret;
i=idx;j=0; ret = ""
for j = 1,k do
if not logdata[i] then break end
if filter and not string.find(logdata[i], filter) then
else
ret = ret .. logdata[i] .. "\n";
end
i=i-1; if i < 1 then i = n end
end
return ret
end
self.listen(1)
end
speaker, msg = self.listen_msg()
if msg then
if string.sub(msg,1,4) == "?log" then
local j = string.find(msg," ",6);
local k = tonumber(string.sub(msg,6) or "") or n;
local text;
if j then text = last(k,string.sub(msg,j+1)) else text = last(k) end
local form = "size[8,8]".. "textarea[0.,0;11.,9.5;text;chatlog;".. text .. "]"
self.show_form(speaker, form)
else
insert(os.date("%X") .. " " .. speaker .. "> " .. msg)
end
end

View File

@ -0,0 +1,89 @@
if not init then
init = true; self.listen(1);
self.spam(1); self.label("help bot")
talk = function(msg) minetest.chat_send_all("<help bot> " .. msg) end
keywords = {
{"tp", 14},
{"help",
{"robot",6},{"",1}
},
{"how",
{"play",1},{"robot", 6},{"stone",4},{"tree",3},{"wood",3},{"lava",5},{"cobble",4},{"dirt",10},
{"do i get",1},{"do i make",1}, {"to get",1}
},
{"i need",
{"wood",3}
},
{"hello",2}, -- words matched must appear at beginning
{"hi",2},
{"back",7},
{" hard",{"",9}}, -- word matched can appear anywhere
{" died", {"",9}},
{" die",{"",8}}, {" dead",{"",8}},
{"rnd",{"",11}},
{"bye",{"",12}},
{"!!",{"",9}},
{"calc", 13},
}
answers = {
"%s open inventory, click 'Quests' and do them to get more stuff", --1
"hello %s",
"do the dirt quest to get sticks then do sapling quest",
"get pumice from lava and water. then search craft guide how to make cobble",
"you get lava as compost quest reward or with grinder", -- 5
"you have to write a program so that robot knows what to do. for list of commands click 'help' button inside robot.",
"wb %s",
"dont die, you lose your stuff and it will reset your level on level 1",
"you suck %s!", -- 9
"to get dirt craft composter and use it with leaves", -- 10
"rnd is afk. in the meantime i can answer your questions",
"bye %s",
function(speaker,msg) -- 13, calc
local expr = string.sub(msg,5); if string.find(expr,"%a") then return end
local res = _G.loadstring("return " .. expr)(); say(expr .. " = " .. res)
end,
function(speaker,msg) -- 14,tp
local p1 = minetest.get_player_by_name(speaker);
local p2 = minetest.get_player_by_name(string.sub(msg,4));
if p1 and p2 then
p1:setpos(p2:getpos())
end
end,
}
end
speaker,msg = self.listen_msg();
if msg then
--msg = string.lower(msg);
sel = 0;
for i = 1, #keywords do
local k = string.find(msg,keywords[i][1])
if k then
if type(keywords[i][2])~="table" then -- one topic only
if k == 1 then sel = keywords[i][2] break end
else
for j=2,#keywords[i] do -- category of several topics
if string.find(msg,keywords[i][j][1]) then
sel = keywords[i][j][2]; break;
end
end
end
end
end
if sel>0 then
local response = answers[sel];
if type(response) == "function" then
response(speaker,msg)
elseif string.find(response,"%%s") then
talk(string.format(response,speaker))
else
talk(response)
end
end
end

View File

@ -0,0 +1,123 @@
if not dict then
lang = "german"
dict = {};
fname = "F:\\games\\rpg\\minetest-0415server\\mods\\basic_translate\\"..lang;
local f = _G.assert(_G.io.open(fname, "r"));local dicts = f:read("*all");f:close()
step = 0; maxwords = 10000;
i=0
while(step<maxwords) do
step=step+1
i1 = string.find(dicts,"\t",i+1)
i2 = string.find(dicts,"\n",i+1)
if not i2 then break end
local word = string.sub(dicts, i+1,i1-1);
local tword = string.sub(dicts, i1+1,i2-1);
local word1; local word2;local data;
i12 = string.find(word," ");
if i12 and i12<i2 then
word1 = string.sub(word,1,i12-1); word2 = string.sub(word,i12+1)
data = dict[word1];
if not data then
dict[word1] = {{[word2] = tword}}
else
data[#data+1] = {[word2] = tword};
end
else
data = dict[word];
if not data then
dict[word] = {{[""] = tword}}
else
data[#data+1] = {[""] = tword};
end
end
--say("X"..word.."X"..tword)
i=i2
end
say(lang .. " dictionary: ".. step .. " words loaded")
-- self.label(string.gsub(_G.dump(dict),"\n",""))
local deb = false;
translate = function(input)
local out = "";
input = string.lower(input)
local i = 0; local n = string.len(input)
local step = 0
while i and i<n and step < 100 do
step = step + 1
local i1 = string.find(input," ",i+1);
local word;
if not i1 then --end
word = string.sub(input, i+1)
else -- just take one word until " "
word = string.sub(input, i+1, i1-1)
end
i1 = i+string.len(word)+1
if deb then say("parsed word " .. word .. " remainder '" .. string.sub(input, i1).."'") end
local data = dict[word];
if data then
if #data == 1 then
local newout = data[1][""];
if not newout then
for key,v in pairs(data[1]) do newout = v; i1 = i1 + string.len(key)+1 break end
end
out = out .. " " .. newout
if deb then say("immediate trans for " .. word) end
else -- more possibilities
if deb then say("more possibilities : ".. #data) end
--check patterns for match:like a de -> c or a b -> d, where data for a = {{[de] = c},{[b] = d}
local found = false
local defaultv = ""
for j=1,#data do
for key,v in pairs(data[j]) do
local keylen = string.len(key)
local pattern = string.sub(input,i1+1,i1+keylen)
if deb then say("pattern '" .. pattern .. "' key '" .. key .. "' len " .. keylen) end
if key == "" then defaultv = v
elseif pattern == key then
found = true;
if deb then say(word .. " " .. pattern .. " -> match key " .. key) end
out = out .. " " .. v; i1 = i1+string.len(key)+1 -- skip to after key
end
end
if found then break end
end
if not found then out = out .. " " .. defaultv end
end
else
out = out .. " " .. word
end
i=i1
if deb then say("next word at i " .. i1 .. " remainder " .. string.sub(input,i1)) end
end
return out
end
-- say(translate("hello world"))
self.listen(1)
end
speaker,msg = self.listen_msg()
if msg then
if string.sub(msg,1,1) == "?" then
msg = string.sub(msg,2)
local transmsg = translate(msg);
_G.minetest.chat_send_all("TRANSLATOR> " .. transmsg)
end
end

View File

@ -0,0 +1,32 @@
-- return minetest object count for 5x5x5 blocks
if not init then init = true
local objs = minetest.get_objects_inside_radius(self.pos(), 30000);
local ret = {};
local round = function(x) return math.floor(x/5)*5 end
local ret = {};
for i = 1, #objs do
local p = objs[i]:get_pos();
local phash = round(p.x) .. " " .. round(p.y) .. " " .. round(p.z);
ret[phash] = (ret[phash] or 0) + 1
end
local out = {};
for k,v in pairs(ret) do
out[#out+1] = {k,v}
end
table.sort(out, function(a,b) return a[2]>b[2] end)
local res = {};
for i = 1, #out do
res[#res+1] = out[i][1] .. "=" .. out[i][2]
end
self.label("#objects " .. #objs .. "\n" .. table.concat(res, "\n"))
end

View File

@ -0,0 +1,16 @@
-- SHOWS ACTIVE ROBOTS AND STATISTICS
if not init then init = true
local data = _G.basic_robot.data;
local ret = {};
for k,v in pairs(data) do
if k~="listening" and v.obj then
local ent = v.obj:get_luaentity();
local t = v.t or 0; if t< 100000 then t = math.floor(t * 10000)/10 else t = 0 end
if ent then ret[#ret+1] = k .. " " .. string.len(ent.code or "") .. " " .. string.len(_G.string.dump(v.bytecode) or "") .. " ~ " .. t end
end
end
mem1 = _G.collectgarbage("count")
self.label("memory used by lua (kbytes) ".. mem1 .. " ( delta " .. mem1 - (mem0 or 0) .. ")\n\nACTIVE ROBOTS\nrobot name | source code size | bytecode size | ~ time (ms)\n" .. table.concat(ret,"\n"))
mem0 = mem1
init = false
end

View File

@ -0,0 +1,76 @@
--SERVER ROBOT : can send various data to other robots that requested it
if not cmds then
-- user auth data
auth = {["rnd1"]=2};
-- server commands
cmds = {
list = {
run = function()
local ret = ""; for i,_ in pairs(cmds) do ret = ret .. " " .. i end; return ret
end,
help = "list all commands",
level = 0
},
help = {
run = function(words)
local arg = words[2];
if not arg then return "help: missing argument" end
local cmd = cmds[arg];
if not cmd then return "help: nonexistent command" end
return cmd.help or ""
end,
help = "display help for command",
level = 0
},
chat = {
run = function(words)
words[1] = "";_G.minetest.chat_send_all("#server bot : " .. table.concat(words," ") or ""); return true;
end,
help = "prints text globally",
level = 2
},
minetest = {
run = function() return minetest end,
help = "returns minetest namespace",
level = 3
}
};
LISTENING = 0; --states
state = LISTENING; -- init
_G.minetest.forceload_block(self.pos(),true)
end
if state == LISTENING then
sender,mail = self.read_mail()
if mail then
if type(mail)~="string" then mail = "" end
self.label("received request " .. mail);
local words = {};
for word in string.gmatch(mail,"%S+") do words[#words+1]=word end -- get arguments
if not words or not words[1] then
self.send_mail(sender,"error: nil request")
else
local cmd = cmds[words[1]];
if not cmd or not cmd.run then
self.send_mail(sender,"error: illegal command")
elseif (auth[sender] or 0) < cmd.level then
self.send_mail(sender,"error: auth level " .. (auth[sender] or 0) ..", need level " .. cmd.level)
else
self.send_mail(sender,cmd.run(words));
self.label("sending data to " .. sender .. " ...")
end
end
else
self.label("listening...")
end
end

View File

@ -0,0 +1,34 @@
-- rnd 2017
if not pos then
pos = self.spawnpos();
n = 6; -- width
m = 4; -- height
door = math.floor(n/2)+1; -- door place
plan = {};
build_cube = function(x,y,z)
plan[#plan+1] = {c= math.random(10)+6, pos={x=pos.x+x,y=pos.y+y,z=pos.z+z}};
end
--floor
y=0;for z=1,n do for x=1,n do build_cube(x,y,z) end end --bottom
z=1;for y=1,m do for x=1,n do build_cube(x,y,z) end end --wall 1
z=n;for y=1,m do for x=1,n do build_cube(x,y,z) end end --wall2
x=n;for y=1,m do for z=2,n-1 do build_cube(x,y,z) end end -- wall3
x=1;for y=1,m do for z=2,n-1 do if z~=door then build_cube(x,y,z) end end end -- wall4
x=1;z=door;for y=3,m do build_cube(x,y,z) end -- door hole
y=m;for x = 2,n-1 do for z = 2,n-1 do build_cube(x,y,z) end end -- ceiling
s=0
--self.remove()
end
s=s+1;
if plan[s] then
keyboard.set(plan[s].pos,plan[s].c)
else
self.remove()
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 B

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 B

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 B

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 B

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 B

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 B

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 B

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 B

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 B

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 B

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 B

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 B

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 B

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 B

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 B

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 B

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 B

After

Width:  |  Height:  |  Size: 118 B

Some files were not shown because too many files have changed in this diff Show More