// Trade-raid encounter generation -- the strategic turn's dominant RNG consumer. // // The original runs this from inside encounter detection, over the server's player vector, // rolling up to three chances per player. Two of the three roll on every turn measured; the // third is behind a subsystem that was not present. The important property, and the reason // this is modelled at all while the rest of encounter detection is not, is that NO BACK EDGE // contains any of the three roll sites: the cost is one word per player per firing site, as a // bound rather than as an observation. // // The probabilities are simulation constants that come from the game data. Their in-image // defaults are used when no tuning table is loaded; either way all three are strictly inside // (0, 1), so the chance helper takes neither of its early-outs and every roll is exactly one // word. That is why the WORD COUNT is independent of the probabilities and the RAID DECISIONS // are not. #pragma once #include "game/sim/rng.h" namespace sots::app { // The chance helper's contract, transcribed: p <= 0 is false with NO draw, p >= 1 is true with // NO draw, otherwise exactly one draw and the test is `p > r` -- equality is a failure. A NaN // probability falls through both early-outs and draws. bool Chance(sots::sim::IRandom& rng, float p); struct TradeRaidOdds { // In-image defaults. A loaded tuning table overrides them; the word cost does not move. float player = 0.2f; float npc = 0.05f; float refugee = 0.05f; }; struct TradeRaidGates { // The NPC-raid roll is behind a single server-level test that does not depend on the // player, so it is all-or-nothing for the whole turn. Measured open on 8 of 8 turns // across two saves; it is not on the wire, so this is a HYPOTHESIS with a stated default. bool npcRaids = true; // The refugee-raid roll needs a subsystem manager that was absent on every measured turn. bool refugeeRaids = false; }; struct TradeRaidResult { int players = 0; // entries of the player vector the loop visited int words = 0; // generator words consumed int playerRaidHits = 0; // rolls that SUCCEEDED -- each one may cost a further word we do int npcRaidHits = 0; // not model (the target-selection draw), so a non-zero total int refugeeRaidHits = 0;// here is the signal that the ledger is about to fall short }; // Roll the raid chances for `players` players, in the original's order: player raid, then NPC // raid, then refugee raid, per player, before moving to the next player. TradeRaidResult RollTradeRaids(sots::sim::IRandom& rng, int players, const TradeRaidOdds& odds, const TradeRaidGates& gates); } // namespace sots::app