feat(levels): plan the second fifty as alternating crowded and deep boards
A crowded board and a deep lock cannot be the same level — at five turns with eight pieces per orientation, 16 of 18 candidates fail the release gate, because pieces packed in leave no room for a turn to open two of them. So the campaign alternates the two instead of ramping one: crowded stretches for the pleasure of clearing a full board, deep stretches as the variation between them, and the last four are the fullest boards in the set. 24 crowded, 14 deep, 12 sparse-and-shallow to open on. Density climbs across the crowded stretches (0.36 → 0.40 → 0.42 → 0.44) since that is the axis left once depth is spoken for. select-50 takes --id and --name so this edition is built beside the first rather than over it; the fifty already played stay exactly as they are. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+14
-7
@@ -12,7 +12,13 @@ import { dirname, resolve } from 'node:path';
|
||||
import { deepLevelKey, showStats } from '../src/deep-lock.js';
|
||||
|
||||
const args = Object.fromEntries(process.argv.slice(2).map(a => { const [k, ...v] = a.replace(/^--/, '').split('='); return [k, v.length ? v.join('=') : true]; }));
|
||||
const OUT = resolve(args.out ?? 'levels/campaign-50.json');
|
||||
// The edition being built. It defaults to c50 so the existing call is unchanged,
|
||||
// but a second edition must be able to exist beside the first rather than
|
||||
// overwrite it — the fifty already played are kept.
|
||||
const ID = String(args.id ?? 'c50');
|
||||
const NAME = String(args.name ?? '五十关调优版');
|
||||
const OUT = resolve(args.out ?? `levels/campaign-${ID}.json`);
|
||||
const MODULE = resolve(args.module ?? `src/levels-${ID}.js`);
|
||||
const COUNT = Number(args.count ?? 50);
|
||||
const FAMILY_CAP = Number(args.familyCap ?? 0.36);
|
||||
// Rotation depth is the campaign's difficulty axis, because the design brief
|
||||
@@ -163,7 +169,7 @@ const levels = picked.map((row, i) => {
|
||||
openingBestMs: row.show?.openingBestMs ?? null,
|
||||
cruises: row.show?.cruises ?? null,
|
||||
};
|
||||
level.id = `c50-${String(i + 1).padStart(2, '0')}-${row.family}`;
|
||||
level.id = `${ID}-${String(i + 1).padStart(2, '0')}-${row.family}`;
|
||||
return level;
|
||||
});
|
||||
|
||||
@@ -175,14 +181,15 @@ writeFileSync(OUT, JSON.stringify({ generated: 'select-50', version: 1, weights:
|
||||
const chapterSize = 10;
|
||||
const chapters = Array.from({ length: Math.ceil(levels.length / chapterSize) },
|
||||
(_, i) => ['起步', '转起来', '看清路', '深一点', '收束'][i] ?? `第 ${i + 1} 章`);
|
||||
writeFileSync(resolve('src/levels-c50.js'),
|
||||
`// Generated by scripts/select-50.mjs — do not edit by hand.\n`
|
||||
+ `export const C50_CAMPAIGN = ${JSON.stringify({ id: 'c50', name: '五十关调优版', description: `从 ${pool.length} 个候选里按演出、反差与旋转深度选出的 50 关,难度递增。`, chapters, chapterSize }, null, 1)};\n`
|
||||
+ `export const C50_LEVELS = ${JSON.stringify(levels)};\n`);
|
||||
const CONST = ID.toUpperCase().replace(/[^A-Z0-9]/g, '_');
|
||||
writeFileSync(MODULE,
|
||||
`// Generated by scripts/select-50.mjs --id=${ID} — do not edit by hand.\n`
|
||||
+ `export const ${CONST}_CAMPAIGN = ${JSON.stringify({ id: ID, name: NAME, description: args.description ?? `从 ${pool.length} 个候选里选出的 50 关。`, chapters, chapterSize }, null, 1)};\n`
|
||||
+ `export const ${CONST}_LEVELS = ${JSON.stringify(levels)};\n`);
|
||||
|
||||
const band = (a, b) => picked.slice(a, b);
|
||||
const avg = (rows, f) => rows.reduce((s, r) => s + f(r), 0) / Math.max(1, rows.length);
|
||||
console.log(`已选 ${levels.length} 关 → ${OUT}`);
|
||||
console.log(`已选 ${levels.length} 关 → ${OUT} 与 ${MODULE}(版本 ${ID})`);
|
||||
console.log(`家族分布 ${JSON.stringify(used)}(单家族上限 ${cap})`);
|
||||
console.log(`旋转深度 ${JSON.stringify(picked.reduce((m, r) => (m[r.stats.rotations] = (m[r.stats.rotations] || 0) + 1, m), {}))}${CURVE ? `(目标 ${JSON.stringify(CURVE)})` : ''}`);
|
||||
for (const s of shortfall) console.log(` !! ${s.depth} 只凑到 ${s.got}/${s.want} 关 —— 符合条件的候选总共才 ${s.pool} 个,缺口已由其他条件补上`);
|
||||
|
||||
Reference in New Issue
Block a user