From 7e711582b04b7799e915923e51b4ad0bbe9661c6 Mon Sep 17 00:00:00 2001 From: sganggs Date: Sun, 4 Oct 2026 21:11:41 +0800 Subject: [PATCH] =?UTF-8?q?feedback3=20WJ:=20the=20bot=20places=20elite=20?= =?UTF-8?q?=E6=AD=8C=E8=95=BE=E8=92=82=E5=A8=85=20with=20HOK-Y=20on=20a=20?= =?UTF-8?q?=E9=AB=98=E5=8F=B0=20that=20covers=20the=20road?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/match/bot.js | 21 +++++++++++++++------ test/match/feedback1-placement.test.js | 11 +++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/server/match/bot.js b/server/match/bot.js index db238f8c..1e2eab83 100644 --- a/server/match/bot.js +++ b/server/match/bot.js @@ -949,8 +949,12 @@ export function* planLayoutSteps(m, ps, pieces, params = LAYOUT_PARAMS, { occupi let bestV = -Infinity; // a "只能部署在召唤者攻击范围内" summon (伺夜's 狼群, 缪尔赛思's 流形): only the tiles of its owner's range const within = p.kind === 'token' && typeof ps.summonRange === 'function' ? ps.summonRange(p) : null; - // ground tiles for a MELEE blocker; elite 歌蕾蒂娅 with HOK-Y may also take a 高台 (everyone else must not) + // ground tiles for a MELEE blocker. placeClass 'all' is only elite 歌蕾蒂娅 + HOK-Y: she may stand on a 高台, + // and when one of those tiles covers the enemy road she is planned there (owner 2026-10-04: the bot uses the 高台). const cls = p.kind === 'token' ? basePositionClass(r0) : placeClass(ps, m.gd.chess(p.id) || r0); + const preferHigh = cls === 'all' && basePositionClass(r0) === 'melee'; + let bestHigh = null; + let bestHighV = -Infinity; for (const [r, c] of legalTiles(map, cls)) { const k = tileKey(r, c); if (taken.has(k) || (within && !within.has(k))) continue; @@ -966,13 +970,18 @@ 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]; } + if (preferHigh && map.get(k) === 'ranged' && [...u.cover].some((ck) => model.ground.has(ck)) && v > bestHighV) { + bestHighV = v; + bestHigh = [k, r, c, dir]; + } } } - if (!best) continue; - taken.add(best[0]); - layout.units.push(unitOf(r0, best[0], best[1], best[2], best[3], model)); - out.set(p.uid, best[0]); - out.dirs.set(p.uid, best[3]); + const pick = bestHigh || best; + if (!pick) continue; + taken.add(pick[0]); + layout.units.push(unitOf(r0, pick[0], pick[1], pick[2], pick[3], model)); + out.set(p.uid, pick[0]); + out.dirs.set(p.uid, pick[3]); yield; } return out; diff --git a/test/match/feedback1-placement.test.js b/test/match/feedback1-placement.test.js index ff4a0f1e..69320418 100644 --- a/test/match/feedback1-placement.test.js +++ b/test/match/feedback1-placement.test.js @@ -472,6 +472,17 @@ test('高台 battle input: elite 歌蕾蒂娅 + HOK-Y is fielded on (10,4) and b } }); +test('高台 bots: elite 歌蕾蒂娅 + HOK-Y takes a 高台 that covers the road while the ground is still free', () => { + const { m, ps } = prep(HIGH_STAGE, 11); + equip(ps, GLADIIA_HOK_Y); + const elite = give(m, ps, GLAD_E); + const k = planLayout(m, ps, [elite]).get(elite.uid); + assert.equal(ps.deployMap().get(k), 'ranged', `planned on a 高台 with the road open (${k})`); + const [r, c] = rc(k); + assert.deepEqual(move(m, elite.uid, board(r, c)), { ok: true }); + m.dispose(); +}); + test('高台 bots: everyone else stays on the road; elite 歌蕾蒂娅 + HOK-Y may take a 高台 when the ground is full', () => { const { m, ps } = prep(HIGH_STAGE, 11); const pieces = ['chess_char_4_02_a', GLAD_N, FORCER_N, 'chess_char_2_03_a'].map((id) => give(m, ps, id));