Welcome to FirstBattle
A coding championship where your API fights for gold.
Important rule: you are free to use AI tools (ChatGPT, Claude, Copilot, etc.) to help write your code. However, your bot must not call an AI service at runtime to decide its moves or allocations. The strategy must be coded in your program — no sending game state to an LLM and asking it what to do next.
What is this?
FirstBattle is a live, long-running game where your HTTP API controls a character
on a grid. Your character collects gold from mines, fights other players, and tries to
climb the leaderboard. The game runs continuously on the server — even when you close
your browser. You watch the live board, think, improve your code, and deploy.
Better strategy = more gold.
Quick Start
- Build an HTTP server (any language) that handles two POST endpoints:
/move and /allocate
- Deploy it so it's publicly reachable (Replit, Render, a VPS, etc.)
- Give your base URL to the game admin (e.g.
https://my-bot.example.com)
- Watch your character on the live game board and iterate!
Game World
- The board is a rectangular grid (default 30 × 20, but configurable by admin). The exact dimensions are sent in every
/move request as gridWidth and gridHeight. Coordinates are 0-indexed. No wrapping — edges are walls.
- Each round takes 2–5 seconds (set by the admin). Your
/move endpoint is called once per round.
- The number of gold mines on the board is configurable by admin (sent as
totalMines in the /move request). When a mine is collected, a new one spawns elsewhere.
- Mine gold base value is configurable by admin (sent as
mineGoldBase). Actual mine gold ranges from the base to roughly 2× the base, and adjusts dynamically based on the economy.
- If multiple players stand on the same mine, the gold is split equally between them.
Movement
- Each round your bot chooses a direction:
"up", "down", "left", or "right".
- Movement is orthogonal only (no diagonals), 1 cell per round.
- Each step costs gold (configurable by admin, sent as
moveCost in the /move request). Standing still is not allowed — if your bot doesn't respond or returns an invalid move, a random direction is chosen.
- Your gold can go negative.
Combat
- Combat is automatic. When two characters are on adjacent cells (Manhattan distance ≤ 1), they enter combat.
- While in combat, characters cannot move and your
/move endpoint is not called.
- Each combat round, both fighters deal damage simultaneously. Damage formula:
damage = max(3, round(attack² / (attack + enemy_defense))). Higher attack deals more; higher defense reduces incoming damage. There is always a minimum of 3 damage per round.
- Combat continues until one fighter's HP drops to 0. The winner keeps their remaining HP (not healed).
Death & Rewards
When a character dies:
- Death penalty — the victim loses a portion of their gold as a penalty. This gold is destroyed (removed from the game, not given to anyone).
- Gold theft — the killer steals a percentage of the victim's remaining gold.
- Kill bounty — the killer receives a bonus bounty (generated gold, not taken from the victim).
- Leader bounty — if the victim is the richest player (the "leader"), the kill bounty is significantly increased. The leader is marked with a crown ♚ on the game board.
- If multiple killers are adjacent, theft and bounty are split equally between them.
- The victim respawns immediately at a random location with full HP.
The bottom line: killing pays well, but dying is costly. Choose your fights wisely.
Tax
Periodically, players with gold above the average are taxed on the excess.
This keeps the economy from running away and gives trailing players a chance to catch up.
Tax deductions are shown in the leaderboard.
Suspension & Inactivity
Your bot must stay online and respond correctly. If your API fails, you will be suspended:
- A player is suspended when their
/move or /allocate endpoint returns an error, times out, or sends an invalid response (e.g. bad JSON, wrong direction, allocation not summing to 100).
- When suspended, your character disappears from the board — no movement, no combat, no gold collection. You are marked as "SUSPENDED" on the leaderboard.
- The game checks suspended players once per minute. If both
/move and /allocate return valid responses, you are automatically unsuspended and your character respawns.
- If you remain suspended for more than 1 hour, you are automatically marked as inactive. Inactive players are fully removed from the game and can only be reactivated by an admin.
- Click a suspended player's name on the leaderboard to see the exact error that caused the suspension.
Keep your bot running and healthy! Fix errors quickly to avoid losing your spot.
Attributes
You have 100 allocation points to distribute across 4 attributes.
The /allocate endpoint is called once per minute, and your new allocation
takes effect immediately.
- HP — your maximum hit points. More HP means you survive longer in combat.
- Attack — how much damage you deal per combat round.
- Defense — reduces incoming damage from enemy attacks.
- Vision — how far you can see (Euclidean distance in grid cells). Vision has diminishing returns — the more you invest, the less each additional point adds.
Every attribute has a base value — even with 0 allocation, you still have some HP, attack, defense, and vision. Your computed stats (after scaling) are sent to you in every /move request in the stats field, so you always know your actual values.
Vision determines what you can see. Mines and enemies within your vision radius appear in the visibleMines and visibleEnemies arrays. You cannot see anything beyond your vision radius.
API Reference
POST /move — called every round (2–5 seconds)
The game server POSTs your bot's current state. You must return a movement decision within 2 seconds.
Request body (JSON):
{
"round": 142,
"gridWidth": 30,
"gridHeight": 20,
"totalMines": 4,
"mineGoldBase": 50,
"moveCost": 1.56,
"position": { "x": 12, "y": 8 },
"hp": 63,
"maxHp": 350,
"gold": 340,
"stats": {
"hp": 350,
"attack": 35,
"defense": 30,
"vision": 17
},
"visibleMines": [
{ "id": 3, "x": 14, "y": 9, "gold": 75 },
{ "id": 7, "x": 10, "y": 6, "gold": 120 }
],
"visibleEnemies": [
{ "playerId": 2, "x": 15, "y": 8, "hp": 40, "gold": 210 }
]
}
Response body (JSON):
{
"direction": "right"
}
Fields:
round — current game round number
gridWidth — board width (x: 0 to gridWidth-1)
gridHeight — board height (y: 0 to gridHeight-1)
totalMines — number of mines currently on the board
mineGoldBase — base gold value for mines (actual mine gold varies)
moveCost — gold cost per step this round
position — your {x, y} on the grid (0-indexed)
hp / maxHp — your current and maximum HP
gold — your current gold (can be negative)
stats — your computed stats (from your allocation)
visibleMines — mines within your vision radius
visibleEnemies — enemies within your vision radius
(playerId, position, hp, gold)
Response fields:
direction — "up" | "down" | "left" | "right"
Notes:
• If your bot times out (>2s) or returns invalid JSON,
a random direction is chosen for you.
• If you are in combat, /move is NOT called (you can't move).
• "up" = y-1, "down" = y+1, "left" = x-1, "right" = x+1.
POST /allocate — called once per minute
Redistribute your 100 attribute points. The new allocation takes effect immediately (stats are recomputed).
Request body (JSON):
{
"round": 300,
"gold": 540,
"currentAllocation": {
"hp": 25,
"attack": 30,
"defense": 20,
"vision": 25
}
}
Response body (JSON):
{
"hp": 25,
"attack": 30,
"defense": 15,
"vision": 30
}
Rules:
• Values must sum to exactly 100.
• Each value must be an integer ≥ 0.
• If the response is invalid or times out (>3s),
your current allocation is kept unchanged.
Minimal Bot Example (Node.js)
Copy this, deploy it, and give the URL to the admin. It's a simple greedy miner that goes for the richest visible mine and flees when low on HP.
server.js
var http = require('http');
http.createServer(function (req, res) {
var chunks = [];
req.on('data', function (c) { chunks.push(c); });
req.on('end', function () {
var body = JSON.parse(Buffer.concat(chunks).toString());
var result;
if (req.url === '/move') {
result = decideMove(body);
} else if (req.url === '/allocate') {
result = { hp: 20, attack: 15, defense: 15, vision: 50 };
} else {
res.writeHead(404);
return res.end();
}
var out = JSON.stringify(result);
res.writeHead(200, {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(out)
});
res.end(out);
});
}).listen(process.env.PORT || 3000);
function decideMove(state) {
// Flee if low HP and enemies nearby
if (state.hp < state.maxHp * 0.3
&& state.visibleEnemies.length > 0) {
var enemy = state.visibleEnemies[0];
return flee(state.position, enemy);
}
// Go to richest visible mine
var mines = state.visibleMines
.sort(function (a, b) { return b.gold - a.gold; });
if (mines.length > 0) {
return moveToward(state.position, mines[0]);
}
// Wander randomly
var dirs = ['up', 'down', 'left', 'right'];
return {
direction: dirs[Math.floor(Math.random() * 4)]
};
}
function moveToward(pos, target) {
var dx = target.x - pos.x;
var dy = target.y - pos.y;
if (Math.abs(dx) >= Math.abs(dy)) {
return { direction: dx > 0 ? 'right' : 'left' };
}
return { direction: dy > 0 ? 'down' : 'up' };
}
function flee(pos, enemy) {
var dx = pos.x - enemy.x;
var dy = pos.y - enemy.y;
if (Math.abs(dx) >= Math.abs(dy)) {
return { direction: dx >= 0 ? 'right' : 'left' };
}
return { direction: dy >= 0 ? 'down' : 'up' };
}
Strategy Tips
- Vision is king for mining — high vision lets you see more mines and take the shortest path. Thanks to diminishing returns, even a moderate allocation gives great results.
- Don't wander aimlessly — each step costs gold (check
moveCost). If you can't see a mine, pick a direction and commit.
- Combat is risky — you can gain a lot from killing (theft + bounty), but you also lose gold to the death penalty when killed. Know when to fight and when to run.
- Killing the leader pays big — the richest player has an increased bounty on their head. Hunt them if you're strong enough.
- Tax favors the underdog — players above average gold are taxed periodically. If you're behind, the game naturally helps you catch up.
- Adapt your allocation — the
/allocate call lets you change strategy mid-game. Switch to combat stats when you see a target, then back to vision for mining.
- Your API must respond fast —
/move has a 2-second timeout, /allocate has 3 seconds. If you time out, default behavior is used.