System Design

    System Design by LLM, Creative Design by Human: A Case Study in Game Development

    Explore how large language models (LLMs) can automate system design while humans focus on creative aspects, illustrated through a real-world game development case study.

    Originsoft TeamEngineering Team
    February 19, 2026
    12 min read
    System Design by LLM, Creative Design by Human: A Case Study in Game Development

    # System Design by LLM, Creative Design by Human: A Case Study in Game Development

    The Game at a Glance

    Chessfall Ascension began as an experiment: what would happen if classical chess movement rules were placed inside a world that refuses to stay still? Instead of a stable board where players calmly calculate variations, the ground itself descends continuously, forcing decisions under pressure. The player must guide one or more chess pieces upward while capturing required flags before they disappear beyond the visible boundary of the screen. Unlike traditional chess, the objective is not to checkmate an opponent. The objective is survival, positioning, and strategic timing within a dynamic environment.

    Each chess piece moves exactly as it would in classical chess — rooks along ranks and files, bishops along diagonals, knights in L-shapes. The familiarity of these movement rules creates a sense of intellectual grounding. However, that familiarity quickly dissolves once the environment begins to evolve. Cells that appear safe may harden into barriers. Solid obstacles may decay and vanish. Portals can instantly relocate a piece across the board. Arrow tiles may push a piece in a direction the player did not intend. Certain tiles even alter the speed of gravity itself, subtly reshaping the rhythm of the entire level.

    As levels grow more advanced, the complexity increases. Players must coordinate multiple pieces, manage countdown-based transformations, and occasionally sacrifice a piece deliberately in order to progress. Decisions are often irreversible. There is no “undo” once gravity has shifted the board downward. The tension does not come from an opponent across the board; it comes from time itself.

    Chessfall Ascension is therefore not a game about defeating another mind. It is a game about planning within constraint — about understanding how space, time, and deterministic movement rules interact in a system that is constantly changing beneath your feet.

    Chessfall Ascension Screenshot

    The Language of the Board: Special Tiles and Mechanics

    The richness of Chessfall Ascension emerges from its tile system. While the chess pieces retain their classical movement rules, the board itself introduces layers of environmental mechanics that shape the player’s decisions.

    Barrier tiles act as solid wooden obstacles that block movement and line-of-sight. They define corridors, restrict diagonals, and force detours. Hardening cells begin as empty spaces but gradually transform into solid barriers after a countdown, creating a sense of closing pathways. In contrast, decaying barriers dissolve after a set duration, temporarily opening routes that may not remain accessible for long.

    Wormgates function as paired portals. Entering one instantly teleports the piece to its linked counterpart. Arrow tiles force directional movement, sometimes pushing a piece into danger or into opportunity. Sacrifice tiles remove a piece from play entirely, often serving as a deliberate strategic mechanic rather than a punishment. Transmutation tiles alter a piece’s identity mid-level, changing its movement capabilities and reshaping the tactical landscape.

    Other tiles manipulate time itself. Slow tiles reduce the falling speed of the board while occupied, granting temporary control over gravity. Claim tiles act as checkpoint mechanisms, capturing all visible flags when activated. Clock tiles represent timed triggers that influence when certain events occur.

    These mechanics transform the board from a passive grid into an active participant in the game. The environment is no longer a static backdrop; it is a system with its own behavior and internal logic.

    Special Tile Types in Chessfall Ascension

    Tile NameDescription
    Barrier (Wooden Tile)A solid obstacle that blocks movement and line-of-sight. It shapes paths and forces detours.
    Hardening Cell (Empty → Solid)An initially empty tile that becomes a Barrier after a short countdown, gradually closing available paths.
    Decaying Barrier (Solid → Empty)A solid tile that disappears after a countdown, temporarily opening new routes.
    Wormgate (A/B Portal)A paired teleportation tile. Entering one instantly transfers the piece to its linked partner.
    Arrow TileAutomatically shifts a piece in a specified direction upon entry, creating forced and often irreversible movement.
    Sacrifice TileRemoves a piece that enters it. Used strategically to reduce complexity or unlock progress.
    Transmutation TileTransforms a piece into another chess type, changing its movement capabilities mid-level.
    Slow Tile (Sc)Reduces the board’s falling speed while occupied, allowing temporary control over time pressure.
    Claim Tile (Pr)Captures all visible objectives (flags) when activated, often serving as a final checkpoint mechanic.
    Clock Tile (Timed Trigger)Represents time-based mechanics that influence when certain events activate or expire.

    To better understand the gameplay and see these mechanics in action, watch the following video:


    The First Version: Speed Over Structure

    The first implementation of Chessfall Ascension was generated with the assistance of modern large language models. With a carefully constructed prompt and iterative refinement, an AI coding agent was able to produce a working prototype remarkably quickly. Gravity behaved correctly. The board rendered properly. Chess movement rules were implemented. From a purely functional standpoint, the system worked.

    This speed was impressive. It demonstrated how effectively AI tools can translate abstract design ideas into executable code. However, as development progressed, an architectural pattern began to emerge — one that is common in AI-generated systems.

    The generated code optimized for visible output. It prioritized immediate functionality. It did not naturally enforce separation of concerns, modular boundaries, or long-term maintainability. The entire board state was recomputed and redrawn on every tick of the game loop. Gravity, tile transitions, and countdown mechanics were applied globally in a monolithic update cycle. Rather than modeling independent entities with their own lifecycles, the system treated the board as a single mutable surface.

    Functionally, nothing was broken. Architecturally, however, the foundation was fragile.

    The difference between a working prototype and a sustainable system became the central turning point of the project.


    The Board as Data: Declarative Design

    One of the most important structural decisions involved how levels were represented. Instead of storing the board as a dense two-dimensional matrix, levels were defined declaratively using sparse JSON structures. Geometry parameters such as width and visible rows were specified explicitly. Player pieces, flags, and special cells were listed individually. Empty space was implicit.

    {
      "id": 2,
      "name": "Mind the Gravity",
      "boardWidth": 8,
      "visibleRows": 6,
      "totalRows": 8,
      "fallSpeed": 50.0,
      "player": [...],
      "flags": [...],
      "cells": [...]
    }

    This approach offered multiple advantages. It kept level files readable and concise. It made manual editing practical. It opened the door to procedural generation. Most importantly, it separated design data from runtime representation.

    The board, on disk, became a description rather than a snapshot. At runtime, that description was translated into structured objects with behavior.

    This separation would later prove essential in enabling architectural refinement.


    From Monolith to Ecosystem

    Refactoring began with a philosophical shift. The board was no longer treated as a grid that changes over time. Instead, it was treated as a small ecosystem of independent, interacting components.

    Rendering was divided into conceptual layers: a board layer responsible for spatial structure, a tile layer responsible for environmental mechanics, a piece layer responsible for chess entities, and an overlay layer responsible for feedback and UI state. This separation reduced coupling and eliminated unnecessary global redraw logic.

    Game elements themselves were modeled as independent entities. Each chess piece encapsulated its own rendering and interaction hooks while delegating movement logic to a separate rule engine. Each tile encapsulated its own internal state transitions. A hardening cell was no longer “a value that flips.” It became an object with a countdown lifecycle. A decaying barrier managed its own expiration logic. A slow tile influenced gravity conditionally without rewriting the entire board state.

    Movement rules were isolated into a dedicated rule engine. Pieces requested legal moves. The rule engine computed them. The board validated them against environmental constraints. Rendering was never entangled with rule computation.

    State management was also made explicit. Rather than deriving game state from repeated grid recomputation, a structured state object tracked active pieces, flags, timers, gravity speed, and win conditions. State changes occurred through controlled transitions instead of incidental side effects.

    Communication between components adopted an event-driven model. Instead of tiles directly mutating piece internals, events were emitted and subscribed to. This reduced tight coupling and allowed new mechanics to be added without rewriting existing systems.

    Time itself became a first-class concept. Timed tiles managed their own countdowns. Gravity speed was represented as a variable in state. Slow tiles temporarily modified that variable while occupied. The world no longer needed to be rebuilt every frame. Time influenced components rather than reconstructing the universe.

    The transformation was not about rewriting everything. It was about reshaping the mental model of the system.


    Understanding the Architecture Through Code

    When explaining architecture, there is always a temptation to draw a large class diagram. Boxes, arrows, inheritance trees --- they look formal and impressive. But in practice, those diagrams often obscure more than they clarify. What matters in Chessfall Ascension is not the sheer number of classes. What matters is how responsibility is distributed, how behavior is abstracted, and how composition replaces condition-heavy logic.

    Rather than presenting a dense UML diagram, it is far more illuminating to examine the core architectural decisions directly through carefully selected code fragments. These fragments reveal the underlying philosophy of the system: interaction is polymorphic, state is explicit, and levels are compositions of behavior-rich entities rather than values in a grid.


    Behavior as a First-Class Abstraction

    At the heart of the interaction model lies a remarkably small abstraction:

    mixin Processor {
      bool process(Piece piece);
    }

    This single method defines the behavioral contract for any entity that reacts when a piece enters its cell. The elegance of this design becomes clear when considering what it avoids. There is no central switch statement that checks tile types. There is no global conditional chain that says, "If this is a wormhole, do X; if this is a slow tile, do Y."

    Instead, behavior is polymorphic.

    Every entity that can respond to interaction implements `Processor`. When a piece moves into a cell, the system does not ask what type of cell it is. It simply invokes `process()` on whatever object resides there. The board does not need to know how a tile behaves. The piece does not need to know what kind of tile it encounters. Interaction becomes a contract, not a condition.

    This small abstraction is the foundation of extensibility. Adding a new tile type does not require rewriting board logic. It simply requires implementing `Processor`.


    Composition Over Conditionals

    The composability of the system becomes even clearer when examining how levels are structured. In `LevelData`, special cells are not represented as numeric markers inside a matrix. They are stored as typed collections:

    final List<ArrowTile> arrowTiles;
    final List<SacrificeTile> sacrificeTiles;
    final List<TransmutationTile> transmutationTiles;
    final List<SlowTile> slowTiles;
    final List<ClaimTile> claimTiles;
    final List<HardeningCell> hardeningCells;
    final List<DecayingBarrier> decayingBarriers;

    A level is not a 2D array with encoded meanings. It is an aggregation of objects.

    Each object encapsulates its own behavior, state, and lifecycle. The board itself becomes a container of interacting entities rather than a surface with embedded rules. This decision replaces conditional logic with composition. It also means that adding a new mechanic does not destabilize existing ones. The ecosystem grows by adding species, not by rewriting laws.


    Layered Responsibility Through Mixins

    Most interactive entities share a similar structural signature:

    class SlowTile extends ChangeNotifier 
        with NamedEventEmitter, Processor {

    This line may look simple, but it encodes a layered architectural philosophy.

    `ChangeNotifier` enables UI reactivity.

    `NamedEventEmitter` enables decoupled event communication.

    `Processor` enables gameplay interaction.

    Each concern is separated.

    The tile does not manually redraw the screen. It notifies listeners. It does not directly orchestrate all game systems. It emits events. It does not embed interaction rules in the board. It implements `process()`.

    By stacking responsibilities in this way, behavior becomes modular and predictable. The system avoids tight coupling between rendering, state transitions, and gameplay effects.


    Encapsulated Interaction Logic

    Consider the implementation of `SlowTile`:

    @override
    bool process(Piece piece) {
      var gameState = GameStateProvider.current.value;
      if (slowFactor != null) {
        gameState?.setFallSpeed(gameState.fallSpeed * slowFactor!);
      }
    
      gameState?.removeEntityAt(row, col, SlowTile);
      emit('slow', null);
      piece.moveTo(row, col);
      notifyListeners();
      return true;
    }

    The tile modifies gravity, removes itself, emits an event, and updates state. What is striking is what it does not do. It does not recalculate the board. It does not inspect unrelated tile types. It does not coordinate with rendering manually.

    Its effect is localized. Its lifecycle is contained.

    This is object-oriented modeling in practice. Each entity is responsible for its own consequences. The board does not simulate everything globally. Instead, behavior emerges from the interaction of independent components.


    Polymorphism in Action: Pieces as Processors

    Interestingly, `Piece` itself implements `Processor`:

    class Piece extends ChangeNotifier 
        with NamedEventEmitter, Processor {

    And its implementation of `process()` looks like this:

    @override
    bool process(Piece piece) {
      capture();
      piece.moveTo(row, col);
      return true;
    }

    A piece can process another piece. This allows capture logic to reuse the same abstraction as tiles. From the board's perspective, there is no special distinction. It simply delegates interaction to the occupant.

    This uniformity simplifies the mental model of the system. Everything that reacts to a piece follows the same contract. Whether the entity is a wormhole, a flag, a slow tile, or an opponent piece, the interaction entry point is identical. Polymorphism replaces special cases.


    Declarative Data, Behavioral Runtime

    Level construction further reinforces this philosophy. Within `LevelData.fromJson`, the JSON file does not embed logic. It describes type:

    var cellType = LevelData.parseCellType(strType);
    switch (cellType) {
      case CellType.arrowTileUp:
      case CellType.arrowTileDown:
        arrowTiles.add(
            LevelData.specialCellFromJson(cell, cellType) as ArrowTile);
        break;
    }

    The JSON declares intent. The factory methods instantiate behavior-rich objects. Declarative data is translated into composable runtime entities.

    This separation ensures that level files remain simple and human-readable, while runtime behavior remains encapsulated and extensible.


    The Role of `GameState`: The Living Snapshot of the World

    While `LevelData` defines what a level should contain, `GameState` represents what the level actually is at any given moment during gameplay. It is the in-memory snapshot of the entire world — tracking the logical board, the runtime entities placed on it, the active pieces, gravity speed, collected flags, and overall game progress. Rather than letting pieces and tiles mutate the grid directly, `GameState` acts as the central authority that coordinates movement, entity registration, removal, and state transitions. It keeps the logical representation used by the rule engine in sync with the behavioral objects that power interactions. In short, if the level file is a blueprint, `GameState` is the running simulation — the orchestrator that ensures consistency, enforces boundaries, and keeps the dynamic world coherent as it evolves over time.

    Conceptual View of `GameState`

    If we strip away implementation details and focus only on architectural intent, `GameState` can be understood as something like this:

    class GameState {
    
      // 1. Blueprint reference
      LevelData currentLevelData;
    
      // 2. Logical rule representation
      Board board;              // lightweight grid used by chess rules
    
      // 3. Runtime entity registry
      EntityRegistry entities;  // pieces, tiles, flags with identity
    
      // 4. Dynamic world parameters
      double fallSpeed;
      bool isGameOver;
      bool isWin;
    
      // 5. Interaction state
      Piece? selectedPiece;
      List<Move> validMoves;
    
      // 6. Controlled mutation API
      moveEntity(...)
      removeEntity(...)
      updateGravity(...)
    }
    

    The UI Layer: From `GameElement` to Concrete Pieces and Tiles

    So far, we have looked at the in-memory model — the declarative `LevelData` and the living `GameState`. But a game is not only logic; it is also presentation. There must be a bridge between the world that exists in memory and the world that the player sees and interacts with.

    In Chessfall Ascension, that bridge begins with a single abstraction: `GameElement`.


    The UI Layer: From `GameElement` to Concrete Pieces and Tiles

    Up to this point, we have explored the in-memory world --- the declarative `LevelData` and the dynamic `GameState`. But a game is not experienced as data structures. It is experienced visually. Something must translate the living simulation into what the player sees, touches, and reacts to.

    In Chessfall Ascension, that translation begins with a single foundational abstraction: `GameElement`.

    `GameElement`: The Visual Anchor

    At the root of the visual hierarchy sits a base class that extends

    Flame's `PositionComponent`:

    abstract class GameElement extends PositionComponent {
      final dynamic model;   // Reference to memory representation
    
      GameElement(this.model);
    
      void bindModel();
    }

    Conceptually, `GameElement` represents anything that:

    • Exists at a position on the board
    • Has a visual presence
    • Is bound to a model object from memory

    The UI component is not the source of truth. It does not own the game state. It observes the model and reacts to it.

    Two Branches: `PieceComponent` and `TileComponent`

    From `GameElement`, the hierarchy splits into two fundamental categories:

    abstract class PieceComponent extends GameElement {
      PieceComponent(Piece piece) : super(piece);
    }
    
    abstract class TileComponent extends GameElement {
      TileComponent(dynamic tile) : super(tile);
    }

    Pieces are dynamic actors. Tiles are environmental mechanics.

    The class hierarchy mirrors the domain model of the game.

    Concrete Implementations

    Each specific entity receives its own concrete component.

    Example for a piece:

    class KingComponent extends PieceComponent {
      KingComponent(Piece piece) : super(piece);
    
      @override
      Future<void> onLoad() async {
        sprite = await loadSprite('king.png');
      }
    }

    Example for a tile:

    class SlowTileComponent extends TileComponent {
      SlowTileComponent(SlowTile tile) : super(tile);
    
      @override
      Future<void> onLoad() async {
        sprite = await loadSprite('slow_tile.png');
      }
    }

    Concrete components define visual identity while the model controls

    behavior.

    Event-Driven Synchronization

    Models emit events. UI components listen.

    void bindModel() {
      model.addListener(() {
        updatePositionFromModel();
      });
    
      model.on('move', (_) {
        animateTo(model.row, model.col);
      });
    }

    The direction of authority is always:

    GameState → Model → Event → UI Component

    Never the reverse.

    Architectural Outcome

    • Clear separation of concerns
    • Single source of truth
    • Extensible design
    • Testable logic layer
    • Clean visual hierarchy

    The memory layer decides what happens.

    The UI layer decides how it looks.

    And they communicate through events, not tight coupling.


    Lessons Learned

    Developing Chessfall Ascension in collaboration with AI revealed several insights that extend beyond this single project.

    First, working code is not equivalent to good architecture. A system can behave correctly while still being structurally brittle. AI tools are remarkably effective at generating functional output, but architecture requires deliberate foresight.

    Second, recalculation is not the same as modeling. Simulating dynamism by recomputing everything each frame is fundamentally different from giving individual entities ownership over their behavior. Systems scale when responsibility is localized.

    Third, time must be treated explicitly. When time is implicit in a global loop, logic becomes tangled. When time is a parameter influencing specific components, reasoning becomes clearer and systems become more predictable.

    Fourth, declarative data design empowers creativity. Separating level definitions from runtime logic allowed design iteration without destabilizing the core engine.

    Fifth, separation of concerns is not optional. Isolating rule computation from rendering, and state from presentation, makes experimentation safe.

    Perhaps most importantly, AI accelerates execution but does not replace architectural thinking. It is exceptionally powerful at generating scaffolding and translating ideas into initial implementations. However, it does not inherently reason about long-term evolution, maintainability, or conceptual clarity.

    Architecture is an act of responsibility toward future versions of the system — and toward the future self who must maintain it.


    Final Reflection

    Chessfall Ascension ultimately became more than a puzzle game. It became a study in collaboration between human reasoning and machine generation. The AI helped move quickly. It helped explore possibilities. It helped transform abstract ideas into concrete prototypes.

    But architecture — the discipline of designing systems that remain coherent, extensible, and understandable over time — required conscious human intervention.

    The lesson is not that AI cannot build systems. It is that AI and human designers serve different roles. One accelerates creation. The other shapes structure.

    The most powerful workflow is not choosing between them, but understanding how they complement each other.

    Chessfall Ascension stands as a small but meaningful example of that balance.


    A Note on the Journey Ahead

    Chessfall Ascension is still under active development. What exists today is a strong architectural foundation and a growing collection of carefully designed levels, but the world of the game is far from complete. The mechanics are extensible by design, and the system was intentionally built to support creative experimentation with new puzzles, time-based interactions, and strategic constraints.

    If you are someone who enjoys designing intricate, thought-provoking levels — puzzles that force players to think several moves ahead under pressure — you are warmly invited to join this journey. The game’s structure makes it possible to compose rich gameplay scenarios using the existing elements without touching the engine itself. If you are interested in collaborating and contributing meaningful level design ideas, I would be happy to explore working together, including the possibility of sharing equity in the project.

    Chessfall Ascension is not just a finished product in progress — it is an evolving system. And like any evolving system, it grows stronger through thoughtful collaboration.

    #System Design#LLM#Game Development#AI#Creativity
    Originsoft Team

    Engineering Team

    The engineering team at Originsoft Consultancy brings together decades of combined experience in software architecture, AI/ML, and cloud-native development. We are passionate about sharing knowledge and helping developers build better software.