World Model in Code · Part 1 of 3

The World and Its Training Data

DE Auf Deutsch lesen

August 24, 2026 · approx. 6 min read

Before a world model can predict anything, it needs an environment and examples. This part shows how a 7×7 map turns into a dataset of states, actions and outcomes.

The environment is just a plain function

For this small example, no framework is needed. The world is a list of strings. Each position is a pair of row and column.

KARTE = [
    "#######",
    "#..#..#",
    "#..#..#",
    "#.....#",
    "#.##..#",
    "#....Z#",
    "#######",
]

AKTIONEN = ["hoch", "runter", "links", "rechts"]
VERSATZ = [(-1, 0), (1, 0), (0, -1), (0, 1)]

In the program, an action is a number from 0 to 3. VERSATZ turns it into a change in position. That keeps the transition function short:

def schritt(pos, aktion):
    dz, ds = VERSATZ[aktion]
    neu = (pos[0] + dz, pos[1] + ds)

    if KARTE[neu[0]][neu[1]] == "#":
        neu = pos

    fertig = neu == ZIEL
    belohnung = 1.0 if fertig else 0.0
    return neu, belohnung, fertig

The function returns three values: the new position, the reward, and whether the episode has ended. If the agent runs into a wall, the position stays unchanged. These unsuccessful actions also belong in the training data. Otherwise the model could learn that every action moves the agent.

The agent doesn't get a position

The position would be a convenient input for the model. But then not much would be left of the problem. Instead, the agent gets an image made of 49 grayscale values.

def beobachtung(pos):
    bild = []
    for z, zeile in enumerate(KARTE):
        for s, feld in enumerate(zeile):
            if (z, s) == pos:
                bild.append(0.3)
            elif feld == "#":
                bild.append(1.0)
            elif feld == "Z":
                bild.append(0.6)
            else:
                bild.append(0.0)
    return bild

The result is a flat list. The model doesn't know that every seven values form a row. It only sees 49 numbers. It has to derive the spatial structure from recurring patterns.

Why so roundabout? In a real visual task, position and obstacles aren't handed over as ready-made variables either. The grid world maps this problem at a scale that's still small enough to print out and check by hand.

A transition is one line of experience

To collect data, the agent picks random actions. Before and after each action, the program renders an observation and stores everything in a dictionary.

uebergaenge.append({
    "vorher": beobachtung(pos),
    "aktion": aktion,
    "nachher": beobachtung(neu),
    "belohnung": belohnung,
    "fertig": fertig,
})

With 3,000 such entries, daten.json is created. In my run, only 1.3 percent of transitions carried a reward. For the reward model, the dataset is therefore heavily imbalanced.

The number 1 in random.Random(1) is the seed. It fixes the sequence of random actions. With the same seed, the program produces the same action sequence on the next run:

zufall = random.Random(1)
aktion = zufall.randrange(len(AKTIONEN))

This doesn't yet show whether the approach works in general. During development it's useful anyway: if the result changes despite the same action sequence, the cause can be tracked down in the code or in the data.

What part 1 leaves us with

The dataset contains many small questions paired with answers: "If this image is visible and the agent moves left, which image follows?" In the next part, every image gets shrunk from 49 down to eight values. Only after that does the dynamics model learn the transitions.