// 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 { 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(); } const msgs = JSON.parse(run(`JSON.stringify(molds.map(m => collectMold(m)))`)); const bars = run('ents.length'); totalBars += bars; 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();