RPG Level Generator: AI-Powered Game Design#
Build a dynamic RPG level generator using LLMs and Jac's by llm syntax.
Time: 30 minutes Level: Advanced
What You'll Build#
An AI-powered system that:
- Generates balanced, progressively challenging game levels
- Creates detailed maps with walls, enemies, and obstacles
- Scales difficulty automatically as players progress
- Produces playable ASCII map visualizations
Why This Example?#
This example showcases:
| Concept | How It's Used |
|---|---|
| byLLM | AI generates level configurations and maps |
| Structured Types | Objects guide AI understanding |
| Progressive Systems | Difficulty curves and variety |
| Practical Output | AI data becomes usable game content |
Prerequisites#
Data Structures#
The key to AI-powered generation is well-defined types. The LLM understands these structures and generates appropriate values.
Position and Walls#
obj Position {
has x: int;
has y: int;
}
obj Wall {
has start_pos: Position;
has end_pos: Position;
}
Level Configuration#
obj Level {
has name: str;
has difficulty: int;
has width: int;
has height: int;
has num_wall: int;
has num_enemies: int;
has time_countdown: int;
has n_retries_allowed: int;
}
Map Layout#
obj Map {
has level: Level;
has walls: list[Wall];
has small_obstacles: list[Position];
has enemies: list[Position];
has player_pos: Position;
}
AI-Powered Generation#
The magic happens with by llm():
import from byllm.lib { Model }
glob llm = Model(model_name="gpt-4o", verbose=True);
obj LevelManager {
has current_level: int = 0;
has current_difficulty: int = 1;
has prev_levels: list[Level] = [];
has prev_level_maps: list[Map] = [];
"""Generate a new level configuration based on difficulty and history."""
def create_next_level(
last_levels: list[Level],
difficulty: int,
level_width: int,
level_height: int
) -> Level by llm();
"""Generate a detailed map for a given level."""
def create_next_map(level: Level) -> Map by llm();
}
Key insight: The AI automatically understands:
- Previous levels (to ensure variety)
- Difficulty scaling
- Spatial constraints (width, height)
- Object relationships (Map contains Level)
Level Flow Management#
def get_next_level() -> tuple[Level, Map] {
self.current_level += 1;
# Keep only last 3 levels for context
if len(self.prev_levels) > 3 {
self.prev_levels.pop(0);
self.prev_level_maps.pop(0);
}
# AI generates the level
new_level = self.create_next_level(
self.prev_levels,
self.current_difficulty,
20, 20 # 20x20 grid
);
self.prev_levels.append(new_level);
# AI generates the map
new_level_map = self.create_next_map(new_level);
self.prev_level_maps.append(new_level_map);
# Increase difficulty every 2 levels
if self.current_level % 2 == 0 {
self.current_difficulty += 1;
}
return (new_level, new_level_map);
}
Map Visualization#
Convert AI-generated maps to ASCII:
def get_map(map: Map) -> list[str] {
# Initialize empty grid
tiles = [['.' for _ in range(map.level.width)]
for _ in range(map.level.height)];
# Place walls
for wall in map.walls {
for x in range(wall.start_pos.x, wall.end_pos.x + 1) {
for y in range(wall.start_pos.y, wall.end_pos.y + 1) {
tiles[y-1][x-1] = 'B';
}
}
}
# Place obstacles
for obs in map.small_obstacles {
tiles[obs.y-1][obs.x-1] = 'B';
}
# Place enemies
for enemy in map.enemies {
tiles[enemy.y-1][enemy.x-1] = 'E';
}
# Place player
tiles[map.player_pos.y-1][map.player_pos.x-1] = 'P';
# Add borders
tiles = [['B'] + row + ['B'] for row in tiles];
border = ['B' for _ in range(map.level.width + 2)];
tiles = [border] + tiles + [border];
return [''.join(row) for row in tiles];
}
Symbols:
.= Empty spaceB= Block/WallE= EnemyP= Player start
Testing the Generator#
# test_generator.jac
import from level_manager { LevelManager }
with entry {
manager = LevelManager();
print("Generating 3 AI-powered levels...\n");
for i in range(3) {
(level, map_obj) = manager.get_next_level();
visual_map = manager.get_map(map_obj);
print(f"=== LEVEL {i+1} ===");
print(f"Difficulty: {level.difficulty}");
print(f"Enemies: {level.num_enemies}");
print(f"Walls: {level.num_wall}");
print("Map:");
for row in visual_map {
print(row);
}
print("\n");
}
}
Run it:
Sample Output#
=== LEVEL 1 ===
Difficulty: 1
Enemies: 2
Walls: 3
Map:
BBBBBBBBBBBBBBBBBBBBBB
B....................B
B.....B..............B
B....................B
B........E...........B
B....................B
B..........P.........B
B....................B
B.E..................B
B....................B
BBBBBBBBBBBBBBBBBBBBBB
Each run produces different layouts as the AI creates unique levels.
How It Works#
- Structured Types -
LevelandMapobjects define what the AI generates - Historical Context - Previous levels prevent repetition
- Difficulty Scaling - Increases every 2 levels
- Spatial Constraints - Width/height guide placement
- AI Creativity - LLM handles the actual creative work
Extension Ideas#
- More object types - Treasure, power-ups, keys, doors
- Different terrain - Water, lava, grass tiles
- Boss levels - Special every 5th level
- Player stats - Health, damage, speed progression
- Theme variations - Dungeon, forest, castle themes
Full Source Code#
- RPG Game Source
- Fantasy Trading Game - Similar patterns
Key Takeaways#
- Types guide AI - Well-defined objects lead to better generation
by llmis declarative - Describe what you want, not how- Context matters - Pass history and constraints for variety
- Practical output - Convert AI data to usable formats
Next Examples#
- EmailBuddy - AI-powered email assistant
- LittleX - Social media platform