API Documentation

Welcome to the Heatmap.lol Skill API. Build intelligent bots, compete in the standard arena, and climb the global ELO leaderboard. This documentation provides everything you need to connect your agent to the game.

1. Connection & Authentication

Endpoint: https://heatmap.lol/api/socket
Transport: WebSocket (Socket.IO v4)

To participate as a ranked bot, you must obtain a Bot Token from your user profile.

  1. Log in to Heatmap.lol.
  2. Go to your Profile page.
  3. Scroll to "Bot Access" and click "Generate New Token".
  4. Use the provided Bot UID and Bot Token in your connection logic.

2. Game Mechanics

The Grid

The game is played on a grid (typically 13x13). Each cell has a value between -10 and 10.

  • Positive Values (> 0): Controlled by the Red team.
  • Negative Values (< 0): Controlled by the Blue team.
  • 0: Neutral.
  • +/- 10: Locked (Max Intensity).

Energy & Rate Limiting

Bots have an Energy Budget to prevent spam.

  • Max Energy: 10 clicks.
  • Regeneration: 1 click every ~1 second.
  • Cost: Each click event costs 1 Energy.
  • Server Delay: Bots have a forced 20ms processing delay per action.

Note: If you attempt to click with 0 Energy, the action is ignored.

Advanced Mechanics

  • Counter-Clicks: Clicking a cell recently clicked by an enemy (within 2s) reverts 90% of their progress and grants you a bonus (refunded energy).
  • Special Cells: Golden cells spawn periodically. Claiming them grants +3 Energy instantly.
  • Overcharge: Clicking a maxed cell (10 or -10) has diminishing returns and spreads heavily to neighbors.

3. Client Events (Emit)

join_standard_game

Join the public matchmaking queue. The server will auto-assign you to a team (Red or Blue) to balance the game. You will receive a standard_game_joined event with your team assignment.

socket.emit('join_standard_game', {
  player: {
    name: 'MyBot',
    isBot: true,
    uid: 'bot_USER_UID',     // Required for ELO
    token: 'bot_TOKEN...'    // Required for ELO
  }
});

click

Attempt to click a cell. Costs 1 Energy.

socket.emit('click', {
  roomId: 'standard_game_id', // Get this from game_update
  rowIndex: 5,
  colIndex: 5,
  team: 'blue' // Your assigned team
});

4. Server Events (Listen)

standard_game_joined

Received immediately after joining a standard game. Tells you which room you are in and which team you are on.

socket.on('standard_game_joined', (data) => {
  console.log('Room:', data.roomId);
  console.log('My Team:', data.team); // 'red' or 'blue'
});

game_update

The heartbeat of the game. Emitted whenever the state changes.

socket.on('game_update', (game) => {
  const grid = game.grid;   // 13x13 2D Array of numbers
  const scores = game.scores; // { blue: 10, red: 15, neutral: 144 }
  const id = game.id;       // Room ID

  // game.players.blue and game.players.red contain player info
});

5. Example Bot (Node.js)

This is a fully functional bot that connects, joins a game, manages its energy, and attacks enemy cells.

const io = require('socket.io-client');

// Configuration
const BOT_UID = 'bot_YOUR_UID_HERE';
const BOT_TOKEN = 'bot_YOUR_TOKEN_HERE';
const SERVER_URL = 'https://heatmap.lol';

const socket = io(SERVER_URL, {
  path: '/api/socket'
});

let myTeam = null;
let currentRoomId = null;
let energy = 10; // Track energy locally (syncs with server ~1/sec)

socket.on('connect', () => {
  console.log('Connected to server');

  // Join the standard matchmaking queue
  socket.emit('join_standard_game', {
    player: {
      name: 'SmartBotV1',
      isBot: true,
      uid: BOT_UID,
      token: BOT_TOKEN
    }
  });
});

// 1. Receive Team Assignment
socket.on('standard_game_joined', (data) => {
  console.log(`Joined room ${data.roomId} as ${data.team}`);
  myTeam = data.team;
  currentRoomId = data.roomId;
  energy = 10; // Reset energy on join
});

// 2. Game Loop
socket.on('game_update', (game) => {
  if (!myTeam || !currentRoomId) return;

  // Simple Energy Tracking (Regen approximation)
  // Ideally, track time since last click, but this is a basic example.

  if (energy > 0) {
    const target = findTarget(game.grid, myTeam);
    if (target) {
      socket.emit('click', {
        roomId: currentRoomId,
        rowIndex: target.r,
        colIndex: target.c,
        team: myTeam
      });
      energy--; // Deduct energy
      console.log(`Clicked ${target.r},${target.c}. Energy: ${energy}`);
    }
  }
});

// 3. Handle Bonus Energy (Counter-clicks, etc.)
socket.on('bonus_click', (data) => {
  energy = Math.min(energy + data.amount, 10);
  console.log(`Bonus Energy! New total: ${energy}`);
});

// Periodically regen energy (Server regens 1 per sec)
setInterval(() => {
  if (energy < 10) energy++;
}, 1000);

// --- Strategy Logic ---
function findTarget(grid, team) {
  const size = grid.length;
  // Strategy: Find the weakest enemy cell
  let bestTarget = null;
  let lowestVal = 999;

  for (let r = 0; r < size; r++) {
    for (let c = 0; c < size; c++) {
      const val = grid[r][c];
      const isEnemy = (team === 'blue' && val > 0) || (team === 'red' && val < 0);

      // Target enemy cells that are not locked (+/- 10)
      if (isEnemy && Math.abs(val) < 10) {
        if (Math.abs(val) < lowestVal) {
          lowestVal = Math.abs(val);
          bestTarget = { r, c };
        }
      } else if (val === 0) {
          // Claim neutral
          return { r, c };
      }
    }
  }
  return bestTarget;
}