// Plays the whole game with a bot and prints a timeline: use it to tune the pacing. // // npm run campaign -- [seconds-per-step] e.g. npm run campaign -- 120 10 // // Tuning knobs can be tried without editing the data files, from the environment: // THRESH LEGACY_EXP PASSIVE_SCALE PASSIVE_POW legacy points and passive bonus // PERK_GROWTH legacy perk cost growth // RP_RATE LAB_GROWTH RESEARCH_COST research // ENDLESS_COST ENDLESS_GROWTH_ADD endless upgrades // SHOW= how many restarts to list (default 8) import { loadContext } from './context.js'; import { runCampaign } from './campaign.js'; const ctx = loadContext(); const env = (name, fallback) => (process.env[name] === undefined ? fallback : Number(process.env[name])); ctx.rules.legacy.threshold = env('THRESH', ctx.rules.legacy.threshold); ctx.rules.legacy.exponent = env('LEGACY_EXP', ctx.rules.legacy.exponent); ctx.rules.legacy.passiveScale = env('PASSIVE_SCALE', ctx.rules.legacy.passiveScale); ctx.rules.legacy.passivePower = env('PASSIVE_POW', ctx.rules.legacy.passivePower); ctx.rules.research.rpPerLabPerSecond = env('RP_RATE', ctx.rules.research.rpPerLabPerSecond); ctx.rules.research.labCostGrowth = env('LAB_GROWTH', ctx.rules.research.labCostGrowth); for (const perk of ctx.legacy) perk.costGrowth = env('PERK_GROWTH', perk.costGrowth); for (const node of ctx.research) node.cost *= env('RESEARCH_COST', 1); for (const line of ctx.endless) { line.baseCost *= env('ENDLESS_COST', 1); line.costGrowth += env('ENDLESS_GROWTH_ADD', 0); } const hours = Number(process.argv[2] ?? 24); const dt = Number(process.argv[3] ?? 5); const show = Number(process.env.SHOW ?? 8); const sci = (n) => (n >= 1e6 ? n.toExponential(2) : n.toFixed(1)); const { state, restarts, goals, probes } = runCampaign(ctx, { hours, dt }); console.log(`Simulated ${hours} h of play (one step = ${dt} s)\n`); for (const r of restarts.slice(0, show)) { console.log( ` hour ${r.hour.toFixed(1).padStart(6)} restart #${String(restarts.indexOf(r) + 1).padEnd(3)} after ${String(Math.round(r.minutes)).padStart(5)} min: ${sci(r.units)} units (${sci(r.rate)}/s), +${r.points} points (total ${r.earned})`, ); } if (restarts.length > show) console.log(` ... ${restarts.length - show} more`); console.log('\nHow a run starts (production per second, X minutes after a restart):'); for (const run of [...new Set(probes.map((p) => p.run))].slice(0, 6)) { const row = probes.filter((p) => p.run === run).map((p) => `${String(p.minutes).padStart(2)} min: ${sci(p.rate)}/s`); console.log(` run ${String(run).padEnd(3)} ${row.join(' ')}`); } console.log('\nGoals (hour of play when reached):'); for (const [name, hour] of goals) console.log(` ${name.padEnd(26)} ${hour.toFixed(1)} h`); const missing = ['ALL research nodes', 'ONE perk maxed', 'ALL perks maxed'].filter((n) => !goals.has(n)); if (missing.length) console.log(` not reached in ${hours} h: ${missing.join(', ')}`); console.log( `\nAt the end: ${restarts.length} restarts, ${state.legacy.earned} points, ${state.research.length}/${ctx.research.length} research, ${state.labs} labs, ${state.achievements.length} achievements`, );