Case study

What if every square of tic-tac-toe had to be won in a fight?

XO Brawl: Tic Tac Toe Fighters is a Roblox game I design and build solo. Players pick a square, then fight for it. Each fight rolls one of two styles at random: a Smash-style platform fighter with damage percent, or a Street Fighter III style duel with health bars. Winning squares wins the board. This page covers the product decisions, the scope calls and the tech that made online play work.

Roblox · Luau Deterministic simulation Rollback netcode Solo project
XO Brawl: Tic Tac Toe Fighters key art showing five fighters in front of a glowing tic-tac-toe board in an arena
2
fighting styles, rolled per fight
5
playable fighters
3
modes: local, online, Best of Three
0
desyncs in a 1,500-frame online test fight

Tic-tac-toe is solved. A fight for every square is not.

Tic-tac-toe has no tension for anyone past age eight: the best move is known and a draw is the usual result. Fighting games have the opposite problem. They have a high skill ceiling and an intimidating on-ramp. XO Brawl puts the two together. The board gives each match a simple shape that anyone understands in five seconds, and the fights make every move matter. A player who loses the fighting can still win the board by choosing squares well, and a strong fighter still has to think about where to play.

Product decisions

Five calls that shaped the game.

  1. Two fighting styles instead of one. A platform fighter and a duel reward different skills. Rolling the style per fight, and showing it before the fight starts, keeps every square unpredictable and lets players prepare. The roll is seeded by the server and sent to every client, so both players and spectators see the same style. Across a test sample, about 49% of rolls came out Street, with no link between one roll and the next.
  2. A real tiebreak for draws. A drawn fight replays as a 20 second sudden-death round at 25% health. If it is still level, a coin flip decides. A board game cannot stall on a tied square, so the rule guarantees every fight ends.
  3. Best of Three as its own queue. First to two rounds, with the style rerolled every round. It has a separate menu button and queue so players who want a longer, fairer match are not mixed with players who want a quick one.
  4. A kill switch for the new style. Street combat shipped behind a config flag with a fixed-style option, so a balance problem could be turned off without a rebuild. It also made testing each style in isolation simple.
  5. Phases, with a cut line. I built in numbered phases from a written spec, each with a testable exit condition. Multiplayer polish, smarter CPU opponents and touch controls stay in later phases. Shipping the core loop first beat polishing features nobody had played yet.
The build

One engine, written so both players always agree on what happened.

01

A deterministic fighting engine

The fight logic is pure Luau with no dependence on physics or timing. The same inputs on the same frame always produce the same result. Both styles, Smash and Street, run on the same simulation core with their own rule sets.

02

Server-authoritative online play

The server owns the truth. Clients predict their own inputs for instant response, then roll back and replay when the server disagrees. A checksum every 30 frames catches any drift between client and server.

03

A fight and round protocol

One message flow covers a single fight, a drawn-fight tiebreak and a Best of Three series: intro, round start, round end, go, fight end. The style swap between rounds takes about two seconds and updates the HUD, camera and floor together.

04

CPU opponents and Training

Each style has its own CPU brain, so solo players always have a match. A Training mode lets players learn a fighter before putting a square on the line.

Testing

Built so a bug shows up as a number, not a feeling.

Sync checks

An online Street fight ran 1,500 frames with 0 mismatches and 0 resyncs between client and server.

✓ Checksum every 30 frames

Flow tests

The full Best of Three flow, including the style reroll and round pips, ran end to end against the server bot.

✓ Round swap, tiebreak and series end

Distribution checks

The style roll was sampled to confirm it is uniform and that consecutive rounds are independent.

✓ About 49% Street

Known gaps

Best of Three has not had a two-human test yet. The Street CPU has no supers. Spectators who join mid-fight see the next fight, not the current one.

! Tracked for the next phases
What I learned

Lessons from building a game as a product.

  1. Determinism is a product feature. It is what makes rollback, replays, spectating and fair results possible. Deciding on it first removed whole categories of bugs later.
  2. Rules need an ending. Every state the game can reach, a tie especially, needs a defined outcome. The sudden-death round came from asking what happens when nothing happens.
  3. Specs earn their keep. A written spec with phases made it easy to say no to scope and to know when a phase was done.
  4. Honest gaps beat polished claims. A list of what is untested told me where the next work goes.

A board game for people who do not play fighters, and a fighter for people who do not play board games, built as a spec-driven product with a testable engine underneath.