diff --git a/init.lua b/init.lua index 5483929..38d0ade 100644 --- a/init.lua +++ b/init.lua @@ -7,7 +7,7 @@ basic_robot.call_limit = 48; -- how many execution calls per script run allowed 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.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses ["craft_guide:sign_wall"] = true, @@ -1724,13 +1724,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 ) diff --git a/scripts/command_robot.lua b/scripts/command_robot.lua new file mode 100644 index 0000000..1ebc33d --- /dev/null +++ b/scripts/command_robot.lua @@ -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 \ No newline at end of file diff --git a/scripts/copy_paste.lua b/scripts/copy_paste.lua new file mode 100644 index 0000000..9dae0a6 --- /dev/null +++ b/scripts/copy_paste.lua @@ -0,0 +1,70 @@ +-- 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 = {}; + 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 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) + 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) + 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) + 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 \ No newline at end of file diff --git a/scripts/craft_guide.lua b/scripts/craft_guide.lua new file mode 100644 index 0000000..fc088e1 --- /dev/null +++ b/scripts/craft_guide.lua @@ -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 \ No newline at end of file diff --git a/scripts/games/CTF_bot.lua b/scripts/games/CTF_bot.lua new file mode 100644 index 0000000..5cff02d --- /dev/null +++ b/scripts/games/CTF_bot.lua @@ -0,0 +1,137 @@ +-- 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=501,z=17}, name = "blue", owner = "", score = 0}, -- team[1] + [2] = {state = 1, flagnode = "basic_robot:buttonFF8080", pos = {x=-79,y=501,z=46}, 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 5 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 + -- check player health + for k,v in pairs(teams) do + local hp = teams[k][3]:get_hp(); + if not hp or hp<10 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 \ No newline at end of file diff --git a/scripts/games/battle_bot_arena.lua b/scripts/games/battle_bot_arena.lua new file mode 100644 index 0000000..a9ceb57 --- /dev/null +++ b/scripts/games/battle_bot_arena.lua @@ -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 \ No newline at end of file diff --git a/scripts/games/battle_minesweeper_game.lua b/scripts/games/battle_minesweeper_game.lua new file mode 100644 index 0000000..b1ebc06 --- /dev/null +++ b/scripts/games/battle_minesweeper_game.lua @@ -0,0 +1,150 @@ +if not data then + m=10;n=10; + players = {}; + paused = true + + turn = 2; + t = 0; + SIGNUP = 0; GAME = 1; INTERMISSION = 2 + state = SIGNUP + + t0 = _G.minetest.get_gametime();spawnpos = self.spawnpos() -- place mines + data = {}; + + init_game = function() + 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 + + 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\n3. if you make move in other player turn you lose. 4. if you hit bomb or mark bomb falsely you lose")) + 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 + state = INTERMISSION; t = 0 + end + + function change_turn() + if turn == 1 then + _G.minetest.sound_play("default_break_glass",{pos=spawnpos, max_hear_distance = 100}) + else + _G.minetest.sound_play("note_a",{pos=spawnpos, max_hear_distance = 100}) + end + + if paused == false then + say(players[turn] .. " lost : didn't make a move"); + player_lost() + else + if turn == 1 then turn = 2 else turn = 1 end + self.label("turn " .. turn .. " " .. players[turn]) + 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!")) + end + + end + end + +elseif state == GAME then + + t = t + 1; + if t>5 then -- change of turn + change_turn() + 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; + if x<1 or x>m or z<1 or z>n then + else + 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 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; init_game(); greeting() end +end \ No newline at end of file diff --git a/scripts/games/blackbox_game.lua b/scripts/games/blackbox_game.lua new file mode 100644 index 0000000..d7f0d1c --- /dev/null +++ b/scripts/games/blackbox_game.lua @@ -0,0 +1,195 @@ +--black box by rnd, 03/18/2017 +--https://en.wikipedia.org/wiki/Black_Box_(game) + +if not data then + m=8;n=8;turn = 0; + attempts = 1; + + self.spam(1);t0 = _G.minetest.get_gametime(); + spawnpos = self.spawnpos() + data = {}; + for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end + + for i=1,4 do -- put in 4 atoms randomly + data[math.random(m)][math.random(n)] = 1 + end + + render_board = function(mode) -- mode 0 : render without solution, 1: render solution + for i = 1,m do for j = 1,n do -- render game + if mode == 0 or data[i][j] == 0 then + if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2) + end + else + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},3) + end + end end + end + + get_dirl = function(dir) + local dirl; -- direction left + if dir[1] > 0.5 then dirl = {0,-1} + elseif dir[1] < -0.5 then dirl = {0,1} + elseif dir[2] > 0.5 then dirl = {-1,0} + elseif dir[2] < -0.5 then dirl = {1,0} + end + return dirl + end + + read_pos = function(x,z) + if x<1 or x>m or z<1 or z>n then return nil end + return data[x][z] + end + + newdir = function(x,z,dir) -- where will ray go next + local retdir = {dir[1],dir[2]}; + local xf = x+dir[1]; local zf = z+dir[2] -- forward + local dirl = get_dirl(dir) + + local nodef = read_pos(xf,zf) + local nodel = read_pos(xf + dirl[1],zf + dirl[2]) + local noder = read_pos(xf - dirl[1],zf - dirl[2]) + if nodef == 1 then + retdir = {0,0} -- ray hit something + elseif nodel == 1 and noder ~= 1 then + retdir = {-dirl[1],-dirl[2]} + elseif nodel ~= 1 and noder == 1 then + retdir = {dirl[1],dirl[2]} + elseif nodel == 1 and noder == 1 then + retdir = {-dir[1],-dir[2]} + end + return retdir + end + + shootray = function(x,z,dir) + --say("ray starts " .. x .. " " .. z .. " dir " .. dir[1] .. " " .. dir[2]) + local xp = x; local zp = z; + local dirp = {dir[1],dir[2]}; + local maxstep = m*n; + + for i = 1,maxstep do + dirp = newdir(xp,zp,dirp); + if dirp[1]==0 and dirp[2]==0 then return -i end -- hit + xp=xp+dirp[1];zp=zp+dirp[2]; + if xp<1 or xp>m or zp<1 or zp>n then return i,{xp,zp} end -- out + end + return 0 -- hit + end + + count = 0; -- how many letters were used up + border_start_ray = function(x,z) + local rdir + if x==0 then rdir = {1,0} + elseif x == m+1 then rdir = {-1,0} + elseif z == 0 then rdir = {0,1} + elseif z == n+1 then rdir = {0,-1} + end + if rdir then + local result,out = shootray(x,z,rdir); + if result >= 0 then + + if out then + if out[1]==x and out[2]==z then -- got back where it originated, reflection + keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},1); + else + if result<=1 then + keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},6); -- immediate bounce off + else + local nodename = "default:obsidian_letter_"..string.char(97+count) .. "u"; + _G.minetest.set_node( + {x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]}, + {name = nodename, param2 = 1}) + _G.minetest.set_node( + {x=spawnpos.x+x,y=spawnpos.y+1,z=spawnpos.z+z}, + {name = nodename, param2 = 1}) + count = count + 1; + keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4); + keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},4); + end + end + end + elseif result<0 then + keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3); -- hit + end + end + end + + -- initial border loop and marking + + --render blue border + for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+0},5) keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+n+1},5) end + for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y,z=spawnpos.z+j},5) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y,z=spawnpos.z+j},5) end + + for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+0},0) keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+n+1},0) end + for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y+1,z=spawnpos.z+j},0) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y+1,z=spawnpos.z+j},0) end + + + z=0 -- bottom + for x = 1,m do + if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then + border_start_ray(x,z) + end + end + + x=m+1 -- right + for z = 1,n do + if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then + border_start_ray(x,z) + end + end + + z=n+1 -- top + for x = m,1,-1 do + if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then + border_start_ray(x,z) + end + end + + x=0 -- left + for z = n,1,-1 do + if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then + border_start_ray(x,z) + end + end + + check_solution = function() + for i = 1,m do + for j = 1,n do + if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}) == "basic_robot:buttonFF8080" then -- red + if data[i][j]~=1 then return false end + else + if data[i][j]~=0 then return false end + end + end + end + return true + end + + --render board + render_board(0) + keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},5) + +end + +event = keyboard.get(); +if event then + local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z; + if x<1 or x>m or z<1 or z>n then + if event.type == 5 then + if check_solution() then + say("#BLACKBOX : CONGRATULATIONS! " .. event.puncher .. " found all atoms after " .. attempts .. " tries."); self.remove() + else + say("#BLACKBOX : " .. event.puncher .. " failed to detect atoms after " .. attempts .. " attempts.") + attempts = attempts+1 + end + end + else -- interior punch + nodetype = 2; + if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button808080" then + nodetype = 3 + end + keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},nodetype); + end + +end +::END:: \ No newline at end of file diff --git a/scripts/games/casino_bot.lua b/scripts/games/casino_bot.lua new file mode 100644 index 0000000..657cef4 --- /dev/null +++ b/scripts/games/casino_bot.lua @@ -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 \ No newline at end of file diff --git a/scripts/games/connect4.lua b/scripts/games/connect4.lua new file mode 100644 index 0000000..fd3ebd6 --- /dev/null +++ b/scripts/games/connect4.lua @@ -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:: \ No newline at end of file diff --git a/scripts/games/fallout_hacking.lua b/scripts/games/fallout_hacking.lua new file mode 100644 index 0000000..54f1c37 --- /dev/null +++ b/scripts/games/fallout_hacking.lua @@ -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 \ No newline at end of file diff --git a/scripts/games/hacking_game.lua b/scripts/games/hacking_game.lua new file mode 100644 index 0000000..5f2abc8 --- /dev/null +++ b/scripts/games/hacking_game.lua @@ -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]; + 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 + 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()) + 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 \ No newline at end of file diff --git a/scripts/games/hide_and_seek.lua b/scripts/games/hide_and_seek.lua new file mode 100644 index 0000000..43d4d9d --- /dev/null +++ b/scripts/games/hide_and_seek.lua @@ -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 \ No newline at end of file diff --git a/scripts/games/hide_and_seek_1.lua b/scripts/games/hide_and_seek_1.lua new file mode 100644 index 0000000..8582446 --- /dev/null +++ b/scripts/games/hide_and_seek_1.lua @@ -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 \ No newline at end of file diff --git a/scripts/games/minesweeper_game.lua b/scripts/games/minesweeper_game.lua new file mode 100644 index 0000000..7f72bbe --- /dev/null +++ b/scripts/games/minesweeper_game.lua @@ -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 \ No newline at end of file diff --git a/scripts/games/nonogram.lua b/scripts/games/nonogram.lua new file mode 100644 index 0000000..367440f --- /dev/null +++ b/scripts/games/nonogram.lua @@ -0,0 +1,259 @@ +-- 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 + --rom.score = "0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999" -- default + 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 + + --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 + say("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 t0 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 + say(msg) + + self.remove() + + else self.label("FAIL") end + elseif event.x == spawnpos.x+2 then -- solve + say("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 \ No newline at end of file diff --git a/scripts/games/sliding_puzzle_game.lua b/scripts/games/sliding_puzzle_game.lua new file mode 100644 index 0000000..5e4a3f6 --- /dev/null +++ b/scripts/games/sliding_puzzle_game.lua @@ -0,0 +1,74 @@ +-- sliding unscramble game by rnd, made in 20 minutes +if not init then + init = true + spos = self.spawnpos(); spos.y = spos.y + 1 + board = {}; + size = 3; + + 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 + + + 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 + end + +end \ No newline at end of file diff --git a/scripts/games/sokoban_game.lua b/scripts/games/sokoban_game.lua new file mode 100644 index 0000000..294155c --- /dev/null +++ b/scripts/games/sokoban_game.lua @@ -0,0 +1,175 @@ + -- SOKOBAN GAME, by rnd, robots port + if not sokoban then + sokoban = {}; + local players = find_player(5); + 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 + + 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 = "default:brick" + SOKOBAN_FLOOR = "default:stonebrick" + SOKOBAN_GOAL = "default:diamondblock" + SOKOBAN_BOX = "basic_robot:button8080FF" + + 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}) + say("games: sokoban level "..sokoban.level .." loaded by ".. name .. ". It has " .. sokoban.blocks .. " boxes to push. "); 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+1,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 + + + end + + +if state == 1 then + sender,fields = self.read_form(); -- get fields from form submittal + if sender then + --say(serialize(fields)) + + if fields.LVL then + load_level((tonumber(fields.LVL) or 1)-1) + state = 0 + self.label("stand close to blue box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all boxes pushed on diamond blocks") + end + end +else + + local ppos = player_:getpos() + if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then say(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 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 \ No newline at end of file diff --git a/scripts/games/tank_bot.lua b/scripts/games/tank_bot.lua new file mode 100644 index 0000000..8e2356f --- /dev/null +++ b/scripts/games/tank_bot.lua @@ -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 \ No newline at end of file diff --git a/scripts/math/fractal_bot.lua b/scripts/math/fractal_bot.lua new file mode 100644 index 0000000..7279c44 --- /dev/null +++ b/scripts/math/fractal_bot.lua @@ -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 nx0 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.sizea^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 \ No newline at end of file diff --git a/scripts/math/perm2cycles.lua b/scripts/math/perm2cycles.lua new file mode 100644 index 0000000..0e6b405 --- /dev/null +++ b/scripts/math/perm2cycles.lua @@ -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 cycles = " .. arr2string(cycles) ) + +end \ No newline at end of file diff --git a/scripts/pine_tree_harvest.lua b/scripts/pine_tree_harvest.lua new file mode 100644 index 0000000..1b69b6d --- /dev/null +++ b/scripts/pine_tree_harvest.lua @@ -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 \ No newline at end of file diff --git a/scripts/programming/brainfuck generator.lua b/scripts/programming/brainfuck generator.lua new file mode 100644 index 0000000..3d910cd --- /dev/null +++ b/scripts/programming/brainfuck generator.lua @@ -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() \ No newline at end of file diff --git a/scripts/programming/brainfuck.lua b/scripts/programming/brainfuck.lua new file mode 100644 index 0000000..221eb08 --- /dev/null +++ b/scripts/programming/brainfuck.lua @@ -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 n then self.label("ram : " .. get_ram()) step = maxsteps state = END end + end +end \ No newline at end of file diff --git a/scripts/programming/os_show_demo.lua b/scripts/programming/os_show_demo.lua new file mode 100644 index 0000000..2a5bda6 --- /dev/null +++ b/scripts/programming/os_show_demo.lua @@ -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 \ No newline at end of file diff --git a/scripts/programming/rndscript.lua b/scripts/programming/rndscript.lua new file mode 100644 index 0000000..b2e304f --- /dev/null +++ b/scripts/programming/rndscript.lua @@ -0,0 +1,271 @@ +-- rnd 2017 +-- call stack, subroutines, recursive subroutines, if jumps, loops, variables + +if not cmd then +prog = "\n".. +""] = 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() \ No newline at end of file diff --git a/scripts/simulators/genetic_trust.lua b/scripts/simulators/genetic_trust.lua new file mode 100644 index 0000000..2e07497 --- /dev/null +++ b/scripts/simulators/genetic_trust.lua @@ -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.points1 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 \ No newline at end of file diff --git a/scripts/simulators/group_assembly.lua b/scripts/simulators/group_assembly.lua new file mode 100644 index 0000000..5088f48 --- /dev/null +++ b/scripts/simulators/group_assembly.lua @@ -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 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 \ No newline at end of file diff --git a/scripts/simulators/layout_designer.lua b/scripts/simulators/layout_designer.lua new file mode 100644 index 0000000..f7e0185 --- /dev/null +++ b/scripts/simulators/layout_designer.lua @@ -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() \ No newline at end of file diff --git a/scripts/simulators/nuclear.lua b/scripts/simulators/nuclear.lua new file mode 100644 index 0000000..7c76aa5 --- /dev/null +++ b/scripts/simulators/nuclear.lua @@ -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 ) \ No newline at end of file diff --git a/scripts/simulators/redstone_emulator.lua b/scripts/simulators/redstone_emulator.lua new file mode 100644 index 0000000..eea67e2 --- /dev/null +++ b/scripts/simulators/redstone_emulator.lua @@ -0,0 +1,448 @@ +-- REDSTONE EMULATOR & EDITOR +--v 10/14a + +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 + + 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")) + + + -- 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); 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); 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); 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 + + 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); 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); 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); 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); 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); 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); 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); 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]; + 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 + --TODO + -- inventory checker(taker) : basic_robot:button_63 -> if player has stuff it will activate items after punch + -- inventory give: give item that is at position1 and activate selected machine at position2 + } + + -- 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: set block at target region {target2,target3} to block at target1", -- equalizer + ["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 + } + + 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 + + +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 \ No newline at end of file diff --git a/scripts/simulators/trust_game.lua b/scripts/simulators/trust_game.lua new file mode 100644 index 0000000..c11b0e3 --- /dev/null +++ b/scripts/simulators/trust_game.lua @@ -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 " .. players[1].points .. " VS " .. players[2].name .. " " .. res2 .. " -> " .. players[2].points); + -- end + +end \ No newline at end of file diff --git a/scripts/simulators/turtlebot.lua b/scripts/simulators/turtlebot.lua new file mode 100644 index 0000000..c486582 --- /dev/null +++ b/scripts/simulators/turtlebot.lua @@ -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 \ No newline at end of file diff --git a/scripts/spawn_quiz.lua b/scripts/spawn_quiz.lua new file mode 100644 index 0000000..b3f97e3 --- /dev/null +++ b/scripts/spawn_quiz.lua @@ -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," 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 \ No newline at end of file diff --git a/scripts/tree_harvest.lua b/scripts/tree_harvest.lua new file mode 100644 index 0000000..242bf2f --- /dev/null +++ b/scripts/tree_harvest.lua @@ -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) \ No newline at end of file diff --git a/scripts/utils/chatlog.lua b/scripts/utils/chatlog.lua new file mode 100644 index 0000000..ee2eaa4 --- /dev/null +++ b/scripts/utils/chatlog.lua @@ -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 \ No newline at end of file diff --git a/scripts/utils/language translator.lua b/scripts/utils/language translator.lua new file mode 100644 index 0000000..626e0e4 --- /dev/null +++ b/scripts/utils/language translator.lua @@ -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 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 \ No newline at end of file diff --git a/scripts/utils/serverbot.lua b/scripts/utils/serverbot.lua new file mode 100644 index 0000000..28c4981 --- /dev/null +++ b/scripts/utils/serverbot.lua @@ -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 \ No newline at end of file diff --git a/scripts/utils/simple_house_builder.lua b/scripts/utils/simple_house_builder.lua new file mode 100644 index 0000000..78f38ca --- /dev/null +++ b/scripts/utils/simple_house_builder.lua @@ -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 \ No newline at end of file diff --git a/textures/face-back.png b/textures/face-back.png new file mode 100644 index 0000000..0a3db4d Binary files /dev/null and b/textures/face-back.png differ diff --git a/textures/face3.png b/textures/face3.png new file mode 100644 index 0000000..e408585 Binary files /dev/null and b/textures/face3.png differ diff --git a/textures/left-hand.png b/textures/left-hand.png new file mode 100644 index 0000000..13d59b9 Binary files /dev/null and b/textures/left-hand.png differ diff --git a/textures/legs.png b/textures/legs.png new file mode 100644 index 0000000..86191a3 Binary files /dev/null and b/textures/legs.png differ diff --git a/textures/right-hand.png b/textures/right-hand.png new file mode 100644 index 0000000..f889478 Binary files /dev/null and b/textures/right-hand.png differ diff --git a/textures/robot_side.png b/textures/robot_side.png new file mode 100644 index 0000000..b24c72f Binary files /dev/null and b/textures/robot_side.png differ diff --git a/textures/topface.png b/textures/topface.png new file mode 100644 index 0000000..2d1b759 Binary files /dev/null and b/textures/topface.png differ