A battleship-style guessing game where you play against an AI opponent. Two Spring Boot instances communicate over REST — one for the human player, one for the AI.
Originally created in 2017.
Stack: Java 25, Spring Boot, Spring MVC, Thymeleaf, JQuery/AJAX, Swagger/OpenAPI
| Sub-module | ArtifactId | Responsibility |
|---|---|---|
| engine | xlspaceship-engine |
Game logic, board, ships, AI, models — no Spring Web dependency |
| server | xlspaceship-server |
Spring Boot app — controllers, HTTP validation, Thymeleaf views, static JS/CSS |
The game runs as two separate JVM instances on different ports:
- Player instance — serves the browser UI, handles human input
- AI instance — runs headless, responds to protocol calls and fires back automatically
The browser communicates with the player instance via AJAX. The player instance communicates with the AI instance via REST (the protocol API). After the AI is fired upon, it asynchronously fires its revenge salvo back at the player instance.
- User clicks opponent cells in the browser to form a salvo.
- Browser sends the salvo to the player instance (
UserController.fireInEnemy). - Player instance forwards the salvo to the AI instance (
ProtocolController.fireFromEnemy). - AI instance processes the salvo and returns which cells were hit.
- Player instance updates opponent board and sends the response to the browser.
- AI fires its own salvo back asynchronously to the player instance.
- Browser polls the player instance until the AI salvo is detected and displayed.
- User shoots again.
Detailed architecture: ARCHITECTURE.md
sequenceDiagram
participant Browser
participant Player as Player Instance<br/>:8077
participant AI as AI Instance<br/>:8080
Browser->>Player: click cells (form salvo)
Browser->>Player: PUT /xl-spaceship/user/game/{id}/fire
Player->>AI: POST /xl-spaceship/protocol/game/{id}
AI-->>Player: FireResponse (hit results)
Player-->>Browser: updated grids + turn info
Note over AI: AI fires revenge salvo (async)
AI->>Player: POST /xl-spaceship/protocol/game/{id}
Player-->>AI: FireResponse
Browser->>Player: GET /xl-spaceship/user/game/{id}/status
Player-->>Browser: updated grids with AI shots
- Grid: 16x16 using hexadecimal coordinates (
0x0toFxF) - Ships: 5 types with different shapes and health points
- BClass (10hp), Winger (9hp), SClass (8hp), AClass (8hp), Angle (6hp)
- Salvo mechanic: each turn you fire N shots, where N = number of opponent's alive ships
- Win condition: destroy all 5 opponent ships
- Starting player: randomly selected at game creation
# build everything
mvn -pl xlspaceship -am clean package
# run engine tests only
mvn -pl xlspaceship/engine test
# run server tests only (includes JS tests via frontend-maven-plugin)
mvn -pl xlspaceship/server testThe server module uses frontend-maven-plugin to download Node/npm locally and run JavaScript tests during the Maven test phase. The first build requires network access.
Start both instances from the xlspaceship/ directory:
Player instance (port 8077, human mode):
./run.shjava -jar -Dspring.application.name=xlspaceship-player -Dserver.port=8077 \
server/target/xl.jar nikilipa "Nikita Lipatov"AI instance (port 8080, AI mode):
./runAI.shjava -jar -Dspring.application.name=xlspaceship-ai -Dserver.port=8080 \
server/target/xl.jarWhen no command-line arguments are provided, the application starts in AI mode automatically.
Open the game in a browser: http://localhost:8077
Available on both instances:
| Instance | URL |
|---|---|
| IDEA | http://localhost:8079/swagger-ui/index.html |
| Player | http://localhost:8077/swagger-ui/index.html |
| AI | http://localhost:8080/swagger-ui/index.html |
Endpoints are sorted alphabetically (tagsSorter: alpha, operationsSorter: alpha).
| Method | Path | Description |
|---|---|---|
POST |
/game/new |
Create a new game against a remote opponent |
GET |
/game/{gameId} |
Get game status by gameId |
POST |
/game/{gameId}/rematch |
Create a rematch against the previous opponent |
PUT |
/game/{gameId}/fire |
Fire a salvo at the opponent |
GET |
/game/{gameId}/status |
Get current game status (board HTML + alive ships) |
| Method | Path | Description |
|---|---|---|
POST |
/game/new |
Accept a new game request from a remote instance |
POST |
/game/{gameId} |
Accept an incoming salvo from the opponent |
| Method | Path | Description |
|---|---|---|
GET |
/ |
New game form (index page) |
GET |
/gameId/{gameId} |
Game play page |
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check |
