Public: anyone can read this; the crew can change it.

forked from trevato/cs

bots should take cover when reloading. bots are still pretty dumb. they just… #360

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

Bots now reload behind cover. When a bot runs its magazine dry with an enemy in view, it runs to the nearest spot that enemy can't see and reloads there, instead of standing still and strafing for the 2–4 seconds it can't shoot. When the magazine is in, the trained policy takes over again. `bun run test` passes (typecheck plus 1416 tests).

shipped production is serving it deploy ae07c2d

opened this

Bots now reload behind cover. When a bot runs its magazine dry with an enemy in view, it runs to the nearest spot that enemy can't see and reloads there, instead of standing still and strafing for the 2–4 seconds it can't shoot. When the magazine is in, the trained policy takes over again. bun run test passes (typecheck plus 1416 tests).

What changed

  • New src/client/cover.ts:

- exposed checks whether a threat's eye can see a standing body at its eye and at both shoulders. A wall or a smoke cloud blocks the view. - coverFrom searches the cells up to 4 out (512 units) around the bot, nearest first, for one that isn't exposed. It returns a path of at most 6 cells, or [] if the bot is already hidden, or null if no cover is in reach.

  • src/client/bots.ts: takeCover runs in botThink before the fight while a reload is running. It picks the spot once per reload, then runs the bot there with its view on where the enemy was, and stands still on arrival. With no cover in reach, the policy keeps the fight as before.

- Only bots on the trained policy do this. The scripted baseline in the training arena never does, so what the policy is measured against hasn't moved. - The policy's inputs, outputs and shipped weights are untouched.

  • Measured on de_forge: cover was found for 419 of 509 sampled positions where two players can see each other (82%). The search averages 0.18 ms, worst case 2.1 ms, and runs once per reload.

Tests

  • New src/client/cover.test.ts: open versus walled-off positions; the path length cap; a bot that is already hidden stays put; a full reload played out, after which the bot stands on the cover cell out of sight; the path is picked only once per reload and the policy takes over after; the scripted baseline never takes cover.
  • Checked against the old code: with cover switched off, the reload test fails, with the bot ending 579 units from the spot. An earlier version of that test only checked that the bot ended out of sight, and it passed without the change because the policy's strafing happened to break line of sight. I tightened it.

Re-pinned digests Four seeded rounds in src/client/replay.test.ts changed on purpose: ROUND, SMOKE_ROUND, SWITCH_ROUND and OPEN_ROUND. All four match the old digests up to second 12 or 13, the first reload under fire, and differ from there. The comment above ROUND explains why. The arena duel, TIGHT_ROUND and the generated round in generate.test.ts still match their old digests.

Not done

  • Bots don't top up a half-empty magazine while out of sight.
  • They don't pick cover by where their team is.
  • Cover is reachable but not safe along the way: the run there can cross open ground.

Also: src/range/boot.test.ts failed once, in a test about drawing range plates that doesn't touch bots, and passed on the rerun.

How to try it: start a round in the preview, empty a rifle's magazine on a bot in the open, and watch it duck behind the nearest wall or smoke before it shoots again. The README's "The bots" section describes the behaviour.

🤖 Generated with Claude Code

Closes #357

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs