Skip to content

Instantly share code, notes, and snippets.

@Krutoy242
Last active January 6, 2023 11:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Krutoy242/1f18eaf6b262fb7ffb83c4666a93cbcc to your computer and use it in GitHub Desktop.
Save Krutoy242/1f18eaf6b262fb7ffb83c4666a93cbcc to your computer and use it in GitHub Desktop.
Lost User 2 - EEPROM for simpliest robot

Lost User - simpliest robot

Robot (or drone!) BIOS program for Minecraft OpenComputers mod.

Why?

OC robots are very difficult to assemble and program. This program for the BIOS will help to use robots as "users" and in many other ways.

Setup

Assemble

Assemble the robot in the minimum configuration:

  • Case
  • CPU
  • RAM

E2E-E

If you play Enigmatica 2: Expert - Extended, modpack have predefined recipe of EEPROM.
Just find it in JEI and craft. It would have colored shining.

If you crafted it, you can skip next step Write program on EEPROM.

Write program on EEPROM.

You need a working OC computer to write the BIOS. See this tutorial to assemble your first computer.

  1. Download file from the internet (need ), run from command line:
wget https://gist.githubusercontent.com/Krutoy242/1f18eaf6b262fb7ffb83c4666a93cbcc/raw/lostuser.min.lua
  1. To write on existing EEPROM run:
flash -q lostuser.min.lua LostUser

Insert in robot

Take EEPROM from computer case and merge with robot.

Combining robot with EEPROM

Usage

Robot programmed by renaming it. You must rename Robot on or with .

Name your Robot R.use(3), place on ground, turn on, and see how its clicking blocks in front.

Syntax

TL;DR

If you don't want to learn Lua and you need the robot to right/left click, a few simple names for the robot and the result:

  • R.use(3) The robot will right click on the block on the front.
  • R.swing(3) The robot will swing with a sword or break the block in front of it.

Loop

Robot will execute it's name as Lua code in while true loop.

Globals

All components are sorted by name length and added to global variables by the first letter.

For example:

R	=>	robot
E	=>	eeprom
T	=>	trading
C	=>	computer
I	=>	inventory_controller
...

Additional globals:

  • sleep(seconds)
  • proxy(partial_name)
  • run(text) run text once same way as robot name

Debug

Debugging with error()

Robots without screen and GPU cant show messages. Use error(msg) that would be visible by right-clicking Analyzer.

e(R.cT) - Error with robot.compareFluidTo documentation.

Debugging from advanced robot

If you want a deeper level of debugging, load the program on a robot with a complete package - , etc.

Download program on robot:

wget https://gist.githubusercontent.com/Krutoy242/1f18eaf6b262fb7ffb83c4666a93cbcc/raw/lostuser.lua

First robot parameter - the program text to execute (instead of the robot's name)

Since the robot screen is very small, I suggest redirecting the output to a file.

Example:

Run this from shell. Robot would move forward and then stop program.

lostuser "Rm(3)o.e()" > out

In out file you will see its code.

Examples

[unfinished readme part]

Links

--[[
Lost User - simpliest robot
Author: Krutoy242
Source and readme:
https://gist.githubusercontent.com/Krutoy242/1f18eaf6b262fb7ffb83c4666a93cbcc
]]
--[[
██╗███╗ ██╗██╗████████╗
██║████╗ ██║██║╚══██╔══╝
██║██╔██╗ ██║██║ ██║
██║██║╚██╗██║██║ ██║
██║██║ ╚████║██║ ██║
╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝
]]
-- Forward declarations
local proxy, sleep, run, loadTranslated
-- If we run from PC
if not debug.upvalueid then
-- If we run from OpenOS
if require then
component, computer = require'component', require'computer'
end
if not print then print = function(...)end end
proxy = function(name)
local p = component.list(name)()
return p and component.proxy(p) or nil
end
-- Define all components as big letter global, long names first
do
local cmpList = {}
for k, v in pairs(component.list()) do
cmpList[#cmpList+1] = {v, k}
end
table.sort(cmpList, function(a,b)return#a[1]<#b[1] end)
for _, v in pairs(cmpList) do
local c = v[1]:sub(1, 1):upper()
_G[c] = component.proxy(v[2])
end
end
sleep = os and os.sleep or function(t)
local u = computer.uptime
local d = u() + (t or 0)
repeat computer.pullSignal(d - u())
until u() >= d
end
end
local function localError(err)
if computer then computer.beep(800, 0.05) else error(err) end
end
--[[
██████╗ ████████╗ █████╗ ██████╗ ██╗ ███████╗
██╔═══██╗ ╚══██╔══╝██╔══██╗██╔══██╗██║ ██╔════╝
██║ ██║█████╗██║ ███████║██████╔╝██║ █████╗
██║▄▄ ██║╚════╝██║ ██╔══██║██╔══██╗██║ ██╔══╝
╚██████╔╝ ██║ ██║ ██║██████╔╝███████╗███████╗
╚══▀▀═╝ ╚═╝ ╚═╝ ╚═╝╚═════╝ ╚══════╝╚══════╝
]]
local q -- q(x) to turn Function / Table into Q
-- Get first object field key by shortand
local function getKey(short, obj)
local t,rgx = {},'^'..short:gsub('.','%1.*')
for k in pairs(obj)do
if type(k)=='string' and k:match(rgx) then t[#t+1] = k end
end
table.sort(t, function(a,b)return#a<#b end)
return t[1]
end
--- Make call function
---@param f function
---@return function
local function QFnc(f)
return function(_, ...)
local result = table.pack(f(...))
for k, v in pairs(result) do
result[k] = q(v)
end
return table.unpack(result)
end
end
local function isQ(t)
local succes, mt = pcall(getmetatable, t)
return succes and mt and mt.__q
end
local function isCallable(t)
return type(t) == 'function' or isQ(t) -- TODO: fix if Q-table not actually callable
end
--- Generate safe function from lua code
---@param txt string Lua code to load as function body
local function makeRunedFunction(txt)
local loaded, err = loadTranslated(
'function(...) local a1,a2,a3,a4 = ... return '..txt..' end',
txt
)
if err then
localError(err)
return q{}
else
return function(...)
local safeResult = table.pack(pcall(loaded(), ...))
if not safeResult[1] then
localError(safeResult[2])
return nil
end
return table.unpack(safeResult, 2)
end
end
end
--- For each t run f(v,k)
--- pack and return Qued result
---@param t table
---@param f function
local function packFor(t,f)
local r,i = {},1
for k, v in pairs(t) do
-- local callable,f1,v1 = isCallable(f),f,v
-- if not callable then f1,v1=v1,f1 end
-- local resultTable = table.pack(f1(v1,k))
local resultTable = table.pack(f(v,k))
if #resultTable>1 or resultTable[1] == nil then
r[i] = q(resultTable)
else
r[i] = resultTable[1]
end
i=i+1
end
return q(r)
end
q = function(t)
local qtype = type(t)
local qIsFunction = qtype == 'function'
if qtype ~= 'table' and not qIsFunction then return t end
if isQ(t) then return t end
local mt = {
__q = true,
__call = QFnc(t),
__mul = QFnc(t),
__tostring = function() return '{q}'..(qIsFunction and tostring(t) or '#'..#t..': '..tostring(t)) end,
__band = function(self, ...) -- Make a function
local args = table.pack(...)
return q(function(k,v)
return t(table.unpack(args))
end)
end,
__bor = function(self, pipe_to) -- Pipe into function
local pipe_to_type = type(pipe_to)
local pipeTo_asFunc, pipe_is_callable
if pipe_to_type == 'string' then
pipeTo_asFunc = makeRunedFunction(pipe_to)
pipe_is_callable = true
else
pipeTo_asFunc = pipe_to
pipe_is_callable = isCallable(pipe_to)
end
if qtype == 'table' then
-- Table | (Function or String)
if pipe_is_callable then
return packFor(self, pipeTo_asFunc)
-- Table | Any
else
return packFor(self, function(v,k) return packFor(pipe_to, v) end)
end
else
-- Function | (Function or String)
if pipe_is_callable then
return q(function(...) return pipeTo_asFunc(t(...)) end)
-- Function | Any
else
return packFor(pipe_to, t)
end
end
end,
}
if qIsFunction then
return setmetatable({}, mt)
end
function mt:__index(key)
local exact = t[key]
if exact ~= nil then return q(exact) end
local v
-- Global key that started with _
if key:sub(1,1) == '_' then
-- Empty: _ global return function that just print output
-- TODO: add functionality for q{}._
if #key == 1 then v = function(...) print(...) end end
-- Number: _8 create table {1,2,3,4,5,6,7,8}
local subCommand = key:sub(2)
local num = tonumber(subCommand)
if num then
local arr={}
for i=1,num do arr[i]=i end
v = arr
end
-- Big letter shortand
elseif key:match'^[A-Z]' then
local c = key:sub(1,1)
local C = t[c]
if C then
v = C[getKey(key:sub(2), C)]
end
end
-- Other cases
if v == nil then
print('target key: '..key)
for k,v in pairs(t) do print('key: '..k) end
print('found key: '..tostring(getKey(key, t)))
v = t[getKey(key, t)]
end
return q(v)
end
mt.__newindex = t
function mt:__pairs()
return function(self, k)
local k, v = next(t, k)
return k, q(v)
end, t, nil
end
function mt:__len() return #t end
return setmetatable({}, mt)
end
--[[
████████╗██████╗ █████╗ ███╗ ██╗███████╗██╗ █████╗ ████████╗███████╗
╚══██╔══╝██╔══██╗██╔══██╗████╗ ██║██╔════╝██║ ██╔══██╗╚══██╔══╝██╔════╝
██║ ██████╔╝███████║██╔██╗ ██║███████╗██║ ███████║ ██║ █████╗
██║ ██╔══██╗██╔══██║██║╚██╗██║╚════██║██║ ██╔══██║ ██║ ██╔══╝
██║ ██║ ██║██║ ██║██║ ╚████║███████║███████╗██║ ██║ ██║ ███████╗
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝
]]
local _MACROS = {}
local tab = 0
local function translate(text)
tab = tab + 1
local result = text
local i = 1
while i <= #_MACROS do
result = result:gsub(_MACROS[i][1], _MACROS[i][2])
i=i+1
end
tab = tab - 1
return result
end
local __id = 0
local function nextID() __id=__id+1; return __id-1 end
--[[
███╗ ███╗ █████╗ ██████╗██████╗ ██████╗ ███████╗
████╗ ████║██╔══██╗██╔════╝██╔══██╗██╔═══██╗██╔════╝
██╔████╔██║███████║██║ ██████╔╝██║ ██║███████╗
██║╚██╔╝██║██╔══██║██║ ██╔══██╗██║ ██║╚════██║
██║ ╚═╝ ██║██║ ██║╚██████╗██║ ██║╚██████╔╝███████║
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
]]
local function addMacro(rgx, fnc_or_str)
_MACROS[#_MACROS+1] = {rgx, fnc_or_str}
end
-----------------------------------------------------------------
-- Simple replaces
-----------------------------------------------------------------
addMacro('`T', [[Tg!|'a1.tr!']]) -- Trade all trades
addMacro('`Z', [[a=`!a ;; ??`!Rm(3){ Rtn(a) c=`!Rm(3) Rtn(a) ??c{Rtn(a)Rm(3)} a=`!a}]]) -- Zig-Zag move
addMacro('`&', ' and ')
addMacro('`!', ' not ')
-- Syntax Sugar
local WRD = '[_%a][_%a%d]*'
for _,c in pairs{'%+','%-'} do
local from, to = '('..WRD..'[%._%a%d]*)('..c..')'..c, '(function() %1=__number(%1)%21 return %1 end)'
addMacro(from..c, to)
addMacro(from, to..'()')
end
-- TODO: Add *= += and stuff
-----------------------------------------------------------------
-- Captures {}
-----------------------------------------------------------------
local function translateTabbed(str,from,to)
return translate(str:sub(from, to)):gsub('\n', '\n'..string.rep(' ',tab))
end
local function captureGen(fnc)
return function (r)
local from,to = r:match'(){()'
if not from then return '' end
local head = r:sub(1, from-1)
local body = translateTabbed(r, to, -2)
if type(fnc)=='function' then return fnc(head, body) or ''
else
return fnc:gsub('HEAD', head):gsub('BODY', body)
end
end
end
local function addCaptureMacro(prefix, fnc)
addMacro(prefix..'(.-%b{})', captureGen(fnc))
end
-- Add Macros
addCaptureMacro('@', addMacro)
-----------------------------------------------------------------
-- Conditionals
-----------------------------------------------------------------
local function makeCondition(cond, body, checkFalsy)
return [[
local __if = (]].. cond ..[[)
if __if ]].. (checkFalsy and 'and __truthy(__if) ' or '') ..[[then
]].. body ..[[
end
]]
end
addCaptureMacro('?%?', makeCondition('HEAD', 'BODY', true)) -- Simple
-- Safe pointer
addCaptureMacro('?%.', [[
if type(HEAD)=='table' then
HEAD.BODY
end
]])
-- Safe call
addCaptureMacro('?!', [[
local __p = HEAD
if type(__p)=='table' and (type(__p.BODY)=='table' or type(__p.BODY)=='function') then
HEAD.BODY()
end
]])
-----------------------------------------------------------------
-- Loops
-----------------------------------------------------------------
local function replLetter(str, letter, to)
return str
:gsub('^'..letter..'([^_%a%d])', to..'%1')
:gsub('([^_%a%d])'..letter..'$', '%1'..to)
:gsub('([^_%a%d])'..letter..'([^_%a%d])', '%1'..to..'%2')
end
-- For Each inventory slot
addCaptureMacro('~#', function (head, body)
local i = 'i'..nextID()
body = replLetter(body, 'i', i)
head = replLetter(head, 'i', i)
local haveP = head ~= nil and not head:match"^%s*$"
return 'for '..i..'=1, (D or R).inventorySize() do\n'
..(haveP and makeCondition(head, body, true) or body)
..'\nend '
end)
-- Pairs
addCaptureMacro('~:', function (head, body)
local id = nextID()
body = replLetter(body, 'k', 'k'..id)
body = replLetter(body, 'v', 'v'..id)
return 'for k'..id..', v'..id..' in pairs('..head..') do\n'..body..'\nend '
end)
-- Loop
addCaptureMacro('~~', function (head, body)
local i = 'i'..nextID()
body = replLetter(body, 'i', i)
return 'for '..i..'='..head..', 1 do\n'..body..'\nend '
end)
-----------------------------------------------------------------
-- Lowest priority
-----------------------------------------------------------------
-- Exec
addMacro('!', '()')
-- -- Get value from global
-- local function api(s, p)
-- if p==nil then p = _G end
-- local t,k = {}
-- for c in s:gmatch'[^.]+' do
-- if p==nil or type(p)=='function' then break end
-- k = getKey(c, p)
-- p = p[k]
-- t[#t+1] = k
-- end
-- return p, table.concat(t,'.')
-- end
-- -- Global Shortand
-- local function globFnc(r)
-- local c = r:sub(1, 1)
-- if c:match'[A-Z]' and _G[c] ~= nil then
-- local res, way = api(r:sub(2), _G[c])
-- return res and c..'.'..way or r
-- end
-- local res, way = api(r)
-- return res and way or r
-- end
-- local globRgx = '%.('..WRD..('%.?[_%a%d]*'):rep(5)..')'
-- addMacro('^'..globRgx, globFnc)
-- addMacro('([^_%a%d%.])'..globRgx, function(p, r) return p..globFnc(r) end)
-----------------------------------------------------------------
-- Weird stuff
-----------------------------------------------------------------
-- Run only once
local is_first_run = true
function on_first_run(fnc)
if is_first_run then fnc() is_first_run = false end
end
addMacro('(.+);;(.*)', function(once, rest)
return 'on_first_run(function()\n '..once..'\n \nend)\n'..rest
end)
--[[
██╗ ██████╗ █████╗ ██████╗
██║ ██╔═══██╗██╔══██╗██╔══██╗
██║ ██║ ██║███████║██║ ██║
██║ ██║ ██║██╔══██║██║ ██║
███████╗╚██████╔╝██║ ██║██████╔╝
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝
]]
-- Global environment inside loaded code
local __ENV = q(_ENV)
loadTranslated = function(text, chunkName)
local code = translate(text)
if code == nil or code:match('^%s*$') then
localError('Unable to translate: '..text)
return nil
end
code = code:gsub('[%s\n]*\n','\n'):gsub('^%s*',''):gsub('%s*$','')
-- print('Code:',code)
local res, err = load('return '..code, chunkName, nil, __ENV)
if err then res, err = load(code, chunkName, nil, __ENV) end
if err then localError(err) end
return res, err
end
run = function(input)
local fnc, err = loadTranslated(input)
local r
while true do
r = fnc()
if isCallable(r) then r() end
end
end
__ENV.__truthy = function(a)
if a and a ~= '' and a ~= 0 then return true end
return false
end
__ENV.__number = function(a)
local t = type(a)
if t=='number' then return a end
if t=='string' then return tonumber(a) end
return __ENV.__truthy(a) and 1 or 0
end
__ENV.run = function(text)
return loadTranslated(text)()
end
__ENV.proxy = proxy
__ENV.sleep = sleep
-- Assemble --
-- Dump everything down and suck 4 slots from top then trade
-- ~#Rc*i{Rsel(i)Rd(0)}Rs(2)~~1,5{IsFS(1,i)`T}
-- ~#Rc*i{Rsel(i)Rd(0)}~~1,5{IsFS(1,i)}Rsk(3)~:Tg(){~~1,5{?!v{tr}}}
local cmd, prog, pointer = ...
-- Program is called from shell
if cmd then prog = cmd
-- Program defined by Robot/Drone name
elseif D then pointer = D elseif R then pointer = R end
if pointer and pointer.name then prog = pointer.name() end
if not prog or prog=='' then error'No program defined' end
-- Play music
if prog:sub(1,1) ~= ' ' then
for s in prog:sub(1,5):gmatch"%S" do
computer.beep(200 + s:byte() * 10, 0.05)
end
end
return run(prog)
--[[
Available ops:
&|~<<>>+-*/^%//==< <=
Some programs:
Dm(tb.u(Nf(300)[a++%2+1].p))s(3)~#{Dsel(i)Dd(0)Dsu(0)}
a++b=Nf(300)[a%2+1]Dm(tb.u(b.p))s(14)run(b.l)
Tg!|'a1.tr!'
~:Tg(){?!v{tr}}
]]
--[[
Test file for LostUser program
Author: Krutoy242
Source and readme:
https://gist.githubusercontent.com/Krutoy242/1f18eaf6b262fb7ffb83c4666a93cbcc
]]
--[[
■ Deploy script:
crunch --lz77 lostuser.lua lostuser.min.lua && flash -q lostuser.min.lua LostUser
■ Download and test
wget -f https://is.gd/mAN3WA lostuser.lua
wget -f https://is.gd/lostuser_test_lua lostuser.test.lua
lostuser.test
]]
local lu = loadfile'lostuser.lua'
print'\n< LostUser tests >\n'
--[[
████████╗███████╗███████╗████████╗
╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝
██║ █████╗ ███████╗ ██║
██║ ██╔══╝ ╚════██║ ██║
██║ ███████╗███████║ ██║
╚═╝ ╚══════╝╚══════╝ ╚═╝
]]
local function tableToString(t)
local s,f = '',true
for k,v in pairs(t) do
s = s .. (f and '' or ' ') .. v
f = false
end
return s
end
-- local _print = print
local printedMessage = ''
-- function print(...)
-- _print(...)
-- local result = table.pack(...)
-- if #result == 0 then return end
-- printedMessage = (printedMessage ~= '' and '\n' or '')
-- .. printedMessage .. tableToString(result)
-- end
local function shouldError(errorRgx, ...)
local succes, result = pcall(lu, ...)
return (not succes)
and result
and errorRgx
and result:match('^.+: ' .. errorRgx)
, result
end
local function shouldPrint(command, message)
local succes, result = pcall(lu, command)
return succes and printedMessage == message
, result
end
local function test(description, succes, result)
io.write('■ '..description..': ')
io.write((succes and '✔' or ('❌\n> ' .. tostring(result))) .. '\n')
printedMessage = ''
end
--[[
██████╗ ██╗ ██╗███╗ ██╗
██╔══██╗██║ ██║████╗ ██║
██████╔╝██║ ██║██╔██╗ ██║
██╔══██╗██║ ██║██║╚██╗██║
██║ ██║╚██████╔╝██║ ╚████║
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
]]
test(' Run without args', shouldError('No program defined'))
test(' Empty name', shouldError('No program defined', ''))
test(' Expose error', shouldError('Test Error', ' error"Test Error"'))
test(' Global shortand', shouldError('Exit', ' e"Exit"'))
test(' Should print msg', shouldPrint(" _'test'o.e!", 'test'))
T = { getTrades = function() return {
{trade=function()print('t1()')end},
{trade=function()print('t2()')end},
n=2,
} end }
test(' >>output ', shouldPrint(" io.w(Tg)io.w(Tg!)", ''))
test(' Trade pipe', shouldPrint(" Tg!|'a1.tr!'", 't1()\nt2()'))
test(' Macros: pairs()', shouldPrint(
" ~:T{??t*v=='table'{_*k}}", "trade\ntrade"
))
test('Macros: safe pntr.', shouldPrint(" ?.io{write'Hello\n'}", ""))
--[[
TODO: Some programs to test
i++Dsw(0)Ds(0)Dp(0)Dm(1>>((i+1)%5),0,(-1)^(i//5))s(1)
]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment