AI and I

A blog about AI, implications, and experiments by Karlheinz Agsteiner

Insulted by the Pirate (Day Two of My RPGMaker Experiment)

Now it's time for a first real experiment. I ask ChatGPT for a plugin with these commands:

  1. SetNPCDescription: a command that allows you to describe in an event what the respective NPC is, what they are called, what their goals are, what their character is. This is important so that the LLM can bring them to life and they behave plausibly.

  2. DecideAndAct: calls the LLM. I’m using “Mistral Large,” which doesn’t perform at the level of the top American models, but it’s fast—a good compromise between performance and response time.

This time ChatGPT messes up a bit—after some experimenting (how do I get a debugger in an RPGMaker game? Can the LLM please log something to the console?), the AI discovers that Mistral’s response is slightly different than expected. After that, it works—I have NPCs acting in parallel to the player, who can move, talk, and give items they have to the player.

Then the big testing begins. The big, tricky testing.

As a test, I wrote my first real RPGMaker event: NPC Rudolf is an evil pirate. I still have no idea how the player is supposed to interact with an LLM NPC. That’s why Rudolf plans to approach the player and, once he's right next to them, insult them pirate-style.

Der Pirat

The Problem of Coordinates

The coordinate system in RPGMaker is like a screen, i.e., it grows downward. Surprisingly, this turned out to be tedious to teach to the LLMs. The pirate, who stood above me to the right, kept wanting to approach me by moving upward until he reached the top of the screen.

Three prompt improvements. No solution. Finally, I asked ChatGPT to write a prompt. Out came this monstrosity:

COORDINATE SYSTEM AND MOVEMENT POLICY (STRICT):
- Origin is top-left. x increases to the right. y increases downward.
- Let dx = player.x - npc.x; dy = player.y - npc.y.
- Choose axis that most reduces distance: if |dy| >= |dx| choose vertical, else horizontal.
- Direction: - If vertical: direction = "down" if dy > 0 else "up".
- If horizontal: direction = "right" if dx > 0 else "left".
- Hard constraint: After applying the move, Manhattan distance must strictly decrease.
- Never pick "down" when dy < 0. Never pick "up" when dy > 0.
- Never pick "right" when dx < 0. Never pick "left" when dx > 0.
- If the chosen axis would not reduce distance, pick the other axis if it reduces distance; otherwise return a "wait" action (ms=800).
- Do not reason with compass directions; follow the numeric rule exactly.

That works for my pirate, but I suspect it’s too heavily tailored to “approach the player.” Well, it’s good enough for now. The sinister pirate runs toward me.

Almost.

The Problem of Parallelism in RPGMaker

In fact, he now runs at me and right past me, circling around me.

It took me a while to understand what was happening here. I’m running the LLM event in “Parallel” mode. I had assumed that in this mode an event runs concurrently, and when it’s finished, a new one is created. But in reality, such events are created at every fixed time interval. For example, this happens:

1. Event is created, notices I am diagonally down-right from it.
2. Event thinks (i.e., the event has transmitted the situation to the LLM and is waiting for a response)
3. Second event is created, notices I am diagonally down-right from it.
4. Event 2 thinks
5. Event 1 is finished, recognizes “I first want to move up to stand next to the player”
6. Event 1 moves up.
7. Event 2 is finished, recognizes “I first want to move up to stand next to the player”
8. Event 2 moves up.
9. Event is created, notices I am diagonally up-right from it.

Many of the NPC events are therefore based on an outdated situation and lead to wrong decisions.

ChatGPT then built me a lock behavior that prevents parallel execution. Alternatively, one could try “Autostart” and a more complex event logic in RPGMaker.

After that, it works great. The pirate comes straight at me and showers me with pirate insults. Woohoo.

Properly insulted by the pirat (in German)