Back to Pursuit PIT
SOURCE / PINNED RELEASE

Made of little things.

Pursuit PIT

Release
565f4981bdc3…
Author-recorded commit
197afdbed44b…
License
LICENSE
Author’s source reference
nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-951414e7fd7

Archive hash verified: 3b0485589942fd5c…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

tools/city-check.ts
// City layouts, headless: style spread, lot counts against the renderer's capacities, nothing on
// the highway verge or a slip road, and bridges per city. Run with: bun tools/city-check.ts
import { setRoadSeed } from '../src/game/road.ts';
import { zoneAt } from '../src/game/zones.ts';
import { cityLayout, CITY_STYLES } from '../src/game/cities.ts';
import { ROAD_HALF, rampAt, rampX, MERGE_X, MERGE_LEN, MERGE_TAPER, RAMP_CURVE } from '../src/game/sim.ts';
const styles = new Map<string, number>();
let worst = { lots: 0, streets: 0, low: 0, tall: 0, parks: 0, garages: 0, bridges: 0, maxH: 0 }, cities = 0, problems = 0;
const classes = [12, 24, 45, 80, 130, 200, 300, 420];
const perClassMax = new Array(classes.length).fill(0);
for (const seed of [1, 2, 3, 4, 5, 6]) {
  setRoadSeed(seed);
  for (let s = 0; s < 120_000;) {
    const z = zoneAt(s);
    if (z.area === 'city') {
      cities++;
      const c = cityLayout(z);
      styles.set(c.style.name, (styles.get(c.style.name) ?? 0) + 1);
      const b = c.lots.filter((l) => l.kind === 'building');
      const per = new Array(classes.length).fill(0);
      for (const l of b) {
        let k = 0;
        for (let i = 1; i < classes.length; i++) if (Math.abs(Math.log(classes[i] / l.h)) < Math.abs(Math.log(classes[k] / l.h))) k = i;
        per[k]++;
      }
      per.forEach((n, i) => (perClassMax[i] = Math.max(perClassMax[i], n)));
      worst = {
        lots: Math.max(worst.lots, c.lots.length), streets: Math.max(worst.streets, c.streets.length),
        low: Math.max(worst.low, b.filter((l) => l.h <= 35).length), tall: Math.max(worst.tall, b.filter((l) => l.h > 35).length),
        parks: Math.max(worst.parks, c.lots.filter((l) => l.kind === 'park').length), garages: Math.max(worst.garages, c.lots.filter((l) => l.kind === 'garage').length),
        bridges: Math.max(worst.bridges, c.bridges.length), maxH: Math.max(worst.maxH, ...b.map((l) => l.h)),
      };
      for (const l of c.lots) {
        if (Math.abs(l.x) - l.w / 2 < ROAD_HALF + 20) problems++; // on the highway's verge
        const d = l.s - rampAt(l.s);
        if (l.x > 0 && d > -(RAMP_CURVE + 260) && d < MERGE_LEN + MERGE_TAPER) {
          const rx = d < 0 ? rampX(-d) : MERGE_X;
          if (l.x - l.w / 2 < rx + 8 && l.x + l.w / 2 > rx - 8) problems++; // on the slip road
        }
        if (!(l.w > 0 && l.d > 0)) problems++;
      }
    }
    s = z.end + 1;
  }
}
console.log(`${cities} cities; styles:`, Object.fromEntries(styles));
console.log('worst city:', worst, '| per height class (max):', perClassMax.join(' '));
console.log('problems:', problems, '| styles defined:', CITY_STYLES.length);