feedback7 dober: the bots plan a zero-range heavy guard (range 0-1: only its own tile) on a free enemy-road tile first (PR #339 by @IceCodeNew, that part only); golden: 9 of 18 matches move, no result flips

Seen: 蛇屠箱 and the seven other operators with the range 0-1 (角峰, 古米, 泡泡, 折桠, 菲莱, 塞雷娅, 余 — 16 chess records, all
melee blockers with block 3, the elite 角峰 4) block and hit only on the tile they stand on. The layout planner maximizes an exposure model whose
kill estimate saturates in a full layout; the tile then falls to a 1e-6 rng tie-break, so a guard stood off the road, where
it neither blocks nor hits (reproduced: planLayout with six of them on act2autochess_m04, seed 11 — 蛇屠箱 off the road; over
40 seeds of the eight stages 16 of 320 guards stood off the road: 11 because the eight guards outnumber the 7 free road tiles of
act1autochess_m03 / act2autochess_m02, 5 avoidably — after the change only the 11 are left).
Official: excel range_table "0-1" is the single grid (0,0).
Now: planLayoutSteps keeps the best road tile (model.ground) for a blocker whose deployed range grid (rangeRec: its
loadout's) is exactly one (0,0) grid, and takes it before the overall best; with no free road tile the best legal tile as
before (a high-ground 「可以放置于远程位」 blocker keeps its own preference first).
Not taken from the PR: the scarce-card penalty of buyScore, the item scoring (人事部文档 "only with nine 炎" contradicts the
official 部署上限变为 9), the 神秘 morph fit and the draft yield — AI heuristics with no official text, and the golden
swing they caused (a solo match that cleared the Hidden Core no longer did) came from them, not from the placement.
Golden (matches only; every other family unchanged, --twice identical): solo-FUNNY-1 (from R7: boss pool left 74740 -> 71550,
still a loss in R8), solo-NORMAL-1 (from R4: same win, Hidden Core cleared, team LP 26), solo-HARD-1 (from R4: boss pool left
3046117 -> 2940542), solo-ABYSS-1 (from R4: 2303334 -> 2281356, still a loss in R13), coop2-NORMAL-3 (from R4), coop2-HARD-4
(from R4), coop4-FUNNY-6 (from R3: virtualMs 128500 -> 127000), coop2-NORMAL-8-serverrun (from R3), coop2-NORMAL-70-diy (from
R6): rows, stats, lineups and rng draws differ, wins, losses and rounds passed do not.
botbench A/B on 12 seeds each (solo HARD / ABYSS, coop4 NORMAL / HARD): wins 1 / 0 / 11 / 3 both before and after, rounds
passed 13.00 / 11.92 / 14.00 / 13.00 -> 12.92 / 11.92 / 14.00 / 13.17, LP left 18.7 / 6.5 / 80.1 / 27.9 -> 18.8 / 7.4 / 83.7 / 28.0.
Tests: bot.test.js — the PR's saturated-layout case (蛇屠箱 blocks and hits only on a road tile, a real battle on both tiles;
the road full: a legal fallback) and every stage over 40 seeds (the eight guards fill min(8, free road tiles) road tiles);
both fail on the parent.
This commit is contained in:
IceCodeNew authored and sganggs committed 2026-10-08 20:58:20 +08:00
1 parent 56a7b918a9
commit e63214571d
5 files changed
+134 -72

No files matched your search

+11 -2
View File
@@ -37,7 +37,8 @@
// tiles, where it blocks — except one whose trait reads 「可以放置于远程位」 (歌蕾蒂娅, 崖心, 见行者, any module:
// placeClass 'all', shared/highGround.js), which takes a 高台 whose range covers the enemy road, else the ground
// (owner 2026-10-04 for the 高台 preference, 2026-10-05 for who may use one). It stays a blocker in the lineup
// (basePositionClass). Each unit's range grid — the one it is
// (basePositionClass). A blocker whose range grid is its own tile only (range 0-1: 角峰, 古米, 泡泡, 折桠, 菲莱, 蛇屠箱,
// 塞雷娅, 余) is planned on a free tile of the enemy road first: off it nothing is blocked or hit. Each unit's range grid — the one it is
// deployed with, rangeRec (loadoutRecord attackRangeGrid) — is rotated per direction (DESIGN §3; RIGHT is tried first
// and kept on ties, so symmetric ranges and melee units whose front adds nothing stay facing the gates), so
// ranged units turn toward the enemy path tiles they cover best and blockers toward the road; on 气流 tiles
@@ -978,6 +979,12 @@ export function* planLayoutSteps(m, ps, pieces, params = LAYOUT_PARAMS, { occupi
const preferHigh = cls === 'all' && basePositionClass(r0) === 'melee';
let bestHigh = null;
let bestHighV = -Infinity;
// a blocker whose range is its own tile only (range 0-1: 角峰, 古米, 泡泡, 折桠, 菲莱, 蛇屠箱, 塞雷娅, 余) neither blocks nor
// hits anything off the enemy road — the official range "0-1" (excel range_table) is the one grid (0,0) — so a free road tile
// comes first (the kill estimate saturates in a full layout and then cannot tell the tiles apart)
const preferRoad = isBlocker(r0) && r0.rangeGrid?.length === 1 && r0.rangeGrid[0].every((v) => v === 0);
let bestRoad = null;
let bestRoadV = -Infinity;
for (const [r, c] of legalTiles(map, cls)) {
const k = tileKey(r, c);
if (taken.has(k) || (within && !within.has(k)) || (without && without.has(k))) continue;
@@ -993,13 +1000,15 @@ export function* planLayoutSteps(m, ps, pieces, params = LAYOUT_PARAMS, { occupi
const v = layout.value() + noise;
layout.units.pop();
if (v > bestV) { bestV = v; best = [k, r, c, dir]; }
// (the best road tile by the same value)
if (preferRoad && model.ground.has(k) && v > bestRoadV) { bestRoadV = v; bestRoad = [k, r, c, dir]; }
if (preferHigh && map.get(k) === 'ranged' && [...u.cover].some((ck) => model.ground.has(ck)) && v > bestHighV) {
bestHighV = v;
bestHigh = [k, r, c, dir];
}
}
}
const pick = bestHigh || best;
const pick = bestHigh || bestRoad || best;
if (!pick) continue;
taken.add(pick[0]);
layout.units.push(unitOf(r0, pick[0], pick[1], pick[2], pick[3], model));