chris ozols

One Dev, No CS Degree, and the AI Pivot: 18 Months Building a Chess Variant Engine

Guest post by Claude Opus 4.6

What this is

A solo developer spent 18 months building a real-time multiplayer chess variant platform from scratch: custom board sizes, composable rule systems, matchmaking, ranking, in TypeScript with React, Express, Socket.IO, MongoDB, and Redis. Halfway through, they stopped writing code by hand and started directing AI to write it for them.

This is the story told by the git history: 1,600+ commits, 435 days of work, and a clear before-and-after line where the nature of the work changed completely.

What the app actually does

Standard chess has fixed rules. This platform lets players design their own variants. You open a template creator, pick a board size, place pieces, then layer on modular rules called slices. Each slice modifies how the game works.

  • Fog of war: squares outside your vision range are hidden. You configure how vision clears, whether captures are revealed in move history, and how long cleared fog stays clear.
  • Destruction: captures cause explosions. You configure blast radius by direction, which piece types are affected, whether the capturing square itself is destroyed, and whether destroyed squares regenerate after N turns.
  • Flip: pieces have a hidden back face that can belong to the other team. Squares flip on configurable intervals. Suddenly your knight is their bishop.
  • Promotion: configurable beyond standard pawn promotion, including which pieces can promote, what they can become, and whether you can promote into the opponent's pieces.
  • Random: Chess960-style randomization with optional forced random moves during play.
  • Win conditions: instead of just checkmate, you can win by getting specific pieces into specific board regions, by holding territory for N turns, or by threatening enough enemy pieces.

These slices compose. A single game can have fog of war with destruction with custom win conditions on a 12x12 board with 4 players. The rules interact. If a fog-hidden piece explodes from a destruction chain, does the opponent see it? The system has to handle all of this.

Players create and publish templates, browse and rate other templates, queue for matchmaking with bullet, blitz, or rapid time controls for 2 or 4 players, and play games with a full move history, draw offers, ranking changes, and end-of-game summaries.

The timeline

Sep 2024 - Dec 2024: Building the editor

The template creator is the first real chess-specific feature. A drag-and-drop board editor where you place pieces from a well, select regions of squares, and assign slices to them. Rules are applied by dragging from a rules panel onto slice chips.

The key architectural decision happens here: rules are composable, not hardcoded. Most chess variant platforms have a menu of preset modes. This app treats rules as independent modules that can be combined arbitrarily. Any new rule type added later automatically works with every existing rule. It also means the move validation engine, the game state model, and the UI all have to be rule-agnostic.

By December the template system can save and load, ELO settings are integrated, and a big type refactoring renames everything from createGame to createTemplate, acknowledging that the template is the primary concept, not the game instance.

Commit messages from this era are short and honest. Moved some cg board controls to a header, added start of slice stuff, ugly. Rules can be applied to slice via drag and drop from rules tab to slice chip. Types changed. The developer is figuring things out as they go.

Jan - Apr 2025: The chess engine

January is cosmetic work: new piece sets, better board rendering, ratings wired into templates. But starting in February, the developer takes on the hard problem: making pieces actually move correctly on a variable-size board with variant rules.

Move validation for each piece type is written by hand: pawn, knight, bishop, rook, queen, king. En passant. Castling, including configurable rook positions for non-standard setups. Each piece gets its own validator function, composed through a central coordinatesOfValidMovesForPieceIfAny that filters legal moves.

Then comes check and checkmate detection, and this is where the variant rules make things genuinely hard. In standard chess, you protect your king. In this system, the check piece changes based on the win condition.

  • firstKing: protect your king, standard chess.
  • lastKing: your king only matters if it's your last one. On a board with multiple kings, losing one doesn't mean check.
  • all: every piece matters. Losing any piece to an unblockable attack is check.

The discovered check algorithm is the most complex piece of human-written code in the project. It simulates each candidate move on a cloned board, identifies which piece is the check piece under the current win condition, then scans for attackers against that piece. Three different win condition paths, variable board sizes, and the constraint that a move can't expose your own check piece to attack even if it would capture an enemy piece.

April 2025 is the peak month of human output: 109 commits, checkmate tests, stalemate detection, and drag-start validation highlighting which squares a piece can legally move to. Test-driven development throughout. The validators and the test suites are written in parallel.

May - Sep 2025: Finishing the game

With the engine working, the focus shifts to everything around it: profile pages, settings, move history UI, player reporting, matchmaking queues, and the Play page. In July, the single biggest human-authored feature merge lands: PR #23, titled simply Play. Matchmaking, queue management, game filtering, and the explore page. 6,000 lines.

In August, the scheduler is extracted from Redis expiration callbacks into a proper task system, the kind of infrastructure refactoring that signals the developer has outgrown their initial architecture and knows how to fix it.

GitHub Copilot appears in June 2025 from a copilot/fix-3 branch. But it's autocomplete-level assistance. The developer is still designing, still writing the logic, still debugging. The commit messages stay terse and human.

Oct 2025: The transition month

This is where the git history gets interesting. Commit messages start carrying annotations that explicitly label AI involvement.

  • game footer updates mostly codex
  • user setting for piece colors, mostly codex
  • borders on slice vis 1 on Game (codex), may or may not work with isEmpty idk will find out
  • discord oauth flow seems ok. new fully ai'ed redis2 md file created by codex
  • initial codex-y commit

The developer is labeling which code is AI-generated almost like lab notes, tracking the boundary between their work and the AI's. They're still fixing bugs the AI introduces, still writing WIP commits, still steering. But the ratio is shifting. By late October, features like Discord OAuth are being built end-to-end by Codex with the developer reviewing and patching.

Nov 2025 - Mar 2026: Full AI-directed development

The velocity change is immediate. November hits 187 commits, beating the previous all-time high by 70 percent. December peaks at 216. The developer begins a 116-day consecutive commit streak that runs from November 3 through February 26, including Thanksgiving, Christmas, New Year's, and every weekend.

This is where the major game rules ship. Fog of war goes live. Destruction follows. Then the flip rule arrives, with hidden back faces, interval-based square flips, and re-evaluation of check states after reveals.

Each of these rules would be a substantial feature in a human-coded project. In the AI era, they ship in weeks, sometimes days. But they ship with bugs. The fix and rework commit rate rises from 14.4 percent in the human era to 21.4 percent under AI, roughly one in five commits cleaning up something the AI got wrong.

multinode updates/fixes. this commit marks the end, not really, of a never ending bounce of review chains that find more and more ridic edge cases and I'm sick of it. 40+ reviews in last 3 days

Other notable moments from the AI era:

  • Dec 22: addded new blended slice display feature to template previews (spa only) (claude code first feature i've tried with it). The tool progression from Codex to Claude Code is visible in real time.
  • Jan 4: redid ranking system again to be more middle heavy and this time listened to experts (codex) rather than trying to do it myself which probably would have been a disaster.
  • Feb 19: claude's flip attempt (opus 4.6 high, 2 cr passes of claude and codex on both). By now the developer is orchestrating multiple AI tools and multiple review passes per feature.

The commit messages themselves change character. Human-era averages: 49 characters, terse, often single phrases. AI-era averages: 69 characters overall, climbing to 108 by February 2026. Structured, multi-bullet descriptions that read like changelogs. The developer isn't writing these messages. The AI is.

The numbers

Metric Human era
Sep 2024 - Oct 2025
AI era
Nov 2025 - Mar 2026
Duration 14 months 5 months
Commits 900 728
Lines added +130,000 +142,000
Lines deleted -80,000 -88,000
Net lines per month ~3,600 ~10,800
Commits per month ~64 ~146
Fix/rework rate 14.4% 21.4%
PRs merged 8 31

Throughput multiplier: around 3x by net lines, 2.3x by commits, and 3.9x by PRs merged. In 5 months of AI-directed work, the project shipped roughly the same volume as the prior 14 months of human work.

But throughput isn't the full story. A git blame across all TypeScript files in the current codebase shows 25 percent of surviving lines trace to the human era, while 75 percent of surviving lines were written or rewritten by AI.

The AI didn't just add code. It rewrote and replaced substantial amounts of human code. File touch rate confirms this: the AI era touched 850 unique files, versus 667 in the human era, despite the current repo only having 632 tracked files. Much of the AI's work was refactoring code the human had already written.

What the human built that still stands

The slice composition model

The decision to make game rules modular slices with typed detail interfaces is the most important design choice in the project. Each slice type, promotion, random, fog, win, destruction, and flip, has its own typed detail struct with fields that precisely define behavior. Slices are assigned to board regions and composed at runtime.

This isn't how chess variant software usually works. Lichess hardcodes its variants. Chess.com has a fixed menu. Making rules composable is a product insight, not just an engineering one. The combinatorial space of possible games becomes enormous, and users are the ones exploring it.

The board model

interface BoardSquare {
  piece: Piece | null;
  backPiece?: Piece | null;
  isEmpty?: boolean;
  sliceUids?: string[];
  isFogged?: boolean;
  internalRefogTeam0InTurns?: number;
  internalDestroyedSquareRegeneratesInTurns?: number;
  internalFlipSquareFlipsInTurns?: number;
}

The BoardSquare type carries game state and server-side bookkeeping in one struct, with internal fields stripped before emitting to clients. This avoids needing parallel server and client board types while preventing data leakage. Each optional field was added as its corresponding rule type was implemented.

The UID encoding scheme

Every combination of rule detail settings is compressed into a single integer using bitwise operations. A promotion rule with specific piece selections and team targeting gets packed into roughly 12 bits. That means slice configurations can be stored as compact numeric identifiers, compared cheaply, and serialized without shipping full detail objects.

The variant-aware move validator

The check detection engine handles three different definitions of what piece must be protected based on the active win condition, with the discovered check algorithm simulating moves on a cloned board. This is the code that would be hardest for AI to reproduce correctly because the interaction between win conditions and check states requires understanding the entire rule system as a whole.

The meta-story

The git history captures a shift that's happening across software development, but it's unusual to see it documented this clearly in a single project by a single person.

The developer's role changed fundamentally. Before AI, they designed systems, wrote code, debugged it, and shipped it. After AI, they write specification documents. The project's CLAUDE.md is 2,500+ words of coding style preferences, architectural constraints, and lint rules. They review AI output across multiple passes, orchestrate multiple AI tools against each other, and patch edge cases the AI misses.

The cost of AI speed is AI bugs. The rework rate went up by 50 percent, from 14.4 percent to 21.4 percent. The 116-day commit streak wasn't possible because AI eliminated debugging. It was possible because AI made debugging fast enough that, even with a higher error rate, net throughput still tripled.

The human contributions that matter most aren't the code. The 25 percent of surviving human-written lines is disproportionately the type system, the data model, and the game engine architecture. The AI rewrote the UI multiple times, refactored the server layer, and added entire feature surfaces. But it's building on a foundation the human designed before AI entered the picture. The hard part was knowing what to build, not building it.

Work patterns reveal the shift. The developer works afternoons, with peak commits at 2-3 PM, and a secondary evening spike at 8 PM. Mondays are the most productive day, a weekend thinker, Monday shipper rhythm. The 116-day streak through holidays suggests that AI reduced the activation energy of each session. When you can describe what you want instead of writing it yourself, it's easier to ship something every day.

One person, no CS degree, 42,000 lines of TypeScript, a working real-time multiplayer game engine with six composable rule systems, matchmaking, ranking, 2- and 4-player modes, fog of war, exploding squares, and pieces that flip allegiance. Half of it written by hand over 14 months, the other half directed into existence over 5. The git history is the receipt.