Tendril

Learn Sprout

The whole language on one page. If you have written a little Python, you already know most of it.

Sprout is the language your bots speak. It looks like plain English, uses indentation for blocks like Python, and has only whole numbers. It’s small on purpose: everything below is everything there is.

Commands

A line that starts with a command does that thing. Arguments follow, separated by commas, no brackets needed.

move east
plant carrot
sell wheat, all
go_to 3, 4
say "Coins so far:", coins()

A command on its own, like harvest or till, runs with no arguments. Comments start with #.

Questions

Questions end in brackets and give you a value back. They are free: asking never costs your bot any time.

soil = scan()
if soil.ripe:
    harvest
say "I am bot", my_id(), "at", position()

Inside a bigger expression, commands need brackets too: if harvest() > 10: works, if harvest > 10: doesn’t.

Values

KindLooks like
Whole numbers0, 42, -7, 1_000
Text"hello" or 'hello'
Yes or notrue, false
Nothingnone
Lists[1, 2, 3], counted from 0, so xs[0] is the first item and xs[-1] the last
Records{x: 3, y: 4}, read with pos.x or pos["x"]

Text in double quotes can include values: say "I have {coins()} coins". Single quotes never do.

Lists and records are shared, not copied: after b = a, changing b changes a. Reading a field a record doesn’t have gives none. false, none, 0, "", [] and {} all count as no in an if; everything else is yes.

Maths and comparisons

+ - * / % work on whole numbers. / divides and rounds down, so 7 / 2 is 3. + also joins text and lists.

Compare with == != < <= > >=. Combine with and, or, not. x in xs checks whether a list holds a value, "k" in r checks whether a record has a key, and "ab" in "cab" checks text.

Variables

target = 5
target += 1
spot = {x: target, y: 2}
spot.y = 3

Each bot has its own variables. To share something with every bot, use the shared record: shared.plan = "wheat". Shared values last until the program restarts.

Choosing

if scan().ripe:
    harvest
elif scan().empty:
    till
    plant carrot
else:
    say "Still growing"

Repeating

repeat forever:
    harvest

repeat 3 times:
    move east

while carried() < capacity():
    harvest
    move east

for each tile in tiles():
    go_to tile

for x from 1 to 7:
    go_to x, 0

for x from 7 to 1 step -2:
    say x

for each works on lists, the keys of a record, and the letters of text. from ... to includes both ends, and counts down by itself when the first number is bigger. break leaves the loop, continue skips to the next round, and stop ends this bot’s program for good.

Functions

define unload():
    go_to barn
    deposit
    sell carrot, all

define best_crop(soil):
    if soil.nitrogen < 15:
        return clover
    return wheat

repeat forever:
    unload()

def works too. Define a function before the line that first calls it. Variables you set inside a function stay inside it, unless you write global name first. Functions can call themselves.

Built-in helpers

len, min, max, abs, range, append, pop, insert, remove, contains, keys, sorted, reversed, str, int, random and distance are all there. See the reference for each one. random(n) is seeded from your farm and the bot’s id, so a program behaves the same way every time it runs on the same farm.

Mistakes

When something is wrong, the editor underlines it and says what it means in plain words, like “I don’t know what plnat means. Did you mean plant?”. A mistake that only shows up while running stops just the bot that hit it and shows the line in the console. The other bots carry on.