|
All checks were successful
Build and Publish / build (push) Successful in 2m31s
|
||
|---|---|---|
| .forgejo/workflows | ||
| .mvn/wrapper | ||
| frontend | ||
| Harena API | ||
| screenshots | ||
| src | ||
| .gitattributes | ||
| .gitattributes:Zone.Identifier | ||
| .gitignore | ||
| .gitignore:Zone.Identifier | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| HARENA_ARENAS.md | ||
| HARENA_ARENAS.md:Zone.Identifier | ||
| HELP.md:Zone.Identifier | ||
| mvnw | ||
| mvnw.cmd | ||
| pom.xml | ||
| pom.xml:Zone.Identifier | ||
| README.md | ||
| renovate.json | ||
Harena
A gladiator management game built API-first. You are a lanista — master of a gladiatorial school in ancient Rome. Train your fighters, send them to the arena, watch them die. The API is the game.
Inspired by Top Eleven (Nordeus). Designed so humans and AI agents play through the same REST API.
Quick start
# Run in dev mode (H2 in-memory database, no setup needed)
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
Open http://localhost:8080 for the game UI, or http://localhost:8080/swagger-ui.html for the raw API explorer.
Game loop
Every action is an API call. No UI required.
1. Create your ludus
POST /api/v1/ludus
{ "name": "Ludus Magnus" }
Gives you 5000 denarii and 3 random gladiators.
2. Seed the market
POST /api/v1/game/tick
Populates the market with ~8 gladiators for sale and processes any pending training.
3. Inspect your roster
GET /api/v1/gladiators
Check stats, health, state, and readyToFight.
4. Send someone to fight
POST /api/v1/fights
{ "gladiatorId": "<uuid>", "aggression": "BALANCED" }
The system generates a matched opponent. Aggression choices: CAUTIOUS / BALANCED / AGGRESSIVE.
5. Resolve the fight
POST /api/v1/fights/<id>/resolve
The match engine runs turn-by-turn. Read eventLog in the response for the play-by-play. Gladiators can die permanently.
6. Train between fights
POST /api/v1/gladiators/<id>/train
{ "stat": "STRENGTH" }
POST /api/v1/game/tick ← training applies here (+3 to +5 points)
7. Buy from the market
GET /api/v1/market
POST /api/v1/market/<listingId>/purchase
Full snapshot (useful for AI agents)
GET /api/v1/game/state
Running with PostgreSQL
# Start the database
docker-compose up -d db
# Run the app
./mvnw spring-boot:run
Or run everything at once:
docker-compose up
Frontend
A Svelte 5 UI is included in frontend/. It embeds into the Spring Boot JAR as static files.
# Dev mode — hot-reload UI against the running backend
cd frontend
npm install
npm run dev # http://localhost:5173 (proxies /api to :8080)
# Build and embed into the JAR (requires Node on PATH)
./mvnw package -P build-frontend
The production build writes to src/main/resources/static/ so Spring Boot serves the UI at /.
Development
# Run tests
./mvnw test
# After editing openapi.yaml — regenerate interfaces
./mvnw generate-sources
# Build a Docker image
docker build -t harena .
OpenAPI-first workflow
The API contract lives in src/main/resources/static/openapi.yaml. It is the single source of truth.
When you change the spec:
- Run
./mvnw generate-sources - Generated interfaces and models are updated in
target/generated-sources/ - The compiler flags any controller that no longer satisfies the interface
- Update the controller
The spec is served at http://localhost:8080/openapi.yaml and can be imported directly into Bruno or Postman.
Tech stack
| Language | Kotlin 1.9.25 (JVM 21) |
| Framework | Spring Boot 3.5.11 |
| Database | PostgreSQL (default), H2 (dev) |
| Migrations | Flyway |
| API contract | OpenAPI 3, code-generated via openapi-generator |
| API docs | Swagger UI (/swagger-ui.html) |
| Frontend | Svelte 5 + Vite 5 (embedded in JAR) |
| Testing | JUnit 5, MockK, Testcontainers |
Game mechanics
Fighting styles (rock-paper-scissors matchup)
| Style | Beats | Loses to |
|---|---|---|
| MURMILLO | RETIARIUS | THRAEX |
| RETIARIUS | SECUTOR | MURMILLO |
| THRAEX | MURMILLO | SECUTOR |
| SECUTOR | THRAEX | RETIARIUS |
Aggression settings
| Setting | Attack | Defense | Death chance on KO |
|---|---|---|---|
| CAUTIOUS | 0.75× | 1.20× | 10% |
| BALANCED | 1.00× | 1.00× | 25% |
| AGGRESSIVE | 1.35× | 0.75× | 55% |
Tick processing (in order)
- Training completes — 3–5 stat points applied, gladiator returns to RESTING
- Injured heal — INJURED (+10 HP/tick → RECOVERING at 30 HP), RECOVERING (+15 HP/tick → RESTING at 80 HP)
- Upkeep — 10 denarii per living gladiator
- Morale — +5 per gladiator (capped at 100)
- Market — topped up to ~8 available listings
Gladiator states
RESTING— ready to fight or trainTRAINING— gains stat on next tickINJURED— health < 30, heals slowlyRECOVERING— health 30–79, heals fasterDEAD— permanent