Grobots are programmed in a simple stack-based **language** derived from Forth. This imitates RoboWar, but the language itself is much closer to Forth. These stack-based languages are good for this sort of application because they are very easy to implement, and easier to learn and use than other similarly low-level languages.

The Grobots language doesn't have a name yet; I will call it Grobocode here for lack of a better name. If you are used to RoboTalk, you will find it pleasantly powerful. If you are used to Forth, you will find it annoyingly underpowered. If you don't know any stack-based languages, you will just find it weird.

## IntroductionEdit

Like all Forth dialects, keeps intermediate results of calculations on a stack: operands are pushed onto the stack, and operators take their arguments from the stack. Expressions are written in postfix order - the operator comes after all its arguments. This may be unfamiliar, but fortunately it doesn't take long to get used to. What in a conventional language would be:

gamma = 1 / sqrt(1 - (v / c)^2)

is in Grobots:

1 v c / square - sqrt reciprocal gamma!

Here's how it works. Start with an empty stack, and execute the first instruction, 1, which pushes a value on the stack.

stack: 1 Then v, which pushes the value of that variable: stack: 1 201285 Then c: stack: 1 201285 299780 / takes the top two arguments and leaves their quotient: stack: 1 0.6714 square takes one argument stack: 1 0.45 - stack: 0.55 sqrt stack: 0.7416 reciprocal stack: 1.35 gamma! stores the value into the variable of that name. (empty stack)

Try clicking on a robot and opening the Debugger window. Then pause the simulation and give the Step Brain command to slowly advance the brain. Watch the stack change.

There are instructions to operate on the stack, so you can do fancier things like using a result more than once, or shuffling things to be in the right order:

fibonacci: ;n -- f_n dup 1 <= if drop 1 return then dup 1 - fibonacci swap 2 - fibonacci + return

See if you can understand how that works. (You'll need to look up a lot of the operators, and this may be confusing to trace since it's recursive.) n -- f_n is a stack diagram, saying the newly defined fibonacci operator takes one argument (n) and returns one result (f_n).

The most confusing errors are stack underflow and overflow. Underflow means means you tried to remove more values from the stack than were actually there. For example, 2 + will probably underflow, because + needs two arguments. Overflow means you kept leaving values on the stack, so they piled higher and higher until the interpreter gave up (when the stack reaches 100 items). Overflow is especially difficult to diagnose because the code that overflows is probably not the code with the bug, but simply an innocent bystander that pushed the almost-full stack over the limit. See the debugging page for suggestions on how to debug overflow errors.

## Model detailsEdit

Grobocode runs on a two-stack machine, like traditional Forth. There is a return stack, which is only used for return addresses, and a data stack, used for arguments and return values of operators.

The only data type is a 32-bit fixed-point number, with 12 bits of fraction (and 1 of sign and 19 of integer). The range is about +/- 524,288, and the precision 1/4096 (.00024). Integer values may be used as addresses.

## SyntaxEdit

Grobocode syntax is a sequence of words separated by white space. There are several kinds of words; suffixes are sometimes used to indicate which kind. Each word (except compile-time words and label declarations) compiles to one instruction.

Kind of word | Suffix |
---|---|

Primitive call | none |

Read variable (including constants, vector variables, and hardware variables) | none |

Write variable | ! |

Read label (put its address on the stack) | & |

Call label | ^ (optional) |

Declare label | : |

Immediate number | none |

Semicolon makes a comment to the end of the line. This works everywhere, not just in code.

Variables and constants are defined somewhat clumsily by leaving the language and using reader tags. #var name [initial-value] defines a variable, #vector name [initial-x initial-y] defines a vector variable, and #const name [value] defines a constant.

## Primitive OperatorsEdit

Hardware access words are not included here, but are listed on the hardware page.

Word | Stack diagram | Comments |
---|---|---|

nop | -- | Does nothing, of course. Used for padding in jump tables. |

Stack manipulation | ||

drop | a -- | Discard the top item on the stack. |

2drop | a b -- | Discard the top two items. |

nip | a b -- b | Discard the second item on the stack. |

rdrop | R: a -- | Discard the top item on the return stack. |

dropn | x_{n} ... x_{2} x_{1} n --
| remove n items from the stack. |

swap | a b -- b a | Exchange the top two items. |

2swap | a b c d -- c d a b | Exchange the top two pairs of items. |

rot | a b c -- b c a | |

rrot | a b c -- c a b | |

dup | a -- a a | These all duplicate items that are on the stack. |

2dup | a b -- a b a b | |

tuck | a b -- b a b | |

over | a b -- a b a | |

2over | a b c d -- a b c d a b | |

stack | -- height | Return the number of items that were on the stack. |

stack-limit | -- limit | Return the maximum number of items that can be on the stack. |

pick | x_{n} ... x_{2} x_{1} n -- x_{n} ... x_{2} x_{1} x_{n}
| copy the nth item to the top of the stack. |

>r | a -- R: -- a | Move an item to or from the return stack. The item must be a valid address, so these instructions are not very useful. |

r> | -- a R: a -- | |

Branches | ||

jump | address -- | Continue execution from address. |

call | address -- R: -- pc | Push the address of the next instruction on the return stack, and continue execution from address. |

return | R: address -- | Remove address from the return stack and continue from it. |

ifg | flag address -- | Jump to address if flag is nonzero. |

nifg | flag address -- | Jump to address if flag is zero. |

ifeg | flag addr1 addr2 -- | Jump to addr1 if flag is nonzero or to addr2 if it is zero. |

ifc | flag address -- [R: -- pc] | Call address if flag is nonzero. |

ifec | flag addr1 addr2 -- R: -- pc | |

nifc | flag address -- [R: -- pc] | Call address if flag is zero. |

ifr | flag -- [R: address --] | Returns (to the top address on the return stack) if flag is nonzero. |

nifr | flag -- [R: address --] | Returns if flag is zero. |

Arithmetic etc. | ||

+ | a b -- a+b | |

- | a b -- a-b | |

negate | a -- -a | |

* | a b -- a*b | |

/ | a b -- a/b | |

reciprocal | a -- 1/a | |

mod | a b -- r | |

rem | a b -- r | |

square | a -- a^2 | square: dup * return |

sqrt | a -- s | square root |

exponent | b x -- b^x | |

is-integer | a -- flag | |

floor | a -- n | Rounds down. |

ceiling | a -- n | Rounds up. |

round | a -- n | Rounds to nearest integer. |

min | a b -- n | Returns the lesser (closer to negative infinity) of the arguments. |

max | a b -- n | Returns the greater (closer to positive infinity) of the arguments. |

abs | a -- b | a| (Absolute number. Makes it positive if it isn't already) |

signum | a -- b | b = 1 if a positive, -1 if negative, 0 otherwise. |

reorient | a -- b | Ensures an angle is in (-pi, pi]. |

sin | a -- sina | |

cos | a -- cosa | |

tan | a -- tana | |

arcsin | sina -- a | |

arccos | cosa -- a | |

arctan | tana -- a | |

arctan2 | x y -- a | |

random | min max -- value | Returns a random real value evenly distributed between the two bounds (inclusive). |

random-angle | -- angle | Returns a random value evenly distributed in (-pi, pi]. |

random-int | min max -- value | Returns a random integer evenly distributed between the two bounds (inclusive). The bounds need not be integers, but the result will be; 1.5 2.5 random-int returns 2. |

random-bool | probability -- boolean |
Returns a boolean, which will be 1 with the given probability. Probability should be a number between 0 and 1. |

pi | -- pi | |

2pi | -- 2pi | |

pi/2 | -- pi/2 | |

e | -- e | 2.7182818284 |

epsilon | -- epsilon | Returns the smallest difference representable. |

infinity | -- inf | Returns the largest positive value representable. (Negative infinity is actually infinity negate epsilon -.) |

Vector operations | ||

rect-to-polar | x y -- magnitude angle | Convert a vector from rectangular to polar form. |

polar-to-rect | magnitude angle -- x y | Convert a vector from polar to rectangular form. |

v+ | x1 y1 x2 y2 -- x1+x2 y1+y2 | Adds two vectors in rectangular form. |

v- | x1 y1 x2 y2 -- x1-x2 y1-y2 | Subtracts two vectors in rectangular form. |

vnegate | x y -- -x -y | Negates a vector. |

vs* | x y s -- x*s y*s | Vector-scalar multiply. |

vs/ | x y s -- x/s y/s | Vector-scalar divide. |

norm | x y -- p | Returns x square y square + sqrt, the Pythagorean sum. |

angle | x y -- angle | Returns arctan2(y, x). |

dot | x1 y1 x2 y2 -- v | Dot product. v = x1 x2 * y1 y2 * + |

project | x1 y1 x2 y2 -- x3 y3 | Returns the projection of the first vector onto the second. |

cross | x1 y1 x2 y2 -- v | Two-dimensional cross product, sort of. v = x1 y2 * y1 x2 * -. This is the z-component of the equivalent three-dimensional cross product. |

unitize | x y -- x' y' | Returns a vector of magnitude 1 in the same direction. |

dist | x1 y1 x2 y2 -- d | Distance (norm of difference) operator. |

in-range | x1 y1 x2 y2 r-- flag | whether the two vectors differ by less than r. |

rotate-to | x1 y1 x2 y2 -- x y | Rotates vector 1 so that vector 2 becomes the new X-axis. SImplifies the math on intercept calculations. |

rotate-from | x1 y1 x2 y2 -- x y | Undoes a rotate-to operation; end result should be the orignal vector 1. |

Comparisons and Boolean operations | ||

= | a b -- flag | |

<> | a b -- flag | |

< | a b -- flag | |

> | a b -- flag | |

<= | a b -- flag | |

>= | a b -- flag | |

not | f1 -- f2 | f2 is zero if f1 was nonzero, and one otherwise. |

and | f1 f2 -- f3 | f3 is one if both f1 and f2 were nonzero, and zero otherwise. |

or | f1 f2 -- f3 | f3 is zero if both f1 and f2 were zero, and one otherwise. |

xor | f1 f2 -- f3 | f3 is one if exactly one of f1 or f2 was non-zero, and zero otherwise. |

nand | f1 f2 -- f3 | f3 is zero if both f1 and f2 were nonzero, and one otherwise. |

nor | f1 f2 -- f3 | f3 is one if both f1 and f2 were zero, and zero otherwise. |

ifev | b | Value conditional: returns a if flag was nonzero, and b otherwise. |

Miscellaneous | ||

| value -- | Remove and display one item, for debugging purposes. |

vprint | x y -- | Remove and display a vector. |

beep | -- | Beep, for debugging purposes. |

pause | -- | Pauses the simulation. For debugging. |

stop | -- | Permanently stops execution of this brain. |

sync | -- | Waits until the next frame to continue execution. Useful for waiting for hardware to work, or for synchronizing to do something all in one frame. |

store | x addr -- | Writes a value to local memory. |

load | addr -- x | Returns value from local memory. |

vstore | x y addr -- | Write vector to local memory address and address +1. |

vload | addr -- x y | Return vector from local memory address and address +1. |

write | x addr -- | Writes a value to shared memory. (See the the hardware interface reference page for the complete listing of inter-robot communication primitives.) |

read | addr -- x | Return value from shared memory address. |

vwrite | x y addr -- | Write vector to shared memory address and address + 1. |

vread | addr -- x y | Return vector from shared memory address and address + 1. |

## Compile-Time WordsEdit

Some words execute at compile time, for convenience in writing common control structures. There are conditionals and loops so far.

Structure | Meaning | Expansion |
---|---|---|

test if body then | execute body if test is true | test label& nifg body label: |

test if body1 else body2 then | execute body1 if test is true, body2 otherwise | test skip& nifg body1 done& jump skip: body2 done: |

nif ... | If, with sense reversed | test label& ifg ... |

test1 if test2 and-if body1 else body2 then | if test1 is true, then if test2 is true, then execute body1... | test skip& nifg test2 skip& nifg body1 done& jump skip: body2 done: |

test1 if body1 else test2 if body2 celse test3 if body3 celse body4 then | Makes if-elsif-elsif-else constructs shorter (no need to write then then then). | test1 skip1& nifg body1 done& jump skip1: test2 skip2& nifg body2 done& jump skip2: test3 skip3& nifg body3 done& jump skip3: body4 done: |

do code forever | infinite loop | repeat: code repeat& jump |

do test while code loop | Execute test and code until test is true (until) or false (while). Note that the end-test is in the middle of the loop. | repeat: test done& nifg code repeat& jump done: |

do test until code loop | repeat: test done& ifg code repeat& jump done: | |

do code test while-loop | optimized versions of while loop and until loop, saving two instructions | repeat: code test repeat& ifg |

do code test until-loop | repeat: code test repeat& nifg |

## Side File StructureEdit

Sides are described by text files. The filenames usually end in .gb.

A file is structured by tags, which begin with a #. Tags mark the beginnings of sections and the end of the file, and also supply particular interesting bits of information.

Semicolons start comments, which continue to the end of the line. Comments are allowed anywhere, even after tags. Text that is not in a hardware or code section is also a comment; semicolons are not required.

Typical structure of a side file:

#side name ;information about the side #seed 1 2 3 ;seeding order for the different types #type name of a type ;information about the type #hardware ;hardware definition #code ;code #type another type ;definition of this type ;and so on... #end

See the above information and hardware for information of the content of those sections. Also look at some sides.

### Tags ReferenceEdit

Tags must be the first non-whitespace on a line.

Example | Meaning |
---|---|

#side Plant with scavengers | Specifies the name of the side. Must be the first tag in the file. |

#author Somebody or other | A convenient machine-recognizable way to identify a side or type's author. Optional. |

#date Spring 2026 #date long ago in the youth of the world | Was supposed to tell when a side was written. Deprecated because no one can remember to update it. |

#seed 3 1 1 2 1 #seed 1 | Optional. The numbers are the IDs of the types to seed, in order. The list implicitly repeats if there are not enough types listed. Energy not used for robots goes as a fetus inside the first robot seeded. If this tag is omitted, the seeding order is simply all the types, in order, excluding types with bombs. |

#color ccf #color ff0090 | Specifies the color of the side or type as a hexadecimal RGB or RRGGBB color string. |

#type fighter plus dodging | Starts a type and specifies its name. |

#decoration FF0000 dot #decoration 015 hline | Sets a type's decoration, which is one of none, dot, circle, square, triangle, cross, x, hline, vline,slash, or backslash. |

#hardware | Starts a type's hardware section. Multiple hardware sections in a type are allowed. |

#code | Starts a type's code section, where it executes instructions. If there is no code section, robots will be passive. There can be multiple code sections in a type. |

#const c 299792458 ;m/s #const max-targetable-speed .2 | Declares a constant. Valid only in code sections. |

#var arrival-time #var state 1 | Declares a variable. Initial value defaults to 0 if omitted. Valid only in code sections. |

#vector target #vector offset 0 2 | Declares a vector variable. Initial value defaults to (0, 0) if omitted. Valid only in code sections. |

#start #start initialize | Specifies where execution starts. If a label name is supplied, execution starts at that label; otherwise it starts after the #start tag. If omitted, start at the first instruction. Valid only in code sections. |

#comment | Starts a comment section, where text is ignored. The comment section ends when any other hashtag is encountered. Useful for commenting out blocks of code. For example put #comment before the code to be commented out and #code at the end of that section.
#side and #typeimplicitly start a comment section. |

#end | Must be the last tag in a file. |

## External LinksEdit

Original language reference page at Sourceforge