Im Rahmen meiner Bachelorarbeit an der Technischen Hochschule Augsburg untersuchte ich die Kombination zweier aktueller Ansätze zur KI-basierten Spielentwicklung: die textbasierte Generierung von Spielinhalten durch Large Language Models (LLMs) und die automatisierte Bewertung spielbarer Inhalte durch Reinforcement Learning (RL).

Ein Agent spielt automatisiert die generierten Level und sammelt Werte zur Validierung.
🔍 Stand der Technik
Procedural Content Generation
Prozedurale Levelgenerierung ist etabliert in der Spieleentwicklung. Klassische Methoden arbeiten regelbasiert oder zufällig – jedoch selten kontextsensitiv.
LLMs zur Levelgenerierung
Neuere Arbeiten zeigen, dass Sprachmodelle wie GPT-4 oder DeepSeek in der Lage sind, strukturierte Spielinhalte (z. B. JSON, XML, oder Doom-WAD) aus Prompt-Text zu erzeugen.
RL zur Evaluation
Anstatt Level manuell zu testen, wurde ein RL-Agent trainiert, der die Spielbarkeit, Schwierigkeit und Navigierbarkeit eines Levels bewertet – rein durch seine Interaktion mit dem generierten Raum.
ViZDoom als Plattform
ViZDoom bietet eine visuelle, steuerbare FPS-Umgebung auf Basis der Doom-Engine – mit API-Zugriff, .wad/.cfg-Levelunterstützung und hoher Modifizierbarkeit.
🧠 Konzeption & Methodik
Zielsetzung
Wie sinnvoll und effizient lassen sich Level durch LLMs erzeugen und automatisiert bewerten?
Systemarchitektur
- LLM erzeugt Doom-Level im Textformat
- Level wird automatisch in
.wad
/.cfg
konvertiert - RL-Agent spielt Level via Sample Factory (APPO)
💡 Implementierung
Tech Stack
- LLMs: DeepSeek-1, Gemini-2.0-flash, CodeLLaMA
- RL-Engine: Sample Factory, APPO
- Game Engine: ViZDoom
- Tools: Python, PyTorch, Git
import json
from openai import OpenAI
TOOL = {...}
user_prompt = f"""
Generate a valid 15x15 ASCII-based ViZDoom level.
### HARD CONSTRAINTS (must never be violated):
1. There must be **exactly one player**, placed in the **second row** (index 1). Do not place the player anywhere else.
2. There must be **exactly one goal item**, placed in the **second last row** (index 13). Do not place the goal anywhere else.
3. There must be **exactly 2 to 4 enemies**, located anywhere except player and goal positions.
...
"""
role_prompt = f"""
You are an AI designed to generate ASCII-based ViZDoom levels in a strict 15x15 format.
The first and last line must be only walls.
Allowed characters:
"#": "wall"
" ": "space for open areas"
"P": "player"
"G": "goal item"
"E": "enemy"
"""
response = AsyncOpenAI(
api_key=os.getenv("GOOGLE_API_KEY"),
base_url="https://generativelanguage.googleapis.com/v1beta/openai/"
)
.chat.completions.create(
model=llm_model,
messages=[
{ "role": "system", "content": role_prompt },
{ "role": "user", "content": user_prompt }
],
tools=[TOOL],
tool_choice={"type": "function", "function": {"name": "generate_level"}}
)
arguments_json = response.choices[0].message.tool_calls[0].function.arguments
return json.loads(arguments_json)
Vereinfachtes Beispiel für einen Prompt zur Levelgenerierung mit gemini-2.0-flash.
import json
from omg import *
class WADBuilder:
...
def build(self):
for h, row in enumerate(self.level):
for w, block in enumerate(row.strip()):
if block == '#':
self.__add_vertex(w, h)
elif block in self.thing_ids:
self.__add_thing(w, h, block)
corners = [(0, 0), (self.max_w, 0), (self.max_w, self.max_h), (0, self.max_h)]
for v in corners:
self.__add_vertex(*v)
for i in range(len(corners)):
if i != len(corners) - 1:
self.__add_line(corners[i], corners[i + 1], True)
else:
self.__add_line(corners[i], corners[0], True)
# Now connect the walls
for h, row in enumerate(self.level):
for w, _ in enumerate(row):
if (w, h) not in self.v_indexes:
continue
if (w + 1, h) in self.v_indexes:
self.__add_line((w, h), (w + 1, h))
if (w, h + 1) in self.v_indexes:
self.__add_line((w, h), (w, h + 1))
return self.things, self.vertexes, self.linedefs
Die Konvertierung der JSON Level zum spielbaren .wad Format übernimmt eine angepasste Version des MazeExplorer.
Training:
python train_custom_vizdoom_env.py --env=perfect_example --train_for_env_steps=100000000 --algo=APPO \
--env_frameskip=4 --use_rnn=True --batch_size=2048 --wide_aspect_ratio=False --num_workers=20 \
--num_envs_per_worker=20 --num_policies=1 --experiment=ml_bachelor_doom --save_every_sec=300 \
--experiment_summaries_interval=10
Evaluating:
python eval_custom_vizdoom_env.py --algo=APPO --env=perfect_example --experiment=ml_bachelor_doom \
--eval_scenario_dir=../doom-map-converter/generated_levels/ --num_episodes_per_scenario=100
Enjoying
python enjoy_custom_vizdoom_env.py --env=perfect_example --algo=APPO --experiment=ml_bachelor_doom
📊 Evaluation
Testdesign
Für die quantitative Bewertung generierter Level wurden pro Level Szenarien mit einer festen Agenten-Konfiguration durchgeführt (Sample Factory, 100 Episoden pro Level). Dabei wurden folgende Metriken berechnet:
avg_reward
: Durchschnittlich erreichter Gesamt-Reward je Episodeavg_hit_rate
: Verhältnis zwischen abgegebenen und erfolgreichen Treffernavg_damage_taken
: Durchschnittlicher erlittenen Schaden pro Durchlaufavg_damage_per_kill
: Schadensmenge pro eliminiertem Gegneravg_ammo_efficiency
: Verhältnis von Damage zu Munitionsverbrauchavg_health
: Durchschnittlicher Health-Wert am Ende der Episodesurvival_rate
: Quote der erfolgreichen Durchläufe ohne Tod
Diese Metriken wurden automatisiert aus den gesammelten Trajektorien extrahiert und in aggregierter Form (Mittelwert über alle Episoden) als Bewertung jedes Levels genutzt.
Ergebnisse
- Spielbarkeit (survival_rate > 0.5) konnte bei ca. 70 % der Level erreicht werden
- Ammo Efficiency war ein sensibler Indikator für Leveldichte und Gegnerplatzierung
- avg_reward korrelierte stark mit Healthkits und Map-Komplexität
Die Kombination dieser Metriken erlaubte eine differenzierte Bewertung, bei der z. B. auch spielbare, aber ineffizient designte Levels identifiziert werden konnten.
🧩 Diskussion
LLMs können Doom-Level erzeugen – aber nicht zuverlässig im richtigen Format. Der Agent als Bewertungsinstanz bringt nicht nur funktionales Feedback, sondern offenbart auch Balancing-Schwächen. In Kombination entsteht ein valider Kreislauf zur automatisierten Levelvalidierung.
✅ Fazit & Ausblick
Die Kombination von textbasierter Generierung und RL-basierter Bewertung bietet eine spannende Grundlage für zukünftige Tools in der Spieleentwicklung – etwa für automatische Testlevels, Schwierigkeitsanalyse oder assistiertes Leveldesign.