Protocols
Agreeing what a message looks like; splitting it into words and numbers.
Do this lesson in the simulatorTwo robots can only understand each other if they agree in advance what messages look like. That agreement is a protocol. It can be as small as "one word, then a number", and on this mat it is: the Guide answers next with an instruction like forward 30, right 25 or done.
Asking and waiting
A question is a message you send and then wait for the answer to. ask does both: it sends, then polls for up to a second, and returns the first reply, or None if nothing came. Radios miss things; a program that waits forever for a reply that never comes is stuck, so the timeout is part of the protocol too.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
def ask(question, seconds=1.0):
# send a question and return the first reply that arrives within `seconds`, or None
send(question)
for tick in range(int(seconds * 10)):
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
for sender, text in messages():
return text
return None
def numbers_in(text):
# every number in a message, as floats: "at 25,55" -> [25.0, 55.0]
out = []
for word in text.replace(",", " ").split():
try:
out.append(float(word))
except ValueError:
pass
return out
reply = ask("next")
print("the guide says:", reply)
Taking a message apart
split() breaks a string into words at the spaces. The first word says what to do, the second how much:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
def ask(question, seconds=1.0):
# send a question and return the first reply that arrives within `seconds`, or None
send(question)
for tick in range(int(seconds * 10)):
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
for sender, text in messages():
return text
return None
def numbers_in(text):
# every number in a message, as floats: "at 25,55" -> [25.0, 55.0]
out = []
for word in text.replace(",", " ").split():
try:
out.append(float(word))
except ValueError:
pass
return out
reply = ask("next")
words = reply.split()
print("words:", words)
command, amount = words[0], float(words[1])
if command == "forward":
forward(50, distance=amount)
elif command == "right":
right(50, distance=amount)
print("did", command, amount, "now at", position())
float(words[1]) turns the text 30 into the number 30.0. The text is what travelled; the number is what you can drive with.
Strict out, forgiving in
Two habits that keep robots talking. Be strict about what you send: always the same words, the same order, the same units. Be forgiving about what you receive: text.lower().strip() before you look at it, and if you do not understand a message, ignore it rather than crash. A robot that stops because someone sent Forward 30 instead of forward 30 is not much of a team mate.
Task: follow instructions
Ask the Guide for instructions one at a time, do each one, and stop at done. You will end up in the green zone.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
def ask(question, seconds=1.0):
# send a question and return the first reply that arrives within `seconds`, or None
send(question)
for tick in range(int(seconds * 10)):
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
for sender, text in messages():
return text
return None
def numbers_in(text):
# every number in a message, as floats: "at 25,55" -> [25.0, 55.0]
out = []
for word in text.replace(",", " ").split():
try:
out.append(float(word))
except ValueError:
pass
return out
reply = ask('next')
print('guide says:', reply)
Challenges
- Handle
left <cm>andbackward <cm>too, even though this Guide never sends them. - Handle
turn <degrees>. - If
askreturnsNone, try again, up to three times, then give up and print why.