FCT-004
Fuel-gated accrual: make the miner need feeding
Production runs only while fuel lasts, and fuel comes from doing the thing you actually want done. Turns idle earning into a loop.
Pure idle accrual has no engagement in it. The user sets it going and the site is just a timer. Gating production on a consumable changes the shape: the miner runs while it has fuel, fuel comes from claims, shortlinks or ads, and the loop closes.
The arithmetic is the same accrual calculation clamped by how long the fuel lasted rather than by wall-clock time alone. Fuel burns at a known rate, so the productive window is fuel divided by burn rate, and elapsed time beyond that produces nothing.
Be honest about it in the interface. A miner that stopped four hours ago and said nothing feels like a bug.
function fuelled_accrual(array $miner, ?int $now = null): array
{
$now = $now ?? time();
$elapsed = max(0, $now - (int) $miner['collected_at']);
// How long the fuel could actually cover.
$burnPerHour = max(1, (int) $miner['fuel_burn_per_hour']);
$fuelSeconds = (int) floor((int) $miner['fuel'] * 3600 / $burnPerHour);
$productive = min($elapsed, $fuelSeconds);
$units = (int) floor($productive * (int) $miner['rate_per_hour'] / 3600);
$units = min($units, (int) $miner['capacity']);
$fuelUsed = (int) ceil($productive * $burnPerHour / 3600);
return [
'units' => $units,
'fuel_used' => min($fuelUsed, (int) $miner['fuel']),
'ran_dry_at' => $fuelSeconds < $elapsed
? (int) $miner['collected_at'] + $fuelSeconds
: null,
];
}
Using it
Return when it ran dry and show it: "stopped 3 hours ago, out of fuel" tells the user exactly what to do next.
Price fuel against the actions you want. If shortlinks are your revenue, make a shortlink worth more fuel than a claim.
What bites people
Floor the production and ceil the fuel used. Rounding the other way lets a user run fractionally longer than they paid for on every collect, which compounds.
Do not let fuel go negative when the two calculations disagree at the boundary. Clamp it, as above.
This one touches real money. Point it at a throwaway wallet and watch a full cycle before you trust it with a live balance.