Conceptio › Archive › arXiv CS
arXiv CS open access

Procedural Content Metageneration via Program Search and Continual Abstraction Discovery

2026 · arxiv_cs
arXiv CS · Papers · 2026
Open Source ↗Direct PDF ↓
machine-learningneural-networks
Conceptio Open Archive

Analyze & explore related research in Conceptio

Connect this record directly into your research workflow or AI agent. Explore citations across open repositories, query full text, and export structured proof bundles.

Matthew Siper

Ahmed Khalifa

Julian Togelius

Game Innovation Lab New York University New York, USA ms12010@nyu.edu

Institute of Digital Games University of Malta Msida, Malta ahmed@akhalifa.com

Game Innovation Lab New York University New York, USA julian@togelius.com

Lineage of a Generator Gen 1 · mutation

Gen 13 · crossover

Gen 19 · crossover best individual

Empty seed replaced with a full generator: player, key & exit placed on supported floor, diamonds & spikes scattered, BFS repair + jump-gap induction.

Conservative placement (gen 3) crossed with multi-strategy floor repair: 4-fallback corridor cascade + permissive BFS with fall-drift.

Adds aggressive raised-platform jump induction with reachability verification and a walk+fall BFS model; diversity jumps 0.52 0.81.

+ def place_on_support(tile, x, y): ... + def repair_path(tx, ty): # carve corridor + def can_reach(sx, sy, tx, ty): # BFS

+ def carve_floor(x, y): ... + def build_stairway(x, y1, y2): ... + def induce_jumps(): # keep reachability

+ def build_jump_platform(x, y, gap_size=2): + def induce_jumps_aggressive(): ... + def is_standable(x, y): ...

+246 lines

+163 lines

+124 lines

0.8 0.650

0.6

Fitness

arXiv:2608.17947v1 [cs.AI] 18 Aug 2026

Procedural Content Metageneration via Program Search and Continual Abstraction Discovery

0.4

0.323

0.520

0.472 gen 3

0.2

seed (empty generator)

0.0

0.087 0

1

3

Generation

level sampled from gen 1

13

19

level sampled from gen 13

level sampled from gen 19

Fig. 1. Evolutionary lineage of the best Dangerous Dave generator. Each callout summarizes the code introduced at that step, and each image shows a level sampled from the corresponding generator. The new generators just need to be better than the starting seed to be accepted in the archive.

Abstract—Large language models can generate executable programs, which makes it possible to search directly over procedural content generators rather than individual levels. We study this approach in Sokoban, Zelda, Dangerous Dave, and Lode Runner. Each run evolves complete Python generators through languagemodel mutation and crossover. We introduce Continual Abstraction Discovery, or CAD, which extracts reusable primitives from high-fitness programs into a run-specific

979-8-3315-9476-3/26/$31.00 ©2026 IEEE

helper module. A 2 × 2 experiment crosses CAD with access to a fixed hand-written domain API. The completed data set contains 160 complete runs, with at least ten 50-generation runs in every cell. CAD raises mean final best fitness in all eight domain and API comparisons. Across all CAD runs, learned libraries are adopted by most later programs and repeatedly rediscover validation, reachability, and structural utilities. These results support that discovering reusable primitives improves evolutionary program search for content generators. Index Terms—Large Language Models, Procedural Content Generation, Level Generation, Program Synthesis, Code Generation, Genetic Programming, Evolutionary Search

I. Introduction Games that feature procedurally generated content usually rely on custom generators tailored to the mechanics, constraints, and design goals of a particular game. Creating these generators requires substantial engineering and repeated evaluation. Procedural content generator generation seeks to automate this process by searching for the generator itself rather than directly searching for individual artifacts [1]. This process can also be called procedural content metageneration. Existing approaches learn generators through evolutionary computation, machine learning, or reinforcement learning [1]–[5]. Many produce neural or otherwise opaque representations. Such generators can be effective, but they are difficult to inspect and edit. Executable source code offers a different representation. A program can be compiled, tested, modified by a designer, and reused outside the search system. Large language models are increasingly capable of writing and revising code. This makes it possible to place a codegenerating model inside an evolutionary loop. A candidate generator is executed, its levels are evaluated, and the resulting feedback guides later mutation and crossover calls. Related systems have used language models as variation operators for executable program search [6]–[8]. Procedural content metageneration presents a demanding instance of this problem since the quality of a generator can only be judged through repeated execution and behavioral evaluation. Search over raw code fixes the vocabulary available to variation. As programs grow, useful routines for reachability, entity normalization, repair, and structural construction may be independently recreated in many candidates. A hand-written helper API can expose reusable operations, although its vocabulary remains fixed before search begins. We introduce Continual Abstraction Discovery, or CAD, as a way to let the set of executable primitives increase during a run. CAD extracts reusable utilities from high-fitness programs into a run-specific helper module, validates the module, and refactors programs to use the learned utilities when this can be done safely. We evaluate the system on four domains from the PCG Benchmark [9]. The main study crosses a fixed domain helper API with CAD. This design separates access to a reusable vocabulary from the discovery of a vocabulary during search. We make four contributions.

•

runs. We show positive CAD effects across the 4 game domains and provide an analysis of the learned-library growth, adoption, and primitives. II. Related Work

A wide variety of computational methods have been applied to generating game content, in particular, game levels. These range from evolutionary computation [10] and constraint satisfaction [11] to supervised learning [3] and reinforcement learning [4]. Published games typically rely on constructive generators [12] specialized for the particular game, which limits reusability. Recently, researchers have applied LLMs to generating game levels [13]–[20]. The results are mixed. In order to represent a game level in a way that an LLM can understand, it needs to be converted to a string somehow. Typically, this means representing the level as a sequence of rows, where each character represents a tile. Initial results showed that fine-tuning generators on levels from particular games improved generation capacity, and that more modern and recent LLMs outperform older and smaller ones on level generation (as on most other things) [13]. However, current LLMs have limitations when generating game content, particularly spatial content such as levels. For example, LLMs tend to have problems ensuring that clear paths exist from point A to point B, and that the right number of items and characters of various types exist (such as exactly one player character, and the same number of doors as keys). Other pathologies of LLM-generated game levels seem to originate in weak spatial reasoning [13], [16], [18]. One of the major use cases of LLMs is code generation. Software engineering is currently being transformed by the emergence of LLMs that can produce working code from an informal specification. However, in many cases, you know what you want but not exactly how to get there. In those cases, you can create a fitness function measuring what you want, and then embed the code-generating LLM inside an evolutionary loop. This approach was popularized by AlphaEvolve [6], which, among other things, discovered an improved matrix multiplication algorithm, and similar methods have also been applied to creating scientific data analysis solutions and financial trading policies [7], [8]. Using LLMs to generate code inside an evolutionary loop can be seen as a form of genetic programming, which is the application of evolutionary computation to program synthesis [21], [22]. From this perspective, the LLM is carrying out the mutation and/or crossover operations in • We demonstrate LLM-driven evolutionary search over executable Python-level generators in Sokoban, Zelda, genetic programming. An important concept in genetic programming is Automatically Defined Functions, where a Dangerous Dave, and Lode Runner. set of callable functions is evolved in tandem with the main • We introduce CAD, a search-time mechanism that extracts, validates, and reuses helper functions discov- program, constituting a form of evolved abstraction [23]. ered within high-fitness programs. Continual abstraction discovery (CAD) is also related • We present a 2 × 2 study that separates CAD from to recent work on agent skill libraries, where reusable code access to a fixed expert API across 160 completed or behaviors are stored for later use, such as Voyager’s

skill library for Minecraft agents [24]. The key difference Every candidate passes through a robustness pipeline. in our setting is that the library is not a record of solved The program must compile and complete at least three tasks or manually exposed skills, but a continually updated of five smoke-test executions. A failure triggers an LLM set of helper functions extracted from high-performing correction call conditioned on the error. The mutation or generator programs during an ongoing evolutionary search. crossover cycle is attempted up to four times. A generation CAD, therefore, functions as a search-time representation that never produces a valid candidate is recorded with mechanism. It changes the program primitives available to fitness zero. Parse failures, compile failures, smoke-test later mutation, crossover, and refactoring calls while the failures, and timeouts are logged separately. After every generation, an LLM reflection call appends population is still being optimized. Returning to PCG research, work on PCG through a structured entry to a per-run memory file. Each entry machine learning [3], including reinforcement learning [4], records the attempted change, the observed result, and generally learns generators, which are then used to generate global lessons for later search. The accumulated global game content. However, they mostly learn neural networks lessons are supplied to subsequent mutation and crossover or other types of black box representations, which do not calls in place of raw per-parent evaluation feedback. allow for easy human interpretation and editing. There are C. Evaluation and Fitness exceptions to this, where evolution or similar methods Each candidate is executed 30 times. The sampled levels are used to learn generators expressed in a symbolic are evaluated with the quality, validity, and diversity representation [1], [2]. The approach presented in this paper functions supplied by the PCG Benchmark [9]. We compute can be seen as procedural procedural content generator the fraction of fully valid levels, the mean benchmark generation, or procedural content metageneration, using quality, and the mean pairwise diversity over all sampled a search-based approach in generator space enhanced by level pairs. Fitness is defined as LLM code writing. A key invention beyond the combination 1 of tool and domain is the Continual Abstraction Discovery F = (V + Q + D) , (1) mechanism, which is similar to Automatically Defined 3 Functions but using LLM-driven evolution. where V is the fraction of levels whose quality is equal to 1.0, Q is mean level quality, and D is mean pairwise III. Method diversity. All terms lie in [0, 1]. Candidates that crash or A. Program Representation fail validation receive fitness zero. Each individual is a complete Python program defining D. Continual Abstraction Discovery generate(context_dict). The function receives the grid One of the contributions of this work is continual dimensions and an initial level and returns a tile grid. A program may contain local functions, constants, and layout abstraction discovery. Starting at generation 8 and at routines, while the public interface remains fixed across five-generation intervals thereafter, the system identifies the population. This representation allows every candidate programs at or above the 75th percentile of fitness within to be compiled, executed, inspected, and evaluated with the run. An LLM extraction call analyzes these programs and proposes reusable utility functions from that code. An the same harness. All runs begin from one minimal seed program. The extracted function must compile and pass a smoke test. seed contains tile constants and a blank or copied initial The system allows up to two compile corrections. The grid, with no generation logic. It is evaluated once and source program is then refactored to call the new helpers. becomes the root of the archive. The archive retains every A refactor is accepted only when the refactored program matches the original program on a fixed-seed behavioral test successfully evaluated individual. (the generated levels has similar behavior characteristics). B. Evolutionary Search Accepted refactors are re-evaluated and their archive entries At each generation, one parent is sampled from the are updated. Refactoring is disabled when its success rate archive with fitness-proportional probability. Fitness values remains below 30% after a ten-generation warmup.The are floored at 10−6 before normalization. When the archive extracted functions are recorded in a system library that contains at least two individuals, crossover is selected with can be used by programs during evolution. probability 0.25. A second parent is then sampled with the IV. Experimental Design same rule. Failed crossover attempts fall back to mutation. Mutation and crossover are performed by an LLM. The A. Domains We evaluate four tile-based domains from the PCG prompt contains the parent program or programs, a domain Benchmark: description, the accumulated run memory, and the fixed generator contract. Mutation asks for a strong behavioral • Sokoban: is a 5x5 Japanese puzzle game where the edit. Crossover asks the model to combine compatible mechplayer needs to push boxes to specific target locations. anisms from both parents into one complete executable • Zelda: is a 7x11 dungeon crawler inspired by dungeon program. rooms from the original The Legend of Zelda game.

The goal of the level is to get a key and reach the exit without getting killed by monsters. • Dangerous Dave:is a 7x11 platformer game based on game with the same name. The goal of the game is for the player to get a key and reach the goal without getting killed by spikes. • Lode Runner: is a 11x16 puzzle platformer game based on a game with the same name. The goal of the game is for the player to collect all the gold and avoid getting caught by the enemies. The puzzle element is that the player can’t jump; they can only navigate the level by walking, digging through the floor, climbing ladders, and moving through ropes. For all these problems, we use the framework’s quality and diversity outputs. These outputs mainly use an A∗ solver to compute quality and use some behavioral and visual characteristics to compute diversity. Each call to a generator is limited to two seconds.

TABLE I Most frequently rediscovered learned abstractions across all CAD runs. Semantically equivalent functions are grouped, and Total Calls is the aggregate call count in the supplied audit. The complete audit is provided in the supplementary source.

Abstraction

Category

Total Calls

In Bounds Entity Count Normalization Ensure One Player Grounded Empty Cells Flood Fill Deep Copy Grid Find Player Position Ladder Placement Reachability BFS Relocate Unreachable Entities Connected Components Find Tiles

Other Validation Validation Utility Other Other Utility Structural Validation Validation Other Other

4,693 3,848 2,611 465 569 118 2,370 768 344 320 297 574

B. Factorial Conditions in Sokoban. Under the fixed API, the largest separation The main study tests 4 experiments that test the occurs in Lode Runner, where the CAD trajectory continues usefulness of having helper functions. To assess this, we improving while the no-CAD trajectory plateaus earlier. have two parameters that we are testing: All experiments favor CAD, which gives an exact two-sided • CAD: allowing the LLM to abstract programs and sign-test result of p = 0.008. An interesting outcome is generate helper functions as explained in section III-D. that only in Zelda, having a predefined API is better than • Expert API: having a group of hand-designed having no API to start with. We believe that the simplicity helper functions that are commonly used in of the problem benefited from the available functions, as PCG. The library contains validated primitives the other problems need more complex solvers than just for entity normalization, reachability, repair, and pathfinding and counting objects. domain-specific structural operations. Examples Figure 3 shows example of the generate levels for the best include connect_floors_with_ladder and generator for every game. Looking at the generated levels, ensure_one_player. The expert API tests whether we can notice that the visual diversity of levels, especially providing helper functions will be enough or CAD is in Dangerous Dave, is lower with CAD than without. We still needed. believe the reason behind that is that the diversity metric Every experiment uses the same minimal seed, evolutionary of Dangerous Dave only cares about the trajectory of the search, reflection memory, correction pipeline, evaluation player solution and not the visual diversity, which makes budget, and fitness function. We ran each experiment 10 these levels diverse even if they look similar. times for 50 generations. C. LLM and Execution Configuration All language-model calls use GLM 5.2. Mutations uses temperature 0.2. Correction and refactoring use temperature 0.1. The mean 50-generation run used approximately 9.5 million input tokens and 2.6 million output tokens. The mean cost was approximately $25 and the mean wall-clock time was approximately 2.5 hours. Each run stores pergeneration statistics, program sources, evaluation analyses, prompts, valid rendered levels, the evolution tree, reflection memory, helper-module snapshots for CAD conditions, and token and timing logs.

B. Learned-Library Growth and Adoption

Figure 4 shows that the learned libraries expand sharply during the early extraction cycles and then stabilize across all four domains. Once helper use begins, the reported adoption rate remains above roughly 80% for most later generations, while programs contain approximately 15 to 20 helper calls on average. The program-size comparison shows a second effect. In the Base condition, CAD reduces the mean length of the best program from roughly 370 lines to roughly 296 lines. Programs with the fixed API are already much shorter, at V. Results roughly 160 lines without CAD and 173 lines with CAD. A. Cross-Domain Search Performance This pattern is consistent with reusable logic moving out Figure 2 shows a positive endpoint difference for CAD of the main generator program. The fixed API absorbs in every domain. The magnitude varies across games. much of this complexity from the beginning, leaving less The Base contrast is small in Lode Runner and larger source-length reduction for CAD.

Continual learning improves search in every domain (solid = CL, dashed = no CL; mean ± SE)

Sokoban

Zelda

0.8 Best-so-far fitness

Dangerous Dave

1.0

0.9

Lode Runner

0.7

0.40

0.9

0.6

0.8

0.5

0.30

0.7

0.4

0.25

0.35

0.7 0.6 0.5 0.4

0.6

0.3

Base+CL (n=10)

Base+CL (n=19)

Base (n=10)

Base (n=10)

0.2

API+CL (n=10)

0.5

10

20 30 Generation

40

0

10

20 30 Generation

40

Base (n=10)

0.10

API+CL (n=11)

50

Base+CL (n=11)

Base (n=12)

0.1

API (n=10)

50

0.15

Base+CL (n=12)

0.2

API+CL (n=10)

API (n=11)

0

0.20

0.3

API+CL (n=10)

API (n=12)

0

10

20 30 Generation

40

API (n=10)

50

0

10

20 30 Generation

40

50

Fig. 2. Best-so-far fitness over 50 generations in each domain. Solid lines show CAD and dashed lines show no CAD. Shading reports mean ± standard error, and legends show the completed run count for each cell. The plot uses CL as the implementation label for CAD. baseline_no_cl

baseline

Zelda

Zelda

Sokoban

Sokoban

Dangerous Dave

Dangerous Dave

Lode Runner

Lode Runner

Fig. 3. Representative generated levels from the Base condition without CAD on the left and with CAD on the right. Rows show Zelda, Sokoban, Dangerous Dave, and Lode Runner. Each row contains four samples. The images illustrate output structure and are not used as evidence of playability, which is measured through benchmark execution.

C. Repeated Abstraction Discovery Table I shows a recurring core of validation, reachability, and structural operations. in_bounds and Entity Count Normalization each appear in 28 of the CAD runs. Ensure One Player and Grounded Empty Cells appear in 24 runs. Find Player Position, Ladder Placement, Reachability BFS, Relocate Unreachable Entities, and connected_components each appear in 20 runs. CAD discovers both a shared set of broadly useful operations and a large tail of run-specific utilities. The repeated functions correspond to common generator requirements such as entity counts, traversal, connectivity, and tile placement. Figure 5 shows the effect of CAD on the generated levels. For Lode Runner, it abstracted a function that put enemies beside gold to make the levels harder, where the next upcoming generators will make sure to place enemies beside the gold. For Dangerous Dave, something similar happen where CAD added a function that adds enemies/harmful objects around the solution path to make the level harder, which affects the upcoming generators that start adding

these objects. VI. Discussion A. Cross-Domain Evidence The results show that one evolutionary program-search framework can operate across four games with different grid sizes and benchmark evaluators. CAD improves the mean fitness in every domain. The largest effect appears in Lode Runner when the expert API is available. Search already has domain-specific callable routines in this condition, yet the run-specific learned vocabulary still improves the best fitness. The three other API cells also favor CAD, although their tests do not reach p < 0.05. B. CAD as Representation Adaptation CAD changes the operations available to later variation calls during search. The executable substrate remains Python, while the effective vocabulary grows as helpers are extracted. The mechanism measurements show that the resulting functions are adopted by later programs and called

The CL mechanism: libraries grow, are adopted, and absorb complexity

A. Learned-library growth

B. Library adoption (all CL runs, n=93)

15.0 12.5 10.0 7.5 5.0

Sokoban (n=20) Zelda (n=29)

2.5

15

60

10

40 20

5

0

0

Helper calls per program

80

Dangerous Dave (n=23) Lode Runner (n=21)

0.0 0

10

20 30 Generation

40

50

0

10

20 30 Generation

40

50

Best-program length (lines)

20

17.5 Adoption (%)

Library size (functions)

C. Program size of best solution 400

100

20.0

Base

350

API

300 250 200 150 100 50 0

Base

API

Base

no CL

API

CL

Fig. 4. Mechanism measurements across CAD runs. Panel A shows learned-library growth by domain. Panel B shows the reported adoption rate and helper calls per program. Panel C compares the line count of the best solution under Base and API prompting with and without CAD. Shading and error bars report uncertainty around the mean. The plot uses CL as the implementation label for CAD.

Applying a Library Primitive (CAD) to a Level Level (before)

Library primitive

Level (after primitive applied)

guard_gold

Lode Runner

Place an ENEMY adjacent to a GOLD on a standable cell. def guard_gold(grid, w, h, golds, enemies): for gx, gy in golds[:len(enemies)]: for dx, dy in [(-1,0),(1,0),(0,-1)]: nx, ny = gx+dx, gy+dy if grid[ny][nx] == EMPTY \ and solid_below(nx, ny): grid[ny][nx] = ENEMY return

helper_loderunnertile · used in gen_015.py

place_enemies_away_from_critical

Zelda

Place enemies on reachable floor tiles, avoiding the player key door critical path. p_to_k = bfs_path(px,py, kx,ky, level) k_to_d = bfs_path(kx,ky, dx,dy, level) critical = set(p_to_k) | set(k_to_d) level = place_enemies_away_from_critical( level, target_enemies, (px, py), critical, width, height)

helper_zelda · call site in gen_013.py

tile changed by the primitive

· white dots: critical path (Zelda)

Fig. 5. Two primitives from the evolved helper library, each applied to a level (before, left; after, right; changed tile circled): guard_gold places an enemy beside a gold piece in Lode Runner, and place_enemies_away_from_critical places an enemy off the player → key → door critical path (white dots) in Zelda.

repeatedly. Commonly recurring helper functions address patterns such as entity normalization, player placement, reachability, connectivity, and structural construction. These are central operations in tile-based generator design. The large run-specific tail also indicates that CAD does not converge to one fixed library. It combines a recurring core with utilities shaped by the domain and search history.

does not establish that shorter programs are intrinsically better. It shows that reusable callable vocabularies can offload logic out of the main generator, which may make later edits easier to express and preserve.

The program-size analysis supports a related interpretation. CAD shortens the best Base programs, while the fixed API already yields compact source libraries. This

Solvability, benchmark quality, and diversity do not fully capture visual style, pacing, novelty, or designer intent. Human review remains important when the desired

C. Practical Implications and Limitations

generator has aesthetic or experiential goals that are absent from the benchmark. The compute cost is substantial. A typical 50-generation run uses millions of tokens of a near-frontier-class model and several hours of wall-clock time. CAD introduces additional extraction, correction, and refactoring calls. Future work should evaluate fitness gains against token use and wall-clock cost. CAD is evaluated as a complete pipeline. Helper extraction, module correction, and source refactoring are not separated into individual ablations. The mechanism traces show growth, adoption, and program-size changes, although they do not isolate the causal contribution of each component. Additional work should test longer budgets, other language models, non-tile content, cross-run library transfer, and designer editing of discovered helpers. VII. Conclusion We presented an evolutionary program-search system that uses an LLM to generate executable procedural content generators. The evaluation covers Sokoban, Zelda, Dangerous Dave, and Lode Runner, where each one is run 10 times. Continual Abstraction Discovery raises the mean final best fitness regardless you start with an empty helper library or expert helper functions. Across the CAD runs, learned libraries grow, are adopted by later programs, and repeatedly recover validation, reachability, and structural utilities. The results support CAD as a practical mechanism for adapting the searchable program vocabulary during procedural content metageneration. References [1] M. Kerssemakers, J. Tuxen, J. Togelius, and G. N. Yannakakis, “A procedural procedural level generator generator,” in 2012 IEEE Conference on Computational Intelligence and Games (CIG). IEEE, 2012, pp. 335–341. [2] A. Khalifa and J. Togelius, “Multi-objective level generator generation with marahel,” in Proceedings of the 15th International Conference on the Foundations of Digital Games, 2020, pp. 1–8. [3] A. Summerville, S. Snodgrass, M. Guzdial, C. Holmgård, A. K. Hoover, A. Isaksen, A. Nealen, and J. Togelius, “Procedural content generation via machine learning (pcgml),” IEEE Transactions on Games, vol. 10, no. 3, pp. 257–270, 2018. [4] A. Khalifa, P. Bontrager, S. Earle, and J. Togelius, “Pcgrl: Procedural content generation via reinforcement learning,” in Proceedings of the AAAI Conference on Artificial Intelligence and Interactive Digital Entertainment, vol. 16, no. 1, 2020, pp. 95–101. [5] J. Liu, S. Snodgrass, A. Khalifa, S. Risi, G. N. Yannakakis, and J. Togelius, “Deep learning for procedural content generation,” Neural Computing and Applications, vol. 33, no. 1, pp. 19–37, 2021. [6] A. Novikov, N. Vũ, M. Eisenberger, E. Dupont, P.-S. Huang, A. Z. Wagner, S. Shirobokov, B. Kozlovskii, F. J. Ruiz, A. Mehrabian et al., “Alphaevolve: A coding agent for scientific and algorithmic discovery,” arXiv preprint arXiv:2506.13131, 2025. [7] E. Aygün, A. Belyaeva, G. Comanici, M. Coram, H. Cui, J. Garrison, R. J. A. Kast, C. Y. McLean, P. Norgaard, Z. Shamsi et al., “An ai system to help scientists write expert-level empirical software,” arXiv preprint arXiv:2509.06503, 2025. [8] M. Siper, A. Khalifa, L. Soros, M. Nasir, and J. Togelius, “Profit: Program search for financial trading,” Available at SSRN 5889762, 2025.

[9] A. Khalifa, R. Gallotta, M. Barthet, A. Liapis, J. Togelius, and G. N. Yannakakis, “The procedural content generation benchmark: an open-source testbed for generative challenges in games,” in Proceedings of the 20th International Conference on the Foundations of Digital Games, 2025, pp. 1–12. [10] J. Togelius, G. N. Yannakakis, K. O. Stanley, and C. Browne, “Search-based procedural content generation: A taxonomy and survey,” IEEE Transactions on Computational Intelligence and AI in Games, vol. 3, no. 3, pp. 172–186, 2011. [11] A. M. Smith and M. Mateas, “Answer set programming for procedural content generation: A design space approach,” IEEE Transactions on Computational Intelligence and AI in Games, vol. 3, no. 3, pp. 187–200, 2011. [12] N. Shaker, J. Togelius, and M. J. Nelson, Procedural Content Generation in Games: A Textbook and an Overview of Current Research. Springer, 2016. [13] G. Todd, S. Earle, M. U. Nasir, M. C. Green, and J. Togelius, “Level generation through large language models,” in Proceedings of the 18th International Conference on the Foundations of Digital Games, 2023, pp. 1–8. [14] S. Sudhakaran, M. González-Duque, M. Freiberger, C. Glanois, E. Najarro, and S. Risi, “Mariogpt: Open-ended text2level generation through large language models,” Advances in Neural Information Processing Systems, vol. 36, pp. 54 213–54 227, 2023. [15] C. Hu, Y. Zhao, and J. Liu, “Game generation via large language models,” in 2024 IEEE Conference on Games (CoG). IEEE, 2024, pp. 1–4. [16] R. Gallotta, G. Todd, M. Zammit, S. Earle, A. Liapis, J. Togelius, and G. N. Yannakakis, “Large language models and games: A survey and roadmap,” IEEE Transactions on Games, 2024. [17] P. Sweetser, “Large language models and video games: A preliminary scoping review,” in Proceedings of the 6th ACM Conference on Conversational User Interfaces, 2024, pp. 1–8. [18] M. U. Nasir, S. James, and J. Togelius, “Word2world: Generating stories and worlds through large language models,” arXiv preprint arXiv:2405.06686, 2024. [19] M. U. Nasir and J. Togelius, “Practical pcg through large language models,” in Proceedings of the IEEE Conference on Games, 2023. [20] Z. Jiang, S. Earle, A. Khalifa, and J. Togelius, “Agentic pcg: Procedural content generation via tool-using llms,” Available at SSRN 6499021, 2026. [21] R. Poli, W. B. Langdon, N. F. McPhee, and J. R. Koza, A Field Guide to Genetic Programming, 2008. [22] J. R. Koza, Genetic programming: A paradigm for genetically breeding populations of computer programs to solve problems. Stanford University, Department of Computer Science Stanford, CA, 1990, vol. 34. [23] ——, “Hierarchical automatic function definition in genetic programming,” in Foundations of Genetic Algorithms. Elsevier, 1993, vol. 2, pp. 297–318. [24] G. Wang, Y. Xie, Y. Jiang, A. Mandlekar, C. Xiao, Y. Zhu, L. Fan, and A. Anandkumar, “Voyager: An open-ended embodied agent with large language models,” arXiv preprint arXiv:2305.16291, 2023.

Appendix The following figures document the LLM call sites used by the evolutionary loop. Template variables such as {domain_block}, {eval_feedback}, and {parent_program} are filled at runtime.

Prompt 1: Mutation Fires every generation with ∼75% probability (when crossover is not selected). All four conditions. System Message You are a Python level-generator mutation engine. Output only valid Python code.

For CL conditions, once the helper module has functions, this is extended with: You may use ‘from helper_loderunnertile_<uuid> import *‘ to access reusable utility functions discovered during this run.

User Prompt You are mutating a Python level-generator function. Task: Produce ONE new Python ‘‘generate(context_dict)‘‘ function that differs from the parent in a meaningful way. Goal: Create a candidate program that produces better-quality levels for the target domain. The function receives a context dict with keys ‘‘"level"‘‘ (2-D list of ints), ‘‘"width"‘‘ (int), and ‘‘"height"‘‘ (int). It must return a 2-D list of ints of the same shape. CRITICAL –- use the input level: The function MUST start from ‘‘context_dict["level"]‘‘ –- the 2-D grid provided as input. Use it as the base grid to modify (e.g. clear entities, reshape platforms, place new items). Do NOT create a new empty grid from scratch (e.g. ‘‘[[0]*width for _ in range(height)]‘‘). A typical first step is to copy the input level and strip entities you want to re-place: level = [row[:] for row in context_dict["level"]] height = context_dict["height"] width = context_dict["width"] You may use any Python standard-library or numpy code. The function will be ‘‘exec()‘‘-ed so all imports must be inside the function or at module top-level. Mutation strength: strong Interpretation: - light: tweak 1-2 numeric constants, add/remove a small code block - medium: restructure a loop, change strategy for a subtask - strong: rewrite larger sections but keep the overall generation approach Do not: - import or use ‘‘sketchnl‘‘, ‘‘sketchnl.runtime‘‘, or any ‘‘rt.*‘‘ functions - output commentary, explanations, or markdown fences - output anything other than valid Python source code - define classes; only define a ‘‘generate‘‘ function (helper functions ok) - hardcode level layouts: the function must use the input grid and algorithmic logic to produce varied outputs on every call - ignore the input level: NEVER create a blank grid from scratch –- always start from ‘‘context_dict["level"]‘‘ and modify it - change the function signature: it MUST be ‘‘def generate(context_dict)‘‘ - change the return type: MUST return a 2-D list of ints Manipulate the 2-D tile grid directly using standard Python (loops, list comprehensions, random, numpy, collections, etc.). Refer to tiles by their integer IDs as listed in the domain block below. {domain_block} {eval_feedback} Parent program: ‘‘‘python {parent_program} ‘‘‘

Fig. 6. Mutation prompt. The prompt specifies the generate(context_dict) contract, domain constraints, run memory, available helper APIs, mutation strength, and the parent program.

Prompt 2: Crossover Fires with ∼25% probability when the archive has ≥2 agents. All four conditions. System Message You are a Python level-generator crossover engine. Output only valid Python code.

Same CL extension as mutation when applicable.

User Prompt You are performing crossover between two Python level-generator functions. Task: Produce ONE child Python ‘‘generate(context_dict)‘‘ function that combines ideas from both parents. Goal: Create a child that produces better-quality levels by combining the strategies of both parents. The function receives a context dict with keys ‘‘"level"‘‘ (2-D list of ints), ‘‘"width"‘‘ (int), and ‘‘"height"‘‘ (int). It must return a 2-D list of ints of the same shape. CRITICAL –- use the input level: The function MUST start from ‘‘context_dict["level"]‘‘ –- the 2-D grid provided as input. Use it as the base grid to modify (e.g. clear entities, reshape platforms, place new items). Do NOT create a new empty grid from scratch (e.g. ‘‘[[0]*width for _ in range(height)]‘‘). A typical first step is to copy the input level and strip entities you want to re-place: level = [row[:] for row in context_dict["level"]] height = context_dict["height"] width = context_dict["width"] You may use any Python standard-library or numpy code. Recombination strength: strong Interpretation: - light: take constants / parameters from both parents - medium: combine strategies or code sections from both - strong: larger recombination but keep functional correctness Do not: - import or use ‘‘sketchnl‘‘, ‘‘sketchnl.runtime‘‘, or any ‘‘rt.*‘‘ functions - output commentary, explanations, or markdown fences - output anything other than valid Python source code - define classes; only define a ‘‘generate‘‘ function (helper functions ok) - hardcode level layouts: the function must use the input grid and algorithmic logic to produce varied outputs on every call - ignore the input level: NEVER create a blank grid from scratch –- always start from ‘‘context_dict["level"]‘‘ and modify it - change the function signature: it MUST be ‘‘def generate(context_dict)‘‘ - change the return type: MUST return a 2-D list of ints Manipulate the 2-D tile grid directly using standard Python (loops, list comprehensions, random, numpy, collections, etc.). Refer to tiles by their integer IDs as listed in the domain block below. {domain_block} {eval_feedback} Parent A: ‘‘‘python {parent_a} ‘‘‘ Parent B: ‘‘‘python {parent_b} ‘‘‘

Same {domain_block}, {eval_feedback}, and CL helper block as Prompt 1.

Fig. 7. Crossover prompt. The prompt receives two parent programs and asks the model to combine compatible mechanisms into a complete child program.

Prompt 3: Correction Fires when a mutation/crossover output fails to compile or crashes during smoke-testing. Up to 3 retries. All conditions. System Message You are a Python syntax-correction engine. Given a Python program and its error traceback, output ONLY the corrected Python source code. No commentary, no markdown fences. Do NOT import or use sketchnl, sketchnl.runtime, or any rt.* functions. Manipulate the tile grid directly using standard Python.

User Prompt The following Python program failed to compile or run.

Fix it.

Program: ‘‘‘python {code} ‘‘‘ Error: {error}

Fig. 8. Correction prompt. The prompt is used when a mutation or crossover output fails to compile or crashes during smoke testing. It receives the broken code and the error traceback.

Prompt 4: Memory Reflection Fires after every generation in all four conditions (memory is enabled by default). System Message You are reflecting on one iteration of an evolutionary level-generator search. Be concise and specific. Output markdown only –- no commentary outside the requested format.

User Prompt You are maintaining a memory file for an evolutionary level-generator search. After each generation you must write a short structured entry summarising what happened and update the global learnings. Here is the memory so far (may be empty on the first generation): --{existing_memory} --This generation: - Generation: {generation} - Mechanism: {mechanism} - Parent: {parent_id} (fitness {parent_fitness:.4f}) - Child fitness: {child_fitness:.4f} (delta {fitness_delta:+.4f}) Child evaluation: {eval_feedback_text} Child code: ‘‘‘python {child_code} ‘‘‘ Write TWO sections of markdown (nothing else): 1. A generation entry with this exact format: ## Gen {generation} ({mechanism} from {parent_id}, fitness {child_fitness:.4f}) - Parent: {parent_id} (fitness {parent_fitness:.4f}) - Tried: <1-sentence description of the key change vs parent> - Eval: <copy the tier scores and FOCUS marker from the eval above> - Failures: <top 3 failure modes with counts> - Learned: <1-2 sentences on what this result teaches us> - Code structure: 1. <step 1> 2. <step 2> ... 2. An updated Global Learnings section: ## Global Learnings - <bullet 1> - <bullet 2> ... Keep global learnings to 3-7 bullets. Update them based on the full history –- drop stale insights, reinforce confirmed ones, add new ones.

{eval_feedback_text} is rendered from per-child analysis: Parent evaluation (30 attempts): fitness (quality+diversity+valid%)/3: 0.4567 mean quality: 0.7234 intra-diversity: 0.3456 valid levels (quality=1): 30% Quality tiers (each must reach 1.0 to unlock the next): Tier 1 –- entity counts: 1.00/1.00 (passed) Tier 2 –- exploration: 1.00/1.00 (passed) Tier 3 –- play stats: 0.85/1.00 <-- FOCUS HERE (gold_collected=6/8, ...) Tier 4 –- decoration: 0.00/1.00 (blocked) Top failure modes: unreachable_gold: 12/30 low_used_tiles: 8/30 Goal: increase the percentage of valid levels (currently 30.0%)

Fig. 9. Memory reflection prompt. The prompt maintains a structured per-run memory with generation observations and global lessons for later variation calls.

Prompt 5: CL Helper Extraction Fires every 5 generations starting at generation 8 for CL conditions only (baseline + CL, gen + CL). One call per high-fitness program in the window. System Message You are a code refactoring engine that extracts reusable primitives from Python programs into a shared helper module. Output ONLY valid Python source code –- no commentary, no markdown fences.

User Prompt Below is a Python level-generator program ({program_id}), followed by the current contents of a shared helper module. Your task: extract reusable DOMAIN UTILITIES from this program into the shared helper module. Think of the helper module as a curated API of composable building blocks –- like a standard library for level generation. The goal is to provide validation, repair, reachability, and grid-primitive functions that ANY generation strategy can benefit from. Do NOT extract generation strategies or high-level orchestration logic. What to extract (priority order): TIER 1 –- Validation & repair (highest priority): - Entity reachability checking and relocation - Entity count normalization (ensure exactly N of tile type) TIER 2 –- Domain-aware primitives: - Grid search utilities - Complete nested function definitions inside generate() that implement a domain utility. Promote them to module-level with grid/height/width as explicit arguments. TIER 3 –- Generic algorithms (only if non-trivial, 15+ lines): - Pathfinding (A*, Dijkstra) with domain-specific movement rules - Constraint solvers that validate placement decisions Good examples (EXTRACT these): - ensure_one_player(level, height, width) - grounded_empty_cells(grid, height, width) - normalize_entity_count(level, height, width, tile, target) BAD examples (NEVER extract): - The entire generate() body or its top-level orchestration logic - clear_tile(grid, tile) –- trivial single loop - solidify_bottom_row(grid, width) –- trivial single row fill Each function should be 5-40 lines of body code (excluding docstring). Already extracted in this batch (do NOT duplicate these): {already_extracted} Rules: - Output the COMPLETE updated helper file (not just the new additions). - PRESERVE every existing function and constant –- never remove or rename. - Append new functions at the end of the file. - Each new function MUST have a Google-style docstring with: (1) a one-line summary, (2) an Args section listing every parameter with its type, (3) a Returns section, (4) an Example section. - Use Python type hints in the function signature. - Keep the module self-contained: only stdlib imports –- no numpy. - If no suitable domain utilities exist, return the file UNCHANGED. --- Program ({program_id}) --‘‘‘python {program_code} ‘‘‘ --- Current helper module ({helper_name}) --‘‘‘python {helper_contents} ‘‘‘ Output the complete updated helper module:

Fig. 10. CAD helper extraction prompt. Starting at generation 8 and every five generations thereafter, the prompt extracts reusable domain utilities from programs in the top fitness quartile.

Prompt 6: CL Helper Correction Fires when a CL-extracted helper module fails to compile. Up to 2 retries. CL conditions only. System Message You are a Python syntax-correction engine. Given a Python helper module and its compilation error, output ONLY the corrected complete Python source code. No commentary, no markdown fences.

User Prompt The following Python helper module failed to compile. Fix the error and output the COMPLETE corrected module. Module: ‘‘‘python {code} ‘‘‘ Error: {error}

Fig. 11. CAD helper correction prompt. The prompt receives the helper module and its compile or smoke-test error.

Prompt 7: Refactor Fires with 25% probability after mutation/crossover when CL is active and the helper module has functions. CL conditions only. System Message You are a Python level-generator refactoring engine. Your goal is to simplify a generate() function by replacing inline code with calls to tested helper functions from the helper module. The refactored program MUST produce identical output to the original given the same input and random seed –- do not change any constants, random ranges, loop bounds, or algorithmic logic. Output only valid Python code –- no commentary, no markdown fences.

User Prompt Below is a level-generator program and a helper module with tested, reusable functions. Your task: refactor the program to USE the helper functions wherever they can replace equivalent inline logic. Rules: - Replace inline code with helper calls where the helper does exactly the same thing. Do NOT change constants, random ranges, or loop bounds. Prefer shorter, cleaner code. - Do NOT change the overall generation strategy or algorithm. - Do NOT remove logic that has no helper equivalent –- leave it as-is. - Use ‘‘from <helper_module> import *‘‘ at the top and call functions directly. - The refactored program must still define ‘‘def generate(context_dict)‘‘ and return a valid level grid. - If no helpers are applicable, return the program unchanged. {eval_feedback_block} Parent program: ‘‘‘python {parent} ‘‘‘ {helper_block} Output the complete refactored program:

Fig. 12. Refactoring prompt. The prompt rewrites a generator to replace duplicated inline logic with calls to tested helper functions.

Prompt 8: Missing Helper Scaffold Fires when a generated program imports a function that does not exist in the CL helper module. CL conditions only. System Message You are a Python helper-function generator. Given the name of a missing function, the program that calls it, and the current helper module, output ONLY the new function definition (with a Google-style docstring including an Example section). No commentary, no markdown fences, no duplicate of existing code –- just the new ‘‘def‘‘.

User Prompt The following program tried to call ‘‘{helper_name}.{func_name}‘‘ but that function does not exist in the helper module yet. Program that uses it: ‘‘‘python {code} ‘‘‘ Current helper module ({helper_name}): ‘‘‘python {helper_source} ‘‘‘ Write ONLY the missing function ‘‘{func_name}‘‘ so it can be appended to the helper module. Follow the same style (type hints, Google docstring with Args/Returns/Example sections). The function must be self-contained using only stdlib imports already in the helper module.

Fig. 13. Missing-helper scaffold prompt. The prompt is used when a CAD program imports a helper that does not yet exist in its run-specific module.

Related documents

Record · ID 463222 · SHA-256 1d74b0cab8f07bee
Retrieved via Conceptio: every document is proof-bundled with source, license, and retrieval metadata.
Bring this data into your agent: Connect via MCP or API · 20 free credits to start · no card required
Read the Docs → · View CLI source on GitHub · Conceptio Open Knowledge Archive