Files

82 lines
6.5 KiB
JavaScript

// Fires a starter furnace design repeatedly the way a player would, without cleaning it out
// between firings, to see whether it clogs. Usage: node test/furnace_play.js [design] [ore] [coal] [firings] [roll] [mode]
// mode: 'mix' (even ore/coal mix blown in), 'layers' (ore first, coal on top), 'vent' (blown in from above through the vent)
const { load, flatWorld, ticks } = require('./harness');
const { run } = load();
const id = process.argv[2] || 'smelter1', ore = +(process.argv[3] || 50), coal = +(process.argv[4] || 50), firings = +(process.argv[5] || 3);
const roll = +(process.argv[6] || 0), mode = process.argv[7] || 'mix';
flatWorld(run, 240, 160, 100);
if (process.env.PATCH) run(process.env.PATCH); // try a rule change without editing the game
for (let k = 0; k < roll; k++) run('rnd()');
run(`bag[RSTONE] = 900; bag[LCLAY] = 200; bagTotal = 1100;`);
const d = run(`JSON.stringify(STARTER_DESIGNS.find(d => d.id === '${id}').bp())`);
const bp = JSON.parse(d);
const ox = 24, oy = 25 - bp.bh;
run(`bot.x = ${ox * 4 + bp.bw * 2 - 2}; bot.y = ${oy * 4 - 14}; pasteGhost(${d}, ${ox}, ${oy});`);
for (let k = 0; k < 40 && run('ghostBlocks.size + ghostMolds.length') > 0; k++) ticks(run, 30);
const ch = run(`(STARTER_DESIGNS.find(d => d.id === '${id}').chamber || 3)`);
const cx0 = (ox + 1) * 4, cx1 = (ox + bp.bw - 1) * 4 - 1, floorY = (oy + 1 + ch) * 4 - 1;
const count = (m, inMold) => run(`(()=>{let n=0;const cav=new Set(molds.flatMap(m=>m.cavs.flat()));for(let i=0;i<N;i++)if(mat[i]===${m}&&(${inMold}?cav.has(i):!cav.has(i)))n++;return n})()`);
let totalBars = 0, bits = 0;
function dump() {
const chm = { [run('EMPTY')]: '.', [run('COAL')]: 'c', [run('CUORE')]: 'o', [run('MCU')]: 'M', [run('COPPER')]: '=', [run('SLAG')]: 's', [run('ASH')]: 'a', [run('FIRE')]: '^', [run('SMOKE')]: '~', [run('STONE')]: '#', [run('SCRCU')]: '%' };
for (let y = oy * 4 - 2; y < (oy + bp.bh) * 4 + 2; y++) { let l = ''; for (let x = ox * 4 - 2; x < (ox + bp.bw) * 4 + 2; x++) { const i = y * run('W') + x; const m = run(`mat[${i}]`); l += run(`part[${i}]`) === 5 && m === 0 ? '+' : (chm[m] || '?'); } console.log(l); }
}
const results = [];
for (let f = 0; f < firings; f++) {
run(`bag[CUORE] += ${ore}; bag[COAL] += ${coal}; bagTotal += ${ore + coal};`);
const blow = seq => {
run(`bot.x = ${(cx0 + cx1) / 2 - 2}; bot.y = ${oy * 4};`);
for (const s of seq) for (let k = 0; k < 400 && run(s === 'mix' ? 'bag[CUORE] + bag[COAL]' : `bag[${s}]`) > 0; k++) {
const tx = cx0 + 1 + (k * 5) % (cx1 - cx0 - 2);
run(s === 'mix' ? `toolBlow(${tx}, ${floorY - 1}, 0, CUORE, [CUORE, COAL]); simTick();` : `toolBlow(${tx}, ${floorY - 1}, 0, ${s}, null); simTick();`);
}
};
// like a player: hold the igniter on a few spots of coal that touch air, or on top of the pile if none shows
const light = () => {
let spots = run(`(()=>{const out=[];for(let y=${oy * 4 + 4};y<=${floorY};y++)for(let x=${cx0};x<=${cx1};x++){const i=y*W+x;if(mat[i]===COAL&&mat[i-W]===EMPTY)out.push([x,y]);}return out})()`);
if (!spots.length) spots = run(`(()=>{const out=[];for(let x=${cx0};x<=${cx1};x++)for(let y=${oy * 4 + 4};y<=${floorY};y++){const i=y*W+x;if(PH[mat[i]]===PH_POWDER){out.push([x,y]);break;}}return out})()`);
const pick = [spots[0], spots[Math.floor(spots.length / 2)], spots[spots.length - 1]].filter(Boolean);
run(`for (let k=0;k<50;k++){ ${pick.map(([x, y]) => `toolIgnite(${x}, ${y});`).join('')} simTick(); }`);
};
if (mode === 'vent') {
run(`bot.x = ${(cx0 + cx1) / 2 - 2}; bot.y = ${oy * 4 - 16};`);
for (let k = 0; k < 600 && run('bag[CUORE] + bag[COAL]') > 0; k++) run(`toolBlow(${(cx0 + cx1) >> 1}, ${floorY - 2}, 0, CUORE, [CUORE, COAL]); simTick();`);
ticks(run, 60); light();
} else if (mode === 'litfirst') {
// light the coal, then blow the ore in on top of the fire
blow(['COAL']); ticks(run, 30); light(); ticks(run, 180); blow(['CUORE']);
} else {
blow(mode === 'layers' ? ['CUORE', 'COAL'] : mode === 'coalfirst' ? ['COAL', 'CUORE'] : ['mix']);
ticks(run, mode === 'mixwait' ? 3600 : 60);
light();
}
run(`bot.x = ${ox * 4 - 30}; bot.y = ${oy * 4 - 20};`);
if (process.env.WATCH) {
// a picture every 10 s: material, and temperature in hundreds of degrees
const chw = { [run('EMPTY')]: '.', [run('COAL')]: 'c', [run('CUORE')]: 'o', [run('MCU')]: 'M', [run('COPPER')]: '=', [run('SLAG')]: 's', [run('ASH')]: 'a', [run('FIRE')]: '^', [run('SMOKE')]: '~', [run('STONE')]: '#', [run('SCRCU')]: '%' };
for (let s = 0; s < 9; s++) {
ticks(run, 600);
const rows = JSON.parse(run(`JSON.stringify((()=>{const o=[];for(let y=${oy * 4 + 2};y<=${floorY + 9};y++){const r=[];for(let x=${cx0 - 1};x<=${cx1 + 1};x++){const i=y*W+x;r.push([mat[i],part[i],Math.round(temp[i]),flags[i]&F_BURN?1:0]);}o.push(r);}return o})())`));
console.log(`-- ${(s + 1) * 10}s`);
for (const r of rows) console.log(r.map(([m, p, t, b]) => (p === 5 && m === 0 ? '+' : b ? '*' : (chw[m] || '?'))).join('') + ' ' + r.map(([m, p, t]) => t < 100 ? ' ' : String(Math.min(9, Math.floor(t / 150)))).join(''));
}
} else ticks(run, 5400);
const st = run(`JSON.stringify(molds.map(m => m.state + (m.flaw ? ':' + m.flaw : '')))`);
if (process.env.DUMP === 'each') { console.log('-- before knock-out, firing', f + 1, st); dump(); }
// Vacuum earlier drops, open the outlet, let the ingot molds empty themselves.
let bars = run('ents.length');
run('for (const e of ents.slice()) removeEnt(e); for (const mo of molds) if (mo.kind === "ingot") for (let y = mo.y + mo.h; y < mo.y + mo.h + 2; y++) for (let x = mo.x; x < mo.x + mo.w; x++) setCell(y * W + x, EMPTY, AMB[y], 0); checkMolds();');
bars += run('ents.length'); totalBars += bars;
const msgs = JSON.parse(run(`JSON.stringify(molds.map(m => m.kind === "ingot" ? "Ingot molds empty themselves." : collectMold(m)))`));
if (run('player.bitTier') > bits) bits = run('player.bitTier');
run('for (const e of ents.slice()) removeEnt(e);');
const left = { ore: count(run('CUORE'), false), coal: count(run('COAL'), false), slag: count(run('SLAG'), false), ash: count(run('ASH'), false), copperOut: count(run('COPPER'), false) + count(run('SCRCU'), false) };
results.push({ f: f + 1, st, msg: msgs[0], bars, ...left });
console.log(`firing ${f + 1}: ${st} -> ${msgs[0]} | bars ${bars} | left in furnace: ore ${left.ore}, coal ${left.coal}, slag ${left.slag}, ash ${left.ash}, copper outside mold ${left.copperOut}`);
if (id === 'bitfurnace') run('player.bitTier = 0;');
}
console.log(`TOTAL bars ${totalBars} bit ${bits}`);
if (process.env.DUMP) dump();