Waste rock pays: richer granite and basalt grinding, bulk rock orders (t3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -8,7 +8,7 @@ const CLAIM_DEBT = 20000;
|
||||
// base price per grain (per bar for bars). 0 = not wanted: it drops through for nothing.
|
||||
const PRICE = new Float32Array(NMAT);
|
||||
for (const [m, v] of [
|
||||
[SAND, 0.1], [LCLAY, 0.2], [LWOOD, 0.3], [ACORN, 0.5], [COAL, 0.5], [RGRAN, 0.2], [RBAS, 0.4],
|
||||
[SAND, 0.1], [LCLAY, 0.2], [LWOOD, 0.3], [ACORN, 0.5], [COAL, 0.5], [RGRAN, 0.3], [RBAS, 0.5],
|
||||
[CUORE, 1], [FEORE, 1.6], [SNORE, 2], [ZNORE, 2.2], [AGORE, 6], [AUORE, 14],
|
||||
[CHARCOAL, 1.2], [RBRICK, 0.8], [CULLET, 1.2], [BARGL, 14], [RSTONE, 0.02], [GRAVEL, 0.03], [BARSB, 0.6], [PLANK, 2.4], [GREENB, 2], [BARBR, 9],
|
||||
[SCRCU, 3], [SCRFE, 5], [SCRST, 11], [SCRSN, 3.5], [SCRZN, 3.5], [SCRAG, 18], [SCRAU, 40], [SCRBZ, 8], [SCRBS, 7],
|
||||
@@ -73,7 +73,7 @@ const STANDING = ['Local buyers', 'County buyers', 'Regional buyers', 'National
|
||||
function standing() { return Math.min(STANDING.length - 1, Math.floor(ordersDone / 4)); }
|
||||
// Builders buy the cheap bulk stuff (rubble, gravel, sand, stone blocks, planks) in big lots at a few
|
||||
// times the market price, so waste rock earns a little. [what, smallest lot, largest lot]
|
||||
const BUILD_GOODS = [[RSTONE, 400, 1500], [GRAVEL, 300, 1000], [SAND, 300, 900], [BARSB, 20, 80], [PLANK, 20, 80], [BARBR, 20, 80]];
|
||||
const BUILD_GOODS = [[RSTONE, 400, 1500], [RGRAN, 300, 1200], [RBAS, 300, 1200], [GRAVEL, 300, 1000], [SAND, 300, 900], [BARSB, 20, 80], [PLANK, 20, 80], [BARBR, 20, 80]];
|
||||
function makeBuildersOrder() {
|
||||
const cands = BUILD_GOODS.filter(([m]) => seen[m] && !orders.some(o => o.m === m));
|
||||
if (!cands.length) return null;
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
// the rest is ground to dust and washed away, so a Grinder halves a pile of waste rock.
|
||||
const REFINE = [];
|
||||
REFINE[RSTONE] = [[0.025, CUORE], [0.02, COAL], [0.015, FEORE], [0.006, SNORE]];
|
||||
REFINE[RGRAN] = [[0.02, FEORE], [0.012, ZNORE], [0.01, CUORE], [0.002, AGORE]];
|
||||
REFINE[RBAS] = [[0.03, FEORE], [0.01, ZNORE], [0.003, AUORE]];
|
||||
REFINE[RGRAN] = [[0.04, FEORE], [0.024, ZNORE], [0.02, CUORE], [0.004, AGORE]];
|
||||
REFINE[RBAS] = [[0.06, FEORE], [0.02, ZNORE], [0.006, AUORE]];
|
||||
REFINE[GRAVEL] = [[0.03, FEORE], [0.02, CUORE], [0.01, SNORE], [0.003, ZNORE]];
|
||||
REFINE[SAND] = [[0.01, SNORE], [0.008, FEORE], [0.0015, AUORE]];
|
||||
REFINE[LDIRT] = [[0.08, LCLAY], [0.01, COAL], [0.008, CUORE], [0.003, ACORN]];
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ function renderMarketTab(body) {
|
||||
+ `<div><div class="k">Earned in total</div><div class="v">${fmtMoney(earned)}</div></div></div>`;
|
||||
if (debt > 0) h += `<div class="mkt-pay"><span class="note" style="margin:0 6px 0 0">Pay toward the claim:</span>${[1000, 5000].map(a => `<button class="btn" data-pay="${a}" ${money >= Math.min(a, debt) ? '' : 'disabled'}>${fmtMoney(Math.min(a, debt))}</button>`).join('')}<button class="btn primary" data-pay="all" ${money > 0 ? '' : 'disabled'}>All I can (${fmtMoney(Math.min(money, debt))})</button></div>`;
|
||||
else h += `<p class="note">The land is yours. Keep building: the ledger has bigger goals.</p>`;
|
||||
h += `<p class="note">Anything that drops through the trade dock's grate is sold on the spot, once it is cool enough to handle. Junk like dirt, rubble, ash and slag goes for nothing. Build walls around the dock to funnel more in.</p>`;
|
||||
h += `<p class="note">Anything that drops through the trade dock's grate is sold on the spot, once it is cool enough to handle. Junk like dirt, ash and slag goes for nothing. Rock you dig still sells, builders order it by the bagful, and a Grinder finds ore in it. Build walls around the dock to funnel more in.</p>`;
|
||||
h += `<p class="label grouphead">Orders · ${esc(STANDING[standing()])}</p>`;
|
||||
if (orders.length) {
|
||||
h += '<div class="orderlist">' + orders.map(o => `<div class="o"><span class="t"><span class="sw" style="display:inline-block;vertical-align:-2px;margin-right:6px;background:${rgbOf(o.m)}"></span>${esc(orderName(o))}</span><span class="pay">${fmtMoney(o.pay)}</span>`
|
||||
|
||||
@@ -68,6 +68,18 @@ const p2 = run('priceNow(BARCU)');
|
||||
check('selling 100 copper bars lowers their price, which recovers within half an hour', p1 < p0 * 0.5 && p2 > p0 * 0.9, `${p0} -> ${p1.toFixed(1)} -> ${p2.toFixed(1)}`);
|
||||
check('dirt earns nothing, and rubble only a pittance (builders pay a few times that)', run('PRICE[LDIRT]') === 0 && run('PRICE[RSTONE]') > 0 && run('PRICE[RSTONE]') < 0.05);
|
||||
check('refined goods pay more than the raw ore in them', run('PRICE[BARCU] > 13 * PRICE[CUORE] + 13 * PRICE[COAL]') && run('PRICE[BARBZ] > 2 * PRICE[BARCU]'));
|
||||
// New bulk rocks stay hidden until discovered; isolate each candidate without random selection.
|
||||
run('orders = []; ordersDone = 0; seen.fill(0);');
|
||||
check('builders do not order unseen granite or basalt rubble', run('makeBuildersOrder()') === null);
|
||||
run('seen[RGRAN] = seen[RBAS] = 1;');
|
||||
for (const material of ['RGRAN', 'RBAS']) {
|
||||
run(`orders = [{ m: ${material === 'RGRAN' ? 'RBAS' : 'RGRAN'} }];`);
|
||||
check(`builders offer bulk ${material === 'RGRAN' ? 'granite' : 'basalt'} rubble after discovery`, run(`(() => {
|
||||
const o = makeBuildersOrder();
|
||||
return !!o && o.m === ${material} && o.who === 'Builders' && o.qty >= 300 && o.qty <= 1200 && o.pay > o.qty * PRICE[o.m];
|
||||
})()`));
|
||||
}
|
||||
check('rubble prices rise from stone to granite to basalt', run('PRICE[RSTONE] < PRICE[RGRAN] && PRICE[RGRAN] < PRICE[RBAS]'));
|
||||
// machines cost money, walls do not
|
||||
flatWorld(run, 240, 160, 100);
|
||||
run('money = 30; bag[RSTONE] = 100; bagTotal = 100; bot.x = 110; bot.y = 70;');
|
||||
|
||||
@@ -91,6 +91,14 @@ const fed = 400, sand = count(run, run('SAND')), o = ore();
|
||||
check('the Grinder grinds all the stone rubble it is fed', count(run, run('RSTONE')) === 0 && run('stats.refined') === fed, `${count(run, run('RSTONE'))} left, ${run('stats.refined')} of ${fed} ground`);
|
||||
check('about half as much sand comes out', sand > fed * 0.35 && sand < fed * 0.6, `${sand} sand from ${fed} rubble`);
|
||||
check('with the odd grain of common ore', o > 0 && o < fed * 0.15, `${o} ore grains`);
|
||||
// Old chances are tried in turn, so later ores require every earlier roll to miss.
|
||||
const oldGraniteChances = [0.02, 0.012, 0.01, 0.002];
|
||||
const oldGraniteExpected = 2000 * (1 - oldGraniteChances.reduce((miss, chance) => miss * (1 - chance), 1));
|
||||
grinderRig(); grind('RGRAN', 2000);
|
||||
const graniteOre = run('stats.refinedOre');
|
||||
check('all 2000 granite rubble grains are ground', run('stats.refined') === 2000, run('stats.refined'));
|
||||
check('granite yields at least 1.5 times its old expected ore', graniteOre >= 1.5 * oldGraniteExpected,
|
||||
`${graniteOre} ore; old expectation ${oldGraniteExpected.toFixed(1)}, minimum ${(1.5 * oldGraniteExpected).toFixed(1)}`);
|
||||
// gravel, sand and dirt are refined too; ore drops straight through
|
||||
grinderRig(); grind('LDIRT', 60);
|
||||
check('dirt goes through the Grinder too', count(run, run('LDIRT')) === 0 && run('stats.refined') === 60);
|
||||
|
||||
Reference in New Issue
Block a user