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

Is a gun on the floor worth walking to? The arithmetic in `plan.ts`, tested without a world #222

Merged opened this

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

Merged

scout/cs:scout/215-is-a-gun-on-the-floor-worth-walking-to-t → main 1 commit fork

  • Merged. scout/215-is-a-gun-on-the-floor-worth-walking-to-t was merged into main as 3118dc1.

Review

Sign in to review this change.
2 files changed +58 −2
src/client/plan.test.ts +29 −2
@@ -1,8 +1,9 @@
11 import { expect, test } from "bun:test";
22 import { COMPETITIVE, type Team } from "./match.ts";
33 import {
4 CALL_LIFE, CALLS, callLabel, COMMIT_MARGIN, HUNT_AT, isCall, liveCall, mustHunt, mustPush,
5 ordersFor, otherSite, PUSH_WINDOW, roundPlan, siteFor, timeToPlant, WALK_SPEED, type RadioCall,
4 CALL_LIFE, CALLS, callLabel, COMMIT_MARGIN, GUN_DETOUR_MAX, HUNT_AT, isCall, liveCall, mustHunt,
5 mustPush, ordersFor, otherSite, PUSH_WINDOW, roundPlan, siteFor, timeToPlant, WALK_SPEED,
6 worthTheDetour, type RadioCall,
67 } from "./plan.ts";
78
89 test("a side gets one site for the round, and every slot on it reads the same one", () => {
@@ -81,6 +82,32 @@ test("the counter-terrorists stop holding at one number, whatever the walk", ()
8182 expect(HUNT_AT).toBeGreaterThan(COMPETITIVE.plantTime + COMPETITIVE.bombTime / 2);
8283 });
8384
85 // --- pickups ----------------------------------------------------------------
86
87 test("a gun is worth the detour up to a bound in seconds of extra walking, and never past it", () => {
88 const goal = 1000;
89 // Straight on the way: no extra distance over walking to the goal at all.
90 expect(worthTheDetour(400, goal - 400, goal)).toBe(true);
91 // A stride off the direct line costs one stride there and one back.
92 const stride = 65;
93 expect(worthTheDetour(400 + stride, goal - 400 + stride, goal)).toBe(true);
94 // Clear across the map: nothing on the way to the goal was saved by it.
95 expect(worthTheDetour(4000, 4000, goal)).toBe(false);
96
97 // The bound itself, checked from both sides.
98 const atBound = goal + GUN_DETOUR_MAX * WALK_SPEED;
99 expect(worthTheDetour(atBound, 0, goal)).toBe(true);
100 expect(worthTheDetour(atBound + 1, 0, goal)).toBe(false);
101 });
102
103 test("a non-finite or negative distance is never worth a detour", () => {
104 for (const bad of [NaN, Infinity, -Infinity, -1, -0.001]) {
105 expect(worthTheDetour(bad, 100, 100)).toBe(false);
106 expect(worthTheDetour(100, bad, 100)).toBe(false);
107 expect(worthTheDetour(100, 100, bad)).toBe(false);
108 }
109 });
110
84111 // --- the radio ------------------------------------------------------------
85112
86113 const CALLED = (call: "A" | "B" | "hold", at = 10): RadioCall => ({ team: "ct", call, at });
src/client/plan.ts +29
@@ -187,3 +187,32 @@ export const HUNT_AT = 42;
187187
188188 /** True when what is left of the round is too little to spend standing still. */
189189 export const mustHunt = (secondsLeft: number): boolean => secondsLeft <= HUNT_AT;
190
191 // --- pickups ----------------------------------------------------------------
192 //
193 // `updatePickups` (world.ts) already hands a gun to any player who walks
194 // within pickup range of it — the same mechanism a person's own automatic
195 // pickup uses. So a bot never needs a take action, only a goal near the gun:
196 // the only decision left is whether the site it was already walking to is
197 // worth delaying for.
198
199 /**
200 * Seconds of extra walking a gun on the floor may cost before it is left for
201 * whoever passes closer. Two seconds is long enough to step off the direct
202 * line for one sitting in a doorway on the way, short enough that one across
203 * the site — or across the map — is not worth the detour.
204 */
205 export const GUN_DETOUR_MAX = 2;
206
207 /**
208 * True when walking `toGun` to the gun and `gunToGoal` on from there costs at
209 * most `GUN_DETOUR_MAX` seconds more than walking `toGoal` straight to the
210 * goal. All three are distances in world units. A non-finite or negative one
211 * is never worth it, rather than sending a bot toward NaN.
212 */
213 export function worthTheDetour(toGun: number, gunToGoal: number, toGoal: number): boolean {
214 if (![toGun, gunToGoal, toGoal].every(Number.isFinite)) return false;
215 if (toGun < 0 || gunToGoal < 0 || toGoal < 0) return false;
216 const detour = (toGun + gunToGoal - toGoal) / WALK_SPEED;
217 return detour <= GUN_DETOUR_MAX;
218 }

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs