diff --git a/lib/rewards.js b/lib/rewards.js index 24b19f8..e7f183a 100644 --- a/lib/rewards.js +++ b/lib/rewards.js @@ -15,17 +15,25 @@ const store = require('./store'); const { ctDay } = require('./missions'); -const DEFAULTS = { minPol: 0.05, maxPol: 1, dailyCapPol: 20, lowBalancePol: 40 }; +// drawSkew k: log-uniform on u^(1/k), so k=1 is the plain log-uniform and k=3 leans hard to the floor +// (mean ~0.13 POL on 0.05..1, one drip in eighty above 0.5). missionsPerDay: finds per hunter per +// Central day (Marty, 2026-09-20: cap 40, k=3, 3 a day, so a launch morning does not empty the pool) +const DEFAULTS = { minPol: 0.05, maxPol: 1, dailyCapPol: 20, lowBalancePol: 40, drawSkew: 3, missionsPerDay: 3 }; function settings() { return Object.assign({}, DEFAULTS, store.read('settings', {})); } function setSettings(patch) { return store.update('settings', {}, s => Object.assign(s, patch)); } -function draw(min, max) { +function draw(min, max, skew) { const lo = Math.log(min), hi = Math.log(max); - const v = Math.exp(lo + Math.random() * (hi - lo)); + const k = Math.max(1, Number(skew != null ? skew : settings().drawSkew) || 1); + const u = Math.pow(Math.random(), 1 / k); // k>1 pushes u toward 1, i.e. the value toward the floor + const v = Math.exp(hi - u * (hi - lo)); return Math.round(Math.max(min, Math.min(max, v)) * 10000) / 10000; } // prize drips (weekly prizes) are paid from the same wallet but never count against the daily pool +// a hunter's finds today (Central), prizes excluded +function findsToday(memberId, day) { return store.read('payouts', []).filter(p => p.memberId === Number(memberId) && p.day === day && p.status !== 'failed' && !p.prize).length; } +function daily(memberId) { const s = settings(); const limit = Math.max(1, Number(s.missionsPerDay) || 3); const done = findsToday(memberId, ctDay()); return { limit, done, left: Math.max(0, limit - done) }; } function paidToday(day) { return store.read('payouts', []).filter(p => p.day === day && p.status !== 'failed' && !p.prize).reduce((n, p) => n + p.pol, 0); } @@ -88,4 +96,4 @@ function totals() { return { paid: paid.length, pol: Math.round(paid.reduce((n, p) => n + p.pol, 0) * 10000) / 10000, queued: all.filter(p => p.status === 'queued').length, today: paidToday(ctDay()), hunters: new Set(paid.map(p => p.memberId)).size }; } -module.exports = { settings, setSettings, draw, grant, completed, payable, mark, ledger, mine, totals, paidToday, pool, nextResetAt }; +module.exports = { settings, setSettings, draw, grant, completed, payable, mark, ledger, mine, totals, paidToday, pool, nextResetAt, daily, findsToday }; diff --git a/public/admin.html b/public/admin.html index 54eab4a..ecf51f2 100644 --- a/public/admin.html +++ b/public/admin.html @@ -5,7 +5,7 @@
This site is still being built.