feedback7: the battle sim uses engine-independent math — Chrome, Firefox, Safari and the server agree bit for bit (PR #373 by @Cloudnyco)

- server/sim/detmath.js: hypot (sqrt of the squares, rescaled outside 2^±500), powi (integer powers in double-double,
  one rounding), sin / cos (fdlibm 5.3) and atan2 (fdlibm / FreeBSD msun) built only from correctly rounded IEEE
  operations; the sim's Math.hypot / sin / cos / atan2 / pow calls go through it (65 files); eslint.config.js forbids
  the approximated Math functions under server/sim and test/sim/detmath.test.js scans for them.
- sin / cos / atan2 are the algorithms V8 runs in Node, so the server's results stay; only hypot differs from V8's.
  Golden: 2 moved — diy-027 (a hypot distance, damage +80) and coop3-ABYSS-5 (a later card pick from round 9, same end).
- Applied unchanged after review (Grok static review: MERGE; the diff read in full: no network, no new dependency).
- Tests: broad node --test 5155 / 5155 with the new file tracked (detmath 7 / 7, package 10 / 10); lint 0 errors,
  typecheck, check:imports.
This commit is contained in:
Cloudnyco authored and sganggs committed 2026-10-08 18:35:55 +08:00
1 parent 2580152a3a
commit ae22335cfe
65 files changed
+616 -141

No files matched your search

+5 -4
View File
@@ -4,6 +4,7 @@
import { COLS, PUSH_TILES, PUSH_TILES_EFFECT, PULL_WEAK_SHARE, PULL_CRAWL, PULL_ORIGIN, PULL_STOP_RADIUS, PUSH_DIRECTIONAL_MIN_DIST } from '../constants.js';
import { fin } from './util.js';
import { hypot } from '../detmath.js';
/**
* Official push distance (tiles) of a 受力等级 (constants.js PUSH_TILES: ≤ −3 → 0, ≥ 3 → the 3 value); `effect` = a 特效
@@ -42,10 +43,10 @@ export class BattleDisplacement {
if (!this._displaceable(e)) return 0;
let level = this.forceLevel(e, force);
const fx0 = fin(from?.x, e.x), fy0 = fin(from?.y, e.y);
const vx = e.x - fx0, vy = e.y - fy0, d = Math.hypot(vx, vy);
const vx = e.x - fx0, vy = e.y - fy0, d = hypot(vx, vy);
let ux = 0, uy = 0;
const dirX = dir ? fin(dir.x, 0) : 0, dirY = dir ? fin(dir.y, 0) : 0;
const dl = Math.hypot(dirX, dirY);
const dl = hypot(dirX, dirY);
if (dl > 0) {
ux = dirX / dl; uy = dirY / dl;
if (from && !fixed && (d < PUSH_DIRECTIONAL_MIN_DIST || (!fixedAngle && vx * ux + vy * uy < d * Math.SQRT1_2))) {
@@ -71,7 +72,7 @@ export class BattleDisplacement {
// an enemy the puller itself blocks already stands in front of it (at contact) [ASSUMED: no pull, no unblocking]
if (center && center.side === 'ally' && e.blockedBy === center) return 0;
const tx = fin(to.x, e.x), ty = fin(to.y, e.y);
const dx = tx - e.x, dy = ty - e.y, d0 = Math.hypot(dx, dy);
const dx = tx - e.x, dy = ty - e.y, d0 = hypot(dx, dy);
if (!(d0 > 1e-6)) return 0;
const ux = dx / d0, uy = dy / d0;
// travel until inside the stop circle around `center` (smaller root of |e + t·u − c| = stop), else up to `to`
@@ -122,7 +123,7 @@ export class BattleDisplacement {
displace(e, dir, distance) {
if (!this._displaceable(e) || !dir) return 0;
const dxv = fin(dir.x, 0), dyv = fin(dir.y, 0);
const len = Math.hypot(dxv, dyv);
const len = hypot(dxv, dyv);
if (!(len > 0)) return 0;
const eff = Math.min(fin(distance, 0), 2 * COLS);
if (!(eff > 0)) return 0;