# Bots

> Players the room drives itself, which take seats, appear in presence, and give their seat to a person who joins a full room.

A **bot** is a player that your room's code drives instead of a person:
an opponent for someone playing alone, or a seat filled so a game can
start. A bot is in presence like anyone else, owns entities, and sends
input to your stepper, but it has no connection. It runs inside the
room, so it costs no traffic and doesn't keep the room awake.

A creature or hazard that isn't a player doesn't need a bot. Make it an
entity the room owns and move it in `onTick`.

```ts
// src/room.ts
import { defineRoom, defineSchema, defineStepper, entity } from "@irt/room";

const Car = entity("car", { owner: "string", x: "f32" });

export default defineRoom({
  schema: defineSchema({ entities: [Car] }),
  stepper: defineStepper<number>({
    step(world, inputs) {
      for (const car of world.all(Car)) car.x += inputs.get(car.owner) ?? 0;
    },
  }),
  bots: {
    racer: {
      start(room, bot) {
        if (![...room.entities.all(Car)].some((c) => c.owner === bot.player)) room.entities.create(Car, { owner: bot.player, x: 0 });
      },
      tick(room, bot) {
        bot.input = bot.data.fast ? 2 : 1;
      },
    },
  },
  onCreate(room) {
    room.bots.add("racer", { name: "Rival", data: { fast: true } });
  },
  onJoin(room, conn) {
    room.entities.create(Car, { owner: conn.player, x: 0 });
  },
  onPlayerLeave(room, player) {
    for (const car of room.entities.all(Car)) if (car.owner === player) room.entities.delete(car);
  },
});
```

## A bot

- **Its player ID** is `bot:` and a number the room gives out in order,
  never reused while the room exists. No player token can have a `sub`
  starting with `bot:`, so nobody can pose as a bot. `isBot(player)`,
  from `@irt/client` or `@irt/room`, tells them apart.
- **Behaviours** are declared by name in `bots` in `defineRoom`, and
  `room.bots.add(name, { name?, status?, data? })` adds a bot running
  one. `start` runs when the bot is added and again after each wake;
  `tick` runs every tick before the stepper, in the order the bots were
  added. Both must be synchronous.
- **Input.** With a stepper, `tick` sets `bot.input` and the stepper
  gets it under the bot's player ID, so a bot's car moves by the same
  `step` as a person's, and clients predict and interpolate it like any
  other player's. The room doesn't run `parseInput` on it, since it's
  your own code. Without a stepper, a behaviour changes entities itself.
- **`bot.data`** is the behaviour's own JSON, at most 4 KB
  (`MAX_BOT_DATA`), saved in room state.
- **Ownership and private state** work by player ID, as for people.
  `onWrite` doesn't run for a bot's entities: the room writes them
  directly.
- **`room.bots.remove(bot)`** ends the bot's presence and runs
  `onPlayerLeave` for it before returning, so code that cleans up after a
  player cleans up after bots too. Bots never cause `onJoin` or
  `onLeave`, which take a connection.
- **A behaviour that throws,** or returns a promise, has the error logged
  and its bot removed, and the room keeps running. So does a bot whose
  `data` stops being JSON or grows over 4 KB.
- **Limits.** A room holds at most 64 bots (`MAX_BOTS`). Bots count
  toward presence's 1,024 entries. The tick profile shows their `tick`s
  as `bots`.
- **No player data.** Bots have no player data, own no collection
  entries and submit no scores, since those outlast the room.
  `room.playerData` throws for a bot, and the data service answers a
  bot's ID with `E_INVALID`. Voice leaves bots out.

## Seats

`room.maxPlayers` is the room's seat limit: its `maxPlayers`, or its room
size's connection limit. People and bots together fill at most that
many, and `room.bots.add` throws when they already do.

A person can always take a bot's seat. When a person joins a room that
people and bots fill, the room removes the bot added last in the join
itself: `onPlayerLeave` runs for the bot, then `onJoin` for the person,
before the person's first state update. Give the bot's entities to the
person in `onJoin` if the game should carry on from where the bot was.

The router counts only people, so the room list and quick join see a
room with one person and three bots as having one player. Quick join
still prefers rooms with more people.

## Hibernation and wake

Bots are room state: their IDs, names, statuses, behaviours and `data`
are saved and come back after a wake or a room host crash. Before the
room hibernates, `onPlayerLeave` runs for every person in presence but
not for bots. On wake each bot is back in presence before `onWake`, and
its `start` runs after `onWake`.

Bots hold no connection, so a room with only bots in it hibernates 30
seconds after its last connection closes, like any room. A game that
should finish without people ends itself in `onLeave` or
`onPlayerLeave`.

## Simulating and testing

A room's bots are room code, so [`irt simulate`](/docs/guides/simulate)
and [`@irt/testing`](/docs/guides/testing) run them with nothing more to
set up. Their simulated bots are client connections, a different thing:
spawn them into a room whose bots fill it to test that people take their
seats. A [Node client](/docs/guides/node-client) can play a room as an
external bot, connecting like a person with its own token, for a game
whose logic lives only in its client.
