October 09, 2026

Event recogntion grammar for maze robot

 _

BNF grammars are known for programming language specification but are also useful for language games between speaker and hearer. A pointing game or an instruction following game can be realized with such a grammar. Reversing a pointing game results into an action recognition game in which a human controls a robot with arrow keys, and the grammar recognizes the demonstrated actions. Such an action recognition game was implemented in python as a simple maze game. The robot can move around and pick&place objects. 
The game doesn't need a large language model and not a vision language action model realized as a neural network, but its a symbolic approach based on a grammar.[1][2]

[1] Dantam, Neil, Irfan Essa, and Mike Stilman. "Linguistic transfer of human assembly tasks to robots." 2012 IEEE/RSJ International Conference on Intelligent Robots and Systems. IEEE, 2012.
[2] Wegner, Peter, and Dina Goldin. "Computation beyond Turing machines." Communications of the ACM 46.4 (2003): 100-102.
 

import re
import pygame


# -------------------- Configuration --------------------

COLS, ROWS = 20, 14
CELL = 36
GRID_W, GRID_H = COLS * CELL, ROWS * CELL
LOG_H = 180
WIDTH, HEIGHT = GRID_W, GRID_H + LOG_H

# '#' is a wall; '.' is a walkable cell.
MAZE = [
    "####################",
    "#....#.............#",
    "#....#..####..###..#",
    "#.......#.....#....#",
    "###.##..#..##.#.##..#",
    "#...##.....##......#",
    "#.######.####.###..#",
    "#........#.........#",
    "#.###.####.###.##..#",
    "#.....#......#.....#",
    "###.#.#.####.#.###..#",
    "#...#..........#...#",
    "#........#.........#",
    "####################",
]

# Named locations are recognized in the event log.
LOCATIONS = {
    "start": (1, 1),
    "junction": (7, 3),
    "workshop": (15, 5),
    "storage": (4, 9),
    "exit": (18, 12),
}

# Objects can be picked up and placed with G.
OBJECTS = {
    "red_key": (3, 3),
    "blue_box": (16, 9),
    "coin": (9, 11),
}

# Robot begins at the named "start" location.
robot = list(LOCATIONS["start"])
carried_object = None
event_log = ["Arrow keys: move    G: pick up / place"]

# Draw colors
WHITE = (255, 255, 255)
BLACK = (25, 25, 25)
WALL_COLOR = (70, 75, 85)
GRID_COLOR = (205, 205, 205)
ROBOT_COLOR = (30, 105, 220)
LOCATION_COLOR = (30, 135, 65)
OBJECT_COLORS = {
    "red_key": (205, 45, 45),
    "blue_box": (45, 80, 205),
    "coin": (190, 145, 0),
}


# -------------------- Event grammar --------------------

# Events emitted by the controls:
#
# <event>       ::= <move-event> | <grasp-event> | <ungrasp-event>
# <move-event>  ::= "movto" <location>
# <grasp-event> ::= "grasp" <object>
# <ungrasp-event> ::= "ungrasp" <object>
#
# <location> includes the named locations above and all walkable maze cells,
# represented as cell_X_Y. <object> includes the names in OBJECTS.
#
# The parser below checks both the event's form and its vocabulary.

def location_names():
    names = set(LOCATIONS)
    for y in range(ROWS):
        for x in range(COLS):
            if MAZE[y][x] != "#":
                names.add(f"cell_{x}_{y}")
    return names


LOCATION_NAMES = location_names()
OBJECT_NAMES = set(OBJECTS)

EVENT_PATTERNS = [
    ("movto", re.compile(r"^movto\s+([a-z][a-z0-9_]*)$")),
    ("grasp", re.compile(r"^grasp\s+([a-z][a-z0-9_]*)$")),
    ("ungrasp", re.compile(r"^ungrasp\s+([a-z][a-z0-9_]*)$")),
]


def parse_event(text):
    """Return (verb, argument) if a generated event matches the grammar."""
    for verb, pattern in EVENT_PATTERNS:
        match = pattern.fullmatch(text)
        if not match:
            continue

        argument = match.group(1)
        valid_names = LOCATION_NAMES if verb == "movto" else OBJECT_NAMES
        if argument in valid_names:
            return verb, argument

    return None


def log_recognized_event(text):
    """Only add events that are accepted by the grammar."""
    if parse_event(text) is not None:
        event_log.append(f"Recognized: {text}")
        del event_log[:-7]


def location_at(x, y):
    """Prefer a named location; otherwise use the cell's generated name."""
    for name, position in LOCATIONS.items():
        if position == (x, y):
            return name
    return f"cell_{x}_{y}"


# -------------------- Robot actions --------------------

def is_walkable(x, y):
    return (
        0 <= x < COLS
        and 0 <= y < ROWS
        and MAZE[y][x] != "#"
    )


def move_robot(dx, dy):
    x, y = robot
    nx, ny = x + dx, y + dy

    if not is_walkable(nx, ny):
        return

    robot[:] = [nx, ny]
    log_recognized_event(f"movto {location_at(nx, ny)}")


def toggle_grasp():
    global carried_object

    current_cell = tuple(robot)

    if carried_object is not None:
        # Place the held object on the robot's current cell.
        if current_cell in OBJECTS.values():
            event_log.append("Cannot place: this cell already has an object.")
            del event_log[:-7]
            return

        object_name = carried_object
        OBJECTS[object_name] = current_cell
        carried_object = None
        log_recognized_event(f"ungrasp {object_name}")
        return

    # Pick up an object on the robot's current cell.
    for object_name, position in OBJECTS.items():
        if position == current_cell:
            carried_object = object_name
            del OBJECTS[object_name]
            log_recognized_event(f"grasp {object_name}")
            return

    event_log.append("No object here to pick up.")
    del event_log[:-7]


# -------------------- Pygame setup and drawing --------------------

pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Robot Maze — BNF Event Log")
clock = pygame.time.Clock()

font = pygame.font.Font(None, 24)
small_font = pygame.font.Font(None, 19)
robot_font = pygame.font.Font(None, 28)


def draw():
    screen.fill(WHITE)

    # Maze
    for y in range(ROWS):
        for x in range(COLS):
            rect = pygame.Rect(x * CELL, y * CELL, CELL, CELL)
            if MAZE[y][x] == "#":
                pygame.draw.rect(screen, WALL_COLOR, rect)
            pygame.draw.rect(screen, GRID_COLOR, rect, 1)

    # Named locations
    for name, (x, y) in LOCATIONS.items():
        label = small_font.render(name, True, LOCATION_COLOR)
        screen.blit(label, (x * CELL + 3, y * CELL + 2))

    # Objects
    for name, (x, y) in OBJECTS.items():
        cx = x * CELL + CELL // 2
        cy = y * CELL + CELL // 2 + 4
        pygame.draw.circle(screen, OBJECT_COLORS[name], (cx, cy), 10)
        label = small_font.render(name, True, BLACK)
        screen.blit(label, (x * CELL + 2, y * CELL + CELL - 17))

    # Robot, displayed as R
    rx, ry = robot
    robot_label = robot_font.render("R", True, ROBOT_COLOR)
    robot_rect = robot_label.get_rect(
        center=(rx * CELL + CELL // 2, ry * CELL + CELL // 2)
    )
    screen.blit(robot_label, robot_rect)

    # White event log panel
    log_top = GRID_H
    pygame.draw.rect(screen, WHITE, (0, log_top, WIDTH, LOG_H))
    pygame.draw.line(screen, BLACK, (0, log_top), (WIDTH, log_top), 2)

    title = font.render("Recognized events", True, BLACK)
    screen.blit(title, (10, log_top + 8))

    if carried_object:
        held_text = small_font.render(f"Holding: {carried_object}", True, BLACK)
        screen.blit(held_text, (WIDTH - 150, log_top + 11))

    visible_events = event_log[-6:]
    for i, message in enumerate(visible_events):
        text = small_font.render(message, True, BLACK)
        screen.blit(text, (10, log_top + 38 + i * 21))

    pygame.display.flip()


# -------------------- Main loop --------------------

running = True

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False
            elif event.key == pygame.K_UP:
                move_robot(0, -1)
            elif event.key == pygame.K_DOWN:
                move_robot(0, 1)
            elif event.key == pygame.K_LEFT:
                move_robot(-1, 0)
            elif event.key == pygame.K_RIGHT:
                move_robot(1, 0)
            elif event.key == pygame.K_g:
                toggle_grasp()

    draw()
    clock.tick(30)

pygame.quit()
 

October 08, 2026

BNF grammar for instruction following of a kitchen robot

The communication between speaker and hearer can be formalized with a dialogue grammar which contains of objects and actions. The grammar stores a language in a compact format. Possible sentences are:

  1. Put the clean plate on the table.
  2. Place the large bowl beside the sink.
  3. Move the blue cup to the counter.
  4. Cut the small red tomato.
  5. Wash the dirty pan.
  6. Open the cabinet.
  7. Take the bottle from the refrigerator.
  8. Put the whole apple into the bowl.
  9. Clean the empty sink.
  10. Please place the warm bread on the table.
  11. ----
  12. Peel the small yellow potato.
  13. Place the clean glass on the counter.
  14. Move the large pan to the stove.
  15. Put the red pepper into the bowl.
  16. Wash the dirty spoon in the sink.
  17. Take the cold bottle from the refrigerator.
  18. Chop the whole onion on the worktop.
  19. Open the white cabinet.
  20. Fill the empty cup with water.
  21. Please serve the warm bread on the table.
  22. ----
  23. Slice the green tomato.
  24. Put the round plate onto the table.
  25. Clean the large sink.
  26. Bring the blue cup to the counter.
  27. Place the sharp knife beside the cutting board.
  28. Mix the ingredients in the bowl.
  29. Close the open cabinet.
  30. Empty the full container into the pan.
  31. Dry the clean glass near the sink.
  32. Heat the cold soup on the stove.


All these sentences doesn't require the full English vocabulary and the standard grammar for English but the sentences are formulated as a mini language specified in the BNF grammar. Such a mini language can be implemented easier as a parser for robot.

<instruction> ::= <command>
                 | <sequence>
                 | <conditional-command>

<command> ::= <imperative>
            | <polite-imperative>

<polite-imperative> ::= "please" <imperative>

<imperative> ::= <transitive-command>
                | <intransitive-command>
                | <movement-command>
                | <placement-command>
                | <cleaning-command>

<transitive-command> ::= <transitive-verb> <object-phrase>

<intransitive-command> ::= <intransitive-verb>
                          | <intransitive-verb> <location-phrase>

<movement-command> ::= <movement-verb> <location-phrase>
                     | <movement-verb> <object-phrase> <location-phrase>

<placement-command> ::= <placement-verb> <object-phrase>
                      | <placement-verb> <object-phrase> <location-phrase>

<cleaning-command> ::= <cleaning-verb> <object-phrase>
                     | <cleaning-verb> <location-phrase>

<sequence> ::= <command> "and then" <command>
             | <command> "," <command>
             | <command> "then" <command>

<conditional-command> ::= "if" <condition> "then" <command>

<condition> ::= <object-existence>
              | <state-condition>

<object-existence> ::= "there is" <object-phrase>
                      | "there are" <object-phrase>

<state-condition> ::= <object-phrase> <state-adjective>
                    | <location-phrase> <state-adjective>


<object-phrase> ::= <determiner> <adjective-sequence> <object-noun>
                   | <determiner> <object-noun>
                   | <possessive> <adjective-sequence> <object-noun>
                   | <possessive> <object-noun>

<location-phrase> ::= <preposition> <determiner> <adjective-sequence> <location-noun>
                    | <preposition> <determiner> <location-noun>
                    | <preposition> <location-noun>

<adjective-sequence> ::= <adjective>
                       | <adjective> <adjective-sequence>


<transitive-verb> ::= "wash"
                    | "cut"
                    | "chop"
                    | "slice"
                    | "peel"
                    | "mix"
                    | "stir"
                    | "pour"
                    | "fill"
                    | "empty"
                    | "open"
                    | "close"
                    | "pick up"
                    | "put down"
                    | "take"
                    | "bring"
                    | "place"
                    | "move"
                    | "put"
                    | "heat"
                    | "cool"
                    | "serve"

<intransitive-verb> ::= "wait"
                      | "stop"
                      | "start"
                      | "continue"
                      | "turn"
                      | "pause"

<movement-verb> ::= "go to"
                  | "move to"
                  | "take"
                  | "bring"
                  | "carry"

<placement-verb> ::= "place"
                   | "put"
                   | "set"
                   | "leave"

<cleaning-verb> ::= "clean"
                   | "wash"
                   | "dry"
                   | "wipe"


<object-noun> ::= "apple"
                 | "banana"
                 | "tomato"
                 | "potato"
                 | "onion"
                 | "carrot"
                 | "pepper"
                 | "bread"
                 | "cheese"
                 | "egg"
                 | "plate"
                 | "bowl"
                 | "cup"
                 | "glass"
                 | "pan"
                 | "pot"
                 | "knife"
                 | "spoon"
                 | "fork"
                 | "bottle"
                 | "container"
                 | "ingredient"
                 | "food"

<location-noun> ::= "kitchen"
                  | "counter"
                  | "table"
                  | "sink"
                  | "stove"
                  | "oven"
                  | "refrigerator"
                  | "drawer"
                  | "cabinet"
                  | "shelf"
                  | "dishwasher"
                  | "cupboard"
                  | "floor"
                  | "worktop"

<adjective> ::= <color-adjective>
              | <size-adjective>
              | <state-adjective>
              | <shape-adjective>
              | <temperature-adjective>
              | <quantity-adjective>

<color-adjective> ::= "red"
                    | "green"
                    | "yellow"
                    | "blue"
                    | "white"
                    | "black"

<size-adjective> ::= "small"
                    | "large"
                    | "big"
                    | "tiny"
                    | "deep"
                    | "shallow"

<state-adjective> ::= "clean"
                    | "dirty"
                    | "empty"
                    | "full"
                    | "open"
                    | "closed"
                    | "hot"
                    | "cold"
                    | "ready"
                    | "broken"

<shape-adjective> ::= "round"
                    | "square"
                    | "flat"
                    | "sharp"

<temperature-adjective> ::= "warm"
                          | "cool"
                          | "hot"
                          | "cold"

<quantity-adjective> ::= "single"
                       | "extra"
                       | "remaining"
                       | "whole"
                       | "half"

<determiner> ::= "the"
               | "a"
               | "an"
               | "this"
               | "that"
               | "these"
               | "those"

<possessive> ::= "my"
               | "your"
               | "the robot's"

<preposition> ::= "to"
                | "from"
                | "on"
                | "in"
                | "into"
                | "onto"
                | "under"
                | "beside"
                | "near"
                | "behind"
                | "inside"
                | "out of"
 

October 05, 2026

NPC Quest generator

 Grounded language can be demonstrated with a speaker to hearer interaction. Such a language game seperates knowledge from each other, that means, the speaker has a different knowledge than the hearer and during the game the mismatch or match gets visible.

The verbal utterances of a speaker can be implemented as NPC Quest generator. This is a random generator who produces quests which have to be fulfilled by the hearer. A possible grammar for a warehouse robot would be:
- action: lookat, moveto, grasp
- noun: greenbox, redbox, roomA, roomB

The NPC quest generator takes this grammar as input and generates possible questions like:
1. lookat greenbox
2. moveto roomA
3. moveto redbox
4. grasp redbox

If the hearer wasn't implemented the robot won't fulfill the quests. The game generates only endless amount of tasks but the robot won't do something.

A NPC quest generator produces high level goals based on a vocabulary. These gals can be used to play a language game which can be "pointing game" or "instruction following". After implementing the hearer in software too, the lahguage game is working autonoamously, that means one side generates the tasks while the other fulfills them.


October 02, 2026

Creating chatty robots

 

The Micromouse challenge is a well known robot competition which is mostly a hardware building plus programming challenge. The rules of the challenge can be modified so that the ability of a robot is determined to generate grounded language. In the screenshot, the robot has a text widget at the bottom for showing the text.

The human user controls the robot and at the same time the textual output of the robot gets updated. This textual output allows a rule based AI to control the robot automatically which wasn't implemented yet. The only difference to the normal micromouse challenge is, that in the example the mentioned text widget is visible.

The text gets produced with a dictionary of words. Possible words like [move, goal, blocked] are describing objects and events of the game. The program detects situations and uses the words to generate the output. Because there is a mapping between the game state and the textual output its called grounded language.

import math, sys, pygame

# 1. SETUP & CONFIGURATION
WIN_W, WIN_H, MAZE_H = 800, 700, 580
WHITE, BLACK, GREEN, BLUE, GRAY = (255, 255, 255), (0, 0, 0), (40, 180, 40), (40, 100, 220), (200, 200, 200)

# Grounded language vocabulary for rule-based AI parsing
DICT = {
    "verbs": ["detected", "reached", "turn", "move"],
    "nouns": ["wall", "path", "goal", "junction", "distance"],
    "adj": ["ahead", "left", "right", "clear", "blocked", "near"]
}

# Solvable Maze Walls [x, y, w, h] & Goal
WALLS = [
    pygame.Rect(0, 0, 800, 10), pygame.Rect(0, 0, 10, 580),
    pygame.Rect(790, 0, 10, 580), pygame.Rect(0, 570, 800, 10),
    pygame.Rect(150, 0, 10, 420), pygame.Rect(300, 160, 10, 420),
    pygame.Rect(450, 0, 10, 420), pygame.Rect(600, 160, 10, 420),
    pygame.Rect(150, 250, 80, 10), pygame.Rect(450, 350, 80, 10)
]
GOAL = pygame.Rect(680, 480, 80, 80)

# 2. ROBOT CLASS
class Micromouse:
    def __init__(self, x, y):
        self.x, self.y, self.angle, self.radius, self.sensor_len = x, y, 0, 14, 55

    def update(self, keys):
        speed = (2.5 if keys[pygame.K_UP] else 0) - (1.5 if keys[pygame.K_DOWN] else 0)
        self.angle = (self.angle + (3.5 if keys[pygame.K_RIGHT] else 0) - (3.5 if keys[pygame.K_LEFT] else 0)) % 360
        rad = math.radians(self.angle)
        nx, ny = self.x + math.cos(rad) * speed, self.y + math.sin(rad) * speed
        
        # Collision detection with walls
        nrect = pygame.Rect(nx - self.radius, ny - self.radius, self.radius * 2, self.radius * 2)
        collided = any(nrect.colliderect(w) for w in WALLS)
        if not collided:
            self.x, self.y = nx, ny
        return collided

    def sense(self):
        """Scans left (-45 deg), ahead (0 deg), right (+45 deg) for obstacles."""
        dirs, res = {"left": -45, "ahead": 0, "right": 45}, {}
        for d, off in dirs.items():
            rad = math.radians(self.angle + off)
            ex, ey = self.x + math.cos(rad) * self.sensor_len, self.y + math.sin(rad) * self.sensor_len
            res[d] = any(w.clipline((self.x, self.y), (ex, ey)) for w in WALLS)
        return res

    def draw(self, surface):
        pygame.draw.circle(surface, BLUE, (int(self.x), int(self.y)), self.radius)
        rad = math.radians(self.angle)
        pygame.draw.line(surface, WHITE, (self.x, self.y), (self.x + math.cos(rad)*self.radius, self.y + math.sin(rad)*self.radius), 3)

# 3. HELPER FOR WORD WRAPPING TEXT
def render_wrapped_text(surface, text, font, rect, color=BLACK):
    words = text.split(" ")
    lines, current_line = [], ""
    for word in words:
        test_line = f"{current_line} {word}".strip()
        if font.size(test_line)[0] <= rect.width:
            current_line = test_line
        else:
            lines.append(current_line)
            current_line = word
    lines.append(current_line)
    
    y = rect.y
    for line in lines:
        if y + font.get_height() <= rect.bottom:
            surface.blit(font.render(line, True, color), (rect.x, y))
            y += font.get_height() + 2

# 4. MAIN LOOP
def main():
    pygame.init()
    screen = pygame.display.set_mode((WIN_W, WIN_H))
    pygame.display.set_caption("Grounded Language Micromouse")
    clock, font = pygame.time.Clock(), pygame.font.SysFont("Arial", 30, bold=False)
    mouse = Micromouse(50, 50)

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit(); sys.exit()

        collided = mouse.update(pygame.key.get_pressed())
        sensors = mouse.sense()
        dist = int(math.hypot(mouse.x - GOAL.centerx, mouse.y - GOAL.centery))

        # Build rule-oriented grounded language output
        tokens = []
        if GOAL.collidepoint(mouse.x, mouse.y):
            tokens.append(f"{DICT['nouns'][2]} {DICT['verbs'][1]}")  # "goal reached"
        else:
            tokens.append(f"{DICT['nouns'][4]} to {DICT['nouns'][2]}: {dist}px")  # "distance to goal: Xpx"

        if collided:
            tokens.append(f"{DICT['nouns'][0]} collision {DICT['adj'][4]}")  # "wall collision blocked"
        
        # Tactical language for rule-based navigation AI
        blocked_dirs = [d for d, is_blocked in sensors.items() if is_blocked]
        if not blocked_dirs:
            tokens.append(f"{DICT['nouns'][1]} {DICT['adj'][3]}: {DICT['verbs'][3]} {DICT['adj'][0]}") # "path clear: move ahead"
        else:
            tokens.append(f"{DICT['nouns'][0]} {DICT['verbs'][0]} on " + " ".join(blocked_dirs))
            if sensors["ahead"]:
                suggested_turn = "right" if not sensors["right"] else ("left" if not sensors["left"] else "back")
                tokens.append(f"recommendation: {DICT['verbs'][2]} {suggested_turn}")

        # Render Scene
        screen.fill(WHITE)
        pygame.draw.rect(screen, GREEN, GOAL)
        screen.blit(font.render("GOAL", True, WHITE), (GOAL.x + 18, GOAL.y + 30))
        for w in WALLS: pygame.draw.rect(screen, BLACK, w)
        mouse.draw(screen)

        # Bottom Text Panel (White Background, Black Text, Word Wrapped)
        panel = pygame.Rect(0, MAZE_H, WIN_W, WIN_H - MAZE_H)
        pygame.draw.rect(screen, WHITE, panel)
        pygame.draw.line(screen, BLACK, (0, MAZE_H), (WIN_W, MAZE_H), 2)

        output_str = f"STATUS: [ {' | '.join(tokens)} ] | CONTROLS: Arrow Keys"
        render_wrapped_text(screen, output_str, font, pygame.Rect(10, MAZE_H + 8, WIN_W - 20, WIN_H - MAZE_H - 12))

        pygame.display.flip()
        clock.tick(60)

if __name__ == "__main__":
    main()

October 01, 2026

Understanding robotics in the past

Until around the year 2010, robotics projects were started with a certain bias which wasn't discussed but seen as mandatory. The philosophy was to see a robot as a closed system similar to a meccano windmill or a steam engine. All these systems are described by its internal working. In case of a windmill there is a mechanical mechanism and in case of a robot there is a computer program available. The assumption was that the power of a robot is located inside the machine encoded in an AI algorithm.

The only debate available was about the details of a closed system, for example which programming language is useful and which algorithm should be preferred. The AI engineers were interested in improving the robot itself because this principle was successful for mechanical machines and its also relevant for classic computer software. A certain software e.g. a graphics routine is optimized by improving the code. The code can become faster in terms of RAM consumption and CPU cycles. Also the lines of codes can be reduced so the machine is working more efficient.

Unfortunately, this paradigm failed for robotics applications. Its not possible to develop a robot with this bias. This is not a theoretical criticism but can be verified with a closer look into robotics software written before 2010. Many examples projects are available at github, some of them consists of 100k lines of code written in C++ with advanced algorithms, but the resulting robot can do nothing. It fails for simple tasks.

In a more harder language, robotics projects in the past were similar to non sense machines. It was cargo cult science. Even real software code was written in C/C++ and executed on a microcontroller, the robot wasn't able to do something. This problem was recognized by researchers in the past, but they didn't know how to improve the robots.

The criticism can be summarized to a single requirement: Robots in the past were not able to communicate with the environment. Even the robot was equipped with a microphone and sensors these hardware parts were not important for the software. Even more, the goal of robotics projects in the past was to avoid any communication with the environment because such a behavior has much in common with teleoperation which was seen as dead end.

AI before the year 2010 was trapped into a philosophy based on closed system plus computionalism. The thought model was a Turing machine without any external sensors. Such a closed system was recognized as best practice method for creating Artificial intelligence because it was fully understood by the software engineers.

One possible explanation for this self created closed system trap was the inability of computer science to describe the reality. Computer science is great in creating computer hardware and software but struggles in capture the environment of a robot. The reality is a highly complex, unspecified and non mathematical system which can't be compressed in algorithm or mathematical equation which creates a gap between the internal model of a robot and the external reality. This gap was first described by Rodney Brooks in 1990 but Brooks didn't mentioned how to overcome the gap.

September 30, 2026

Pointing game with heatmap

Similar to the previous example the game generates NPC Quests for the human to point at a certain location in a maze. Typical quests are "point at room C", "point at door to room A". To make the task easier, a heat map is shown, which provides a feedback at which direction the target location is. After clicking on the desired location, the user gets +10 reward and the next quest is shown.

The prototype demonstrates grounded language for a pointing game. The task for the user is to convert a textual command "point at ..." into an action with the mouse.

The source code is very short and consists of only 150 lines of code in Python with the pygame and the random library.

Robotik als geschlossenes System

 Bis ungefähr zum Jahr 2010 dominierte in der Robotik Community das Paradigma des geschlossenen Systems ohne es als solches zu benennen. Das Ziel der Programmierer war häufig einen Algorithmus oder ein Robot control Program zu erstellen was den Roboter autonom steuert. Der Fokus lag also auf der Programierung ganz so wie auch die Informatik den Schwerpunkt auf Software und Algorithmen legt.

Die Informatik stellt für diese Aufgabe zahlreiche Werkzeuge bereit, wie z.B. vorhandene Bilbiotheken mit pLanungsalgorithmen, Compiler für Hochsprachen wie Java sowie leistungsfähige Code editoren mit denen einmal erstellter Programm code getestet und verbessert werden kann. Die Annahme vor 2010 lautete dass diese Werkzeuge ausreichen um damit Roboter zu programmieren.

Im wesentlichen ging es also darum ein Computerprogram zu entwickeln was einen Roboter autonom steuern kann. Diese selbstgesetzte Zielstellung erzeugt einen besonderen Workflow. Er erzeugt eine massive Komplexität. Je anspruchsvoller der Roboter desto umfangreicher die benötigte Software.

Das Problem war den Informatikern bewusst, sie versuchten die Komplexität zu senken indem sie die Aufgabenstellung modifizierten. Anstatt die Software für ein Selbstfahrendes Auto im Straßenverkehr zu programmieren wurde versucht ein Spielzeug auto auf einem Parkurs zu steuern. Aber selbst diese reduzierte Aufgabe führte zu einer Software die 100k lines of code und mehr umfasste.

Bis 2010 war den meisten Robotik-Pionieren nicht bewusst dass sie sich in einer Sackgasse befanden weil die Roboter als geschlossene Systeme funktionierten. Damit ist gemeint dass die Roboter sowohl von der Software als auch von der Hardware extrem hochentwickelt waren, aber dabei auschließlich nach internen Mechanismen funktionierten und die Umwelt ignorierten. Rein formal waren Roboter zwar mit Sensoren ausgestattet es gab jedoch keine Theorie wie die Sensorwerte in Aktionen übersetzt werden können.

Geschlossene Systeme sind das gegenteil von einer Fernsteuerung. Bei einer Fernsteuerung sendet die Umwelt Befehle an den Roboter. Genau diese Art von Fernkontrolle gab es bei den Roboter vor 2010 nicht. Alle bekannten Robotik Wettbewerbe aus dieser Zeit funktionieren nach dem Prinzip der autonomen Steuerung. Es gab ein Program was von einer CPU ausgeführt wurde, und das Programm allein entschied was der Roboter tat. Ferngesteuerte Robotik wurde als unzulässig verworfen. Es war zwar technisch bekannt wie man das realisiert, aber das Prinzip wurde nicht ernsthaft diskutiert.

Ferngesteuerte Roboter sind offene Systeme. Auch die vielzietierte These von Rodney Brooks emboeddied AI zu realisieren ist nur eine andere Formulierung für Teleoperation. Sobald man Daten von Außen an den Roboter sendet entsteht ein offenes System. nicht der Roboter entscheidet was zu tun ist, sondern die umwelt übernimmt die Aufgabe. So kann ein abstandssensor ebenfalls als Fernsteuerung betrachtet werden. Sobald der abstandssensor ein Hinderniss erkennt stoppt der Roboter. Das heißt der Sensor sendet an den Roboter ein Kommando.

Bei offenen Systemen ist die Unterscheidung zwischen Roboter und Umwelt zentral. Als Roboter wird die Hardware und Software des Roboters bezeichnet, also der Arduino Microcontroller, das Linux Betriebssystem, das C program was auf dem Board läuft. Also jene Elemente für die sich die Informatik zuständig fühlt. Umgekehrt besteht die Umgebung eines Roboter aus der Aufgabe die es zu lösen, also ein Labyrinth, Hindernisse in diesem Labyrinth, ein Zielpunkt den der Roboter erreichen muss. Diese Umwelt wird von der Informatik vor dem Jahr 2010 ignroiert. Die Umwelt lässt sich nicht als Hardware und Software beschreiben sondern die Umwelt funktioniert nach anderen Prinzipien.

Sobald man Roboter baut die ferngesteuert werden, nimmt die Umwelt einen höheren Stellenwert wert. Der Roboter selbst wird zu einer Trivalen Maschine die lediglich Befehle über ein funksignal empfängt und einen Motor hat der sich bewegen kann. Der Roboter besitzt jedoch keine Software um Entscheidungen zu treffen sondern die Umwelt übernimmt das Timing und die Zieldefinition. Offene Systeme lassen sich über das Interface beschreiben, also jenes Bautteil oder jenes Softwaremodul was Befehle der Umwelt empfängt und was Informationen an die Umwelt sendet. Dieses Interface beinhaltet die eigentliche Künstliche Intelligenz, zumindest nach einer modernen Betrachtung ab dem Jahr 2010.

September 29, 2026

Text based pointing game for car driving

One problem with grounded language that its hard to give a sense making example which can be implemented in a short amount of code lines. A possible answer is a NPC quest generator which is generating tasks as textual output. The following python program has only 33 lines of code and generates random quests for a car driving game.

All the quests are pointing challenges, the human is asked to point at a certain object in the scene. The software can't verified if the human has pointed at the correct object, but only the quest itself is shown on the screen.

The implementation in python is as simple as possible. There is a python dict with possible target location which are selected randomly by the software. So its some sort dictionary with a random element. To solve the tasks, that human needs to know what a certain word means. For example "Focus on the pedestrian on sidewalk." is asking for a certain object at a certain position. 

screenshot:

==================================================
  3D DRIVING GAME: NPC POINTING QUEST GENERATOR
==================================================
Press [ENTER] for a new quest | Type 'q' + [ENTER] to quit

[Quest #1] Ready? 
 >> NEW QUEST: Focus on the pedestrian on sidewalk.
--------------------------------------------------
[Quest #2] Ready? 
 >> NEW QUEST: Focus on the parking spot.
--------------------------------------------------
[Quest #3] Ready? 
 >> NEW QUEST: Track the parking spot.
--------------------------------------------------
[Quest #4] Ready? 
 >> NEW QUEST: Locate the car on the left lane.
--------------------------------------------------
[Quest #5] Ready? 
 

source code:

import random

# Driving game targets organized by relative 3D perspective from behind the wheel
TARGETS = {
    "traffic_controls": ["traffic light", "speed limit sign", "stop sign", "yield sign"],
    "road_features": ["street ahead", "crosswalk", "lane line", "pothole", "guardrail"],
    "vehicles": ["car in front", "car on the left lane", "car on the oncoming side", "truck in side mirror", "motorcycle in rearview mirror"],
    "environment": ["pedestrian on sidewalk", "billboard", "parking spot", "street lamp"]
}

VERBS = ["point at", "focus on", "locate", "target", "track"]

def generate_quest():
    category = random.choice(list(TARGETS.keys()))
    obj = random.choice(TARGETS[category])
    verb = random.choice(VERBS)
    return f'{verb.capitalize()} the {obj}.'

def main():
    print("=" * 50 + "\n  3D DRIVING GAME: NPC POINTING QUEST GENERATOR\n" + "=" * 50)
    print("Press [ENTER] for a new quest | Type 'q' + [ENTER] to quit\n")
    
    count = 1
    while True:
        cmd = input(f"[Quest #{count}] Ready? ").strip().lower()
        if cmd == 'q':
            print("\nGenerator stopped. Keep your eyes on the road!")
            break
        print(f" >> NEW QUEST: {generate_quest()}\n" + "-" * 50)
        count += 1

if __name__ == "__main__":
    main()