- Java 49.5%
- JavaScript 31.5%
- CSS 18.3%
- HTML 0.3%
- Dockerfile 0.2%
- Other 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| backend | ||
| docs | ||
| frontend | ||
| tests | ||
| .gitignore | ||
| docker-compose.yml | ||
| PROTOCOL.md | ||
| README.md | ||
| run-tests.sh | ||
MyBattleship
A two-player, real-time online Battleship game built from the Group 1 design
documents (Group1Assign2.pdf and
RefinedRequirementsSpecification-Group1.pdf).
The game is server-authoritative: all placement, shot resolution, mine chains, scoring, turn timers and win detection happen on the Java backend. The React client is a renderer that sends intents over WebSocket.
What is implemented
- React single-page client with five screens: home/lobby browser, waiting room, fleet deployment, tactical combat and the win/loss overlay.
- Java 21 backend with no runtime dependencies: a small HTTP + WebSocket server, an authoritative game engine and an in-memory lobby service.
- 10 × 10 boards with three icebergs per player and five mines seeded after both fleets deploy.
- Fleet of five ships: Carrier 5, Battleship 4, Cruiser 3, Submarine 2, Destroyer 1.
- Interactive placement with hover previews, green/red validity highlighting,
Rrotation, server-side rejection reasons and a fleet dock. - Fog-of-war combat: hidden enemy ships and mines; misses, hits, iceberg reveals, cross-shaped mine blasts and chain reactions.
- 30-second turn timer, automatic turn pass, random first player and disconnect grace/forfeit.
- Scoring and persistent scoreboard: per-match score plus lifetime wins/losses and ships sunk, kept even after lobbies expire.
- Lobby lifecycle: public/private lobbies, six-character codes, ready check, host start, idle cleanup and a finished-lobby TTL so results can be reviewed.
- Rematch flow, session resume after a refresh, reconnect handling, battle log and a synthesised audio pipeline (music plus water/hit/mine/ice effects).
- Docker deployment matching the design's Nginx → Java architecture, with WebSocket proxying and an HTTPS-ready reverse-proxy boundary.
Quick start
Option A — single Java process (production-style local run)
Requirements: Java 21+ and Node 22+ (Vite 8 / React 19).
# Build the React bundle once
cd frontend
npm install
npm run build
cd ..
# Compile and start the Java server (serves frontend/dist on :8080)
./backend/run.sh
Open http://localhost:8080. The backend serves the React build, the /api
endpoints and the /ws WebSocket on the same port.
Option B — frontend dev server with hot reload
Terminal 1:
./backend/run.sh
Terminal 2:
cd frontend
npm install
npm run dev
Open http://localhost:5173. Vite proxies /ws and /api to the Java server
on :8080.
Option C — Docker Compose
docker compose up --build
Open http://localhost:8080. Nginx serves the React build and proxies /ws
and /api to the Java container, matching the deployment diagram in the design
document. Put this stack behind a TLS-terminating reverse proxy for HTTPS.
Docker error: Extension DNAT revision 0 not supported
This is a host Docker/kernel problem, not a project problem. Docker must install
an iptables DNAT rule for ports: "8080:80". If it reports:
Unable to enable DNAT rule ... iptables ... Extension DNAT revision 0 not supported
the running kernel does not have matching netfilter modules available. Check:
uname -r # running kernel
ls /lib/modules # kernels that actually have modules installed
If those do not match (for example running 7.2.11-arch1-1 while only
7.2.14-arch1-1 is installed), reboot into the installed kernel or install the
matching kernel modules, then:
sudo modprobe xt_nat
sudo systemctl restart docker
docker compose up --build
Do not change the compose file to network_mode: host for this — published port
mapping requires Docker's NAT support, and the error will disappear once the host
kernel and its modules match.
How to play
- Enter a commander name.
- Create a lobby (set an optional password to keep it private) or join an open lobby. Alternatively, share the six-character lobby code and use Join by code.
- Both players click I'm Ready (the host can also use Start Game once both are ready). The server creates both boards and seeds three icebergs.
- Deploy all five ships. Icebergs and existing hulls block placement; the
preview turns red and the server rejects invalid placements with the exact
blocking square. Press
Ror use Rotate to flip the selected ship. - Once both fleets are deployed, the server seeds five mines per board, picks a random first player and starts the 30-second turn clock.
- Click a square in enemy waters. Misses, hits, icebergs, mines and chain explosions are resolved centrally. A valid shot ends your turn; an invalid shot (iceberg, already fired, out of bounds) keeps it.
- Sink all five enemy ships to win. The overlay shows final score, match duration, enemy losses and lifetime record. Choose Rematch or Return to Lobby.
Scoring
| Event | Points |
|---|---|
| Miss | 10 |
| Hit a ship segment | 100 |
| Sink a ship | +300 bonus |
| Detonate a mine | 150 (+300 per ship sunk in the blast) |
| Win | +500 |
Scores are per match; the scoreboard also stores lifetime totals.
Project structure
mybattleship/
├── backend/
│ ├── src/main/java/
│ │ ├── gameengine/
│ │ │ ├── model/ Board, BoardTile, Ship, Coordinate, enums
│ │ │ ├── placement/ PlacementValidator
│ │ │ ├── combat/ ShotProcessingEngine, ExplosivesLogic, CombatSession
│ │ │ ├── lobby/ GameLobby, LobbyManager, ScoreBoard, Player
│ │ │ └── rules/ GameRulesController
│ │ └── server/ Main, GameServer, WebSocketServer, Json, ClientSession
│ ├── test/java/ EngineTests (dependency-free engine suite)
│ ├── build.sh / run.sh
│ └── Dockerfile
├── frontend/
│ ├── src/
│ │ ├── App.jsx socket orchestration and screen routing
│ │ ├── components/ Home, Lobby, Placement, Game, Board, FleetPanel, overlay
│ │ ├── lib/ socket hook, audio synthesiser, game helpers
│ │ └── styles.css full visual system
│ ├── vite.config.js
│ ├── nginx.conf / Dockerfile
│ └── package.json
├── tests/integration.mjs full protocol/gameplay E2E test
├── PROTOCOL.md WebSocket message contract
├── docker-compose.yml
└── run-tests.sh
Architecture
Browser (React)
│ JSON intents over WebSocket (/ws)
▼
Nginx (Docker) ── serves frontend/dist, proxies /ws and /api
│
▼
GameServer (Java 21)
├── Session registry / resume tokens
├── LobbyManager ── GameLobby × N ── ScoreBoard
├── PlacementValidator + GameRulesController
└── CombatSession ── ShotProcessingEngine ── ExplosivesLogic
│
└── authoritative Board[10][10] × 2
- A single-threaded game executor serialises all game mutations, making turn resolution race-free even though WebSocket connections are concurrent.
- Each client receives a personalised
gameStatewith fog-of-war applied to the opponent board. - All action messages are validated against the server's state. The client never computes damage, mine chains, scores or turn changes.
- See
PROTOCOL.mdfor the complete message reference.
Design documents → implementation
| Design artefact | Implementation |
|---|---|
GameServer controller |
server/GameServer.java |
GameLobby, Player, LobbyStatus |
gameengine/lobby/ |
Board, BoardTile, Ship, Shot payload |
gameengine/model/ |
PlacementValidator boundary/collision loop |
gameengine/placement/PlacementValidator.java |
ShotProcessingEngine + ShotOutcome |
gameengine/combat/ShotProcessingEngine.java |
ExplosivesLogic cross blast |
gameengine/combat/ExplosivesLogic.java |
ScoreBoard, LobbyManager cleanup |
gameengine/lobby/ScoreBoard.java, LobbyManager.java |
GameRulesController |
gameengine/rules/GameRulesController.java |
| Sound pipeline (client) | frontend/src/lib/audio.js |
| Nginx + Java deployment | docker-compose.yml, frontend/nginx.conf |
Deliberate choices / deviations
- React was requested by the user; Vite is used for the build. The design documentation described a generic client frontend.
- No framework backend. The documents specify Java; the server uses plain
Java 21 (
com.sun.net.httpserveris not even needed — HTTP and WebSocket are implemented directly) so it has zero third-party runtime dependencies and is easy to build withjavac. - Ship lengths follow the explicit refined requirement (one each of 1–5) rather than the illustrative health-dot counts in the mockup.
- Finished lobbies remain briefly (assign2's
finishedTtl) so both players can see the result and choose a rematch, even though one scenario in the refined specification describes immediate deletion. Assign2 takes precedence. - Audio is synthesised with Web Audio rather than shipped asset files, so the game remains fully offline-capable. Effects map to water, ship impacts, icebergs and mine blasts; a looping sea-shanty style track plays quietly.
- Scoring is a documented, deterministic algorithm (above) because the documents specify a score but do not prescribe exact points.
Configuration
| Environment variable | Default | Meaning |
|---|---|---|
PORT |
8080 |
HTTP/WebSocket port |
FRONTEND_DIR |
frontend/dist (auto-detected) |
static bundle to serve |
TURN_SECONDS |
30 |
turn timer (minimum 5 in code) |
DISCONNECT_GRACE_SECONDS |
30 |
reconnect window before forfeit |
IDLE_TIMEOUT_SECONDS |
600 |
waiting/abandoned lobby cleanup |
FINISHED_TTL_SECONDS |
300 |
finished-lobby retention |
TEST_PORT |
8091 |
port used by the integration test |
HTTP helpers: GET /api/health, GET /api/lobbies, GET /api/stats,
GET /api/config.
Tests
./run-tests.sh
This compiles the backend and runs:
EngineTests— placement bounds/iceberg collisions, mine blast shape and chain reactions, turn consumption, invalid-shot handling, winning shot, scoreboard accumulation and lobby lifecycle.tests/integration.mjs— a full two-player protocol test. It spawns a server on a test port and verifies lobby creation, full-lobby rejection, fog-of-war, invalid placement, deployment, turn ownership, iceberg shots, scoring, turn timeout, a complete match, game-over payloads, rematch, disconnect forfeit, session resume and the HTTP API.
The UI was additionally exercised with two headless Firefox sessions (create → join → ready → deploy → full match → win/loss overlays).
Limitations / next steps
- Game state is in memory; a production deployment would add Redis or a database for multi-instance scale and durable scoreboards.
- Lobby passwords are stored in memory in plain text. Add hashing and rate limiting before exposing the service publicly.
- TLS is expected to be terminated by the reverse proxy in front of the compose stack.
- There is no spectator mode or chat; the design documents only require two players.
