mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-17 14:21:25 +02:00
fix(chain): WOW block time is 5 min, not 2 (measured); drop cheating-monster tuning
WOW meanBlockTimeMs 120000 -> 300000. Measured 5.01 min/block over 1000 blocks on
the live daemon; the code comment claiming it was "corrected to 2 min" was backwards.
WOW is a SLOW chain, closer to BTC than XMR for calibration.
Per user feedback ("fast monster feels like cheating") + what the sim then proved:
a supernaturally fast monster doesnt even WORK on slow chains — WOW (5-min) caps
~64% house-win even at a 2.2x monster on a 111x55 map, because a single dungeon
clears in ~1 min << the block so the timer (the fair, on-theme edge) barely bites.
So NETWORK_TUNING drops the monster-speed lever entirely and keeps size∝√blockTime
only (monster stays at its fair preset speed). WOW sizeScale 1.0->1.6 for 5-min blocks.
Consequence: slow chains (WOW/BTC) run honest-but-under-target on the timer until
MULTI-LEVEL DEPTH lands — the real edge lever (cumulative run length across levels
without any single map being an unplayable slog). Deb (WOW/normal) now gets a bigger,
more block-appropriate dungeon (45x22 -> 72x35) with a fair monster.
meanBlockTimeMs has no runtime consumer yet (metadata) so the WOW fix is behavior-safe
today; it corrects the sim + future block-bounded features. Suite 60/428 green.
This commit is contained in:
@@ -17,7 +17,7 @@ const DEFAULT_DECIMALS = 12;
|
||||
|
||||
// family: 'monero' (subaddress + transfer_split), 'utxo' (BTC/LTC-style), 'mimblewimble' (Grin).
|
||||
const PROFILES = Object.freeze({
|
||||
WOW: Object.freeze({ id: 'WOW', symbol: 'WOW', decimals: 11, meanBlockTimeMs: 120000, family: 'monero', uriScheme: 'wownero' }),
|
||||
WOW: Object.freeze({ id: 'WOW', symbol: 'WOW', decimals: 11, meanBlockTimeMs: 300000, family: 'monero', uriScheme: 'wownero' }),
|
||||
XMR: Object.freeze({ id: 'XMR', symbol: 'XMR', decimals: 12, meanBlockTimeMs: 120000, family: 'monero', uriScheme: 'monero' }),
|
||||
BTC: Object.freeze({ id: 'BTC', symbol: 'BTC', decimals: 8, meanBlockTimeMs: 600000, family: 'utxo', uriScheme: 'bitcoin' }),
|
||||
LTC: Object.freeze({ id: 'LTC', symbol: 'LTC', decimals: 8, meanBlockTimeMs: 150000, family: 'utxo', uriScheme: 'litecoin' }),
|
||||
|
||||
@@ -13,13 +13,16 @@
|
||||
* 5. Treasure placement (farther from exit = riskier)
|
||||
* 6. Number of monsters (future enhancement)
|
||||
*
|
||||
* Block times:
|
||||
* - Wownero (WOW): ~2 minutes (120 seconds)
|
||||
* - Monero (XMR): ~2 minutes (120 seconds)
|
||||
* - Bitcoin (for reference): ~10 minutes
|
||||
*
|
||||
* Note: Previously docs said WOW was 5 min but it's actually 2 min like XMR.
|
||||
* We keep the config flexible for future adjustments.
|
||||
* Block times (measured against live daemons; see chainProfile.js meanBlockTimeMs):
|
||||
* - Grin (GRIN): ~1 minute
|
||||
* - Monero (XMR): ~2 minutes
|
||||
* - Litecoin (LTC): ~2.5 minutes
|
||||
* - Wownero (WOW): ~5 minutes (measured 5.01 min/block over 1000 blocks — an earlier "correction"
|
||||
* to 2 min was WRONG; WOW is a SLOW chain, closer to BTC than XMR for calibration purposes)
|
||||
* - Bitcoin (BTC): ~10 minutes
|
||||
*
|
||||
* Slow chains (WOW/BTC) can't get a timer-driven house edge from a single dungeon without it
|
||||
* becoming an unplayable slog → they want MULTI-LEVEL depth. See NETWORK_TUNING below.
|
||||
*/
|
||||
|
||||
const DIFFICULTY_PRESETS = {
|
||||
@@ -47,7 +50,7 @@ const DIFFICULTY_PRESETS = {
|
||||
targetHouseWinRate: 0.3 // 30% house wins (player-friendly)
|
||||
},
|
||||
|
||||
// Normal - balanced for Wownero (2 min blocks)
|
||||
// Normal - balanced baseline (~2 min blocks, i.e. XMR; WOW is actually ~5 min — see header)
|
||||
normal: {
|
||||
dungeon: {
|
||||
width: 45,
|
||||
@@ -144,32 +147,32 @@ function getDifficultyPreset(cryptoType = 'WOW', overridePreset = null) {
|
||||
return { ...DIFFICULTY_PRESETS.casino, presetName: 'casino' };
|
||||
}
|
||||
|
||||
// Per-network difficulty tuning, SOLVED by the balance sim (src/sim/calibrate.js) so cryptoType
|
||||
// finally shapes difficulty instead of being a dead parameter. Two independent levers:
|
||||
// sizeScale ∝ √(blockTime/ref) — PACING: keeps a run's length a consistent fraction of the block
|
||||
// interval (GRIN sprint … BTC epic), applied to EVERY preset; the timer then contributes a
|
||||
// stable baseline. Reference network = WOW/XMR (2-min blocks) = 1.0.
|
||||
// monsterSpeed (movesPerPlayerMove) — the house EDGE lever; calibrated for the CASINO preset
|
||||
// (70% target) only, so it's applied ONLY to casino. On slow chains (BTC) the timer barely bites
|
||||
// a ~30s run, so the monster carries the edge (and caps ~64% even maxed — a real property of
|
||||
// block-timed play, not a bug).
|
||||
// CAVEAT: the sim bots don't actively evade the monster, so monsterSpeed is a STARTING POINT that
|
||||
// under-provisions vs skilled humans — validate/retune with live telemetry. The sizeScale half is
|
||||
// bot-robust. Operator env overrides (DUNGEON_*, MONSTER_*) still win. Kill-switch: NETWORK_TUNING_DISABLED=true.
|
||||
// Per-network SIZE tuning so cryptoType shapes difficulty (it used to be a dead parameter).
|
||||
// sizeScale ∝ √(blockTime/ref) — PACING: a run's length as a consistent fraction of the block
|
||||
// interval, applied to EVERY preset. Reference = XMR (2-min blocks) = 1.0. Clamped [0.7, 1.6]
|
||||
// so single maps stay playable.
|
||||
//
|
||||
// DELIBERATELY NO monster-speed lever. The sim proved a supernaturally fast monster (a) feels like
|
||||
// cheating and (b) doesn't even WORK on slow chains — WOW (5-min blocks) caps ~64% house-win even
|
||||
// at a 2.2× monster on a 111×55 map, because a single dungeon clears in ~1 min << the block, so the
|
||||
// timer (the fair, on-theme "race the block" edge) barely bites. The real edge lever for slow chains
|
||||
// is MULTI-LEVEL DEPTH (cumulative run length across levels), which is the next build. Until then,
|
||||
// slow chains (WOW/BTC) run honest-but-under-target on the timer; the monster stays at its fair
|
||||
// preset speed. Operator env overrides (DUNGEON_*, MONSTER_*) still win. Kill: NETWORK_TUNING_DISABLED=true.
|
||||
const NETWORK_TUNING = {
|
||||
WOW: { sizeScale: 1.00, monsterSpeed: 1.08 },
|
||||
XMR: { sizeScale: 1.00, monsterSpeed: 1.08 },
|
||||
LTC: { sizeScale: 1.12, monsterSpeed: 1.11 },
|
||||
BTC: { sizeScale: 1.60, monsterSpeed: 2.20 },
|
||||
GRIN: { sizeScale: 0.71, monsterSpeed: 0.94 }
|
||||
GRIN: { sizeScale: 0.71 }, // ~1 min blocks
|
||||
XMR: { sizeScale: 1.00 }, // ~2 min (reference)
|
||||
LTC: { sizeScale: 1.12 }, // ~2.5 min
|
||||
WOW: { sizeScale: 1.60 }, // ~5 min (measured) — wants multi-level, not one giant map
|
||||
BTC: { sizeScale: 1.60 } // ~10 min — wants multi-level
|
||||
};
|
||||
|
||||
// Fold the per-network tuning into a resolved preset (size for all presets, monster for casino).
|
||||
// Fold the per-network SIZE scale into a resolved preset (pacing; monster left at the fair preset speed).
|
||||
function applyNetworkTuning(preset, cryptoType) {
|
||||
if (process.env.NETWORK_TUNING_DISABLED === 'true') return preset;
|
||||
const t = NETWORK_TUNING[String(cryptoType || '').trim().toUpperCase()];
|
||||
if (!t) return preset;
|
||||
const out = {
|
||||
return {
|
||||
...preset,
|
||||
dungeon: {
|
||||
...preset.dungeon,
|
||||
@@ -177,10 +180,6 @@ function applyNetworkTuning(preset, cryptoType) {
|
||||
height: Math.max(12, Math.round(preset.dungeon.height * t.sizeScale))
|
||||
}
|
||||
};
|
||||
if (preset.presetName === 'casino' && t.monsterSpeed) {
|
||||
out.monster = { ...preset.monster, movesPerPlayerMove: t.monsterSpeed };
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,7 @@ const { BOTS } = require('./simBots');
|
||||
const { meanBlockTimeMsFor } = require('../chain/chainProfile');
|
||||
const { DIFFICULTY_PRESETS } = require('../game/difficultyConfig');
|
||||
|
||||
const BASE_BLOCK_MS = 120000; // WOW/XMR — the reference the size scale is relative to
|
||||
const BASE_BLOCK_MS = 120000; // XMR (2-min) — the reference the size scale is relative to
|
||||
|
||||
function parseArgs(argv) {
|
||||
const a = { target: 0.70, preset: 'casino', bot: 'explorer-greedy', runs: 160, cadence: 320,
|
||||
|
||||
@@ -14,27 +14,31 @@ beforeEach(() => { KEYS.forEach(k => { SAVE[k] = process.env[k]; delete process.
|
||||
afterEach(() => { KEYS.forEach(k => { if (SAVE[k] === undefined) delete process.env[k]; else process.env[k] = SAVE[k]; }); });
|
||||
|
||||
describe('per-network difficulty tuning', () => {
|
||||
test('WOW is the reference (sizeScale 1.0) — casino dims unchanged', () => {
|
||||
const t = applyNetworkTuning(casino(), 'WOW');
|
||||
expect(t.dungeon.width).toBe(DIFFICULTY_PRESETS.casino.dungeon.width);
|
||||
expect(t.dungeon.height).toBe(DIFFICULTY_PRESETS.casino.dungeon.height);
|
||||
test('XMR is the reference (sizeScale 1.0) — casino dims unchanged; WOW (5-min) scales up', () => {
|
||||
const xmr = applyNetworkTuning(casino(), 'XMR');
|
||||
expect(xmr.dungeon.width).toBe(DIFFICULTY_PRESETS.casino.dungeon.width);
|
||||
expect(xmr.dungeon.height).toBe(DIFFICULTY_PRESETS.casino.dungeon.height);
|
||||
// WOW is a slow chain now (5 min) → bigger map than XMR.
|
||||
expect(applyNetworkTuning(casino(), 'WOW').dungeon.width).toBeGreaterThan(xmr.dungeon.width);
|
||||
});
|
||||
|
||||
test('dungeon size scales with block time: GRIN < WOW < BTC', () => {
|
||||
test('dungeon size scales with block time: GRIN < XMR < WOW', () => {
|
||||
const g = applyNetworkTuning(casino(), 'GRIN').dungeon.width;
|
||||
const x = applyNetworkTuning(casino(), 'XMR').dungeon.width;
|
||||
const w = applyNetworkTuning(casino(), 'WOW').dungeon.width;
|
||||
const b = applyNetworkTuning(casino(), 'BTC').dungeon.width;
|
||||
expect(g).toBeLessThan(w);
|
||||
expect(w).toBeLessThan(b);
|
||||
expect(g).toBeLessThan(x);
|
||||
expect(x).toBeLessThan(w);
|
||||
});
|
||||
|
||||
test('monster speed is tuned for casino but NOT for other presets', () => {
|
||||
expect(applyNetworkTuning(casino(), 'BTC').monster.movesPerPlayerMove).toBe(NETWORK_TUNING.BTC.monsterSpeed);
|
||||
// normal keeps its own monster speed (tuning was calibrated for casino only)
|
||||
test('tuning scales SIZE only — the monster stays at its fair preset speed', () => {
|
||||
// No cheating-fast monster: every preset keeps its own movesPerPlayerMove.
|
||||
expect(applyNetworkTuning(casino(), 'BTC').monster.movesPerPlayerMove)
|
||||
.toBe(DIFFICULTY_PRESETS.casino.monster.movesPerPlayerMove);
|
||||
expect(applyNetworkTuning(normal(), 'BTC').monster.movesPerPlayerMove)
|
||||
.toBe(DIFFICULTY_PRESETS.normal.monster.movesPerPlayerMove);
|
||||
// ...but normal STILL gets the pacing size scale
|
||||
// ...but both get the pacing size scale.
|
||||
expect(applyNetworkTuning(normal(), 'BTC').dungeon.width).toBeGreaterThan(DIFFICULTY_PRESETS.normal.dungeon.width);
|
||||
expect(NETWORK_TUNING.BTC.monsterSpeed).toBeUndefined();
|
||||
});
|
||||
|
||||
test('unknown network is left untouched', () => {
|
||||
|
||||
Reference in New Issue
Block a user