Asking

A question and a reply, with a timeout; going where you are told.

9.4Talking to each otherRobot club20 min

Do this lesson in the simulator

Lesson 9.3 asked for instructions. This one asks for information: where another robot is, then goes there. The Scout on this mat answers where are you? with at <x>,<y>.

The same map

A position is only useful if both robots measure it the same way. The Scout reports positions the way your position() does: from your starting point, x to the right, y forward. Agreeing a shared origin is the first thing any group of robots has to do, and on this mat it is done for you. In the games every robot uses the mat's corner.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

# maths: atan2, hypot, sin, cos, radians
import math
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("where are you?")
print("scout says:", reply)
sx, sy = numbers_in(reply)
# where am I? (cm from where I started)
x, y = position()
print(f"the scout is {math.hypot(sx - x, sy - y):.0f} cm away")

Run this in the simulator

numbers_in pulls every number out of a message, whatever else is in it: at 25,55 becomes [25.0, 55.0]. Forgiving in.

Going there

go_to from Module 5 slides the robot to a point. Aim a robot's length short of the Scout, or you will drive into it:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

# maths: atan2, hypot, sin, cos, radians
import math

def wrapped(h):
    return (h + 180) % 360 - 180

def go_to(x, y, speed=60):
    # where am I?
    px, py = position()
    a = math.radians(wrapped(math.degrees(math.atan2(x - px, y - py)) - heading()))
    # forward, sideways, rotation: -100 to 100 each, until the next command
    drive(speed * math.cos(a), speed * math.sin(a), wrapped(0 - heading()) * 3)

def near(x, y, cm=4):
    # where am I?
    px, py = position()
    return math.hypot(x - px, y - py) < cm

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

sx, sy = numbers_in(ask("where are you?"))
while not near(sx, sy - 13):
    go_to(sx, sy - 13)
    # pause 0.1 s (the robot keeps doing what it was told)
    wait(0.1)
# all motors off
stop()
print("stopped at", position(), "facing the scout at", (sx, sy))

Run this in the simulator

Ask again

The answer was true when it was sent. A robot that moves needs asking again. In the games, me.others gives every robot's position for free, but a real team of robots gets that only by asking each other, over and over.

Task: meet the scout

Ask the Scout where it is and stop in the green square, 13 cm in front of it, without touching it.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# maths: atan2, hypot, sin, cos, radians
import math

def wrapped(h):
    return (h + 180) % 360 - 180

def go_to(x, y, speed=60):
    # where am I?
    px, py = position()
    a = math.radians(wrapped(math.degrees(math.atan2(x - px, y - py)) - heading()))
    # forward, sideways, rotation: -100 to 100 each, until the next command
    drive(speed * math.cos(a), speed * math.sin(a), wrapped(0 - heading()) * 3)

def near(x, y, cm=4):
    # where am I?
    px, py = position()
    return math.hypot(x - px, y - py) < cm
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('where are you?')
print('scout says:', reply)

Challenges

  1. Ask again once you arrive and print how far off the first answer was.
  2. End facing the Scout: turn to the bearing of (sx, sy) from where you stopped.
  3. Print no answer and stay put if the Scout does not reply within a second.