Robot (or drone!) BIOS program for the Minecraft OpenComputers mod.
Lost User
- The Simplest Robot
OC robots are complex to assemble and program. This BIOS program helps to use robots as "users" and in many other ways.
Assemble the robot in the minimum configuration:
- Case
- CPU
- RAM
If you play Enigmatica 2: Expert - Extended, the modpack has a predefined EEPROM recipe.
Find it in JEI and craft it. It will have a colored glow.
If you crafted it, you can skip the next step Write the Program to EEPROM
.
You need a working OC computer to write the BIOS. See this tutorial to assemble your first computer.
- Download the file from the internet (requires an Internet Card), run from the command line:
wget https://raw.githubusercontent.com/Krutoy242/lostuser/main/lostuser.min.lua
- To write to an existing EEPROM, run:
flash -q lostuser.min.lua LostUser
Take the EEPROM from the computer case and insert it into the robot.
Program the robot by renaming it. Rename the Robot on an Anvil or with a Labeller.
Name your Robot robot.use(3)
, place it on the ground, turn it on, and watch it click blocks in front.
TL;DR
If you don't want to learn Lua and you need the robot to right/left click, here are a few simple names for the robot and the result:
robot.use(3)
- The robot will right-click on the block in front.robot.swing(3)
- The robot will swing with a sword or break the block in front of it.
The robot will execute its name as Lua code in a while true
loop.
Code can be run in any variation - statement
or expression
, but must still follow Lua code flow rules.
This is a statement
sleep(1)This is an expression
1, sleep(1)Combining a statement and an expression
a = robot.move(3) return a and robot.use(0)
If an expression returns one or more functions, they will be executed recursively.
Note that all return values are calculated first, and only then will the functions be called.
Calling
robot.use(3)
, and thensleep()
function() return sleep end, robot.use(3)
- All components exposed as globals
- Components added to globals by big first letter
C = computer
E = eeprom
I = inventory_controller
R = robot
T = trading
...
⚠️ Warning: If two components starts with same letter, only one that shorter and came first after sorting will be exposed by single letter.For example, if robot have
Redstone Card
component, letterR
will stand forrobot
rather thanredstone
.
Additional globals:
i
- current loop index, starting from 0.You can add a number after
i
to get it by modulus +1.i16 = i % 16 + 1
sleep(seconds: number = 1)
long(shortand)
- write the long name of the shorthand.long'Ru3' -- prints "robot.use(3)"
Since a Robot or Drone name can have only 64
characters, pointers must be shortened.
So, instead of writing the full pointer name, you can shorten it. For example, instead of writing robot.use(3)
, you can write r.u(3)
, or even Ru3
.
Shortening rules:
-
If a key has an exact non-nil match, it will be returned.
R.use(3)
-R
is a global representing therobot
component. -
The shorthand must contain the first letter and then, optionally, any number of remaining letters.
tbl.unk => table.unpack t.u => table.unpack
-
If several names have the same first letter, the shortest, alphabetically sorted name will be picked first.
robot.s -- robot.slot robot.se -- robot.space robot.sel -- robot.select
-
A big first letter with a dot
.
can be used without the dot.- First, bug letter would be indexed.
- If not found, anything that start with this big letter.
- Lastly, anything that start with lowercased first letter.
-- Same pointers robot.use == R.use == Ruse == Ru
-
A number at the end of a shorthand will call the shorthand as a function with that number as the first argument.
Ru3 -- robot.use(3) s10 -- sleep(10)
At the same time, if it's a table instead of a function, all keys of the table will be naturally sorted and the
N
th element returned.R16 -- robot.select
See more in Numeric Dictionary.
-
Local variables can't be shortened.
local query = {len=4} q.l -- Exception: q is nil query.l -- l is nil query.len -- 4
The low dash _
is a special helper function.
- Using
_
with numbers_123
Will return a new array-like list with the length of the number. If the first digit is0
, the table will be zero-based._8 -- returns {1,2,3,4,5,6,7,8} _08 -- returns {[0]=0,1,2,3,4,5,6,7}
- Using
_
with words_abc
Creates a function that will write the result into theabc
variable. The function returns the passed value. Note that_abc
is functional.-- Writes `4` into global `a`, returns 4 _a(4) == (function() a = 4; return a end)() -- Create func. that write result of `Ru` into global `a` _a^Ru == function(...) a = robot.use(...); return a end -- Writes into table member b._a^3 == b.a = 3
-
Using
_
on a string Will load the code inside this string and return it as a function. Calling this function is always error-safe—if an exception occurs inside, the function will simply returnnil
._'Rm,s2'()(0) -- calls `sleep(2),robot.move(0)`
Note that in this example, the
_
function returns two values—therobot.move
function and the resultsleep(2)
. Only when we call the returned values a second time doesrobot.move(0)
get called. -
Using
_
on a table or function Will convert them into a_{}
table or_''
function to use with Functional Programming.{1,2}^1 -- would error _{1,2}^1 -- would return {1,1} (see Functional Programming)
Any table or function that you can get from a global will be converted into a special _{}
table.
This table is enhanced with additional operator metamethods that help with functional-style programming.
Any iteration or pairs()
calls on these converted tables will output elements in naturally sorted order.
Operators behave differently depending on the left and right side of the operator.
Note that whenever a string
is detected, it will be loaded and converted to a function in the manner of _'fnc'
.
Operator precedence in Lua follows the table below, from higher to lower priority:
^
- unary
not
#
-
~
*
/
//
%
+
-
..
<<
>>
&
~
|
<
>
<=
>=
~=
==
and
or
^
, +
, and &
operators do the same. There are three of them only to manage precedence.
-- Assume that f,g and h is functions f&g+h -- equal to `f & g(h())` f+g&h -- equal to `f(g()) & h`
-
Note¹:
^
is right associative. This means the right side will be computed first. -
Note²: You can also call uncallable tables.
t(x)
is the same ast^x
. Uncallable tables are tables without a__call
metatable. Example (mapt^f
):_{1,2,3}'0' -- _{0,0,0}
Left | Right | Result |
---|---|---|
Table | Function |
Classical map _{4,5,6}^f -- {f(4),f(5),f(6)} |
Table |
Pick indexes _{4,5,6}^{3,1} -- {6,4} | |
Number, Boolean |
Push value in END of table _{1,[3]=3,a=6,[4]=4}^5
-- _{1,3=3,4=4,5=5,a=6} | |
Function | Function |
Composition f^g -- (...)=>f(g(...)) |
Table |
Unpack as arguments f^{1,2,3} -- f(1,2,3) | |
Number, Boolean |
Simple call f^1 -- f(1) | |
Number, Boolean | Table |
Get by numerical or boolean index 2^_{4,5,6} -- 5 |
Function |
Not yet implemented |
Left | Right | Result |
---|---|---|
Table | Function |
Filter, keep only if value is Truthy _{4,5,6,7}/'v%2' -- {5,7} |
Table |
Not yet implemented | |
Number, Boolean |
Remove index _3/2 -- {1=1,3=3} | |
Function | Function |
Reversed composition f/g -- (...)=>g(f(...)) |
Table |
Simple call f/R -- f(R) | |
Number, Boolean |
Composition f/1 -- (...)=>f(1,...) | |
Number, Boolean | Table |
Get by modulus i/t -- t[i % #t + 1] |
Function |
Rotated composition 2/f -- (...)=>f(..., 2) |
Left | Right | Result |
---|---|---|
Table | Function |
Not yet implemented |
Table |
Not yet implemented | |
Number, Boolean |
Not yet implemented | |
Function | Function |
While truthy do f~g -- while truthy(g(j++)) do f(j) end |
Table |
Not yet implemented | |
Number, Boolean |
For loop f~n -- for j=1,TONUMBER(n) do f(j) end | |
Number, Boolean | Table |
Not yet implemented |
Function |
Same as n~f -- for j=1,TONUMBER(n) do f() end |
Unary | Object | Result |
---|---|---|
| Function |
While truthy do ~f -- repeat until not truthy(f()) |
Table |
Flatten table, using numerical indexes.
~_{1,{2,3},{4,a=5,b={6,c=7}}}
-- {1,2,3,4,5,{6,c=7}} | |
| Function |
Make a function whose result will be flipped.
If the result is -- id here is function that returns its first arg
(-id)(0) -- 1
(-id)(4) -- 0
(- -id)(4) -- 1 |
Table |
Swap keys and values -_{'a','b','c'}
-- {a=1,b=2,c=3} | |
| Function |
Make a function that would wrap its result into a table. Useful for functions that return several values. -- Consider `f(n)` returns three values - 2,3,n
f&4 -- 2
#f&4 -- _{2,3,4} |
A value is considered truthy
if it is not falsy
.
falsy
values are:
false
ornil
''
(empty string)0
(number zero)nan
(not a number,n ~= n
)inf
or-inf
(result of1/0
or-1/0
)
The program has several predefined macros - symbols that will be replaced everywhere with another text.
! => '()'
ⓐ => ' and '
ⓞ => ' or '
ⓝ => ' not '
ⓡ => ' return '
⒯ => '(true)'
⒡ => '(false)'
-
Travel between two waypoints and run its label
Drone name:
P=i/Nf300ⓡDm^Pp,s/1~'Dg0>1',_(Pl)
Nf300
: Runnavigation.findWaypoints(300)
.i/Nf300
:i
is the index of script execution.i / table
is "Get by index modulus"t[i % #t + 1]
.P=i/Nf300
: Write into the global variableP
a different waypoint each script cycle.ⓡ
: will be replaced byreturn
Dm^Pp
: callingdrone.move(table.unpack(P.position))
.s/1~'Dg0>1'
=>while drone.getOffset() > 1 do sleep(1) end
._(Pl)
: LoadP.label
as Lua code. This loaded function would be returned and executed.
Waypoints labels. The first one just sucks from the bottom, the second one iterates over 4 slots and drops down.
_'Dsk0'~4 Dsel-'Dd0'~4
-
Zig-Zag + Use Down, useful for farms
Required upgrades: none
Robot name:
m,t=_'Rm3,Ru0',Rtn/(i2>1)ⓡ~m,t!,_'m!,t!'!ⓞt/m
m,t=_'Rm3,Ru0',Rtn/(i2>1)
: define two functions for moving and rotating_'Rm3,Ru0'
: define a functionRm3,Ru0
that would move forward and use a tool downRtn/(i2>1)
: this makes a function that would callRtn
(robot.turn
) with the argumenti2>1
.i2
is shorthand fori%2+1
~m
: Makes the robot move forward until it can't move.t!
:t
is Rtn/(i2>1) while!
replaced with()
so the line will becomeRtn/(i2>1)()
, which means execute turn immediately._'m!,t!'!ⓞt/m
: Move and turn. If the move wasn't successful, turn and move again.
-
Trader bot
Setup:
- Robot must be placed on top of inventory of size
16
. - Robot must have only one Inventory Upgrade.
- Villagers must be in 8 meter radius.
Features:
- Robot will pull items required for trades and sell them.
- Robot will not sell emeralds.
Robot name:
a=-~Tg0'388^-g0ⓞ{g0.n,~tr}'ⓡ_16&R16-'Rd0'&IgI/0&'a[n]ⓐI8/0&k'
- Robot must be placed on top of inventory of size
-
Rune maker
Place ingredients in the first 6 slots of the Robot. Living Rock in the 7th, wand in the 8th.
Robot name:
_8/'Rsel^v,v==7ⓐ{s3,Rm1,Rd(3,1),Rm0}ⓞ{Ie!,Ru3,Ie!}'
Rsel^v
: Select iterated slotv==7ⓐ{s3,Rm1,Rd(3,1),Rm0}
: if it's the 7th slot with Living Rock, wait 3 seconds until the craft is finished, then drop Rock on top.Ie!,Ru3,Ie!
: Other slots - just right-click with the item
-
Single tree farm
This robot is intended to use with Forestry saplings, which usually can't be placed as blocks but need to be right-clicked instead.
Also, the robot needs an unbreakable Broad Axe from TCon with the Global Traveler trait. Additionally, my Axe has the Fertilizing trait - right-click to fertilize.
Place the robot on top of a container with saplings.
Robot name:
#(1|#Rdt&3)<6ⓐRsw/3-s/1-Rsk/0-Ie-Ru/3-IeⓞRu3,s
(1|#Rdt&3)
: Detect the block in front, select the second returned value - block description#()<6
: a trick to determine if the block is solidRsw/3-s/1
: Cut the whole tree, wait 1 secondRsk/0-Ie-Ru/3-Ie
: Suck sapling from the bottom, then plant it. Note thatRsk
derived one value fromsleep
returnRu3,s
: Fertilize sapling
-
Other examples
-
Circular Miner. Using a Hammer with an Alumite part (Global Traveler trait). Place the Robot underground, place a stack of Charcoal Blocks in the selected robot slot. The Robot will start to circle around, mining everything.
Gi,_'Rm3,Rsw3'~i*2,Rtn⒯
-
Robot sorting mob drop. Take from the bottom, damageable items to the top, others forward.
Rd|3%2^(IsF(0,i%Igz0+1)ⓐIgSII!.mDⓞ2)
-
Cat opener. Takes 16 items in front, right-clicks them, and then dumps the inventory on top.
Rsk/3&16ⓐIe!,~_'Ru0',_16/Rc|Rsel/'Rd1'
-
Compressing bot. Takes from the front, crafts 3x3, then dumps back.
-(_16-Rc&12)|'Rd3'&Rsel,IsF/3/'_11/8/4&Rc!/9/RtT'|i81,Cc
-
Unstackable bot. Takes an item from the front only if they are unstackable and puts it on top. If it can't drop the item on top, pushes up and places a block.
Flood all robot slots except 1. Slot 9 should have new inventories for unstackables.
(IgSI/3&_a^i1728ⓞ{}).mS^_{_'IsF/3&a,Rd1ⓞ{Pps1,Rsel9,Rp1,Rsel1}'}
-
This is not an actual dictionary - all this information can be generated in-game for every table.
To get sorted numeric values, name the robot this way, where T
is a pointer to the desired table:
e((~-T'k')"'\\n'..k..' '..v")
Cheatsheet of most common tables:
|
|
|
- Repo with source code and readme
- Modpack this robot was programmed for: Enigmatica 2: Expert - Extended