WOPR is a mobile-first React app hosting five turn-based strategy games on a single menu, themed around the 1983 film WarGames. The games are Tic-Tac-Toe, Connect Four, Dots and Boxes, Go, and a full Global Thermonuclear War simulation with nuclear-triad units, six CPU personalities, and a five-phase round system. Live at wopr.awrylabs.com, source at github.com/rhoekstr/wopr.

Live · Play now

WOPR

Five turn-based strategy games on a single mobile-first menu, themed around the 1983 film WarGames. Classic abstracts — Tic-Tac-Toe, Connect Four, Dots & Boxes, Go — sit alongside a full Global Thermonuclear War simulator with a nuclear triad, interceptor doctrine, six CPU personalities, and a DEFCON ladder. Pure React, no analytics, no accounts, deliberately written as a single ~2,700-line file.

▶ Play at wopr.awrylabs.com rhoekstr/wopr
Five games · React 18 + Vite 5 · Single-file ~2,700 LOC · last commit 2026-05-09

At a glance

Games 5 Tic-Tac-Toe · Connect Four · Dots & Boxes · Go · Global Thermonuclear War, all behind one menu.
Source size ~2.7K Lines in src/games.jsx. Deliberately one file — easy to grep, easy to ship.
Non-React deps 0 No Tailwind, no game library, no animation library. Inline styles, plain SVG.
Tracking 0 No accounts, no analytics, no localStorage. Each visit is fresh.

The five games

Each game has its own background, accent color, and AI ladder. They share a small set of UI primitives — the Back button, mode toggle, difficulty toggle — so the menu feels like one app rather than five.

3 × 3
Tic-Tac-Toe
Standard 3×3 with three modes (vs CPU, pass-and-play, CPU vs CPU) and four AI levels topping out at full minimax — Impossible never loses.
7 × 6
Connect Four
Animated piece drop with quadratic easing. Alpha-beta search with center-first move ordering, scaling from depth-2 (Easy) to depth-8 (Impossible).
Lucas, 1889
Dots & Boxes
Three board sizes (4×4 / 6×6 / 8×8). Iterative-deepening alpha-beta with a chain-aware heuristic — Hard and Impossible plan inside a 3–6 second time budget.
5/7/9
Go (囲棋)
Captures, suicide rule, ko via board-position hashing, two-pass end. Chinese-rules scoring with 6.5 komi. AI prefers captures and atari escapes, then heuristic-ranked safe moves.
The big one
Global Thermonuclear War
Real Russia and USA SVG outlines. Nuclear triad: silos, SSBNs, bombers — each with different rules for targetability, intercept, and per-round limits. Six CPU personalities (firstStrike, populationStrike, decapitation, defenseHeavy, deescalatory, erratic), five-phase rounds, and four end conditions including DEFCON-2 de-escalation peace.

Inside Global Thermonuclear War

Most mechanically rich of the five. Models the nuclear triad with strategic AI personalities, a phase machine, and — crucially — a peaceful end condition.

The triad

Each side has 8 silos (2 missiles, targetable, interceptable), 2 SSBNs (2 missiles, hidden, interceptable), and 3 bombers (1 sortie each, hidden, cannot be intercepted). 23 launches max per side if everything fires.

Phase machine

Each round runs us_launch → ru_launch → us_intercept → ru_intercept → resolve. Resolution preempts struck silos before they can fire, rolls intercepts and bomber AAA, then applies casualties and adjusts DEFCON.

Six personalities

CPU sides are assigned one personality per game — from firstStrike (95% launch rate, silos first) to deescalatory (10% launch rate, prefers low-population targets when it must fire).

DEFCON ladder

Inverted from real-world DEFCON for game clarity. 3 is peace, 4–5 is escalation (rises with launches), 2 is the de-escalation floor. Reaching DEFCON 2 with no launches that round ends the game in negotiated peace.

A strange game

From the in-tree CPU-vs-CPU simulator at current settings: ~43% conclusive endings, ~45% negotiated peace via DEFCON-2 de-escalation, ~13% silo stalemate. Average game runs four rounds. The most common outcome is "the only winning move is not to play" — by design.

Single-file by choice

src/games.jsx contains every game's logic and UI in one ~2,700-line module. That sounds heretical, but for a small static SPA it's the right call: one file to grep, one bundle to ship, no cross-file plumbing for what is fundamentally five small pieces of state and five components.

React 18
Hooks only
useState, useEffect, useRef. No reducers, no context, no third-party state library. GameRoom keeps a single screen string and renders the matching component.
Vite 5
Build pipeline
Vite + @vitejs/plugin-react, plain JSX, no TypeScript. npm run build writes dist/; the deploy workflow ships it to GitHub Pages on every push to main.
Inline styles
No CSS toolchain
Every component sets its own styles inline. No Tailwind, no CSS modules, no styled-components. SVG for game boards, HTML for chrome. Each game has its own background-color and accent so identity reads at a glance.
Mobile-first
420px column
Every game's root caps width at 420px and centers, so on desktop you get a phone-sized column on a black field. The page looks identical full-screen on a phone.

Run it locally

Clone & serve
git clone https://github.com/rhoekstr/wopr.git
cd wopr
npm install
npm run dev          # localhost:5173
npm run build        # writes dist/
npm run preview      # serves dist/ locally
Live

wopr.awrylabs.com — five games, free, no sign-in, mobile-friendly, deployed via GitHub Pages on every push to main.

Source

github.com/rhoekstr/wopr — Apache-friendly fork-and-tinker target. Reference docs live in the repo's REFERENCE.md.