Procedural Content Generation: Grammars, Search, Constraint Satisfaction, & Wave Function Collapse
Rule-Based PCG: Recap & Limitations
- Previously covered: simple rule systems can generate impressive outputs (e.g.
- Intricate height-map terrain
- Cellular-automata caverns)
- Fundamental issue: they give no formal guarantees about desirable global
properties
- Example: A terrain might look convincing yet be unnavigable for the
player or AI - Fixes require ad-hoc rule tweaks → hard to prove they work for all
outcomes
- Example: A terrain might look convincing yet be unnavigable for the
- Motivating question → How do we add guarantees while keeping generative
power? ⇒ Enter grammars & L-systems
Generative Grammars
- Historical roots: Noam Chomsky (1960s)
- Studied the formal structure of natural language
- Abstract view: a language = symbols + production rules specifying legal
arrangements
- Core components
- Symbols / variables – placeholders (e.g., nouns, limbs, tiles)
- Terminals / constants – atomic tokens that appear in the final string
- Production rules – rewrite one symbol sequence into another
- Axiom / start symbol – initial string (seed)
- Repeated rule application ⇒ large space of outputs
- Flexibility lets us model
- Sentences → natural-language generation
- Limbs → creature or avatar synthesis
- Tiles/rooms → level layouts
Strengths & Weaknesses
- Pros
- Highly general-purpose
- Capable of high-quality, hierarchical structure
- Fast to evaluate once rules exist
- Designer retains full control via authored rules/components
- Cons
- Heavy authorial burden – every piece & relationship must be encoded
- Debugging nightmare – to test a fix you must in theory sample
outputs - Risk of over-constraining (no variety) or under-constraining
(nonsensical outputs)
Case Study: Spelunky Level Generation
- Each level begins as a grid of rooms (4×4 in classic Spelunky)
- Designer-made templates (e.g., Landing, Corridor, Drop) are the
building blocks - Generation algorithm
- Place templates one at a time
- When choosing a template, enforce neighborhood constraints (edges must
align, entrance/exit connectivity, etc.) - Resulting macro-layout later filled with finer entities (traps, gold,
monsters)
- Effect:
- Combines designer authorship (templates) + grammatical positioning rules
- Guarantees a fully traversable level while preserving theme variety
L-Systems (Lindenmayer Systems)
- Special grammar where every symbol is rewritten simultaneously each
timestep ⇒ good for growth simulations - Formal tuple:
- = variables (rewriteable, draw commands optional)
- = constants (no rewrite, usually turtle commands like +, -, [, ])
- = axiom (initial string)
- = production rules applied in parallel
- Classic tree example
- Variables: (0=leaf, 1=branch segment)
- Constants: (push/pop turtle state → branch)
- Rules: ,
- After several iterations, yields branching structure with self-similar
fractal detail
- Fractal plant (angle ) rules
- F = draw forward, +/- = rotate ±angle, [ ] = push/pop
- Significance
- Concise rule set ⇒ visually rich organic forms
- Widely used for foliage, roads, lightning, coral, etc.
Search-Based Procedural Content Generation (SB-PCG)
- Idea: Skip explicit rules; instead define a fitness / heuristic that
scores output quality and let a search algorithm find high-scoring artifacts - Common algorithms
- Hill Climbing
- Simulated Annealing
- Genetic Algorithms / Evolutionary Search
- Reference: Togelius et al., “Search-Based Procedural Content Generation”
Heuristics & Energy Analogy
- Model generation as an optimization problem
- Energy ↔ negative fitness (lower better) or vice-versa
- Need
- Representation of content (parameter vector, tile grid, etc.)
- Neighbor function (small random tweak) ⇒ defines search space graph
- Fitness/energy function
Hill Climbing
- Greedy ascent (or descent) along best neighboring state
- Characteristics
- Simple, fast
- Local maxima/minima trap risk
- Mitigation
- Multiple random restarts
- Momentum variants (keep moving even if small decline, hoping to pass
narrow valleys)
- Visualization: “fitness landscape” with peaks; algorithm follows gradient
Simulated Annealing (SA)
- Metallurgy inspiration: heat explore, cool exploit
- Pseudocode essence
- random initial
- For
- random neighbor
- If \Delta E = E(new) - E(state) < 0 accept (better)
- Else accept with probability
- Track best-ever state as fallback
- Properties
- Can (in theory) reach global optimum if cooling schedule is
slow enough - Bad schedule ⇒ random walk or premature freezing
- Can (in theory) reach global optimum if cooling schedule is
Genetic Algorithms (GA)
- Population-based; loosely mirrors biological evolution
- Key steps each generation
- Selection / Reduce – keep top individuals
- Crossover – pair parents (prob. ∝ fitness) and recombine their genetic
representation to create offspring until population size (Y > X) - Mutation – with low probability, randomly alter parts of an individual
- Exploration vs exploitation
- Mutation ⇒ exploration (avoids local traps, but pure mutation = random
search) - Crossover ⇒ exploitation (refines existing good traits, but alone may
converge to suboptimal)
- Mutation ⇒ exploration (avoids local traps, but pure mutation = random
- Pros & Cons
- Moderate authoring burden (representation + fitness)
- Tunable balance between search breadth/depth
- Requires experience to choose mutation/crossover rates, map genotype
(bitstring, graph) to phenotype (level, texture)
Comparing Broad PCG Families
- Rule-based: Simple local rules, emergent complexity; hard to control
- Generative grammars: High quality, explicit control; heavy design & debugging effort
- Search-based: Minimal authorship (heuristic only); heuristic design is unintuitive for many
Constraint Satisfaction Problems (CSP)
- Alternative framing: specify constraints that must hold; solver finds
any assignment satisfying them - Terminology
- Variables / slots – empty placeholders (tiles, names, pixels)
- Domains / values – options each variable can take
- Constraints – relations over subsets of variables that restrict joint
assignments (e.g., adjacency, count limits, connectivity)
- Trivial example: name generator
- Variables:
- Domain: list of names
- Constraint: initial letters must differ
- General solving loop (resembles Wave Function Collapse)
- Initially each variable domain = all values
- Observe / Collapse – pick variable, assign one value (via heuristic or
entropy measure) - Propagate – remove incompatible values from neighbors via constraints
- Repeat until every domain has size (solution) or contradiction ⇒ backtrack
- Relation to search
- CSP = search with aggressive pruning (propagation) ⇒ typically faster than
naive heuristic search
- CSP = search with aggressive pruning (propagation) ⇒ typically faster than
Worked Example: 4×4 Dungeon Room
- Domain values: {1 Blank, 2 Wall, 3 Enemy, 4 Treasure}
- Constraints
- Path must exist from entrance to all treasures/enemies
- ≤1 treasure overall
- ≤2 enemies overall
- Enemies cannot be orthogonally adjacent
- Stepwise collapse/propagation illustrated: domains shrink from {1-4} to singletons
- Insight: If output looks bad, add or tweak constraints (vs tweaking heuristic weight)
Wave Function Collapse (WFC)
- Invented by Max Gumin (2016); popularised by gif demos & games like Caves of Qud
- Key insight: auto-derive constraints from a sample image or tilemap →
lowers authorial burden further
Big Ideas
- Pattern extraction – scan input for every tile patch
(e.g., ). Each unique patch = a pattern; frequency counts stored - Legal overlaps – for every relative offset (up, down, left, right), record
which pattern pairs appear in sample ⇒ propagator matrix
High-Level Algorithm
PatternsFromSample() # derive variables & domains
BuildPropagator() # precompute allowed adjacencies
while !Finished:
Observe() # choose lowest-entropy cell, collapse to pattern
Propagate() # eliminate incompatible neighbors (classic CSP)
Return output # visualize by stitching collapsed patterns
- Observe step often chooses value via weighted random (weights = pattern
frequencies) giving similarity to input - Larger neighborhood size ⇒ output more closely mimics source, but
computation heavier and variety lower
Novelty vs Classic CSP
- Novelty: automatic constraint learning from example + entropy-driven
observation order ("wave" = probability field over patterns) - Under the hood: pure CSP propagation (Arc Consistency, backtracking on
contradiction)
CSP versus Other Techniques
Pros
- Constraints more intuitive to author than numerical heuristics
- Often faster than search thanks to propagation
- WFC shows constraints can even be learned, reducing design labor
Cons - Requires content be expressible in formal logic
- Propagation cost > simple grammar rewrite (though usually acceptable)
Practical Guidance for Designers / Engineers
- Choose approach based on
- Desired authorial control vs procedural surprise
- Team’s familiarity with formal grammars, optimization, or logic
- Performance constraints (mobile vs PC, runtime vs offline pre-compute)
- Common workflow mix
- Prototype with WFC (fast results) → analyze failures → add explicit
constraints or grammar tweaks for edge cases - Combine grammar for macro layout + search/CSP to ensure
playability, difficulty pacing
- Prototype with WFC (fast results) → analyze failures → add explicit
- Remember debugging strategies
- Grammars: unit-test components & derivations
- Search: log fitness curve, visualize neighborhoods
- CSP: visualize domain sizes, heat-map remaining possibilities
Key Equations & Notation Recap
- Simulated annealing acceptance
- Entropy used in WFC (Shannon)
(select cell with lowest non-zero ) - L-system definition tuple
Further Resources & References
- Search-Based PCG survey – Togelius, Shaker, Nelson (ACM TOG)
- AI for Games (Millington & Funge) – Figures 7.2, 8.14, 8.16, 8.20 (mazes,
MST rooms) - Game Maker’s Toolkit video on Spelunky PCG (1:36 mark)
- WaveFunctionCollapse GitHub – https://github.com/mxgmn/WaveFunctionCollapse
- Procedural generation talks by Jeff Wilson (Georgia Tech)