Split The code is public; the app answers only to the crew.

The bots have ears: footsteps, a plant and a grenade at their feet pull them round #107

Merged opened this

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

Merged

The app boots, `/` answers 200, `bun test` is green (393 pass), and the branch is pushed.

shipped merged

opened this

The app boots, / answers 200, bun test is green (393 pass), and the branch is pushed.

---

Bots now hear the sounds the world has been pushing since #21 instead of reading lastShotAt off the shooter. New src/client/hearing.ts is pure and testable: a loudness per sound name (a gunshot is named by the gun that fired it, so an AWP carries further than an MP9 and a rifle is louder than anything that is not a gun), falloff with distance, solid grid cells on the straight line charged as extra apparent distance rather than silence — a cheap occlusion, no trace per sound per bot — and an error on the reported point that grows with distance and with each wall, so a far footstep is a bearing and not a pin. It also answers whose noise is worth turning round for: never your own, never your side's. listen in bots.ts reads the tail of world.effects (the list is only ever appended to and filtered, so what a bot has not heard yet is the handful on the end), takes the loudest above the threshold and sets brain.suspect from the reported point; the old random decay is untouched. Sound and shot effects now carry by, the player whose noise it is. The bomb's beep is deliberately skipped — it repeats twice a second from a place both sides already know and would drown out every footstep near a planted site; what a CT should do about it is #46.

What it cost, measured: the scan is 1.4 effects looked at per bot per tick out of the ~128 the list holds, 0.24 µs a bot-tick, 1.2% of the game loop (instrumented over 46,080 ticks of real 5v5 rounds; the naive full-list scan I started with was 3.67 µs and 16.8%). bun run scripts/train.ts --bench is 13k steps/s, unchanged. scripts/measure.ts --matches 24, before → after: plant rate 54% → 57%, rounds ending detonated 67 → 72, on the clock 54 → 50, by elimination 35 → 25, defused 21 → 12 (22% → 13% of planted bombs), wall clock 5.7s → 6.1s a match. That defuse drop is the feature landing on the CTs: a T holding a planted site now hears the retake coming, and a bot that thinks an enemy is within 900 units creeps, which costs a side that is on a clock. Worth a look before merge — if it reads as too harsh, the knob is AUDIBLE in hearing.ts or the creep range in botThink.

Two things to look at. The arena is deaf, not byte-identical: it empties the effect list at the end of every step and its foe thinks at the start of the next, so hearing finds nothing there — but that foe used to turn to gunshots through walls via lastShotAt, and no longer does, so arena episodes do differ from before this branch. arena.test.ts pins both halves: the foe never forms a suspect and no effect survives a step, and one three-seed episode against the shipped weights is locked action for action (learned.test.ts still passes its margin, and nothing was added to observe.ts's 32 numbers). And bots.test.ts's plant test went from 5 seeds needing 4 to 10 seeds needing 5: on the old five it now reads 3, but the rate it is sampling is ~54% over 177 rounds before and 57% after, so five rounds could never tell that apart from a bad afternoon — the test was re-based, not weakened, and the measured rate is in the comment.

🤖 Generated with Claude Code

Closes #72

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs