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 bot 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.
How does my bot talk to the game?
Your bot is a small program that runs on the internet and waits for messages. The game server
sends your bot a message every few seconds saying "here is what's around you — where do you
want to go?" Your bot replies with a direction: up, down, left, or right. That's it!
Technically, your bot is an HTTP server — a program that listens for requests
over the web (just like a website does). The game sends it a POST request
(a message with data) to your /move address, and your bot sends back a
JSON response (a small structured text) with your chosen direction.
If this sounds new, don't worry — the example code at the bottom of this page does all
the technical stuff for you. You just need to focus on the strategy: the "if this,
then go there" logic.
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.
- Each round outside of combat, your character regenerates 1% of max HP (up to 100%). This means injured survivors gradually recover over time.
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. Base damage formula:
attack² / (attack + enemy_defense). Higher attack deals more; higher defense reduces incoming damage.
- Fights are compressed to ~3 rounds: damage is scaled up so the loser dies in about 3 rounds. The outcome (winner, remaining HP) is the same as an unscaled fight — just faster. There is a minimum damage of 20 per round, so some fights end in 1–2 rounds.
- The winner keeps their remaining HP (not instantly healed). However, every round outside of combat, characters regenerate 1% of their max HP.
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.
Think Like a Strategist
You don't need to be an expert programmer to win. When you're using AI tools to help write
your bot, focus on describing the behavior you want — the "if I see this,
do that" rules. Here are some ideas to get you thinking:
- Go towards the nearest gold mine — but if two mines are close, pick the richer one.
- Run away from enemies with more HP than you — don't pick a fight you'll lose.
- Chase weak enemies who are carrying a lot of gold — an injured, rich player is a great target.
- Don't walk right into an enemy — if a mine has an enemy standing on it, maybe go for a different mine instead.
- Explore when you can't see anything — if no mines or enemies are visible, move in a pattern (zigzag, spiral) instead of randomly bouncing around.
- Stay near the center — edges and corners limit your escape routes. The middle of the board gives you more options.
- Switch your stats based on the situation — when you're rich and leading, boost defense and HP to survive. When you're behind, boost attack and go hunting.
Try telling your AI assistant something like: "When I see an enemy with less HP than me
and they have more than 100 gold, chase them. Otherwise, go to the closest mine.
If I don't see any mines, move in a zigzag pattern." That's a solid starting strategy!
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 day, 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' };
}