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

Thirty per cent of rounds end on the clock: bots that feel the round timer #137

Merged opened this

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

Merged

Time pressure is now a term both sides read, and it lives in `plan.ts` with two numbers a reader can change: `PUSH_WINDOW` (12 s — the fighting the terrorists can still afford on the way in) and `HUNT_AT` (42 s — the second a counter-terrorist stops holding a corner). `bots.ts` reads them: a terrorist inside the window walks onto the site with the policy still holding the gun, and a counter-terror…

shipped merged

opened this

Time pressure is now a term both sides read, and it lives in plan.ts with two numbers a reader can change: PUSH_WINDOW (12 s — the fighting the terrorists can still afford on the way in) and HUNT_AT (42 s — the second a counter-terrorist stops holding a corner). bots.ts reads them: a terrorist inside the window walks onto the site with the policy still holding the gun, and a counter-terrorist past the line leaves its angle and sweeps the half of the map the terrorists came from — map.ts now names that point, muster, the middle of a side's spawn. A call outranks both: hold stops the push where it stands, go A names the room to hunt in.

bun run scripts/measure.ts --matches 30 --seed 41, normal bots, the person idle. Before, at 23360b9:

rounds played  216
plant rate     61%
ends           detonated 103 · time 55 · elimination 39 · defused 19
defuse rate    15% of planted bombs
round length   median 123s, longest 156s

After:

rounds played  223
plant rate     62%
ends           detonated 105 · time 21 · elimination 72 · defused 25
defuse rate    18% of planted bombs
round length   median 123s, longest 144s

Rounds lost to the clock: 55 of 216 (25%) → 21 of 223 (9%). The plant rate holds and detonations hold at 105, so nothing was bought by making the terrorists worse; the defuse rate went up rather than down.

Two things the issue asks for that I have to answer honestly. The median round did not come down — 123 s before and after. The distribution moved at the top and in the middle of the round rather than at the median: p75/p90 fall from 156 s to 144 s, and the median plant moves from 116 s into the round to 104 s. The median cannot move much inside this issue's scope, because 47% of rounds end in a detonation and a detonation is the plant plus the 40-second fuse, which is match.ts and out of bounds here. Nothing is suiciding: 123 s is comfortably above the 70 s floor the issue names. And elimination did rise, 39 → 72, which needs its cause stated: I traced every clock round in the baseline, and 43 of the 55 were rounds where the idle person is alive on the terrorist side — after half time the measurement's idle person is a terrorist standing in spawn, 17 times holding the bomb. Those rounds can never be planted and can never end in a terrorist win; what ends them now is a counter-terrorist walking over to find out why nobody has come. That is the honest reading of "30% of rounds end on the clock", and it is now written in README's Measuring the round alongside the new table.

What I measured and did not keep: a 28-second push window (6 fewer detonations, 4 more clock rounds; 5 points of plant rate when the terrorists push and the defence does not hunt), a hunt that swaps sites instead of sweeping (25% → 12% only, since the last terrorist is usually near neither site), and HUNT_AT at 55 (hands the site over: detonations 123, defuse rate 10%, still 12% on the clock). Each of those numbers is in the comment above the constant it argues for. observe.ts's 32 numbers are untouched and the weights are the shipped ones; bun run scripts/train.ts --bench reports 12k steps/s at this branch and 12k steps/s at 23360b9, identical. Not touched: the retake (#46), grenades (#58), and the 115-second clock.

To look at: plan.ts's time pressure block is the whole rule; chooseGoal and pushing/hunting in bots.ts are where a bot reads it. New tests in bots.test.ts pin a terrorist's feet going to the site with the clock low and the policy keeping them with the clock high, the defence sweeping at HUNT_AT and holding its site above it, and both radio exceptions. bun run check passes, 535 tests.

🤖 Generated with Claude Code

Closes #105

commented

Review submitted on pull #137 — changes_requested, anchored at src/client/bots.ts:231.

What I verified before writing it:

  • bun run check at c2dea07 in a scratch clone: tsc clean, 535 pass.
  • Both measurement tables reproduce exactly: base 4161986 → 216 rounds · plant 61% · detonated 103 · time 55 · elimination 39 · defused 19 · median 123s · longest 156s; head → 223 · 62% · 105 · 21 · 72 · 25 · 123s · 144s. Clock rounds 25% → 9%.
  • Mutation-checked the new tests — carrier-only pushing, the hunt goal, the hold guard and the ordered guard each break a test when removed.
  • No server/tty surface touched; booted the head locally, / answers 200 in ~6 ms with the CSP/nosniff/referrer floor intact. The preview host is unreachable from this sandbox (production 404s the same way), so I read the forge's screenshots at this head instead: phone and desktop both render.

The ask: pushing's bomb.state === "planted" guard can be deleted with all 535 tests still green, and it is load-bearing now that the rule covers the whole terrorist side; second, :hunt in repath's objective string is unpinned the same way. Both need one test each. muster.ct is unused in production code — noted as small. The unmet "median comes down" box and the elimination rise are stated honestly in the pull and I reproduced both, so I asked for nothing there.

commented

Time pressure is now a term both sides read, and it lives in plan.ts with two numbers a reader can change: PUSH_WINDOW (12 s — the fighting the terrorists can still afford on the way in) and HUNT_AT (42 s — the second a counter-terrorist stops holding a corner). bots.ts reads them: a terrorist inside the window walks onto the site with the policy still holding the gun, and a counter-terrorist past the line leaves its angle and sweeps the half of the map the terrorists came from — map.ts now names that point, muster, the middle of a side's spawn. A call outranks both: hold stops the push where it stands, go A names the room to hunt in.

bun run scripts/measure.ts --matches 30 --seed 41, normal bots, the person idle. Before, at the base:

rounds played  216
plant rate     61%
ends           detonated 103 · time 55 · elimination 39 · defused 19
defuse rate    15% of planted bombs
round length   median 123s, longest 156s

After:

rounds played  223
plant rate     62%
ends           detonated 105 · time 21 · elimination 72 · defused 25
defuse rate    18% of planted bombs
round length   median 123s, longest 144s

Rounds lost to the clock: 55 of 216 (25%) → 21 of 223 (9%). The plant rate holds and detonations hold at 105, so nothing was bought by making the terrorists worse; the defuse rate went up rather than down. Two boxes I did not tick, stated plainly: the median round did not come down — 123 s either side, because 47% of rounds end in a detonation and a detonation is the plant plus a 40-second fuse, which is match.ts and out of scope here; what moved is p75/p90 (156 s → 144 s) and the median plant (116 s → 104 s into the round). And elimination nearly doubles, 39 → 72: I traced every clock round in the baseline and 43 of the 55 are rounds where the measurement's idle person is a terrorist standing in spawn, 17 of them holding the bomb. Those rounds cannot be planted and cannot end in a terrorist win, so a counter-terrorist walking over to find out why nobody has come is the only ending they have. That reading is now in README's Measuring the round next to the new table.

Since @sentinel's review: the two lines that could be deleted with the suite still green now each have the test that falls without them. pushing's post-plant guard gets one in the underFire shape — bomb down, clock low, terrorist mid-duel, the policy keeps the feet — and :hunt in repath's objective string gets one that steps only the clock, inside a live repath window, and expects the goal to turn on the tick rather than up to six seconds later. :called had the same gap and is closed the same way: a defender rotating to a contact, called onto the site it was already rolled onto, turns round immediately. I mutated each term away one at a time and confirmed exactly the intended test falls. muster stays built for both sides — it is the average of a list map.ts already holds, and the comment now says only t is read today.

Also measured and not kept, each argued in the comment above the constant it belongs to: a 28-second push window (6 fewer detonations, 4 more clock rounds; 5 points of plant rate when the terrorists push and the defence does not hunt), a hunt that swaps sites instead of sweeping (25% → 12% only — the last terrorist is usually near neither site), and HUNT_AT at 55 (hands the site over: detonations 123, defuse rate 10%, still 12% on the clock). observe.ts's 32 numbers are untouched and the weights are the shipped ones; bun run scripts/train.ts --bench reports 12k steps/s on this branch and 12k steps/s at the base. Not touched: the retake (#46), grenades (#58), and the 115-second clock. bun run check exits 0 — tsc clean, 538 tests.

🤖 Generated with Claude Code

commented

Review submitted on pull #137 — approved, anchored at src/client/bots.test.ts:469. The clone is back exactly as I found it.

What I verified this round at 7ff09e6:

  • tsc --noEmit && bun test green — 538 pass, 0 fail.
  • Both requested tests are load-bearing: deleting pushing's planted guard fails once the bomb is down…, deleting :hunt from repath's objective fails the defence leaves its angle the tick the clock says so. They also closed the pre-existing :called gap — deleting it fails a call turns a rotating defender round inside its repath window.
  • bots.ts and plan.ts are byte-identical to c2dea07, so the measurement tables I reproduced myself last round (clock rounds 25% → 9%, plant rate and detonations holding) still describe this head.
  • muster.ct now documents why both halves exist and which is read.
  • Nothing outside the client simulation is touched; booted this head and confirmed / answers 200 in 19 ms with the repo's declared floor and a cross-origin POST /api/match refused 403. Preview and production both 404 from my sandbox, so I read the forge's pictures at this head instead — phone and desktop render.

I opened no follow-up issue: the one unticked box in #105 (median round length) is dominated by plant-plus-fuse rounds, which the issue itself put out of scope, and the pull states it rather than hides it.

commented

The review was an approval with no requested changes, so there is nothing to push: the branch is still 7ff09e6, the working tree is clean, and bun run check exits 0 there (538 pass). Below is the pull as it stands.

Time pressure is now a term both sides read, and it lives in plan.ts with two numbers a reader can change: PUSH_WINDOW (12 s — the fighting the terrorists can still afford on the way in) and HUNT_AT (42 s — the second a counter-terrorist stops holding a corner). bots.ts reads them: a terrorist inside the window walks onto the site with the policy still holding the gun, and a counter-terrorist past the line leaves its angle and sweeps the half of the map the terrorists came from — map.ts now names that point, muster, the middle of a side's spawn. A call outranks both: hold stops the push where it stands, go A names the room to hunt in.

bun run scripts/measure.ts --matches 30 --seed 41, normal bots, the person idle. Before, at the base:

rounds played  216
plant rate     61%
ends           detonated 103 · time 55 · elimination 39 · defused 19
defuse rate    15% of planted bombs
round length   median 123s, longest 156s

After:

rounds played  223
plant rate     62%
ends           detonated 105 · time 21 · elimination 72 · defused 25
defuse rate    18% of planted bombs
round length   median 123s, longest 144s

Rounds lost to the clock: 55 of 216 (25%) → 21 of 223 (9%), with the plant rate and the detonation count holding, so the improvement is not bought from the terrorists. Two boxes I did not tick, stated plainly rather than quietly: the median round did not come down — 123 s either side, because 47% of rounds now end in a detonation and a detonation is the plant plus a 40-second fuse, which is match.ts and out of this issue's scope; what moved is p75/p90 (156 s → 144 s) and the median plant (116 s → 104 s into the round). And elimination nearly doubles, 39 → 72: I traced every clock round in the baseline and 43 of the 55 are rounds where the measurement's idle person is a terrorist standing in spawn, 17 of them holding the bomb. Those rounds cannot be planted and cannot end in a terrorist win, so a counter-terrorist walking over to find out why nobody has come is the only ending they have. That reading sits in README's Measuring the round next to the new table, where the next person to run the script will find it.

Review round: the two lines that could be deleted with the suite still green now each have the test that falls without them — pushing's post-plant guard (bomb down, clock low, terrorist mid-duel, the policy keeps the feet) and :hunt in repath's objective string (only the clock moves, inside a live repath window, and the goal turns on the tick instead of up to six seconds later). :called had the same gap before this pull touched the file and is closed the same way: a defender rotating to a contact, called onto the site it was already rolled onto, turns round immediately. Each term was mutated away one at a time to confirm exactly the intended test falls. muster stays built for both sides, with the type now saying why and which half is read.

Also measured and not kept, each argued in the comment above the constant it belongs to: a 28-second push window (6 fewer detonations, 4 more clock rounds; 5 points of plant rate when the terrorists push and the defence does not hunt), a hunt that swaps sites instead of sweeping (25% → 12% only — the last terrorist is usually near neither site), and HUNT_AT at 55 (hands the site over: detonations 123, defuse rate 10%, still 12% on the clock). observe.ts's 32 numbers are untouched and the weights are the shipped ones; bun run scripts/train.ts --bench reports 12k steps/s on this branch and 12k steps/s at the base. Not touched: the retake (#46), grenades (#58), the 115-second clock, and anything outside the client simulation — no route, no handler, no src/tty.ts, no dependency.

🤖 Generated with Claude Code

commented

My review for this exact head is already on the pull — no second one is needed, and posting a duplicate verdict on the same commit would be noise.

The forge shows pull #137 still at 7ff09e6, open, unmerged, with two reviews from me:

  • c2dea07, anchored at src/client/bots.ts:231 — changes requested: the planted guard in pushing and :hunt in repath's objective were each deletable with the suite still green.
  • 7ff09e6, anchored at src/client/bots.test.ts:469 — approved, after I re-ran tsc --noEmit && bun test (538 pass, 0 fail) and mutation-checked all three now-pinned lines, confirmed bots.ts/plan.ts are byte-identical to the head whose measurement tables I reproduced myself (clock rounds 25% → 9%, plant rate and detonations holding), checked the page and a cross-origin write against this code, and read the forge's pictures at this head.

Nothing in the diff, the head, or the preview has changed since that approval, so I am standing on it rather than submitting another.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs