Design of a mini-language / minimal instruction-set for a turtlebot
based on basic_robots.
The goal is to teach programming to children, using turtle-like programmable robots in minetest.
We have turtleminer, with ambitious plans, but still in early development.
- One big item on that todo-list is a visual code-editor, like blockly or scratch.
We have basic-robots, that are working, really powerful, and can do almost everything.
They are programmable in lua, using an in-game texteditor.
basic-robots should be 'more than good enough' to teach programming in general,
but (for kids) the presentation may need some tweaks.
It would be nice to have something like ComputerCraftEdu
with a visual-editor like blockly or scratch for minetest,
but implementing that would be a lot of work.
Maybe we can come up with smaller, simpler 'programming language'
that is more easy to incorporate into the basic-robot
that is still useful for an introduction to programming.
Concept of Turtle-commands
Turtles use commands like
move-forward, move-up, turn-right, dig-forward.
These commands can be represented in different ways,
eg. as text, code, numbers, symbols, graphics/icons, etc..
Graphic symbols look nice, but a system like blockly or scratch would need a lot of them ...
For a limited number of simple actions this is easy,
but gets much more complicated if you want to express variables,
calculations, control-structures (if-then-else, for-loops) etc.)
There is a point where it becomes simpler to just write code, eg. for lua, in a texteditor.
Some examples of existing programs that use basic_robots with turtle-commands:
- Build4 - House1 - build a house / small hut.
- Build5 - another housebuilder - builds a bigger house
- Mining103 - tunnel 2x1 - this is a small & pretty basic turtle-program, that does not stop.
- Demolition-Turtle - it clears the construction-site for robot #17, by digging away the old building.
Note1: all of these programs have a different instruction-set,
and no 'advanced features', like variables, calculation, or conditional execution.
But I remember that rnd once made such an advanced turtle...
Note2: all of the above example-program have the instructions of their turtle-scipt hardcoded.
But it would be possible to change that script on the fly, eg. from a book, with a remote-control,
by chat or via mail from another robot.
To minimize the work,
we design a mini-language, a minimal instruction-set for a turtlebot,
that still introduces some basic concepts of programming.
I want to keep the number of instructions down (say 10-20),
as well as avoiding composite and variable-length instructions.
If the basics have been learned, move straight to 'real' coding, with lua and texteditor.
- what kind of programs do we want to 'write' with that language ?
- as in, what instructions, how many steps/statements in a program
- Other required features (as in variables / fuel, input/output, flowcontrol***...) ?
- (usecase-examples please: task, environment/terrain, program that solves the task)
- When do we want/need it to be usable ?
For example, I would like to use those robots to do simple tasks such as
- move the robot around
- need instructions: move-forward, move-up, move-down, turn-right, turn-left
- optional: move-backward, move-right, move-left
- press a button, activate = open/close a door
- more instructions: activate-forward
- maybe other direcions too
- dig a straight tunnel, size 1x2 (width 1, heigth 2)
- more instructions: dig-forward
- dig bigger tunnels, eg. 1x3, 2x2, 3x3
- more instructions: dig-up, dig-down
- dig stairs up, dig stairs down
- nice-to-have, but optional: dig-forward-down and dig-forward-up
- dig a room, eg. 5x5
- stop after x operations
- needs a counter (a variable), and operations for setting, counting up/down, checking
- or some kind of 'fuel' that is used up by each operation
- that means counting, checking and stopping is done internally
- moving, and react to obstacles (eg. turn when hitting a wall)
- check for success, conditional execution / jump / exceptions ...
- might also need block-start / block-end, or labels / markers
Also, it would be nice to have the instructions of several of those 'programs'
available at the same time.
Eg. ability to select different starting-points within a program,
or several remote-controls with different programs.
More complicated tasks
- build a wall
- needs an inventory for at least 1 type of material
- needs instructions: build-forward
- maybe build-backwards, or switch building on/off (for building a 'trail')
- like pen-up / pen-down of a logo-turtle
- optional: build-up, build-down
- build stairs up
- build stairs down
- build a bridge
- basic-robots can only move one space above ground,
- so moving to the position for building a block might be a problem
- maybe needs instructions: build-forward-down, build-forward-up
- build a house
- needs bigger inventory (different materials for floor, walls etc.),
- and a command to select
- look for ore
- needs an instruction for looking at nodes
- and then do what ?
- output a message ...
- plant & harvest wheat, flowers, trees
- goal-seeking/homing: use find_nodes() to find an item, eg. a flower
- this needs 2 real numbers (current- and old value), and a compare-instruction for them
These might be already too complicated for our 'simple' design.
TurtleBot using inventory as code-editor
For example, it might be fairly easy to implement the 'turtle-instructions' move-Forward,Backward,Up,Down,turn-Left,turn-Right, Dig-forward, Activate-forward (that makes a 'palette' of 8 instructions**, to fill the hotbar) with programs of upto 30 steps, using the inventory of the robot (That leaves 2 inventory-slots free for digging stone and ore).
Simple, as in 1 block = 1 instruction - but it means no i/o, no flowcontrol, no vars, no extras.
(**) The exact set of instruction can be discussed/modified somewhat: Eg. we could do without turn-Right (just do 3* turn-Left instead), or without move-Backward (do right-right-forward-right-right instead). Also, we might want to add a check-instruction (eg. 'if ok then...'), and perhaps one or more types of marker (start-, stop-marker). The robot consuming 'fuel' would be much like having a stop-marker.
(***) even without instructions for flowcontrol, at least we need to define the behaviour
- when encountering an empty slot / an invalid instruction (such as dirt, stone etc.)
- when reaching the last inventory-slot
- when an instruction fails (eg. moving against a wall, diggging air, ...)
- indicate success/error (eg. with message, label, sound-effect...)
- ignore / treat as success / treat as failure
- stop program
- continue with next instruction
- continue with first instruction
If we add markers to our instructionset, we get some more options, eg. 'continue at next start-marker'.
For that, we 'just' need some blocks that represent those instructions/actions. Without making nice new graphics, we could use the existing keyboard-blocks for that. (see chars.png in the texture-directory of basic_robots)
Give the kids a chest full of such blocks, and let them place these blocks into the robots inventory, as a turtle-script, and we can think of a way to start the robot so that it would interpret the contents of its inventory as a script. (Eg. a special start-button, a special id, a special new command, look at the inventory for fuel or a start-marker...)
Do you have some suitable graphics to use as textures for such blocks ?
As an alternative idea: using a special remotecontrol as the visual-editor for the turtle-script (but that would need much more programming, formspec-design, rc-design, etc.).
A 'turtlebot' that works pretty much like that (but with its program represented as a text-script) is already on TechWelt - see the Robot_#18
See also Alphabeth-blocks
- There is a set of available instructions ('turtle-commands'), see above.
- There is a script with a sequence of turtle-commands
- This script can be represented as a string, or by items in the inventory
- Each turtle-command is represented by one character.
- eg. 'f' stands for 'move-Forward'
- If the script-string is empty, the inventory is used as the source for the script,
- where the item in each slot represent one turtle-command (or building-materialsm etc.)
- eg. the block 'arrow-right' means 'move-Forward' - same as 'f' when expressed as string.
- There is a 'pause' variable. While true, execution of the script stops.
- At the start, pause is set to false.
- This can be changed with a remotecontrol
- There is a variable 'mat', that contains the name of the current building-material.
- At the start, mat is set to an empty string.
- maybe start with 'dirt', or contents of slot 1 ?
- mat can be set with the instructions 1,2, ... 8 to the name of the material in slots 1 .. 8.
- the item-count of the material is removed
- Some slots might be reserved for fixed strings, such as "farming:wheat_8" or "air"
Note: this might allow some tricks, eg. building with instruction-blocks / selfmodifying code
- I'm not sure if/why we should restrict this
- (eg. start script at position 9 would waste 1/4 script-length)
- There is a variable 's', that is used as a temp. string, eg. for read_node().
- At the start, s is set to an empty string.
- maybe set it to the name of the robot, or the version-number,
- or some status, eg. "robot1, fuel=99" ?
- There is an instruction-pointer 'ip' that keeps track of the current instruction
- i.e. the instruction-pointer starts at position 1.
- Every script starts at the begin.
- Every second, one instruction is executed
- maybe only count actions like moving, digging, building ?
- and the instruction-pointer advances one step
- If the ip reaches the end of the script, it continues at the start.
- This repeats until fuel runs out, or an end-instruction is reached.
- Invalid instructions are ignored
- such as blanks / empty slots, fuel, building-materials etc.
- The turtle-interpreter scans forward until a valid instruction is found,
- or the same position is reached again
- eg. if the script is complete garbage, or empty, etc.
- ? errormessage / failure-sound ??
- ? instruction for pickup() -- p is already used for pause
- also what range -- maybe first pickup(4), second pickup(8) ?
- A Turtle needs 'fuel' to run,
- eg. leaves (coal would be too valuable - think of mining coal with a coal-burning turtle :)
- maybe different types of fuel, eg. leaves for moving, wood or sticks for digging
- maybe create a new fuel-item, with stacklimit > 99
- Each step consumes 1 unit of fuel
- this stops endless loops, and allows precise stopping of the turtle
- maybe only have 'actions' (move,dig,build) consume fuel, not 'steps'
- so internal processing would be free (look/read_node, check, output...)
- Check for fuel is done before/after ...
- If fuel runs out, the turtle goes into pause-mode
- so the player can refill, and unpause with a remote-control
- maybe require that fuel is put into a certain slot, eg. the last slot ?
- How to represent fuel when using a script from a string ?
- There is a global 'ok' variable that keeps track of the result of each operation, eg.
- Some instructions cannot fail, e.g. turning.
- The check-instruction tests this variable, and can skip forward to the next marker.
- Eg. this lua-code:
ok=move.forward() if not ok then turn.left() end
- could be expressed as turtle-instructions
- or, using instruction-blocks in the inventory:
- where 0 is the next (green) marker, and '<' is executed when move-Forward failed
- The instructions "n" does s=read_node.forward()
- The instructions "t" does s=read_text.forward(); ok=...
- and also sets 'ok' if some text was found
- so we can at least identify objects with text (sign, chest, door, furnace...) even without string-compare
- The instruction "=" compares s with mat, and (on mismatch) can skip forward to the next marker.
Eg. this little harvester
if read_node.forward() == "farming:wheat_8" then dig.forward() end if read_node.forward() == "air" then place.forward("farming:seed_wheat") end move.forward()
Could be expressed as
8n=-0 7n=1+0 f
- or, as instruction-blocks in the inventory:
where slot 8 has "farming:wheat_8", slot 7 has "air", slot 1 has "farming:wheat", and 0 is a (green) marker.
- slot 1 needs to be setup by the user, slot 7 & 8 are fixed strings built into the turtle-interpreter.
Note: in this harvester-example, we cannot check for soil, because we have no turtle-instructions for 'read_node.down()' or 'read_node.forward_down()'
- Also, 'farming:soil' would need an additional special string
- ? store / recall strings ?
- ? random ?
It would be nice to have variables and arithmetic, but with a small instruction-set,
and very little room (<30) for scripts, it seems not practical.
Eg. it would need instructions to set a value, add, substract, check, specify which var,
and represent values.
A simpler type of variable are flags and counters.
- flag (aka boolean) - true and false (such as our internal 'ok'-variable)
- counter with a specific max-value. Only needs an instruction 'decrement&test-for-zero'.
- after reaching 0, the counter is restored to its max-value.
- with a max-value of 1, a counter works just like a flag
- to reset it, just repeat reading, until it reaches zero
The turtle only gets two counters (using the instructions # and %),
the max-value is specified by their stackcount in the inventory.
- That means the max-value must be 1..99
To represent this when using a script from a string,
we can extend the script-string, eg. append ";#=5 %=3 fuel=99"
- The instruction "#" decrements the current value of counter1 , and sets the ok-value:
- ok=true: success means the value of the counter was > 0, anc could be decremented
- ok=fail: value was = 0, and is now set to its max-value again
- The instruction "%" works likewise on counter2
Such counters are also useful to keep trace of some state-of-processing.
There is a specific usage for real numbers I would like to include with turtles:
finding the distance to something via find_nodes(), compare old & new distance-value,
with the goal of moving closer to it.
- Eg. finding flowers on a plain of grass, without visiting each square.
That would need a special instruction like 'compare & store-when-less',
and a way to clear the old value, for the next search.
- Maybe reset when find_nodes() returns nil, meaning 'no such node in range' ?
This is optional, but really nice-to-have:
- Uppercase letters are reserved as names for subroutines
- To define a subroutines:
- dig-Forward, dig-Up, move-Forward
- To call a subroutines:
- If an uppercase letter is followed by '(', the body of the subroutine followes, ended by ')'.
- Otherwise, the interpreter tries to find & execute that subroutine
- If not found, the program stops
- ? errormessage ?
- The call-stack is only 1 level deep, so calls from a subroutine, and recursion are not allowed (yet).
- If a '(' is encountered outside of a subroutine-definition, the interpreter ignores all following instructions upto the next ')'
- So, (..) also serves as a block-comment.