feedback1 integrate: the bot's bench shed never sells a piece of this prep
The buy loop's pre-emptive shed sold the single it had just bought back for 1 (the buy picks by lineup gain, the shed by piece value). Same-prep buy then sell in co-op 绝境 seeds 21-26: 29 -> 3. Seeds 1-40: solo 绝境 22 -> 24 wins, co-op 9 -> 10 on the 22-match bounty lists; decision time unchanged.
This commit is contained in:
1 parent
14e292f491
commit
a80b163aed
3 files changed
+42
-3
No files matched your search
@@ -374,6 +374,15 @@ rehearsal 3, before → after: solo 绝境 27 → 27 wins, LP left 17.9 → 18.4
|
||||
→ 24 / 57 ms solo and 16 / 36 → 16 / 36 ms co-op; the whole prep with its rehearsal wall clock 80 / 148 → 78 / 153 ms and
|
||||
74 / 152 → 73 / 151 ms.
|
||||
|
||||
**0.1.1 after the QA (DESIGN §21.6, §21.19).** Seeds 1–40, rehearsal 3 (wins solo 绝境 / co-op 绝境, 4 AI): the merged
|
||||
build `dbd45c8` 27 / 11; with WB's 22-match bounty lists (a rule change: R11 is a 悬赏决策 in 14 of 22 and every R11 bounty
|
||||
list holds a 特异III giant) the same bot 22 / 9 (LP left 16.3 / 10.9); the residual sim fixes change no outcome (22 / 9);
|
||||
the bench-shed fix (the buy loop's shed never sells a piece that came this prep) 24 / 10 (LP left 17.4 / 13.1, co-op
|
||||
leaks per match 217 → 210). 0.1.0 on the same seeds: 19 / 5. The 29 same-prep buy → sell of co-op 绝境 (2 AI 托管 + 2
|
||||
bots, seeds 21–26) drop to 3 (a reward pick's room, `arrange`, `sellJunk`). Decision time unchanged (one thread, seeds
|
||||
1–3, both builds side by side): the whole prep p50 / p95 81 / 187 → 81 / 184 ms solo and 71 / 153 → 71 / 129 ms co-op,
|
||||
heuristics CPU 36 / 88 → 35 / 86 ms and 16 / 40 → 16 / 40 ms.
|
||||
|
||||
---
|
||||
|
||||
## 6. Match follow-ups shipped with this pass
|
||||
|
||||
+7
-3
@@ -1166,8 +1166,9 @@ function refreshValue(m, ps, ctx) {
|
||||
/**
|
||||
* Sell the weakest bench chess (temp and hand; the hand only with `handOnly`) that is not part of a merge pair (or
|
||||
* anything when keepPairs is false) and does not carry a 突变细胞 (its transformation after the battle is the point).
|
||||
* `keepFresh`: never a piece that came this prep (`boughtRound`: bought for the lineup, an elite just merged, a reward).
|
||||
*/
|
||||
function sellWeakestHand(m, ps, { keepPairs = true, below = Infinity, handOnly = false } = {}) {
|
||||
function sellWeakestHand(m, ps, { keepPairs = true, below = Infinity, handOnly = false, keepFresh = false } = {}) {
|
||||
const ctx = context(m, ps);
|
||||
let worst = null;
|
||||
let worstV = Infinity;
|
||||
@@ -1175,6 +1176,7 @@ function sellWeakestHand(m, ps, { keepPairs = true, below = Infinity, handOnly =
|
||||
if (!p || p.kind !== 'chess' || (p.items || []).some((it) => isMutationCell(m.gd, it.id))) continue;
|
||||
const base = m.gd.baseIdOf(p.id);
|
||||
if (keepPairs && !m.gd.isGolden(p.id) && (ctx.copies.get(base) || 0) >= 2) continue;
|
||||
if (keepFresh && p.boughtRound === m.round) continue;
|
||||
const v = pieceValue(m, ps, p, ctx);
|
||||
if (v < worstV) { worstV = v; worst = p; }
|
||||
}
|
||||
@@ -1375,9 +1377,11 @@ function* buyLoopSteps(m, ps, { fillOnly = false, maxRefreshes = 0 } = {}) {
|
||||
const boardFull = ownedN >= ps.deployCap;
|
||||
// the first pass fills the board, then completes the merges it can afford — before any level-up spends the funds
|
||||
const mergesOnly = fillOnly && boardFull;
|
||||
// keep room for merges: a crowded bench sheds its weakest single
|
||||
// keep room for merges: a crowded bench sheds its weakest single — never one that came this prep (the buy picks by
|
||||
// lineup gain, the shed by piece value: it used to sell the single just bought for 3–5 back for 1; QA of the 0.1.1
|
||||
// bots). The shed is pre-emptive (one slot is still free), so with only fresh singles it waits for the next prep.
|
||||
const used = ps.hand.filter(Boolean).length;
|
||||
if (used >= gd.benchSize - 1) sellWeakestHand(m, ps);
|
||||
if (used >= gd.benchSize - 1) sellWeakestHand(m, ps, { keepFresh: true });
|
||||
let best = -1;
|
||||
let bestS = 0;
|
||||
let bestMerges = false;
|
||||
|
||||
@@ -316,3 +316,29 @@ test('cost guard: the prep heuristics of a late-round 4-bot match stay cheap (re
|
||||
assert.equal(m.errorCount, 0);
|
||||
m.dispose();
|
||||
});
|
||||
|
||||
test('bench shed: the buy loop never sells a piece that came this prep (it used to sell the single it had just bought back for 1)', async () => {
|
||||
// QA of the 0.1.1 bots: co-op 绝境, 2 humans on AI 托管 + 2 bots, seeds 21–23 — 29 same-prep buy → sell in 6 matches,
|
||||
// every one the buy loop's pre-emptive bench shed (the buy picks by lineup gain, the shed by piece value)
|
||||
const { PlayerState } = await import('../../server/match/PlayerState.js');
|
||||
const oSell = PlayerState.prototype.sell;
|
||||
const shed = [];
|
||||
PlayerState.prototype.sell = function (uid) {
|
||||
const loc = this.find(uid);
|
||||
const p = loc && loc.piece;
|
||||
if (p && p.boughtRound === this.m.round && /buyLoopSteps/.test(new Error().stack)) shed.push(`${p.id} R${this.m.round}`);
|
||||
return oSell.call(this, uid);
|
||||
};
|
||||
try {
|
||||
for (const seed of [21, 22, 23]) {
|
||||
const h = makeMatch({ mode: 'coop', difficulty: 'HARD', humans: 2, bots: 2, seed, botRehearsal: 0 });
|
||||
h.start();
|
||||
h.autoHumans();
|
||||
const m = h.m;
|
||||
h.run(() => h.ended != null, { maxSteps: 3e7 });
|
||||
assert.equal(m.errorCount, 0);
|
||||
m.dispose();
|
||||
}
|
||||
} finally { PlayerState.prototype.sell = oSell; }
|
||||
assert.deepEqual(shed, [], 'no piece of this prep sold by the shed');
|
||||
});
|
||||
Reference in new issue
Block a user