Skip to content

ChrisSc/contact-v2

Repository files navigation

CONTACT V2: 3D Naval Combat

Built with Claude TypeScript License: MIT

Online multiplayer 3D naval combat game. Two players command submarine fleets hidden in a 7x7x7 volumetric grid (343 cells), firing torpedoes and deploying earned abilities to locate and destroy enemy vessels. Features server-authoritative gameplay, persistent player profiles, invite-based matchmaking, and AI opponent modes (BYOK).

Architecture

contact-v2/
├── packages/
│   ├── shared/    @contact/shared — types, config, pure engine functions, AI tools
│   ├── client/    @contact/client — Vite SPA: Three.js renderer, Tone.js audio, UI
│   └── server/    @contact/server — Fargate container: REST API, WebSocket, session management, persistence
│       ├── auth/        JWT token verifier for WebSocket upgrade
│       ├── db/          DynamoDB repositories (players, sessions, invites)
│       ├── middleware/  JWT auth, session access validation, rate limiter (sessions, profiles, invites)
│       ├── notifications/ SES email service for invites
│       ├── routes/      REST endpoints (profile, sessions, session-actions, invites)
│       ├── session/     GameSession wrapper, SessionStore, state filter, DynamoDBLogger, LogArchiver, LogUrlGenerator
│       └── ws/          WebSocket handler, message router, turn timer, reconnection
├── infra/         AWS CDK — Cognito, DynamoDB, ECS Fargate, ALB, S3, CloudFront
├── scripts/       CLI tools: simulate, analyze-log
├── tests/         Mirrors packages/ structure
├── artifacts/     Design docs and V2 specs
└── docs/          JSONL log format reference

@contact/shared — Pure TypeScript with no DOM, Three.js, or Tone.js imports. Contains the game engine (grid, fleet, combat, credits, perks, abilities), type definitions, GameConfig factory, LoggerAdapter interface, AI briefing/tools, and observability.

@contact/client — Single-page application built with Vite. Handles 3D rendering (Three.js), synthesized audio (Tone.js), all UI screens and components, WebSocket client (GameClient with auto-reconnection), and client-side AI opponent integration (BYOK). Screens run in dual mode: local (GameController) or online (GameClient over WebSocket).

@contact/server — Express REST API + WebSocket server on Fargate. GameSession wraps GameController with player identity mapping, turn ownership validation, parallel setup, and anti-cheat state filtering. WebSocket layer provides real-time message routing, server-enforced 90-second turn timer with consecutive-timeout forfeit, and 90-second reconnection window with timer pause. DynamoDB persistence for session meta, game snapshots, and event logs.

infra/ — AWS CDK V2 app. Defines Cognito User Pool (Google OAuth), DynamoDB single-table (GSI for join codes, TTL for auto-expiry), SES email identity for invite notifications, ECS Fargate service behind an ALB with sticky sessions (24h lb_cookie) and 120s idle timeout for WebSocket support, PostConfirmation Lambda for auto-provisioned player profiles, S3 bucket for game logs (lifecycle: IA→Glacier→delete), S3 bucket for client assets with OAC, CloudFront distribution with security response headers (CSP, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy), and Route53/ACM for custom domain.

Tech Stack

Client

Layer Choice
Language TypeScript 5.x
Build Vite 6.x
3D Rendering Three.js r128
Audio Tone.js 14.x (synthesized, no sample files)
UI Vanilla TypeScript + DOM (no frameworks)
Testing Vitest
Fonts Press Start 2P + Silkscreen (Google Fonts CDN)

Server + Infrastructure

Layer Choice
Server Runtime Node.js/TS on AWS Fargate
HTTP Express
WebSocket ws
Auth AWS Cognito (Google OAuth)
Database DynamoDB (single-table design)
CDN CloudFront (client assets, /api/* + /ws/* proxy to ALB)
Infra-as-Code AWS CDK V2

Getting Started

npm install
npm run dev

Opens the client at localhost:5173 with hot module replacement.

To run the server locally:

npm run dev:server

Starts the Express API on localhost:3001. Requires Cognito environment variables for auth (see packages/server/CLAUDE.md).

Commands

Command Description
npm run dev Dev server with HMR on localhost:5173
npm run build Typecheck + production build to dist/
npm run build:single Single portable HTML file (dist/contact.html)
npm run typecheck Run tsc --noEmit across all packages
npm run test Run tests in watch mode
npm run test:run Run tests once
npm run dev:server Express API server with hot reload on localhost:3001
npm run cdk:synth Synthesize CloudFormation template
npm run cdk:deploy Full deploy: CDK + post-deploy env + client build + S3 sync
npm run cdk:diff Preview infrastructure changes
npm run cdk:destroy Tear down infrastructure
npm run simulate Run bot-vs-bot game simulations

Game Modes

Currently Available

Human vs AI (BYOK) — Play as ALPHA against Claude as BRAVO. Select VS AI on the title screen, choose a model (Haiku/Sonnet/Opus), and enter your Anthropic API key. Key is validated client-side before starting. Your key stays client-side and never touches CONTACT servers. AI actions are routed through the server via WebSocket for state consistency.

Online Multiplayer — Real-time two-player matches via WebSocket. Create a session and invite an opponent via email (30-minute expiry, deep link join), or share the join code directly. Server-enforced 90-second turn timer, automatic timeout forfeit (2 consecutive), and 90-second reconnection window. Select ONLINE on the title screen.

How to Play

  1. Setup: Each player places 7 submarines + 1 decoy in the 7x7x7 grid (AI places automatically in VS AI mode)
  2. Combat: Alternate turns — fire a torpedo, use a perk, or do both (one per slot)
  3. Victory: Sink all 7 enemy subs to win

Each turn you have three action slots: one ping, one attack, and one defend. Firing a torpedo always uses the attack slot. Perks consume the slot matching their type. Only the attack slot is required to end your turn.

Credit Economy

Credits fund the perk store. You earn them by landing shots:

Action Credits
Hit +1
Consecutive hit (chain) +3 bonus
Sink +15

Starting credits: 5. Credits accumulate across turns within a game.

Rank (Difficulty)

Select a rank on the title screen to control the stalemate bonus — a dry-spell mechanic that awards bonus credits when neither player makes contact for too long.

Rank Dry Turn Threshold Bonus Credits Description
Recruit 8 +8 Generous bonuses keep the perk economy flowing
Enlisted 10 +5 Moderate safety net for intermediate players
Officer -- -- No bonuses (default, original experience)

When the threshold is reached, both players receive the bonus. The counter resets on any contact (torpedo hit, sonar positive, drone contact, depth charge hit, or decoy hit).

Fleet

Vessel Size
Typhoon 5
Akula 4
Seawolf 3
Virginia 3
Narwhal 3
Midget Sub 2
Piranha 2

Ships may be placed along 8 axes (any direction except purely vertical):

  • Within a depth slice: col, row, diag+, diag-
  • Crossing depth layers: col-depth, col-depth-, row-depth, row-depth-

Press R during placement to cycle axes. Press F to flip direction.

Perk Store

Perk Slot Cost Description
Sonar Ping Ping 2 Scans a 2x2x2 volume (up to 8 cells) for ship presence
Radar Jammer Defend 12 Inverts the next enemy Sonar Ping; suppresses Recon Drone
Recon Drone Attack 10 Reveals contents of a 3x3x3 volume (up to 27 cells)
Silent Running Defend 8 Masks one ship from recon scans for 2 opponent turns
Acoustic Cloak Defend 14 Masks your entire fleet from recon for 2 opponent turns
G-SONAR Attack 14 Scans a full depth layer (49 cells)
Depth Charge Attack 20 Strikes all occupied cells in a 3x3x3 volume

Keyboard Shortcuts

Key Action
R Cycle placement axis (setup)
F Flip placement direction (setup)
S Toggle own grid / targeting grid (combat)

View Modes

Switch between three 3D views during combat:

  • Cube: Full volumetric 7x7x7 cube, orbit freely
  • Slice: Single depth layer shown as a flat grid
  • X-Ray: Semi-transparent cube revealing interior cells

Scripts

Simulation

Run automated bot-vs-bot games to test game balance:

npm run simulate                        # 100 games, officer rank
npm run simulate -- 1000                # custom game count
npm run simulate -- 1 -v                # single game, verbose
npm run simulate -- --rank recruit      # test stalemate bonus
npm run simulate -- 1 --export          # export JSONL log

Log Analysis

Parse any exported JSONL session log into an After Action Report:

npx tsx scripts/analyze-log.ts <log.jsonl>          # formatted report
npx tsx scripts/analyze-log.ts <log.jsonl> --json    # machine-readable JSON

Session Logging

Every game session produces a structured JSONL event log covering placements, shots, perk uses, and phase transitions. Export from the victory screen for analysis.

See docs/JSONL_FORMAT.md for the full event schema.

Docs

Game Design

Architecture + Specs

License

MIT

About

Online multiplayer 3D naval combat in a 7x7x7 volumetric grid. Server-authoritative WebSocket gameplay, BYOK AI opponents (Claude), and structured JSONL observability. TypeScript + Three.js + Tone.js client, Express/Fargate server, AWS CDK infrastructure.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages