Thanks to visit codestin.com
Credit goes to github.com

Skip to content

karthikvetrivel/qwirkle

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

3 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Qwirkle - Digital Board Game

A fully playable digital version of Qwirkle with online multiplayer via room codes (2-4 players) and local hotseat mode. Built with React + TypeScript frontend, WebSocket real-time sync, and a shared TypeScript game engine. Includes comprehensive testing with random fuzzing, heuristic agents, multiplayer integration tests, and a rules judge.

๐ŸŽฎ Features

  • Online Multiplayer: Create/join rooms with shareable codes, real-time gameplay, no login required
  • Local Hotseat Mode: Pass-and-play on one device
  • Server-Authoritative: All game logic runs server-side to prevent cheating
  • Reconnect Support: Players automatically rejoin if they refresh or disconnect
  • Deterministic Replay: Every game is reproducible from seed + action log
  • Dark Theme UI: Polished interface with pan/zoom board

Rules Assumptions & Digital Implementation Decisions

  • 108 tiles: 6 colors (red, orange, yellow, green, blue, purple) x 6 shapes (circle, diamond, square, star, clover, cross) x 3 copies each.
  • Starting player: Determined by who has the largest set of tiles sharing one attribute (color or shape) with no duplicates. Tie-break: lower seat index.
  • Trading: You may trade 1 to N tiles where N = min(hand size, bag size). You draw replacements first, then traded tiles are shuffled back into the bag.
  • Forced trade: If a player has no legal placements and the bag is not empty, they must trade.
  • Endgame: When the bag is empty, players do not refill. The first player to empty their hand gets +6 bonus points. If all remaining players are stuck (no legal placements and bag is empty), the game ends immediately.
  • Scoring: Each tile placed scores 1 point per tile in each line it participates in (horizontal and vertical). A single isolated tile scores 1 point. A Qwirkle (line of 6) scores 6 + 6 bonus = 12.
  • Lines: Maximum length 6. A color line has all same color with distinct shapes. A shape line has all same shape with distinct colors. No duplicates allowed.
  • Placement: All tiles played on a turn must be in the same row or column, must be contiguous (gaps allowed if filled by existing board tiles), and must connect to the existing board (except the first move).

Project Structure

qwirkle/
โ”œโ”€โ”€ shared/src/          # Core game engine (TypeScript)
โ”‚   โ”œโ”€โ”€ types.ts         # All types, constants, helpers
โ”‚   โ”œโ”€โ”€ rng.ts           # Seeded PRNG (xoshiro128**)
โ”‚   โ””โ”€โ”€ engine.ts        # Game engine, validation, scoring, replay
โ”œโ”€โ”€ frontend/src/        # React + Vite frontend
โ”‚   โ”œโ”€โ”€ hooks/useGame.ts # Game state management hook
โ”‚   โ”œโ”€โ”€ components/      # UI components (Board, Hand, Controls, etc.)
โ”‚   โ””โ”€โ”€ App.tsx          # Main app
โ”œโ”€โ”€ backend/src/         # Express API server (optional)
โ”‚   โ””โ”€โ”€ server.ts        # REST API for game management
โ”œโ”€โ”€ simulation/src/      # Testing infrastructure
โ”‚   โ”œโ”€โ”€ runner.ts        # Fuzz test runner (random + heuristic)
โ”‚   โ”œโ”€โ”€ random-agent.ts  # Random legal move agent
โ”‚   โ”œโ”€โ”€ heuristic-agent.ts # Greedy score-maximizing agent
โ”‚   โ”œโ”€โ”€ judge.ts         # Rules oracle / LLM judge
โ”‚   โ”œโ”€โ”€ replay.ts        # Deterministic replay CLI
โ”‚   โ””โ”€โ”€ regression-runner.ts # Regression test runner
โ”œโ”€โ”€ tests/regressions/   # Auto-populated regression tests
โ””โ”€โ”€ package.json

Quick Start

Prerequisites

  • Node.js 20+
  • npm

Install

npm install
cd frontend && npm install && cd ..

Run Multiplayer Mode (Recommended)

# 1. Build the frontend
npm run build

# 2. Start the server
npm run server

Opens at http://localhost:3001. Features:

  • Online multiplayer via room codes
  • WebSocket real-time sync
  • Server-authoritative game logic
  • Auto-reconnect on disconnect

Usage:

  1. Click "Create Room" โ†’ share room code with friends
  2. Others click "Join Room" โ†’ enter code
  3. All players click "Ready" โ†’ host starts game
  4. Play turns in real-time!

Run Local Hotseat Mode (Development)

npm run dev

Opens at http://localhost:5173/local.

  • No server needed
  • Pass device between players
  • Pure client-side gameplay

Build for Production

npm run build

Outputs to dist/ for static hosting.

How to Run Tests & Simulations

Quick Test (regressions only)

npm test

Full Test Suite

npm run test:all

Runs: 200 random games + 100 heuristic games + 50 judge-checked games + all regressions.

Individual Test Commands

Command Description
npm run fuzz:random -- 1000 Run N random-agent games (default 100)
npm run fuzz:heuristic -- 500 Run N heuristic-agent games (default 100)
npm run fuzz:random:1000 1000 random games shortcut
npm run fuzz:heuristic:500 500 heuristic games shortcut
npm run judge -- 50 Run rules judge on N games
npm run regressions Run all regression tests
npm run replay -- <file.json> Replay a saved game and verify
npm run test:multiplayer Multiplayer integration tests (2p)
npm run test:multiplayer:3p Multiplayer tests (3 players)
npm run test:multiplayer:4p Multiplayer tests (4 players)

Runner Arguments

npx tsx simulation/src/runner.ts <agent> <numGames> <numPlayers> <startSeed>
# Example: npx tsx simulation/src/runner.ts random 1000 4 1

Deterministic Replay

Every game can be replayed from its seed and action log:

# In the web UI, click "Replay" to download a replay JSON
npx tsx simulation/src/replay.ts path/to/replay.json

Multiplayer Architecture

Room System

  • Room Codes: 5-character codes (e.g., "ABCDE") from safe alphabet (no confusing chars)
  • No Logins: Players identified by UUID + secret token stored in localStorage
  • Auto-Reconnect: Tokens allow rejoining after disconnect/refresh
  • TTL: Rooms expire after 2 hours of inactivity

Network Protocol

REST Endpoints:

  • POST /api/rooms - Create room (returns code, player credentials)
  • POST /api/rooms/:code/join - Join room (returns credentials)
  • GET /api/rooms/:code/snapshot - Get current state (auth required)

WebSocket Events (Client โ†’ Server):

  • SET_READY - Toggle ready status
  • START_GAME - Host starts game
  • PROPOSE_ACTION - Submit play/trade action
  • KICK_PLAYER - Host kicks player
  • LEAVE_ROOM - Disconnect

WebSocket Events (Server โ†’ Client):

  • ROOM_SNAPSHOT - Initial state on connect
  • ROOM_UPDATED - Room state changed (player joined/ready/etc)
  • GAME_STARTED - Game began, includes initial game state
  • ACTION_APPLIED - Move executed, new game state
  • ACTION_REJECTED - Invalid move (with reason)
  • KICKED - You were removed from room
  • ERROR - General error message

Server-Authoritative Design

  • All validation on server: Clients propose actions, server validates + applies
  • Prevents cheating: Server is source of truth for game state
  • State broadcast: After each action, server sends updated state to all clients
  • Hand privacy: Each player only receives their own hand in snapshots
  • Turn enforcement: Server rejects actions from non-current player

Multiplayer Integration Tests

The simulation/src/multiplayer-test.ts runs automated tests:

  • โœ… Invalid token rejection
  • โœ… Turn enforcement (wrong player can't act)
  • โœ… Reconnect preserves player state
  • โœ… Full 2/3/4-player games with bot clients
  • โœ… Duplicate name rejection

Invariants Enforced

The engine checks these after every action:

  1. Tile count conservation: 108 tiles always distributed across bag + hands + board
  2. No duplicate tile IDs: Every tile has a unique ID tracked globally
  3. Board line validity: No line exceeds 6, no duplicates within lines, all tiles share one attribute
  4. Board connectivity: All board tiles form a single connected component
  5. Hand size: Never exceeds 6
  6. Score non-negativity: All scores >= 0
  7. Turn synchronization: Turn counter matches action log length

Evidence Summary

Final Test Results

Test Count Result
Random agent 2-player games 1,000 All clean
Random agent 3-player games 200 All clean
Random agent 4-player games 200 All clean
Heuristic agent 2-player games 500 All clean
Heuristic agent 3-player games 100 All clean
Heuristic agent 4-player games 100 All clean
Judge state checks (random) 1,100 0 violations
Judge state checks (heuristic) 537 0 violations
Regression tests 1 All passing
Multiplayer integration (2p) 5 tests All passing
Multiplayer integration (3p) 5 tests All passing
Multiplayer integration (4p) 5 tests All passing

Bugs Found & Fixed

  1. Turn sync at endgame (turn_sync_endgame): The turn counter was not incremented when the game ended via the empty-hand + empty-bag path, causing a mismatch between state.turn and state.actionLog.length. Fixed by incrementing turn before setting gameOver. Regression test auto-generated.

Game Statistics

Metric Random Agent Heuristic Agent
Avg turns (2p) 107.4 50.6
Avg score (2p) 151 / 150 205 / 207
Avg turns (3p) 106.3 50.3
Avg turns (4p) 105.4 49.2

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages