sandbox bugfixes
parent
c3a021d1fc
commit
2020477700
76
commands.lua
76
commands.lua
|
@ -595,6 +595,19 @@ local write_keyevent = function(data,pos, puncher,type)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local button_punched = function(pos, node, player,type)
|
||||||
|
local name = player:get_player_name(); if name==nil then return end
|
||||||
|
local round = math.floor;
|
||||||
|
local r = basic_robot.radius; local ry = 2*r;
|
||||||
|
local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r}; -- just on top of basic_protect:protector!
|
||||||
|
|
||||||
|
local hppos = minetest.hash_node_position(ppos)
|
||||||
|
local rname = basic_robot.data.punchareas[hppos];
|
||||||
|
local data = basic_robot.data[rname];
|
||||||
|
if data then
|
||||||
|
write_keyevent(data,pos, player:get_player_name(),type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local register_robot_button = function(R,G,B,type)
|
local register_robot_button = function(R,G,B,type)
|
||||||
minetest.register_node("basic_robot:button"..R..G..B,
|
minetest.register_node("basic_robot:button"..R..G..B,
|
||||||
|
@ -605,20 +618,8 @@ local register_robot_button = function(R,G,B,type)
|
||||||
wield_image = "robot_button.png^[colorize:#"..R..G..B..":180",
|
wield_image = "robot_button.png^[colorize:#"..R..G..B..":180",
|
||||||
|
|
||||||
is_ground_content = false,
|
is_ground_content = false,
|
||||||
groups = {cracky=3},
|
groups = {cracky=3,not_in_craft_guide = 1},
|
||||||
on_punch = function(pos, node, player)
|
on_punch = function(pos, node,player) button_punched(pos, node,player,type) end
|
||||||
local name = player:get_player_name(); if name==nil then return end
|
|
||||||
local round = math.floor;
|
|
||||||
local r = basic_robot.radius; local ry = 2*r; -- note: this is skyblock adjusted
|
|
||||||
local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r}; -- just on top of basic_protect:protector!
|
|
||||||
local meta = minetest.get_meta(ppos);
|
|
||||||
local name = meta:get_string("name");
|
|
||||||
local data = basic_robot.data[name];
|
|
||||||
if data then
|
|
||||||
write_keyevent(data,pos, player:get_player_name(),type)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -632,19 +633,8 @@ minetest.register_node("basic_robot:button"..number,
|
||||||
paramtype2 = "facedir",
|
paramtype2 = "facedir",
|
||||||
|
|
||||||
is_ground_content = false,
|
is_ground_content = false,
|
||||||
groups = {cracky=3},
|
groups = {cracky=3,not_in_craft_guide = 1},
|
||||||
on_punch = function(pos, node, player)
|
on_punch = function(pos, node,player) button_punched(pos, node,player,type) end
|
||||||
local name = player:get_player_name(); if name==nil then return end
|
|
||||||
local round = math.floor;
|
|
||||||
local r = basic_robot.radius; local ry = 2*r;
|
|
||||||
local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r};
|
|
||||||
local meta = minetest.get_meta(ppos);
|
|
||||||
local name = meta:get_string("name");
|
|
||||||
local data = basic_robot.data[name];
|
|
||||||
if data then
|
|
||||||
write_keyevent(data,pos, player:get_player_name(),type)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -657,21 +647,9 @@ minetest.register_node("basic_robot:button_"..number,
|
||||||
inventory_image = string.format("%03d",number).. ".png",
|
inventory_image = string.format("%03d",number).. ".png",
|
||||||
wield_image = string.format("%03d",number).. ".png",
|
wield_image = string.format("%03d",number).. ".png",
|
||||||
is_ground_content = false,
|
is_ground_content = false,
|
||||||
groups = {cracky=3},
|
groups = {cracky=3,not_in_craft_guide = 1},
|
||||||
paramtype2 = "facedir",
|
paramtype2 = "facedir",
|
||||||
on_punch = function(pos, node, player)
|
on_punch = function(pos, node,player) button_punched(pos, node,player,type) end
|
||||||
local name = player:get_player_name(); if name==nil then return end
|
|
||||||
local round = math.floor;
|
|
||||||
local r = basic_robot.radius; local ry = 2*r;
|
|
||||||
local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r};
|
|
||||||
local meta = minetest.get_meta(ppos);
|
|
||||||
local name = meta:get_string("name");
|
|
||||||
local data = basic_robot.data[name];
|
|
||||||
if data then
|
|
||||||
write_keyevent(data,pos, player:get_player_name(),type)
|
|
||||||
--data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = player:get_player_name(), type = type}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -683,20 +661,8 @@ minetest.register_node("basic_robot:button_"..number,
|
||||||
inventory_image = texture .. ".png",
|
inventory_image = texture .. ".png",
|
||||||
wield_image = texture .. ".png",
|
wield_image = texture .. ".png",
|
||||||
is_ground_content = false,
|
is_ground_content = false,
|
||||||
groups = {cracky=3},
|
groups = {cracky=3,not_in_craft_guide = 1},
|
||||||
on_punch = function(pos, node, player)
|
on_punch = function(pos, node,player) button_punched(pos, node,player,type) end
|
||||||
local name = player:get_player_name(); if name==nil then return end
|
|
||||||
local round = math.floor;
|
|
||||||
local r = basic_robot.radius; local ry = 2*r;
|
|
||||||
local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r};
|
|
||||||
local meta = minetest.get_meta(ppos);
|
|
||||||
local name = meta:get_string("name");
|
|
||||||
local data = basic_robot.data[name];
|
|
||||||
if data then
|
|
||||||
write_keyevent(data,pos, player:get_player_name(),type)
|
|
||||||
--data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = player:get_player_name(), type = number}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
63
init.lua
63
init.lua
|
@ -1,4 +1,4 @@
|
||||||
-- basic_robot by rnd, 2016
|
-- basic_robot by rnd, 2016-2021
|
||||||
|
|
||||||
|
|
||||||
basic_robot = {};
|
basic_robot = {};
|
||||||
|
@ -26,7 +26,7 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento
|
||||||
|
|
||||||
basic_robot.http_api = minetest.request_http_api();
|
basic_robot.http_api = minetest.request_http_api();
|
||||||
|
|
||||||
basic_robot.version = "2021/03/02a";
|
basic_robot.version = "2021/06/28a";
|
||||||
|
|
||||||
basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management
|
basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management
|
||||||
basic_robot.data = {}; -- stores all robot related data
|
basic_robot.data = {}; -- stores all robot related data
|
||||||
|
@ -40,6 +40,7 @@ basic_robot.ids = {}; -- stores maxid for each player
|
||||||
basic_robot.virtual_players = {}; -- this way robot can interact with the world as "player" TODO
|
basic_robot.virtual_players = {}; -- this way robot can interact with the world as "player" TODO
|
||||||
|
|
||||||
basic_robot.data.listening = {}; -- which robots listen to chat
|
basic_robot.data.listening = {}; -- which robots listen to chat
|
||||||
|
basic_robot.data.punchareas = {}; -- where robots listen punch events, [hashes of 32 sized chunk] = robot name
|
||||||
|
|
||||||
dofile(minetest.get_modpath("basic_robot").."/robogui.lua") -- gui stuff
|
dofile(minetest.get_modpath("basic_robot").."/robogui.lua") -- gui stuff
|
||||||
dofile(minetest.get_modpath("basic_robot").."/commands.lua")
|
dofile(minetest.get_modpath("basic_robot").."/commands.lua")
|
||||||
|
@ -63,6 +64,7 @@ function getSandboxEnv (name)
|
||||||
if not basic_robot.data[name].rom then basic_robot.data[name].rom = {} end -- create rom if not yet existing
|
if not basic_robot.data[name].rom then basic_robot.data[name].rom = {} end -- create rom if not yet existing
|
||||||
local env =
|
local env =
|
||||||
{
|
{
|
||||||
|
_Gerror = error,
|
||||||
pcall=pcall,
|
pcall=pcall,
|
||||||
robot_version = function() return basic_robot.version end,
|
robot_version = function() return basic_robot.version end,
|
||||||
|
|
||||||
|
@ -113,7 +115,7 @@ function getSandboxEnv (name)
|
||||||
obj:set_animation({x=anim_start,y=anim_end}, anim_speed, anim_stand_start)
|
obj:set_animation({x=anim_start,y=anim_end}, anim_speed, anim_stand_start)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
listen = function (mode)
|
listen = function (mode) -- will robot listen to chat?
|
||||||
if mode == 1 then
|
if mode == 1 then
|
||||||
basic_robot.data.listening[name] = true
|
basic_robot.data.listening[name] = true
|
||||||
else
|
else
|
||||||
|
@ -121,6 +123,24 @@ function getSandboxEnv (name)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
listen_punch = function(pos, is_remove) -- robot will listen to punch events in 32 sized chunk containing pos
|
||||||
|
local round = math.floor;
|
||||||
|
local r = basic_robot.radius; local ry = 2*r; -- note: this is skyblock adjusted
|
||||||
|
if not pos then return end
|
||||||
|
local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r}; -- just on top of basic_protect:protector!
|
||||||
|
local hppos = minetest.hash_node_position(ppos)
|
||||||
|
local rname = basic_robot.data.punchareas[hppos];
|
||||||
|
if is_remove then -- remove listener
|
||||||
|
basic_robot.data.punchareas[hppos] = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if rname then -- area already registered for listening
|
||||||
|
return rname
|
||||||
|
end
|
||||||
|
basic_robot.data.punchareas[hppos] = name; -- register robot name
|
||||||
|
end,
|
||||||
|
|
||||||
listen_msg = function()
|
listen_msg = function()
|
||||||
local msg = basic_robot.data[name].listen_msg;
|
local msg = basic_robot.data[name].listen_msg;
|
||||||
local speaker = basic_robot.data[name].listen_speaker;
|
local speaker = basic_robot.data[name].listen_speaker;
|
||||||
|
@ -153,7 +173,7 @@ function getSandboxEnv (name)
|
||||||
reset = function()
|
reset = function()
|
||||||
local pos = basic_robot.data[name].spawnpos;
|
local pos = basic_robot.data[name].spawnpos;
|
||||||
local obj = basic_robot.data[name].obj;
|
local obj = basic_robot.data[name].obj;
|
||||||
obj:setpos({x=pos.x,y=pos.y+1,z=pos.z}); obj:setyaw(0);
|
obj:set_pos({x=pos.x,y=pos.y+1,z=pos.z}); obj:setyaw(0);
|
||||||
end,
|
end,
|
||||||
|
|
||||||
set_libpos = function(pos)
|
set_libpos = function(pos)
|
||||||
|
@ -575,7 +595,7 @@ end
|
||||||
|
|
||||||
check_code = function(code)
|
check_code = function(code)
|
||||||
--"while ", "for ", "do ","goto ",
|
--"while ", "for ", "do ","goto ",
|
||||||
local bad_code = {"repeat", "until", "_c_", "_G", "while%(", "while{", "pcall","%.%.[^%.]"} --,"\\\"", "%[=*%[","--[["}
|
local bad_code = {"repeat", "until", "_G", "while%(", "while{", "pcall","%.%.[^%.]"} --,"\\\"", "%[=*%[","--[["}
|
||||||
for _, v in pairs(bad_code) do
|
for _, v in pairs(bad_code) do
|
||||||
if string.find(code, v) then
|
if string.find(code, v) then
|
||||||
return v .. " is not allowed!";
|
return v .. " is not allowed!";
|
||||||
|
@ -666,8 +686,8 @@ preprocess_code = function(script, call_limit) -- version 07/24/2018
|
||||||
script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") -- strip comments
|
script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") -- strip comments
|
||||||
|
|
||||||
-- process script to insert call counter in every function
|
-- process script to insert call counter in every function
|
||||||
local _increase_ccounter = " _c_ = _c_ + 1; if _c_ > " .. call_limit ..
|
local _increase_ccounter = " _Gc = _Gc + 1; if _Gc > " .. call_limit ..
|
||||||
" then _G.error(\"Execution count \".. _c_ .. \" exceeded ".. call_limit .. "\") end; "
|
" then _Gerror(\"Execution count \".. _Gc .. \" exceeded ".. call_limit .. "\") end; "
|
||||||
|
|
||||||
local i1=0; local i2 = 0;
|
local i1=0; local i2 = 0;
|
||||||
local found = true;
|
local found = true;
|
||||||
|
@ -726,7 +746,7 @@ preprocess_code = function(script, call_limit) -- version 07/24/2018
|
||||||
-- must reset ccounter when paused, but user should not be able to force reset by modifying pause!
|
-- must reset ccounter when paused, but user should not be able to force reset by modifying pause!
|
||||||
-- (suggestion about 'pause' by Kimapr, 09/26/2019)
|
-- (suggestion about 'pause' by Kimapr, 09/26/2019)
|
||||||
|
|
||||||
return "_c_ = 0 local _pause_ = pause pause = function() _c_ = 0; _pause_() end " .. script;
|
return "_Gc = 0 local _Gpause = pause pause = function() _Gc = 0; _Gpause() end " .. script;
|
||||||
|
|
||||||
--return script:gsub("pause%(%)", "_c_ = 0; pause()") -- reset ccounter at pause
|
--return script:gsub("pause%(%)", "_c_ = 0; pause()") -- reset ccounter at pause
|
||||||
end
|
end
|
||||||
|
@ -843,15 +863,18 @@ local robot_spawner_update_form = function (pos, mode)
|
||||||
|
|
||||||
form =
|
form =
|
||||||
"size[9.5,8]" .. -- width, height
|
"size[9.5,8]" .. -- width, height
|
||||||
"textarea[1.25,-0.25;8.75,10.25;code;;".. code.."]"..
|
"style_type[textarea;font_size=12;font=mono;bgcolor=#000000;textcolor=#00FF00;border=false]"..
|
||||||
|
"style_type[button;font_size=14;font=mono;bgcolor=#000000;border=false]"..
|
||||||
|
"style_type[button_exit;font_size=14;font=mono;bgcolor=#000000;border=false]"..
|
||||||
|
"textarea[1.25,-0.25;8.8,10.25;code;;".. code.."]"..
|
||||||
"button[-0.15,7.5;1.25,1;EDIT;EDIT]"..
|
"button[-0.15,7.5;1.25,1;EDIT;EDIT]"..
|
||||||
"button[-0.15,-0.25;1.25,1;OK;"..minetest.colorize("yellow","SAVE").."]"..
|
"button[-0.15,-0.25;1.25,1;OK;"..minetest.colorize("yellow","SAVE").."]"..
|
||||||
"button_exit[-0.15, 0.75;1.25,1;spawn;"..minetest.colorize("green","START").."]"..
|
"button_exit[-0.15, 0.75;1.25,1;spawn;"..minetest.colorize("green","START").."]"..
|
||||||
"button[-0.15, 1.75;1.25,1;despawn;"..minetest.colorize("red","STOP").."]"..
|
"button[-0.15, 1.75;1.25,1;despawn;"..minetest.colorize("red","STOP").."]"..
|
||||||
"field[0.15,3.;1.2,1;id;id;"..id.."]"..
|
"field[0.15,3.;1.2,1;id;id;"..id.."]"..
|
||||||
"button[-0.15, 3.6;1.25,1;inventory;storage]"..
|
"button[-0.15, 3.6;1.25,1;inventory;STORAGE]"..
|
||||||
"button[-0.15, 4.6;1.25,1;library;library]"..
|
"button[-0.15, 4.6;1.25,1;library;LIBRARY]"..
|
||||||
"button[-0.15, 5.6;1.25,1;help;help]";
|
"button[-0.15, 5.6;1.25,1;help;HELP]";
|
||||||
|
|
||||||
else -- when robot clicked
|
else -- when robot clicked
|
||||||
form =
|
form =
|
||||||
|
@ -1531,9 +1554,9 @@ minetest.register_on_player_receive_fields(
|
||||||
local title,text = basic_robot.commands.read_book(itemstack);
|
local title,text = basic_robot.commands.read_book(itemstack);
|
||||||
title = title or ""; text = text or "";
|
title = title or ""; text = text or "";
|
||||||
local dtitle = minetest.formspec_escape(title);
|
local dtitle = minetest.formspec_escape(title);
|
||||||
local form = "size [8,8] textarea[0.,0.;8.75,8.5;book; TITLE : " .. minetest.formspec_escape(title) .. ";" ..
|
local form = "size [8,8] textarea[0.,0.15;8.75,8.35;book; TITLE : " .. minetest.formspec_escape(title) .. ";" ..
|
||||||
minetest.formspec_escape(text) .. "] button_exit[-0.25,7.5;1.25,1;OK;SAVE] "..
|
minetest.formspec_escape(text) .. "] button_exit[-0.25,7.5;1.25,1;OK;SAVE] "..
|
||||||
"button_exit[1.,7.5;2.75,1;LOAD;USE AS PROGRAM] field[4,8;4.5,0.5;title;title;"..dtitle.."]";
|
"button_exit[0.9,7.5;3,1;LOAD;USE AS PROGRAM] field[4,8;4.5,0.5;title;title;"..dtitle.."]";
|
||||||
minetest.show_formspec(player:get_player_name(), "robot_book_".. sel.. ":".. minetest.pos_to_string(libpos), form);
|
minetest.show_formspec(player:get_player_name(), "robot_book_".. sel.. ":".. minetest.pos_to_string(libpos), form);
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -1746,6 +1769,11 @@ minetest.register_node("basic_robot:spawner", {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
effector = {
|
||||||
|
action_on = spawn_robot,
|
||||||
|
action_off = despawn_robot
|
||||||
|
},
|
||||||
|
|
||||||
on_receive_fields = on_receive_robot_form,
|
on_receive_fields = on_receive_robot_form,
|
||||||
|
|
||||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||||
|
@ -1836,9 +1864,10 @@ minetest.register_craftitem("basic_robot:control", {
|
||||||
local pos = pointed_thing.under
|
local pos = pointed_thing.under
|
||||||
if not pos then return end
|
if not pos then return end
|
||||||
local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r}; -- just on top of basic_protect:protector!
|
local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry+1,z=round(pos.z/r+0.5)*r}; -- just on top of basic_protect:protector!
|
||||||
local meta = minetest.get_meta(ppos);
|
|
||||||
local name = meta:get_string("name");
|
local hppos = minetest.hash_node_position(ppos)
|
||||||
local data = basic_robot.data[name];
|
local rname = basic_robot.data.punchareas[hppos];
|
||||||
|
local data = basic_robot.data[rname];
|
||||||
if data then data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = owner, type = 0} end
|
if data then data.keyboard = {x=pos.x,y=pos.y,z=pos.z, puncher = owner, type = 0} end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
12
robogui.lua
12
robogui.lua
|
@ -244,12 +244,14 @@ local help_pages = {
|
||||||
["KEYBOARD AND USER INTERACTIONS"] = {
|
["KEYBOARD AND USER INTERACTIONS"] = {
|
||||||
"back to [Commands reference]",
|
"back to [Commands reference]",
|
||||||
"KEYBOARD","",
|
"KEYBOARD","",
|
||||||
" EVENTS : place spawner at coordinates (r*i,2*r*j+1,r*k) to monitor",
|
" EVENTS : first attach listener to robot with self.listen_punch",
|
||||||
" events. value of r is ".. basic_robot.radius,
|
" self.listen_punch(pos,is_remove) robot will listen to punch events in ",
|
||||||
|
" ".. basic_robot.radius .. " sized chunk containing position",
|
||||||
|
" pos = {x=.., y=.., z=..}. if is_remove==true then remove listener",
|
||||||
" keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. }",
|
" keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. }",
|
||||||
" for keyboard event",
|
" if there was keyboard event, nil if there was none",
|
||||||
" keyboard.set(pos,type) set key at pos of type 0=air,1-6,7-15,16-271,",
|
" keyboard.set(pos,type) set key as a node in world at pos of type",
|
||||||
" limited to range 10 around spawner",
|
" 0=air,1-6,7-15,16-271, limited to range 10 around spawner",
|
||||||
" keyboard.read(pos) return node name at pos",
|
" keyboard.read(pos) return node name at pos",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ if not data then
|
||||||
state = SIGNUP
|
state = SIGNUP
|
||||||
|
|
||||||
t0 = _G.minetest.get_gametime();spawnpos = self.spawnpos() -- place mines
|
t0 = _G.minetest.get_gametime();spawnpos = self.spawnpos() -- place mines
|
||||||
|
self.listen_punch(self.pos()); -- attach punch listener
|
||||||
data = {};
|
data = {};
|
||||||
|
|
||||||
init_game = function()
|
init_game = function()
|
||||||
|
|
|
@ -2,14 +2,16 @@
|
||||||
--https://en.wikipedia.org/wiki/Black_Box_(game)
|
--https://en.wikipedia.org/wiki/Black_Box_(game)
|
||||||
|
|
||||||
if not data then
|
if not data then
|
||||||
m=16;n=16;
|
-- novice: 8x8, 4
|
||||||
atoms = 32
|
m=8;n=8;
|
||||||
|
atoms = 8
|
||||||
attempts = 1;turn = 0;
|
attempts = 1;turn = 0;
|
||||||
spawnpos = self.spawnpos(); spawnpos.x = spawnpos.x-m/2; spawnpos.y = spawnpos.y+2; spawnpos.z = spawnpos.z-n/2
|
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);
|
local players = find_player(5,spawnpos);
|
||||||
if not player then self.remove() else pname = players[1] end
|
if not player then self.remove() else pname = players[1] end
|
||||||
|
|
||||||
|
self.listen_punch(self.pos()) -- attach punch listener
|
||||||
self.spam(1);t0 = _G.minetest.get_gametime();
|
self.spam(1);t0 = _G.minetest.get_gametime();
|
||||||
data = {};
|
data = {};
|
||||||
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
|
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
--checkers by rnd, 1.5 hr
|
--checkers by rnd, 1.5 hr
|
||||||
if not init then init=true
|
if not init then init=true
|
||||||
spos = self.spawnpos()
|
spos = self.spawnpos()
|
||||||
|
self.listen_punch(self.pos()) -- attach punch listener
|
||||||
sizex = 8; sizez= 8
|
sizex = 8; sizez= 8
|
||||||
|
|
||||||
gamepieces = {
|
gamepieces = {
|
||||||
|
@ -81,7 +82,10 @@ if not init then init=true
|
||||||
build_game()
|
build_game()
|
||||||
punchpos = nil; -- pos of last punched piece
|
punchpos = nil; -- pos of last punched piece
|
||||||
step = 0;
|
step = 0;
|
||||||
self.label("checkers\npunch piece then punch board to move")
|
self.label("checkers\npunch piece then punch board to move\n"..
|
||||||
|
"RULES\n\n1. move only diagonal and forward. capture pieces by jumping over them.\nif you can capture you must\n"..
|
||||||
|
"2. once you reach end of board you get king. it can move backward too"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
event = keyboard.get()
|
event = keyboard.get()
|
||||||
|
|
|
@ -4,6 +4,7 @@ if not data then
|
||||||
-- m=3;n=3;turn = 0; num = 3;
|
-- m=3;n=3;turn = 0; num = 3;
|
||||||
self.spam(1);t0 = _G.minetest.get_gametime();
|
self.spam(1);t0 = _G.minetest.get_gametime();
|
||||||
spawnpos = self.spawnpos() -- place mines
|
spawnpos = self.spawnpos() -- place mines
|
||||||
|
self.listen_punch(self.pos()) -- attach punch listener
|
||||||
state = 0; -- 0 signup 1 game
|
state = 0; -- 0 signup 1 game
|
||||||
players = {};
|
players = {};
|
||||||
data = {};
|
data = {};
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
--cyberpunk 2077 'breach protocol puzzle' generator
|
||||||
|
--by rnd, 20 min
|
||||||
|
|
||||||
|
|
||||||
|
if not init then init = true
|
||||||
|
n=4; -- size of square
|
||||||
|
steps = n*n; -- length of sequence
|
||||||
|
tries = n*10; -- how many tries in current row/col before giving up
|
||||||
|
|
||||||
|
tb = {};
|
||||||
|
for i = 1,n do
|
||||||
|
tb[i] ={}; local tbi = tb[i]
|
||||||
|
for j = 1,n do
|
||||||
|
tbi[j] = (i-1)*n+j
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--make random path col/row/col... starting at random position
|
||||||
|
|
||||||
|
row = true;
|
||||||
|
posi = 1; -- row
|
||||||
|
posj = 1; -- col
|
||||||
|
path = {}
|
||||||
|
used = {}; -- [num] = true, when taken
|
||||||
|
|
||||||
|
for i = 1, steps do
|
||||||
|
if row then
|
||||||
|
local tmp = posj;
|
||||||
|
local s = 0
|
||||||
|
while (tmp == posj or used[tb[posi][tmp]]) and s < tries do
|
||||||
|
tmp = math.random(n);
|
||||||
|
s=s+1
|
||||||
|
end
|
||||||
|
if s == tries then say("stuck at lenght " .. #path) break end
|
||||||
|
posj = tmp
|
||||||
|
else
|
||||||
|
local tmp = posi;
|
||||||
|
local s = 0
|
||||||
|
while (tmp == posi or used[tb[tmp][posj]]) and s < tries do
|
||||||
|
tmp = math.random(n);
|
||||||
|
s=s+1
|
||||||
|
end
|
||||||
|
if s == tries then say("stuck at lenght " .. #path) break end
|
||||||
|
posi = tmp
|
||||||
|
end
|
||||||
|
row = not row
|
||||||
|
path[#path+1] = tb[posi][posj];
|
||||||
|
used[path[#path]] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local ret = {};
|
||||||
|
for i = 1,n do
|
||||||
|
for j = 1,n do
|
||||||
|
ret[#ret+1] = string.format("%02d",(i-1)*n+j).." ";
|
||||||
|
end
|
||||||
|
ret[#ret+1] = "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
self.label(table.concat(path," ") .. "\n\n"..table.concat(ret))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
--go by rnd
|
--go by rnd
|
||||||
if not init then init=true
|
if not init then init=true
|
||||||
spos = self.spawnpos()
|
spos = self.spawnpos()
|
||||||
|
self.listen_punch(self.pos()) -- attach punch listener
|
||||||
sizex = 9; sizez = 9
|
sizex = 9; sizez = 9
|
||||||
|
|
||||||
gamepieces = {
|
gamepieces = {
|
||||||
|
|
|
@ -19,6 +19,7 @@ if not init then
|
||||||
punchstate = 1; -- first punch
|
punchstate = 1; -- first punch
|
||||||
punchpos = {}
|
punchpos = {}
|
||||||
pos = self.spawnpos()
|
pos = self.spawnpos()
|
||||||
|
self.listen_punch(self.pos()) -- attach punch listener
|
||||||
dice = 0
|
dice = 0
|
||||||
spawns = {
|
spawns = {
|
||||||
{2,2,1,2,2,"basic_robot:buttonFF8080"}, -- xstart,zstart,ystart, dimx, dimz, nodename
|
{2,2,1,2,2,"basic_robot:buttonFF8080"}, -- xstart,zstart,ystart, dimx, dimz, nodename
|
||||||
|
@ -61,11 +62,12 @@ if not init then
|
||||||
local idx = msgs[1] or 1;
|
local idx = msgs[1] or 1;
|
||||||
msgs[idx+1] = text;idx = idx+1; if idx>5 then idx = 1 end msgs[1] = idx
|
msgs[idx+1] = text;idx = idx+1; if idx>5 then idx = 1 end msgs[1] = idx
|
||||||
end
|
end
|
||||||
show_msgs = function() -- last message on top
|
show_msgs = function()
|
||||||
local out = {}; local idx = msgs[1] or 1;
|
local out = {};
|
||||||
for i = idx,2,-1 do out[#out+1] = msgs[i] or "" end
|
local idx = msgs[1] or 1;
|
||||||
for i = 6, idx+1,-1 do out[#out+1] = msgs[i] or "" end
|
for i = idx,2,-1 do out[#out+1] = msgs[i] or "" end
|
||||||
self.label(table.concat(out,"\n"))
|
for i = 6, idx+1,-1 do out[#out+1] = msgs[i] or "" end
|
||||||
|
self.label(table.concat(out,"\n"))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,8 @@ if not data then
|
||||||
self.spam(1)
|
self.spam(1)
|
||||||
t0 = _G.minetest.get_gametime();
|
t0 = _G.minetest.get_gametime();
|
||||||
data = {}; spawnpos = self.spawnpos() -- place mines
|
data = {}; spawnpos = self.spawnpos() -- place mines
|
||||||
|
self.listen_punch(self.pos()); -- attach punch listener
|
||||||
|
|
||||||
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
|
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
|
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;
|
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
-- INIT
|
-- INIT
|
||||||
if not grid then
|
if not grid then
|
||||||
n=6 -- size
|
n=6
|
||||||
solved = false -- do we render solution or blank?
|
solved = false -- do we render solution or blank?
|
||||||
-- _G.math.randomseed(3)
|
-- _G.math.randomseed(3)
|
||||||
|
|
||||||
self.spam(1)
|
self.spam(1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ if not grid then
|
||||||
end
|
end
|
||||||
|
|
||||||
_,scores_string = book.read(1); scores = minetest.deserialize(scores_string)
|
_,scores_string = book.read(1); scores = minetest.deserialize(scores_string)
|
||||||
if not scores then scores = init_score(5,5,-999) end -- 5 levels, 5 top records, smaller time is better (thats why - in top 5, there largest value counts)
|
if not scores then scores = init_score(5,5,-999) end -- 5 levels, 5 top records
|
||||||
|
|
||||||
t0 = _G.minetest.get_gametime()
|
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 )." ..
|
local intro ="numbers at beginning of each row (coloumn) tell how many\nred blocks are together in each row ( coloumn )." ..
|
||||||
|
@ -168,6 +169,11 @@ if not grid then
|
||||||
if not players then error("nonogram: no players near") end
|
if not players then error("nonogram: no players near") end
|
||||||
local pname = players[1];
|
local pname = players[1];
|
||||||
|
|
||||||
|
self.listen_punch(self.pos()) -- attach punch listener
|
||||||
|
|
||||||
|
--self.label()
|
||||||
|
|
||||||
|
--self.label(string.gsub(_G.dump(read_field()),"\n","") )
|
||||||
difficulty = get_difficulty()
|
difficulty = get_difficulty()
|
||||||
reward = 0; limit = 0;
|
reward = 0; limit = 0;
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,8 @@ if not init then
|
||||||
|
|
||||||
create_board(size)
|
create_board(size)
|
||||||
render_board()
|
render_board()
|
||||||
|
self.listen_punch(self.pos()) -- attach punch listener
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
event = keyboard.get();
|
event = keyboard.get();
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
|
|
||||||
self.spam(1)
|
self.spam(1)
|
||||||
|
self.listen_punch(self.pos()); -- attach punch listener
|
||||||
sokoban.push_time = 0
|
sokoban.push_time = 0
|
||||||
sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0;
|
sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0;
|
||||||
imax = 0; jmax = 0
|
imax = 0; jmax = 0
|
||||||
|
|
|
@ -79,6 +79,7 @@ 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("GOAL OF GAME: punch buttons with numbers in correct order to turn all blocks to 0")
|
||||||
|
self.listen_punch(self.pos()) -- attach punch listener
|
||||||
|
|
||||||
--self.label(serialize(switches))
|
--self.label(serialize(switches))
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
--ALL UNICODE by rnd, 2021
|
||||||
|
|
||||||
|
-- range to 32-1550, then 7400-10000
|
||||||
|
--cyrillic 0410-042F: 1040-1071
|
||||||
|
--cyrilic 0430-044F: 1072-1103
|
||||||
|
if not init then init = true
|
||||||
|
|
||||||
|
function utf8(decimal)
|
||||||
|
local bytemarkers = { {0x7FF,192}, {0xFFFF,224}, {0x1FFFFF,240} }
|
||||||
|
if decimal<128 then return string.char(decimal) end
|
||||||
|
local charbytes = {}
|
||||||
|
for bytes,vals in ipairs(bytemarkers) do
|
||||||
|
if decimal<=vals[1] then
|
||||||
|
for b=bytes+1,2,-1 do
|
||||||
|
local mod = decimal%64
|
||||||
|
decimal = (decimal-mod)/64
|
||||||
|
charbytes[b] = string.char(128+mod)
|
||||||
|
end
|
||||||
|
charbytes[1] = string.char(vals[2]+decimal)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return table.concat(charbytes)
|
||||||
|
end
|
||||||
|
|
||||||
|
ret= {"ALL UNICODE\n0 = "}
|
||||||
|
|
||||||
|
for i = 1550, 4000 do
|
||||||
|
if i%25==0 then ret[#ret+1] = "\n"..(i).. " = " end
|
||||||
|
ret[#ret+1] = utf8(i)
|
||||||
|
end
|
||||||
|
self.label(table.concat(ret))
|
||||||
|
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,27 @@
|
||||||
|
-- coal maker mod idea in 30 minutes by rnd
|
||||||
|
-- build dirt box around 3x3x3 area filled with wood, remove one boundary wood (lower one) and start fire there. start robot then!
|
||||||
|
|
||||||
|
check_firebox = function(pos)
|
||||||
|
local p = minetest.find_node_near(pos, 5, "fire:basic_flame") -- locate fire nearby!
|
||||||
|
if not p or minetest.get_node(p).name ~= "fire:basic_flame" then say("light fire first!"); self.remove() end
|
||||||
|
d=3; -- inner size of box, area filled with wood
|
||||||
|
local dirs = {{-1,0,0},{1,0,0},{0,0,-1},{0,0,1}};local dir1,dir2; -- position of vertices on dirt box
|
||||||
|
for i = 1,#dirs do
|
||||||
|
local dir = dirs[i];
|
||||||
|
if minetest.get_node({x=p.x+d*dir[1],y=p.y+d*dir[2],z=p.z+d*dir[3]}).name == "default:dirt" and
|
||||||
|
minetest.get_node({x=p.x+(d-1)*dir[1],y=p.y+(d-1)*dir[2],z=p.z+(d-1)*dir[3]}).name == "default:wood" then dir2 = dirs[i]; break end
|
||||||
|
end
|
||||||
|
if not dir2 then say("error, place fire in correct place in correctly built dirt box!") self.remove() end
|
||||||
|
dir1 = {dir2[3], dir2[2], -dir2[1]};
|
||||||
|
local v1 = {x=p.x-(d-1)*dir1[1]-dir2[1],y=p.y-1,z=p.z-(d-1)*dir1[3]-dir2[3]}
|
||||||
|
local v2 = {x=p.x+(d-1)*dir1[1]+(d)*dir2[1],y=p.y+d,z=p.z+(d-1)*dir1[3]+(d)*dir2[3]}
|
||||||
|
local res = minetest.find_nodes_in_area(v1,v2,{"default:wood","default:dirt"},true);
|
||||||
|
if (#(res["default:dirt"] or {})) == 97 and #(res["default:wood"] or {})==26 then
|
||||||
|
say("all ok. making charcoal now!")
|
||||||
|
minetest.swap_node(p,{name = "air"}) -- turn off fire!
|
||||||
|
else say("fail! check that you built dirt box/wood correctly!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
check_firebox(self.pos())
|
||||||
|
self.remove()
|
|
@ -0,0 +1,187 @@
|
||||||
|
-- "natural language" programming demo by rnd, 2021
|
||||||
|
-- input is lines of 'natural' language, will be translated into lua code
|
||||||
|
|
||||||
|
--[[
|
||||||
|
move forward 3
|
||||||
|
turn left
|
||||||
|
dig left
|
||||||
|
if see dirt turn left and move forward
|
||||||
|
|
||||||
|
subroutine circle commands..
|
||||||
|
|
||||||
|
--> TRANSFORMED into lua :
|
||||||
|
|
||||||
|
for i = 1,3 do move.forward(); pause(); end
|
||||||
|
turn.left(); pause();
|
||||||
|
dig.left(); pause()
|
||||||
|
if read_node.forward()=="dirt" then turn.left();pause(); move.forward(); pause() end
|
||||||
|
|
||||||
|
TODO: integrate into robots, maybe command: code.natural(..)
|
||||||
|
--]]
|
||||||
|
|
||||||
|
if not init then init = true
|
||||||
|
|
||||||
|
prog = [[
|
||||||
|
subroutine walk
|
||||||
|
if see air move forward and move down
|
||||||
|
if see dirt move up
|
||||||
|
if see wood turn right
|
||||||
|
subroutine end
|
||||||
|
|
||||||
|
walk
|
||||||
|
]]
|
||||||
|
|
||||||
|
subroutines = {}; -- [subname] = true , so we know its subroutine
|
||||||
|
|
||||||
|
nodenames = -- translation of block names into minetest
|
||||||
|
{
|
||||||
|
["dirt"] = "default:dirt",
|
||||||
|
["cobble"] = "default:cobble",
|
||||||
|
["stone"] = "default:stone",
|
||||||
|
["wood"] = "default:wood",
|
||||||
|
["water"] = "default:water_source",
|
||||||
|
}
|
||||||
|
|
||||||
|
keywords = {
|
||||||
|
["quit"] = function() return "break;" end,
|
||||||
|
["move"] = {
|
||||||
|
["forward"] = function() return "if move.forward() then pause();paused=true; end;" end,
|
||||||
|
["backward"] = function() return "if move.backward() then pause();paused=true; end;" end,
|
||||||
|
["left"] = function() return "if move.left() then pause();paused=true; end;" end,
|
||||||
|
["right"] = function() return "if move.right() then pause();paused=true; end;" end,
|
||||||
|
["up"] = function() return "if move.up() then pause();paused=true; end;" end,
|
||||||
|
["down"] = function() return "if move.down() then pause();paused=true; end;" end,
|
||||||
|
},
|
||||||
|
|
||||||
|
["turn"] = {
|
||||||
|
["left"] = function() return "turn.left(); pause();paused=true;" end,
|
||||||
|
["right"] = function() return "turn.right(); pause();paused=true;" end,
|
||||||
|
["random"] = function() return "if math.random(2)==1 then turn.right() else turn.left() end; pause(); paused=true;" end
|
||||||
|
},
|
||||||
|
|
||||||
|
["dig"] = function() return "dig.forward(); pause();" end, --TODO: remember to set robot energy to large value at start
|
||||||
|
|
||||||
|
["place"] = function(line)
|
||||||
|
local pattern1 = "place";
|
||||||
|
local i = string.find(line,pattern1)+ string.len(pattern1)+1
|
||||||
|
local nodename = string.sub(line, i) -- what are we placing?
|
||||||
|
if nodenames[nodename] then nodename = nodenames[nodename] end -- translate name
|
||||||
|
return "place.forward('" .. nodename .. "'); pause();"
|
||||||
|
end,
|
||||||
|
|
||||||
|
["if"] = {
|
||||||
|
["see"] = function(line)
|
||||||
|
|
||||||
|
local pattern1 = "see";
|
||||||
|
local pattern2 = "and" .. " "; -- important, space after 'and'
|
||||||
|
|
||||||
|
local i = string.find(line,pattern1) + string.len(pattern1) + 1
|
||||||
|
--nodename command
|
||||||
|
local j = string.find(line," ",i)
|
||||||
|
local nodename, command
|
||||||
|
nodename = string.sub(line,i,j-1)
|
||||||
|
if nodenames[nodename] then nodename = nodenames[nodename] end -- translate name
|
||||||
|
-- maybe command has several parts separated by 'and' ?
|
||||||
|
--cmd1 and cmd2 and cmd3
|
||||||
|
j = j+1; local cmds = {}; local found = false
|
||||||
|
while true do
|
||||||
|
local k = string.find(line,pattern2,j+1)
|
||||||
|
if not k then-- no more AND
|
||||||
|
if found then
|
||||||
|
cmds[#cmds+1] = string.sub(line,j + string.len(pattern2)) break
|
||||||
|
else
|
||||||
|
cmds[#cmds+1] = string.sub(line,j) break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if found then
|
||||||
|
cmds[#cmds+1] = string.sub(line,j+string.len(pattern2),k-1)
|
||||||
|
else
|
||||||
|
cmds[#cmds+1] = string.sub(line,j,k-1)
|
||||||
|
end
|
||||||
|
found = true
|
||||||
|
j=k
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1,#cmds do
|
||||||
|
cmds[i] = parse_line(cmds[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
return "if read_node.forward()=='" ..nodename .. "' then " .. table.concat(cmds," ") .. " end"
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_line = function(line)
|
||||||
|
local struct = keywords;
|
||||||
|
for word in string.gmatch(line,"%S+") do
|
||||||
|
local matched = struct[word]
|
||||||
|
local issub = subroutines[word]
|
||||||
|
if matched or issub then
|
||||||
|
--say(word .. " = " .. type(matched))
|
||||||
|
if type(matched) == "table" then
|
||||||
|
struct = matched; -- climb deeper into structure
|
||||||
|
else
|
||||||
|
local instruction;
|
||||||
|
if issub then
|
||||||
|
instruction = word.."();"
|
||||||
|
else
|
||||||
|
instruction = matched(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- do we have need to repeat instruction?
|
||||||
|
local i = string.find(line,word) + string.len(word) + 1
|
||||||
|
local snum = tonumber(string.sub(line,i)) or 1 -- repeating?
|
||||||
|
if snum>1 and snum<10 then
|
||||||
|
return "for i = 1,"..snum .." do " .. instruction .. " end;"
|
||||||
|
end
|
||||||
|
return instruction
|
||||||
|
end
|
||||||
|
else
|
||||||
|
say("error in line: " .. line .. ", unknown command " .. word) return ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
parse_prog = function(code)
|
||||||
|
local out = {};
|
||||||
|
local subdef = false; -- are we defining subroutine?
|
||||||
|
local subname;
|
||||||
|
local subcmds = {}
|
||||||
|
local pattern1 = "subroutine"
|
||||||
|
|
||||||
|
for line in string.gmatch(code,"[^\n]+") do -- line by line
|
||||||
|
|
||||||
|
local i = string.find(line,pattern1)
|
||||||
|
if i then -- do we define new subroutine command?
|
||||||
|
local j = i+string.len(pattern1)+1
|
||||||
|
local sname = string.sub(line,j)
|
||||||
|
if subdef and sname == "end" then -- end of subroutine
|
||||||
|
subdef = false
|
||||||
|
if not keywords[subname] then
|
||||||
|
out[#out+1 ] = "function " .. subname .. "()\n" .. table.concat(subcmds,"\n") .. "\nend"
|
||||||
|
subroutines[subname] = true;
|
||||||
|
else
|
||||||
|
-- error, subroutine name is reserved keyword
|
||||||
|
end
|
||||||
|
subcmds = {};
|
||||||
|
else
|
||||||
|
subdef = true -- all commands will now register with subroutine
|
||||||
|
subname = sname
|
||||||
|
end
|
||||||
|
else -- normal command
|
||||||
|
if subdef then
|
||||||
|
subcmds[#subcmds+1] = parse_line(line)
|
||||||
|
else
|
||||||
|
out[#out+1] = parse_line(line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
return "--coroutine NaturalLanguage autogenerated\nwhile true do paused = false; "..table.concat(out,"\n") .."if not paused then pause() end end"
|
||||||
|
end
|
||||||
|
|
||||||
|
parsed_prog = parse_prog(prog)
|
||||||
|
self.label(prog .. "\n\n==>\n\n" .. parsed_prog)
|
||||||
|
code.set(parsed_prog) -- actually run code by robot
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,158 @@
|
||||||
|
-- Natural language compiler, outputs lua code
|
||||||
|
-- (C) rnd 2021
|
||||||
|
-- TODO: add IF: if condition arg1 ACTION1 else ACTION2 ?
|
||||||
|
-- ADD SUBROUTINE: sub NAME = enter subroutine definition mode, sub end = ends definition mode
|
||||||
|
|
||||||
|
if not init then init = true
|
||||||
|
dtext = {};
|
||||||
|
dout = function(text) dtext[#dtext+1] = text end
|
||||||
|
text =
|
||||||
|
[[
|
||||||
|
if cond1 value action1 and action2 and action3 else actiona1 and actiona2
|
||||||
|
]]
|
||||||
|
|
||||||
|
translate = { -- dictionary of used words
|
||||||
|
["forward"] = "forward",
|
||||||
|
["backward"] = "backward",
|
||||||
|
["left"] = "left",
|
||||||
|
["right"] = "right",
|
||||||
|
["random"] = "random",
|
||||||
|
["dirt"] = "default:dirt",
|
||||||
|
["wood"]= "default:wood",
|
||||||
|
["cobble"] = "default:cobble",
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds = {
|
||||||
|
["if"] = function(code,ibeg,iend) -- if COND value ACTION1 and ACTION2 and ... ACTIONn else(optional) ACTION1 and ... and ACTIONm
|
||||||
|
-- COND: 'see' nodename (block right in front), 'var = value' value of variable?,...
|
||||||
|
local ELSE = " else"
|
||||||
|
local AND = " and"
|
||||||
|
local i,j,condtype,value;
|
||||||
|
condtype, j = get_next_word(code,ibeg,iend)
|
||||||
|
local out = {};
|
||||||
|
dout(condtype)
|
||||||
|
|
||||||
|
if condtype== "see" then
|
||||||
|
value, j = get_next_word(code,j,iend)
|
||||||
|
value = translate(value or "");
|
||||||
|
if not minetest.registered_nodename(value) then say("error: unknown block name " .. value .. "used in 'if'") return "" end
|
||||||
|
out[#out+1] = "if read_node.forward() == " .. value .. " then ";
|
||||||
|
else
|
||||||
|
say("error: unknown condition " .. condtype .. " used in 'if'") return "" end
|
||||||
|
end
|
||||||
|
-- now after j left: ACTION1 else(optional) ACTION2, ACTION can be multiple, separated by ' and '
|
||||||
|
|
||||||
|
|
||||||
|
-- parse and before else
|
||||||
|
local k = j;
|
||||||
|
lcoal cmds = {}
|
||||||
|
while k do
|
||||||
|
k = string.find(code,AND,j)
|
||||||
|
if k and k<ielse then
|
||||||
|
cmds[#cmds+1] = string.sub(code,j,k-1)
|
||||||
|
j=k+1;
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
dout(table.concat(cmds,"\n"))
|
||||||
|
|
||||||
|
|
||||||
|
--j=...
|
||||||
|
out[#out+1] = "end"
|
||||||
|
|
||||||
|
|
||||||
|
return table.concat(out," "),j
|
||||||
|
|
||||||
|
end,
|
||||||
|
|
||||||
|
["move"] = function(code,ibeg,iend)
|
||||||
|
-- move forward count
|
||||||
|
local i,direction,count
|
||||||
|
direction,i = get_next_word(code,ibeg,iend)
|
||||||
|
direction = translate[direction];
|
||||||
|
if not direction then say("error: unknown direction used in 'turn'") return "" end
|
||||||
|
return "move."..direction .."(); pause();",i;
|
||||||
|
end,
|
||||||
|
|
||||||
|
["turn"] = function(code,ibeg,iend)
|
||||||
|
-- move forward count
|
||||||
|
local i,direction,count
|
||||||
|
direction,i = get_next_word(code,ibeg,iend)
|
||||||
|
|
||||||
|
direction = translate[direction];
|
||||||
|
if not direction then say("error: unknown direction used in 'turn'") return "" end
|
||||||
|
local c;
|
||||||
|
if direction == "random" then
|
||||||
|
c = "if math.random(2) == 1 then turn.left() else turn.right() end;";
|
||||||
|
else
|
||||||
|
c = "turn."..direction .."();"
|
||||||
|
end
|
||||||
|
return c.." pause();",i
|
||||||
|
end,
|
||||||
|
|
||||||
|
["dig"] = function() return "dig.forward(); pause();" end,
|
||||||
|
|
||||||
|
["place"] = function(code,ibeg,iend)
|
||||||
|
-- place nodename
|
||||||
|
local nodename,i
|
||||||
|
nodename,i = get_next_word(code,ibeg,iend)
|
||||||
|
nodename = translate[nodename]
|
||||||
|
if not nodename then say("error: unknown nodename used in 'place'") return "" end
|
||||||
|
|
||||||
|
return "place.forward('" .. nodename .. "'); pause();",i
|
||||||
|
end,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
-- given position ibeg in string find next word, return it and then return position immediately after word.
|
||||||
|
-- word is defined as a sequence of alphanumeric characters (%w)
|
||||||
|
-- example 'hello world', ibeg = 1. -> 'hello', 6
|
||||||
|
|
||||||
|
get_next_word = function(code, ibeg,iend) -- attempt to return next word, starting from position ibeg. returns word, index after word
|
||||||
|
if not ibeg or not iend then return end
|
||||||
|
local j = string.find(code,"%w",ibeg); -- where is start of word?
|
||||||
|
if not j or j>iend then return "", iend+1 end -- no words present
|
||||||
|
ibeg = j;
|
||||||
|
j = string.find(code,"%W",j);--where is end of word?
|
||||||
|
if not j or j>iend then return string.sub(code,ibeg,iend-1),iend+1 end
|
||||||
|
return string.sub(code,ibeg,j-1), j
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
parse_code = function(code)
|
||||||
|
local out = {};
|
||||||
|
local ibeg,iend,word;
|
||||||
|
local clen = string.len(code)
|
||||||
|
local step =0
|
||||||
|
|
||||||
|
iend = 1; ibeg = 1;
|
||||||
|
while step < 10 do
|
||||||
|
if ibeg>clen then break end
|
||||||
|
step = step+1
|
||||||
|
iend = string.find(code, "\n", ibeg)
|
||||||
|
if not iend then iend = clen end -- get out of loop, no more lines to process
|
||||||
|
|
||||||
|
word, ibeg = get_next_word(code,ibeg,iend)
|
||||||
|
--dout("rem " .. string.sub(code,ibeg,iend))
|
||||||
|
--dout("Dword '" .. word .. "' " .. ibeg .. " " .. iend)
|
||||||
|
local cmd = cmds[word];
|
||||||
|
|
||||||
|
if cmd then out[#out+1],ibeg = cmd(code,ibeg,iend) end
|
||||||
|
if not ibeg then ibeg = iend+1 end
|
||||||
|
if ibeg<=iend then -- still some space remaining in line, last parameter is repetition
|
||||||
|
local count,i
|
||||||
|
count,i = get_next_word(code,ibeg,iend);
|
||||||
|
count = tonumber(count) or 1
|
||||||
|
if count>9 then count = 9 elseif count<1 then count=1 end
|
||||||
|
if count > 1 then out[#out] = "for i=1,"..count.. " do " .. out[#out] .. " end" end
|
||||||
|
end
|
||||||
|
ibeg = iend +1 -- go new line
|
||||||
|
end
|
||||||
|
return table.concat(out,"\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
self.label(parse_code(text) .. "\n\n" .. table.concat(dtext,"\n"))
|
||||||
|
end
|
|
@ -0,0 +1,40 @@
|
||||||
|
-- given position ibeg in string find next word, return it and then return position immediately after word.
|
||||||
|
-- word is a sequence of alphanumeric characters
|
||||||
|
-- example 'hello world', ibeg = 1. -> 'hello', 6
|
||||||
|
|
||||||
|
get_next_word = function(code, ibeg,iend) -- attempt to return next word, starting from position ibeg. returns word, index after word
|
||||||
|
if not ibeg or not iend then return end
|
||||||
|
local j = string.find(code,"%w",ibeg); -- where is start of word?
|
||||||
|
if not j or j>iend then return "", iend+1 end -- no words present
|
||||||
|
ibeg = j;
|
||||||
|
j = string.find(code,"%W",j);--where is end of word?
|
||||||
|
if not j or j>iend then return string.sub(code,ibeg,iend-1),iend+1 end
|
||||||
|
return string.sub(code,ibeg,j-1), j
|
||||||
|
end
|
||||||
|
|
||||||
|
text = [[
|
||||||
|
hello world
|
||||||
|
today
|
||||||
|
day night
|
||||||
|
]]
|
||||||
|
ibeg = 1; iend = string.find(text,"\n",ibeg) or string.len(text) -- where is next new line
|
||||||
|
say("INIT LINE " .. ibeg .. " " .. iend .. " LINE '" .. string.sub(text,ibeg,iend-1) .."'")
|
||||||
|
|
||||||
|
|
||||||
|
for i = 1,10 do
|
||||||
|
|
||||||
|
|
||||||
|
word, ibeg = get_next_word(text,ibeg,iend)
|
||||||
|
say("word '" .. word.."', end " .. ibeg)
|
||||||
|
if ibeg>=iend then -- newline!
|
||||||
|
--say("newline")
|
||||||
|
local j = ibeg;
|
||||||
|
iend = string.find(text,"\n", iend+1) -- find next newline
|
||||||
|
|
||||||
|
if not iend then say("END") iend = string.len(text) break end -- end of text!
|
||||||
|
say("LINE " .. ibeg .. " " .. iend .. " LINE '" .. string.sub(text,ibeg,iend-1) .."'")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
self.remove()
|
|
@ -1,46 +1,48 @@
|
||||||
--rnd 2017
|
--rnd 2017
|
||||||
if not logdata then
|
if not logdata then
|
||||||
self.label("chatlog bot");
|
self.label("");--chatlog bot");
|
||||||
_G.minetest.forceload_block(self.pos(),true)
|
_G.minetest.forceload_block(self.pos(),true)
|
||||||
n = 250;
|
n = 500;
|
||||||
idx = 1;
|
idx = 1;
|
||||||
logdata = {};
|
authusers = {["rnd"] = 1} -- who is allowed to use ?log
|
||||||
|
|
||||||
insert = function(text) -- insert new message
|
logdata = {};
|
||||||
idx = idx +1;
|
|
||||||
if idx > n then idx = 1 end
|
insert = function(text) -- insert new message
|
||||||
logdata[idx] = text;
|
idx = idx +1;
|
||||||
end
|
if idx > n then idx = 1 end
|
||||||
|
logdata[idx] = text;
|
||||||
last = function(k,filter) -- return last k messages
|
end
|
||||||
if k > n then k = 30 end
|
|
||||||
local i,j,ret;
|
last = function(k,filter) -- return last k messages
|
||||||
i=idx;j=0; ret = ""
|
if k > n then k = 30 end
|
||||||
|
local i,j,ret;
|
||||||
for j = 1,k do
|
i=idx;j=0; ret = ""
|
||||||
if not logdata[i] then break end
|
|
||||||
if filter and not string.find(logdata[i], filter) then
|
for j = 1,k do
|
||||||
else
|
if not logdata[i] then break end
|
||||||
ret = ret .. logdata[i] .. "\n";
|
if filter and not string.find(logdata[i], filter) then
|
||||||
end
|
else
|
||||||
i=i-1; if i < 1 then i = n end
|
ret = ret .. logdata[i] .. "\n";
|
||||||
end
|
end
|
||||||
return ret
|
i=i-1; if i < 1 then i = n end
|
||||||
end
|
end
|
||||||
|
return ret
|
||||||
self.listen(1)
|
end
|
||||||
end
|
|
||||||
|
self.listen(1)
|
||||||
speaker, msg = self.listen_msg()
|
end
|
||||||
if msg then
|
|
||||||
if string.sub(msg,1,4) == "?log" then
|
speaker, msg = self.listen_msg()
|
||||||
local j = string.find(msg," ",6);
|
if msg then
|
||||||
local k = tonumber(string.sub(msg,6) or "") or n;
|
if string.sub(msg,1,4) == "?log" and authusers[speaker] then
|
||||||
local text;
|
local j = string.find(msg," ",6);
|
||||||
if j then text = last(k,string.sub(msg,j+1)) else text = last(k) end
|
local k = tonumber(string.sub(msg,6) or "") or n;
|
||||||
local form = "size[8,8]".. "textarea[0.,0;11.,9.5;text;chatlog;".. text .. "]"
|
local text;
|
||||||
self.show_form(speaker, form)
|
if j then text = last(k,string.sub(msg,j+1)) else text = last(k) end
|
||||||
else
|
local form = "size[8,8]".. "textarea[0.,0;11.,9.5;text;chatlog;".. minetest.formspec_escape(text) .. "]"
|
||||||
insert(os.date("%X") .. " " .. speaker .. "> " .. msg)
|
self.show_form(speaker, form)
|
||||||
end
|
else
|
||||||
end
|
insert(os.date("%X") .. " " .. speaker .. "> " .. msg)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue