--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::