Skip to content

World

The aitk.robots simulator world. All simulations begin with a world.

beacon property writable

Get the beacon.

__init__(self, width=500, height=250, seed=0, scale=3.0, boundary_wall=True, boundary_wall_color='purple', boundary_wall_width=1, ground_color='green', ground_image_filename=None, filename=None, quiet=False, smell_cell_size=None, **kwargs) special

The constructor for the aitk.robots simulator world.

Parameters:

Name Type Description Default
width int

width of world in pixels

500
height int

height of world in pixels

250
seed int

random number generator seed

0
scale float

value to use in drawing the world

3.0
boundary_wall bool

draw a boundary around the world?

True
boundary_wall_color str

color name of boundary wall

'purple'
boundary_wall_width int

width of boundary wall

1
ground_color str

color name

'green'
ground_image_filename str

image file used for backgound

None
filename str

name of json world file

None
quiet bool

if True, don't print any messages

False
smell_cell_size int

is set automatically based on world size

None

You can also pass any valid item from the world config settings.

from aitk.robots import World

world = World(200, 200)
Source code in robots/world.py
def __init__(
    self,
    width=500, # type: int
    height=250,  # type: int
    seed=0,  # type: int
    scale=3.0, # type: float
    boundary_wall=True,  # type: bool
    boundary_wall_color="purple",  # type: str
    boundary_wall_width=1,  # type: int
    ground_color="green",  # type: str
    ground_image_filename=None,  # type: str
    filename=None,  # type: str
    quiet=False,  # type: bool
    smell_cell_size=None,  # type: int
    **kwargs
):
    """
    The constructor for the aitk.robots simulator world.

    Args:
        width (int): width of world in pixels
        height (int): height of world in pixels
        seed (int): random number generator seed
        scale (float): value to use in drawing the world
        boundary_wall (bool): draw a boundary around the world?
        boundary_wall_color (str): color name of boundary wall
        boundary_wall_width (int): width of boundary wall
        ground_color (str): color name
        ground_image_filename (str): image file used for backgound
        filename (str): name of json world file
        quiet (bool): if True, don't print any messages
        smell_cell_size (int): is set automatically based on world size

    You can also pass any valid item from the world config settings.

    ```python
    from aitk.robots import World

    world = World(200, 200)
    ```
    """
    # For faster-than real time display with synchronous backends,
    # keep processing time below this percentage of throttle_period:
    config = {
        "width": width,
        "height": height,
        "seed": seed,
        "scale": scale,
        "boundary_wall": boundary_wall,
        "boundary_wall_width": boundary_wall_width,
        "boundary_wall_color": boundary_wall_color,
        "ground_color": ground_color,
        "quiet": quiet,
        "smell_cell_size": smell_cell_size,
    }
    if filename is not None:
        config["filename"] = filename
    if ground_image_filename is not None:
        config["ground_image_filename"] = ground_image_filename
    config["walls"] = kwargs.pop("walls", [])
    config["bulbs"] = kwargs.pop("bulbs", [])
    config["beacon"] = kwargs.pop("beacon", None)
    config["robots"] = kwargs.pop("robots", [])
    config["food"] = kwargs.pop("food", [])
    if len(kwargs) != 0:
        raise AttributeError(
            "unknown arguments for World: %s" % list(kwargs.keys())
        )
    self.time_step = 0.10  # seconds
    self._events = []
    self._show_throttle_percentage = 0.40
    self._time_decimal_places = 1
    self._throttle_period = 0.1
    self._time_of_last_call = 0
    self._step_display = "tqdm"
    self.debug = False
    self._watchers = []
    self._robots = []
    self._bulbs = []
    self._beacon = None
    self._backend = None
    self._recording = False
    self.config = config.copy()
    self._initialize()  # default values
    self.robots = List(self._robots)
    self.bulbs = List(self._bulbs)
    self.reset()  # from config

add_beacon(self, x, y, z=0)

Add a beacon to the world.

Parameters:

Name Type Description Default
x int

the x coordinate

required
y int

the y coordinate

required
z int

optional, the z coordinate

0
Source code in robots/world.py
def add_beacon(self, x, y, z=0):
    """
    Add a beacon to the world.

    Args:
        x (int): the x coordinate
        y (int): the y coordinate
        z (int): optional, the z coordinate
    """
    self._add_beacon(x, y, z)
    self.update()  # request draw
    self.save()

add_bulb(self, color, x, y, z, brightness, name=None)

Add a bulb to the world.

Parameters:

Name Type Description Default
color str

the color of the light

required
x int

the x coordinate

required
y int

the y coordinate

required
z int

the z coordinate

required
brightness float

the distance in pixels of 1 standard deviation

required
name str

the name of the bulb

None
Source code in robots/world.py
def add_bulb(self, color, x, y, z, brightness, name=None):
    """
    Add a bulb to the world.

    Args:
        color (str): the color of the light
        x (int): the x coordinate
        y (int): the y coordinate
        z (int): the z coordinate
        brightness (float): the distance in pixels of 1 standard deviation
        name (str): the name of the bulb
    """
    self._add_bulb(color, x, y, z, brightness, name)
    self.update()  # request draw
    self.save()

add_food(self, x, y, standard_deviation, state='on')

Add food at x, y with a brightness of standard_deviation (in pixels).

Parameters:

Name Type Description Default
x int

the x coordinate

required
y int

the y coordinate

required
standard_deviation float

distance int pixels of 1 standard deviation

required
Source code in robots/world.py
def add_food(self, x, y, standard_deviation, state="on"):
    """
    Add food at x, y with a brightness of standard_deviation
    (in pixels).

    Args:
        x (int): the x coordinate
        y (int): the y coordinate
        standard_deviation (float): distance int pixels
            of 1 standard deviation
    """
    self._add_food(x, y, standard_deviation, state)
    self._grid.need_update = True
    self.update()  # request draw
    self.save()

add_robot(self, robot)

Add a new robot to the world. If the robot's position is 0,0 then place it randomly in the world.

Parameters:

Name Type Description Default
robot Robot

the robot to add

required
Source code in robots/world.py
def add_robot(self, robot):
    """
    Add a new robot to the world. If the robot's position is 0,0 then
    place it randomly in the world.

    Args:
        robot (Robot): the robot to add
    """
    if robot not in self._robots:
        if robot.x == 0 and robot.y == 0:
            robot.x, robot.y, robot.a = self._find_random_pose(robot)
        self._robots.append(robot)
        robot.world = self
        # Bounding lines form a wall:
        if len(robot._bounding_lines) == 0:
            print("WARNING: adding a robot with no body")
        wall = Wall(robot.color, robot, *robot._bounding_lines, wtype="robot")
        self._walls.append(wall)
        self._complexity = self._compute_complexity()
        self.update()
        self.save()
    else:
        raise Exception("Can't add the same robot to a world more than once.")

add_wall(self, color, x1, y1, x2, y2, box=True, wtype='wall')

Add a wall as a line or as a box.

Parameters:

Name Type Description Default
color str

the color of the wall

required
x1 int

the upper left x coordinate

required
y1 int

the upper left y coordinate

required
x2 int

the lower right x coordinate

required
y2 int

the lower right y coordinate

required
box bool

whether this is a box (True) or line (False)

True
wtype str

"wall", "robot", or "boundary"

'wall'
Source code in robots/world.py
def add_wall(self, color, x1, y1, x2, y2, box=True, wtype="wall"):
    """
    Add a wall as a line or as a box.

    Args:
        color (str): the color of the wall
        x1 (int): the upper left x coordinate
        y1 (int): the upper left y coordinate
        x2 (int): the lower right x coordinate
        y2 (int): the lower right y coordinate
        box (bool): whether this is a box (True) or line (False)
        wtype (str): "wall", "robot", or "boundary"
    """
    p1 = Point(x1, y1)
    p3 = Point(x2, y2)
    if box:
        ## Pairs of points make Line:
        p2 = Point(x2, y1)
        p4 = Point(x1, y2)
        wall = Wall(
            Color(color),
            None,
            Line(p1, p2),
            Line(p2, p3),
            Line(p3, p4),
            Line(p4, p1),
            wtype=wtype,
        )
    else:
        wall = Wall(
            Color(color), None, Line(p1, p3), wtype="wall"
        )
    self._walls.append(wall)
    self._complexity = self._compute_complexity()
    self._grid.update_wall(wall)
    self.update()  # request draw

clear_watchers(self)

Clear all of the watchers.

Source code in robots/world.py
def clear_watchers(self):
    """
    Clear all of the watchers.
    """
    self._watchers[:] = []

del_robot(self, robot)

Remove a robot from the world.

Parameters:

Name Type Description Default
robot Robot

the robot to remove

required
Source code in robots/world.py
def del_robot(self, robot):
    """
    Remove a robot from the world.

    Args:
        robot (Robot): the robot to remove
    """
    if not isinstance(robot, Robot):
        # Then look it up by index/name/type:
        robot = self.robots[robot]
    for wall in list(self._walls):
        if wall.robot is robot:
            self._walls.remove(wall)
    if robot in self._robots:
        robot.world = None
        self._robots.remove(robot)
    self._complexity = self._compute_complexity()
    self.update()  # request draw

display(self, index=None, size=100)

Display a picture of the world, or of a robot.

Parameters:

Name Type Description Default
index str or int

index of robot

None
size int

size of robot picture

100
Source code in robots/world.py
def display(self, index=None, size=100):
    """
    Display a picture of the world, or of a robot.

    Args:
        index (str or int, optional): index of robot
        size (int, optional): size of robot picture
    """
    picture = self.get_image(index=index, size=size)
    display(picture)

draw(self)

Force a redraw of the world.

Source code in robots/world.py
def draw(self):
    """
    Force a redraw of the world.
    """
    if self._backend is None:
        return

    self._grid.update(self._food)

    with self._backend:
        self._backend.clear()
        self._backend.noStroke()
        if self._ground_image is not None:
            self._backend.draw_image(self._ground_image, 0, 0)
        else:
            self._backend.set_fill(self.ground_color)
            self._backend.draw_rect(0, 0, self.width, self.height)

        if len(self._food) > 0:
            smell = self._grid.get_image()
            smell = smell.resize((int(self.width * self.scale), int(self.height * self.scale)))
            self._backend.image.paste(smell, (0, 0), smell)

        ## Draw all bulbs in world:
        maxRange = max(self.width, self.height)
        for bulb in self._get_light_sources(all=True):
            if bulb.state == "on":
                color = bulb.color
                self._backend.line_width = 0
                self._backend.noStroke()
                x, y = bulb.get_position(world=True)
                # Cast rays once:
                all_hits = []
                for ray in range(bulb.rays):
                    angle = ray/bulb.rays * TWO_PI
                    hits = cast_ray(self, bulb.robot, x, y, -angle + PI_OVER_2, maxRange)
                    all_hits.append(hits)
                # Now draw the rings:
                for i in range(bulb.draw_rings):
                    radius = (bulb.draw_rings - i) * 2
                    ray_length = bulb.brightness/30 * radius
                    color.alpha = (i + 1)/bulb.draw_rings * 255
                    self._backend.set_fill_style(color)
                    points = []
                    for ray in range(bulb.rays):
                        hits = all_hits[ray]
                        ray_length = bulb.brightness/30 * radius
                        if len(hits) > 0:
                            if hits[-1].distance < ray_length:
                                ray_length = hits[-1].distance
                        angle = ray/bulb.rays * TWO_PI
                        x2, y2 = rotate_around(x, y, ray_length, angle)
                        points.append((x2, y2))
                    self._backend.draw_polygon(points)
                self._backend.line_width = 1

        ## Draw walls:
        for wall in self._walls:
            if wall.wtype == "wall":
                c = wall.color
                if len(wall.lines) == 1:
                    self._backend.strokeStyle(c, 5)
                    self._backend.draw_line(
                        wall.lines[0].p1.x,
                        wall.lines[0].p1.y,
                        wall.lines[0].p2.x,
                        wall.lines[0].p2.y,
                    )
                else:
                    self._backend.set_fill(c)
                    self._backend.noStroke()
                    self._backend.beginShape()
                    for line in wall.lines:
                        self._backend.vertex(line.p1.x, line.p1.y)
                        self._backend.vertex(line.p2.x, line.p2.y)
                    self._backend.endShape()

                self._backend.lineWidth(1)
                self._backend.noStroke()

            elif wall.wtype == "boundary":
                c = wall.color
                self._backend.strokeStyle(c, 3)
                self._backend.draw_line(
                    wall.lines[0].p1.x,
                    wall.lines[0].p1.y,
                    wall.lines[0].p2.x,
                    wall.lines[0].p2.y,
                )
                self._backend.lineWidth(1)
                self._backend.noStroke()

        ## Draw robots:
        for robot in self._robots:
            robot.draw(self._backend)

        # Draw the beacon:
        if self.beacon is not None:
            if self.beacon.state == "on":
                x, y = self.beacon.get_position(world=True)
                self._backend.set_fill_style(Color("blue"))
                self._backend.draw_circle(x, y, 5)

        text = format_time(self.time)
        self._backend.draw_status(text)

        for items in self._draw_list:
            if len(items) == 1:
                command = items[0]
                args = tuple()
                kwargs = {}
            elif len(items) == 2:
                command = items[0]
                args = items[1]
                kwargs = {}
            elif len(items) == 3:
                command = items[0]
                args = items[1]
                kwargs = items[2]

            self._backend.do_command(command, *args, **kwargs)

    self._draw_watchers()

from_json(self, config)

Load a json config file.

Source code in robots/world.py
def from_json(self, config):
    """
    Load a json config file.
    """
    self.config = config

    if "quiet" in config:
        self.quiet = config["quiet"]

    seed = config.get("seed", 0)
    self.set_seed(seed)

    if "filename" in config:
        self.filename = config["filename"]
    if "width" in config:
        self.width = config["width"]
    if "height" in config:
        self.height = config["height"]
    if "scale" in config:
        self.scale = config["scale"]
    if "smell_cell_size" in config:
        self.smell_cell_size = config["smell_cell_size"]

    if self.smell_cell_size is None:
        self.smell_cell_size = max((self.width * self.height) // 20000, 1)

    if "boundary_wall" in config:
        self.boundary_wall = config["boundary_wall"]
    if "boundary_wall_color" in config:
        self.boundary_wall_color = Color(config["boundary_wall_color"])
    if "boundary_wall_width" in config:
        self.boundary_wall_width = config["boundary_wall_width"]
    if "ground_color" in config:
        self.ground_color = Color(config["ground_color"])
    if "ground_image_filename" in config:
        self.set_ground_image(config["ground_image_filename"], show=False)

    # Now, we create the grid:
    self._grid = Grid(self.width, self.height, self.smell_cell_size)
    self._grid.update_walls(self._walls)

    # Add walls:
    self._add_boundary_walls()
    for wall in config.get("walls", []):
        # Walls are either "boxes" with 4 lines, or a single line:
        if "wtype" not in wall or wall["wtype"] == "box":
            self.add_wall(
                wall["color"],
                wall["p1"]["x"],
                wall["p1"]["y"],
                wall["p2"]["x"],
                wall["p2"]["y"],
                box=True,
            )
        elif wall["wtype"] == "line":
            self.add_wall(
                wall["color"],
                wall["p1"]["x"],
                wall["p1"]["y"],
                wall["p2"]["x"],
                wall["p2"]["y"],
                box=False,
            )

    beacon = config.get("beacon", None)
    if beacon is not None:
        # beacon is {x, y, z}
        self._add_beacon(**beacon)

    for bulb in config.get("bulbs", []):
        # bulbs are {x, y, z, color, brightness}
        self._add_bulb(**bulb)

    for food in config.get("food", []):
        # food x, y, standard_deviation, state
        self._add_food(**food)

    ## Create robot, and add to world:
    for i, robotConfig in enumerate(self.config.get("robots", [])):
        # FIXME: raise if lengths don't match
        if i < len(self._robots):  # already a robot; let's reuse it:
            robot = self._robots[i]
            robot._initialize()
            robot.from_json(robotConfig)
        else:
            robot = Robot(**robotConfig)
            self.add_robot(robot)
    # Create the backend if first time:
    if self._backend is None:
        self._backend = make_backend(self.width, self.height, self.scale)
    # Update the backend if it already existed, but differs in config
    self._backend.update_dimensions(self.width, self.height, self.scale)

get_bulb(self, item)

Get the bulb by name or index. Equivalent to world.bulbs[item]

Parameters:

Name Type Description Default
item int or string

index or name of bulb

required
Source code in robots/world.py
def get_bulb(self, item):
    """
    Get the bulb by name or index. Equivalent to
    world.bulbs[item]

    Args:
        item (int or string): index or name of bulb
    """
    return self.bulbs[item]

get_food(self)

Return a list of the food in the world in (x, y, standard_deviation, state) form.

Source code in robots/world.py
def get_food(self):
    """
    Return a list of the food in the world in
    (x, y, standard_deviation, state) form.
    """
    return self._food

get_ground_color_at(self, x, y, radius=1)

Get the pixel(s) of the ground image at position (x,y). Requires a ground image to have already been set.

Parameters:

Name Type Description Default
x int

the x coordinate

required
y int

the y coordinate

required
radius int

size of area

1
Source code in robots/world.py
def get_ground_color_at(self, x, y, radius=1):
    """
    Get the pixel(s) of the ground image at position (x,y). Requires
    a ground image to have already been set.

    Args:
        x (int): the x coordinate
        y (int): the y coordinate
        radius (int): size of area
    """
    if self._ground_image:
        results = []
        for i in range(-radius, radius + 1, 1):
            for j in range(-radius, radius + 1, 1):
                results.append(
                    self._ground_image_pixels[
                        ((x + i) * self.scale, (y + j) * self.scale)
                    ]
                )
        return results

get_image(self, index=None, size=100, copy=True)

Get a PIL image (copy) of the world, or of a robot.

Parameters:

Name Type Description Default
index str or int

index of robot

None
size int

size of robot picture

100
copy bool

get a copy of image if True, otherwise return a reference to world image

True
Source code in robots/world.py
def get_image(self, index=None, size=100, copy=True):
    """
    Get a PIL image (copy) of the world, or of a robot.

    Args:
        index (str or int, optional): index of robot
        size (int, optional): size of robot picture
        copy (bool, optional): get a copy of image if True,
            otherwise return a reference to world image
    """
    try:
        picture = self._backend.get_image(self.time)
    except RuntimeError:
        raise Exception("Backend is not ready yet; try again")

    if index is not None:
        robot = self.robots[index]
        if robot:
            start_x = round(max(robot.x * self.scale - size / 2, 0))
            start_y = round(max(robot.y * self.scale - size / 2, 0))
            rectangle = (
                start_x,
                start_y,
                min(start_x + size, self.width * self.scale),
                min(start_y + size, self.height * self.scale),
            )
            picture = picture.crop(rectangle)
            return picture
    else:
        if copy:
            picture = picture.copy()
        return picture

get_robot(self, item)

Get the robot by name or index. Equivalent to world.robots[item]

Parameters:

Name Type Description Default
item int or string

index or name of robot

required
Source code in robots/world.py
def get_robot(self, item):
    """
    Get the robot by name or index. Equivalent to
    world.robots[item]

    Args:
        item (int or string): index or name of robot
    """
    return self.robots[item]

get_widget(self, width=None, height=None)

Returns the IPython simulation widget.

Parameters:

Name Type Description Default
width int

the width of the world

None
height int

the height of the world

None
Source code in robots/world.py
def get_widget(self, width=None, height=None):
    """
    Returns the IPython simulation widget.

    Args:
        width (int): the width of the world
        height (int): the height of the world
    """
    if isinstance(width, int):
        width = "%spx" % width
    if isinstance(height, int):
        height = "%spx" % height
    self._step_display = "notebook"
    self.update()
    return self._backend.get_widget(width=width, height=height)

paste_ground_image(self, image, x, y)

Paste an image onto the ground image. Requires a ground image to have already been set.

Parameters:

Name Type Description Default
image Image

a Python Image Library image

required
x int

the x coordinate of upper lefthand corner

required
y int

the y coordinate of upper lefthand corner

required
Source code in robots/world.py
def paste_ground_image(self, image, x, y):
    """
    Paste an image onto the ground image. Requires
    a ground image to have already been set.

    Args:
        image (Image): a Python Image Library image
        x (int): the x coordinate of upper lefthand corner
        y (int): the y coordinate of upper lefthand corner
    """
    if self._ground_image:
        self._ground_image.paste(image, (x, y))

record(self)

Create and return a world recorder.

Source code in robots/world.py
def record(self):
    """
    Create and return a world recorder.
    """
    from .watchers import Recorder

    recorder = Recorder(self)
    self._watchers.append(recorder)
    self._recording = True
    return recorder

reset(self)

Reloads the config from initialization, or from last save.

Source code in robots/world.py
def reset(self):
    """
    Reloads the config from initialization, or from
    last save.
    """
    self.stop()
    self._initialize()
    self._reset_watchers()
    self._food = []
    self._events = []
    self._grid = None
    self.from_json(self.config)
    self.time = 0.0
    for robot in self._robots:
        robot.reset()
        # Re-add the robot's boundaries:
        wall = Wall(robot.color, robot, *robot._bounding_lines, wtype="robot")
        self._walls.append(wall)
    self._stop = False  # should stop?
    self.status = "stopped"
    self.update(show=False)  # twice to allow robots to see each other
    self.update(show=False)
    self.draw()  # force

run(self, function=None, time_step=None, show=True, real_time=True, show_progress=True, quiet=False, background=False, interrupt=False)

Run the simulator until one of the control functions returns True or Control+C is pressed.

Parameters:

Name Type Description Default
function Function

either a single function that takes the world, or a list of functions (or None) that each take a robot. If any function returns True, then simulation will stop.

None
time_step float

time unit to advance the world

None
show bool

update the watchers

True
real_time bool

run simulation in real time

True
show_progress bool

show progress bar

True
quiet bool

if True, do not show the status message when completed

False
background bool

if True, run in the background.

False
interrupt bool

if True, raise KeyboardInterrupt, otherwise just stop and continue

False
Source code in robots/world.py
def run(
    self,
    function=None,
    time_step=None,
    show=True,
    real_time=True,
    show_progress=True,
    quiet=False,
    background=False,
    interrupt=False,
):
    """
    Run the simulator until one of the control functions returns True
    or Control+C is pressed.

    Args:
        function (Function): either a single function that takes the
            world, or a list of functions (or None) that each take
            a robot. If any function returns True, then simulation will
            stop.
        time_step (float): time unit to advance the world
        show (bool): update the watchers
        real_time (bool): run simulation in real time
        show_progress (bool): show progress bar
        quiet (bool): if True, do not show the status message when
            completed
        background (bool): if True, run in the background.
        interrupt (bool): if True, raise KeyboardInterrupt, otherwise
            just stop and continue
    """
    time_step = time_step if time_step is not None else self.time_step
    if background:
        if self._thread is None:
            kwargs = {
                "function": function,
                "time_step": time_step,
                "show": show,
                "real_time": real_time,
                "show_progress": False,
                "quiet": True,
                "background": False
            }
            print("Starting world.run() in background. Use world.stop()")
            self._thread = Thread(target=self.run, kwargs=kwargs)
            self._thread.start()
        else:
            print("The world is already running in the background. Use world.stop()")
    else:
        return self.steps(
            float("inf"), function, time_step, show, real_time, show_progress, quiet, interrupt
        )

save(self)

Save the current state of the world as the config.

Source code in robots/world.py
def save(self):
    """
    Save the current state of the world as the config.
    """
    self.config = self.to_json()

save_as(self, filename)

Save the world config JSON as a new file.

Parameters:

Name Type Description Default
filename str

the name of the file to save the world in

required
Source code in robots/world.py
def save_as(self, filename):
    """
    Save the world config JSON as a new file.

    Args:
        filename (str): the name of the file to save
            the world in
    """
    if not filename.endswith(".json"):
        filename = filename + ".json"
    # First, save internally.
    self.config = self.to_json()
    with open(filename, "w") as fp:
        json_dump(self.to_json(), fp, sort_keys=True, indent=4)
    self.config["filename"] = filename

save_file(self)

Save the current state of the world as the config, and save it back to disc if it was loaded from disk.

Source code in robots/world.py
def save_file(self):
    """
    Save the current state of the world as the config, and
    save it back to disc if it was loaded from disk.
    """
    # First, save internally.
    self.config = self.to_json()
    if self.filename is not None and os.path.exists(self.filename):
        with open(self.filename, "w") as fp:
            json_dump(self.config, fp, sort_keys=True, indent=4)
    else:
        if not self.quiet:
            print("Saved in memory. Use world.save_as('filename') to save to disk.")

seconds(self, seconds=5.0, function=None, time_step=None, show=True, real_time=True, show_progress=True, quiet=False, interrupt=False)

Run the simulator for N seconds, or until one of the control functions returns True or Control+C is pressed.

Parameters:

Name Type Description Default
seconds float

how many simulation seconds to run

5.0
function Function

either a single function that takes the world, or a list of functions (or None) that each take a robot. If any function returns True, then simulation will stop.

None
time_step float

time unit to advance the world

None
show bool

update the watchers

True
real_time float

run simulation in real time

True
show_progress bool

show progress bar

True
quiet bool

if True, do not show the status message when completed

False
interrupt bool

if True, raise KeyboardInterrupt, otherwise just stop and continue

False
Source code in robots/world.py
def seconds(
    self,
    seconds=5.0,
    function=None,
    time_step=None,
    show=True,
    real_time=True,
    show_progress=True,
    quiet=False,
    interrupt=False,
):
    """
    Run the simulator for N seconds, or until one of the control
    functions returns True or Control+C is pressed.

    Args:
        seconds (float): how many simulation seconds to run
        function (Function): either a single function that takes the
            world, or a list of functions (or None) that each take
            a robot. If any function returns True, then simulation will
            stop.
        time_step (float): time unit to advance the world
        show (bool): update the watchers
        real_time (float): run simulation in real time
        show_progress (bool): show progress bar
        quiet (bool): if True, do not show the status message when
            completed
        interrupt (bool): if True, raise KeyboardInterrupt, otherwise
            just stop and continue
    """
    time_step = time_step if time_step is not None else self.time_step
    steps = round(seconds / time_step)
    return self.steps(steps, function, time_step, show, real_time, show_progress, quiet, interrupt)

set_ground_color_at(self, x, y, pen)

Set the pixel(s) of the ground image at position (x,y). Requires a ground image to have already been set.

Parameters:

Name Type Description Default
x int

the x coordinate

required
y int

the y coordinate

required
pen tuple

the (color, radius) to draw with

required
Source code in robots/world.py
def set_ground_color_at(self, x, y, pen):
    """
    Set the pixel(s) of the ground image at position (x,y). Requires
    a ground image to have already been set.

    Args:
        x (int): the x coordinate
        y (int): the y coordinate
        pen (tuple): the (color, radius) to draw with
    """
    if self._ground_image:
        color, radius = pen
        for i in range(-radius, radius + 1, 1):
            for j in range(-radius, radius + 1, 1):
                try:
                    self._ground_image_pixels[
                        ((x * self.scale) + i, (y * self.scale) + j)
                    ] = color.to_tuple()
                except Exception:
                    pass

set_ground_image(self, filename, show=True)

Set the background image

Parameters:

Name Type Description Default
filename str

the name of the image

required
show bool

update the world now if True

True
Source code in robots/world.py
def set_ground_image(self, filename, show=True):
    """
    Set the background image

    Args:
        filename (str): the name of the image
        show (bool): update the world now if True
    """
    self.ground_image_filename = filename
    self._reset_ground_image()
    if show:
        self.update(show=False)
        self.draw()  # force

set_scale(self, scale)

Change the scale of the rendered world.

Parameters:

Name Type Description Default
scale float

the scale of the world (usually between 0.5 and 10)

required
Source code in robots/world.py
def set_scale(self, scale):
    """
    Change the scale of the rendered world.

    Args:
        scale (float): the scale of the world (usually between 0.5 and 10)
    """
    self.scale = scale
    self._backend.update_dimensions(self.width, self.height, self.scale)
    # Save with config
    self.config["scale"] = self.scale
    self.update(show=False)
    self.draw()  # force

set_seed(self, seed)

Set the random seed.

Source code in robots/world.py
def set_seed(self, seed):
    """
    Set the random seed.
    """
    if seed == 0:
        seed = random.randint(0, 9999999)
        if not self.quiet:
            print("Random seed set to:", seed)
    else:
        if not self.quiet:
            print("Using random seed:", seed)
    random.seed(seed)
    self.seed = seed
    self.config["seed"] = seed

steps(self, steps=1, function=None, time_step=None, show=True, real_time=True, show_progress=True, quiet=False, interrupt=False)

Run the simulator for N steps, or until one of the control functions returns True or Control+C is pressed.

Parameters:

Name Type Description Default
steps int

either a finite number, or infinity

1
time_step float

time unit to advance the world

None
show bool

update the watchers

True
real_time bool

run simulation in real time

True
show_progress bool

show progress bar

True
quiet bool

if True, do not show the status message when completed

False
interrupt bool

if True, raise KeyboardInterrupt, otherwise just stop and continue

False
Source code in robots/world.py
def steps(
    self,
    steps=1,
    function=None,
    time_step=None,
    show=True,
    real_time=True,
    show_progress=True,
    quiet=False,
    interrupt=False,
):
    """
    Run the simulator for N steps, or until one of the control
    functions returns True or Control+C is pressed.

    Args:
        steps (int): either a finite number, or infinity
        function (Function) either a single function that takes the
            world, or a list of functions (or None) that each take
            a robot. If any function returns True, then simulation will
            stop.
        time_step (float): time unit to advance the world
        show (bool): update the watchers
        real_time (bool): run simulation in real time
        show_progress (bool): show progress bar
        quiet (bool): if True, do not show the status message when
            completed
        interrupt (bool): if True, raise KeyboardInterrupt, otherwise
            just stop and continue
    """
    self.status = "running"
    stop_values = []
    time_step = time_step if time_step is not None else self.time_step
    if steps == float("inf"):
        step_iter = count()
    else:
        step_iter = range(steps)
    with self._no_interrupt():
        start_real_time = time.monotonic()
        start_time = self.time
        # If you want to show, and in google colab, then
        # we use tqdm's update to force widget updates
        if show and in_colab():
            clear_output(wait=True)
        for step in progress_bar(
            step_iter,
            (show_progress and not quiet) or (show and in_colab()),
            self._step_display
        ):
            if self._stop:
                self.status = "stopped"
                if interrupt:
                    raise KeyboardInterrupt()
                break
            if function is not None:
                if isinstance(function, (list, tuple)):
                    if len(function) < len(self._robots):
                        print_once("WARNING: you have not provided a controller function for every robot")
                    # Deterministically run robots round-robin:
                    stop_values = [
                        function[i](self._robots[i])
                        for i in range(len(function))
                        if function[i] is not None
                    ]
                else:
                    stop_values = [function(self)]
                stop = any(stop_values)
                if stop:
                    break
            self._step(time_step, show=show, real_time=real_time)
    self.status = "stopped"
    stop_real_time = time.monotonic()
    stop_time = self.time
    speed = (stop_time - start_time) / (stop_real_time - start_real_time)
    if steps > 1 and not quiet and not self.quiet:
        print(
            "Simulation stopped at: %s; speed %s x real time"
            % (format_time(self.time), round(speed, 2))
        )
    if show:
        self.draw()  # force to update any displays
    for value in stop_values:
        return value

stop(self)

Stop the simulator, if in thread or not.

Source code in robots/world.py
def stop(self):
    """
    Stop the simulator, if in thread or not.
    """
    self._stop = True
    self.status = "stopped"
    if self._thread is not None:
        print("Stopping thread...")
        self._thread.join()
        self._thread = None

summary(self)

Print a summary of information about the world and all of its robots.

Source code in robots/world.py
def summary(self):
    """
    Print a summary of information about the world and
    all of its robots.
    """
    print("World details:")
    if self.filename:
        print("This world was loaded from %r" % self.filename)
    print("Size: %s x %s" % (self.width, self.height))
    print("-" * 25)
    print("Robots:")
    print("-" * 25)
    if len(self._robots) == 0:
        print("  This world has no robots.")
    else:
        for i, robot in enumerate(self._robots):
            print("  .robots[%s or %r]: %r" % (i, robot.name, robot))
            robot.summary()
    print("-" * 25)
    print("Food:")
    print("-" * 25)
    if len(self._food) == 0:
        print("  This world has no food.")
    else:
        for food in self._food:
            print("  x: %s, y: %s, brightness: %s, state: %s" % (
                food.x, food.y, food.standard_deviation, food.state))
    print("-" * 25)
    print("Lights:")
    print("-" * 25)
    if len(self._bulbs) == 0:
        print("  This world has no lights.")
    else:
        for bulb in self._bulbs:
            print("  x: %s, y: %s, brightness: %s, name: %r, color: %s" % (
                bulb.x,
                bulb.y,
                bulb.brightness,
                bulb.name,
                bulb.color))
    print("-" * 25)
    print("Beacon:")
    print("-" * 25)
    if self.beacon is None:
        print("  This world has no beacon.")
    else:
        x, y = self.beacon.get_position(world=True)
        print("  x: %s, y: %s" % (x, y))

to_json(self)

Get the world as a JSON dict.

Source code in robots/world.py
def to_json(self):
    """
    Get the world as a JSON dict.
    """
    config = {
        "seed": self.seed,
        "width": int(self.width),
        "height": int(self.height),
        "scale": self.scale,
        "boundary_wall": self.boundary_wall,  # bool
        "boundary_wall_color": str(self.boundary_wall_color),
        "boundary_wall_width": self.boundary_wall_width,
        "ground_color": str(self.ground_color),
        "ground_image_filename": self.ground_image_filename,
        "quiet": self.quiet,
        "smell_cell_size": self.smell_cell_size,
        "walls": [],
        "bulbs": [],
        "beacon": None,
        "robots": [],
        "food": [],
    }
    if self._beacon is not None:
        config["beacon"] = {
            "x": self._beacon.x,
            "y": self._beacon.y,
            "z": self._beacon.z,
        }
    for wall in self._walls:
        # Not a boundary wall or robot bounding box:
        if wall.wtype == "wall":
            if len(wall.lines) == 4:
                # Box:
                w = {
                    "color": str(wall.color),
                    "p1": {"x": wall.lines[0].p1.x, "y": wall.lines[0].p1.y,},
                    "p2": {"x": wall.lines[2].p1.x, "y": wall.lines[2].p1.y,},
                    "wtype": "box",
                }
            elif len(wall.lines) == 1:
                # Line:
                w = {
                    "color": str(wall.color),
                    "p1": {"x": wall.lines[0].p1.x, "y": wall.lines[0].p1.y,},
                    "p2": {"x": wall.lines[0].p2.x, "y": wall.lines[0].p2.y,},
                    "wtype": "line",
                }
            else:
                raise Exception("invalid wall length; should be 1 or 4: %s" % len(wall.lines))
            config["walls"].append(w)

    for bulb in self._bulbs:
        config["bulbs"].append(
            {
                "color": str(bulb.color),
                "x": bulb.x,
                "y": bulb.y,
                "z": bulb.z,
                "brightness": bulb.brightness,
            }
        )

    for food in self._food:
        config["food"].append(
            {
                "x": food.x,
                "y": food.y,
                "standard_deviation": food.standard_deviation,
                "state": food.state,
            }
        )

    for robot in self._robots:
        config["robots"].append(robot.to_json())

    return config

update(self, show=True)

Update the world, robots, and devices. Optionally, draw the world.

Parameters:

Name Type Description Default
show bool

if True, draw the world.

True
Source code in robots/world.py
def update(self, show=True):
    """
    Update the world, robots, and devices. Optionally, draw the
    world.

    Args:
        show (bool): if True, draw the world.
    """
    ## Update robots:
    self._draw_list = self._overlay_list[:]
    for robot in self._robots:
        robot.update(self._draw_list)
    if show:
        self._request_draw()

watch(self, width=None, height=None)

Display a live (dynamically updated) view of the world.

Parameters:

Name Type Description Default
width int

the width of the world

None
height int

the height of the world

None
Source code in robots/world.py
def watch(self, width=None, height=None):
    """
    Display a live (dynamically updated) view of the world.

    Args:
        width (int): the width of the world
        height (int): the height of the world
    """
    # Force update:
    self.update(show=True)
    # Force draw:
    self.draw()
    widget = self.get_widget(width, height)
    display(widget)