Files
2026-09-23 16:31:05 +03:00

7.4 KiB
Raw Permalink Blame History

Tic-Tac-Toe vs CPU — Implementation Plan

A polished, mobile-friendly tic-tac-toe game (X vs O) playable in the browser against a random-move CPU opponent. Pure HTML/CSS/JS — no build step, no dependencies. Open index.html directly in a browser to play.

1. Project Structure

game1/
├── index.html      # Page structure (board, scoreboard, status, overlay)
├── style.css       # All styling: glassmorphism dark theme, animations
├── game.js         # Game logic: state, win detection, CPU, scoring, FX
└── docs/
    ├── PLAN.md     # This document
    └── mockup.svg  # UI mockup (open in a browser to view)

2. Visual Design

Research (Dribbble/Figma/CodePen examples) shows the current popular style for tic-tac-toe: dark glassmorphism with neon accents, frosted panels, soft glows, and confetti celebrations on wins.

  • Background: deep gradient (dark navy → violet) with two soft blurred colored blobs (cyan + magenta) for depth.
  • Panels: frosted glass cards — background: rgba(255,255,255,0.06), backdrop-filter: blur(14px), 1px translucent white border, soft shadow. (Blur kept modest for mobile performance.)
  • Colors:
    • X (player) — neon cyan #22d3ee
    • O (CPU) — neon pink/magenta #f472b6
    • Text: white / muted gray
  • Typography: system font stack, bold title, tabular numerals for scores.
  • Mockup: see docs/mockup.svg (open in a browser) for the envisioned look — dark glass panels, neon cyan X / pink O with glow, gradient title.
  • Layout (top → bottom):
    1. Title: "Tic-Tac-Toe" with subtle gradient text
    2. Scoreboard: three glass chips — You (X) · CPU (O) · Draws
    3. Status bar: "Your turn" / "CPU is thinking…" / result text
    4. Board: 3×3 grid of glass cells
    5. Buttons: "New Game" (primary) and "Reset Scores" (ghost)

3. Mobile-Friendliness

  • <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  • Board width: min(88vw, 340px); cells are large touch targets (≥ 72px on phones).
  • touch-action: manipulation on buttons/cells to kill double-tap zoom delay.
  • No hover-only interaction: hover glow is a desktop enhancement only; taps work everywhere. Use :active press feedback.
  • Layout uses flex column, centered, max-width 420px — looks like a mobile app on desktop too.
  • user-select: none on the board to avoid text selection while tapping fast.
  • Test: 360px-wide viewport, fast repeated taps, orientation change.

4. Game Rules

  • Standard 3×3 tic-tac-toe. Player is X, CPU is O.
  • Player always starts the round with X (keeps it simple and predictable).
  • Click/tap an empty cell to place your mark.
  • CPU responds on a random empty cell after a 600 ms "thinking" delay (feels natural, shows the "CPU is thinking…" status).
  • Win: 3 in a row (8 winning lines). Draw: board full with no winner.
  • After a round ends, a result overlay appears; "Play Again" starts a fresh round (board clears, scores persist).

5. Scoring

  • Track You, CPU, and Draws across rounds.
  • Persisted in localStorage (key ttt-scores) so scores survive reloads.
  • "Reset Scores" button zeroes them (with a short confirm-less toast feedback).
  • Score chips animate a quick pop when the value changes.

6. Game-Over Reactions

  • Winning line: an SVG line is drawn across the three winning cells with a stroke-dashoffset animation (draws itself in ~400 ms).
  • Result overlay: frosted glass modal fades/scales in with:
    • Win → "You Win! 🎉" + confetti burst (DOM particles, ~40 pieces, random colors/velocities, fall + fade over ~1.8 s, then removed).
    • Loss → "CPU Wins" + a subtle red-tinted shake of the board.
    • Draw → "It's a Draw" + neutral pulse.
  • Overlay has a single big "Play Again" button.
  • Small micro-feedback during play:
    • Marks pop in with a scale+fade animation (150 ms).
    • Empty cells glow faintly on hover (desktop) and brighten on :active.
    • Status bar text cross-fades on turn change; "CPU is thinking…" pulses.

7. JavaScript Architecture (game.js)

Single file, plain functions + one state object:

state = {
  board: Array(9).fill(null),   // null | 'X' | 'O'
  turn: 'X',                    // 'X' = player, 'O' = CPU
  gameOver: false,
  scores: { you: 0, cpu: 0, draws: 0 }
}

Modules (sections in the file):

  1. DOM refs — cells, status, score chips, overlay, buttons.
  2. Win detection — WIN_LINES = 8 combos; checkWinner(board) returns {winner, line} or {winner: 'draw'} or null.
  3. Player move — onCellTap(i): ignore if occupied/game over/CPU turn; place X, render, check end; else schedule CPU.
  4. CPU move — cpuMove(): after 600 ms, pick a random empty index, place O, render, check end.
  5. Render — renderMark(i, mark) (pop animation), renderStatus(), renderScores(), drawWinLine(line) (SVG overlay on the board).
  6. Round end — endRound(result): update + persist scores, show overlay with appropriate FX (confetti / shake / pulse).
  7. Confetti — burstConfetti(): create N absolutely-positioned divs with random transforms, animate via CSS transitions, remove after finish.
  8. Reset — newRound() (clear board, player first), resetScores().
  9. Init — load scores from localStorage, build board DOM, bind events.

8. Implementation Steps

  1. index.html — semantic structure, viewport meta, SVG defs for win line.
  2. style.css — theme tokens (CSS custom properties), background, glass panels, board grid, marks (X/O drawn with CSS or inline SVG for crispness), animations (pop, pulse, shake, confetti fall), overlay, responsive tweaks.
  3. game.js — implement sections 1–9 above.
  4. Manual test pass:
    • Win all 8 line types (play through scenarios).
    • Draw scenario.
    • Rapid tapping (no double placement, no CPU overlap).
    • Scores persist across reload; reset works.
    • 360px viewport: no overflow, big touch targets, no zoom on tap.
  5. Polish: check animation smoothness on a mid-range phone if available.

9. Hosting the App

The app is three static files, so any static file server works. The goal is a one-line start, no config files, no build step.

Primary option — Python (already installed, zero setup):

python3 -m http.server 8000

Run from the game1/ folder. It binds to all network interfaces by default, so the game is immediately reachable:

  • Same machine: http://localhost:8000
  • Same network (LAN): http://<machine-ip>:8000 (find the IP with hostname -I or ip addr)
  • Internet: forward port 8000 in the router/firewall, or put the machine behind a reverse proxy. For public exposure, prefer caddy (see below) and keep the raw port closed otherwise.

Alternative — Caddy (nicest for internet exposure, auto-TLS with a domain):

caddy file-server --root . --listen :8000

Alternative — Node (if Python is unavailable):

npx serve -l 8000

Notes:

  • These dev servers are fine for LAN / home use. For a public internet endpoint, use caddy behind a domain (it handles TLS automatically) and do not expose the raw port to the internet with a bare dev server.
  • No deploy step: the folder is the app — copy it anywhere and serve it.

10. Non-Goals (kept simple on purpose)

  • No minimax / unbeatable AI (CPU is random, as requested).
  • No sound effects, no multi-language support, no user-vs-user mode.
  • No framework, no bundler — the game is three files.