"""Pluggable PRNG predictors for arena-battle token forensics. The server computes: auth_token = "token_" + id + "_" + std::to_string(rand()) with NO srand() call => default-seeded PRNG, one draw per fighter, in id order. Two predictors: GlibcRandom - glibc TYPE_3 additive-feedback random() (Ubuntu 22.04 target) DarwinRandom - macOS/BSD rand() (Schrage multiplicative LCG) for local tests """ import sys MASK32 = 0xFFFFFFFF class GlibcRandom: """glibc random()/rand() TYPE_3: r[i] = (r[i-3] + r[i-31]) mod 2^32, out = r[i]>>1.""" name = "glibc" def __init__(self, seed=1, n=200000): r = [0] * (344 + n) r[0] = seed & MASK32 for i in range(1, 31): # r[i] = 16807 * r[i-1] mod 2147483647 (Schrage), result in [0, 2^31-2] hi, lo = divmod(r[i - 1], 127773) word = 16807 * lo - 2836 * hi if word < 0: word += 2147483647 r[i] = word for i in range(31, 34): r[i] = r[i - 31] for i in range(34, 344 + n): r[i] = (r[i - 3] + r[i - 31]) & MASK32 self.values = [x >> 1 for x in r[344:]] def index_of(self, value): return [i for i, v in enumerate(self.values) if v == value] class DarwinRandom: """macOS rand(): Lehmer LCG next = 16807*next mod (2^31-1), out = next (seed 1).""" name = "darwin" def __init__(self, seed=1, n=200000): ctx = seed vals = [] for _ in range(n): ctx = (16807 * ctx) % 2147483647 vals.append(ctx) self.values = vals def index_of(self, value): return [i for i, v in enumerate(self.values) if v == value] def get_predictor(name, n=200000): cls = {"glibc": GlibcRandom, "darwin": DarwinRandom}[name] return cls(1, n) if __name__ == "__main__": g = get_predictor("glibc", 10) d = get_predictor("darwin", 10) print("glibc :", g.values[:5]) print("darwin:", d.values[:5]) assert g.values[:5] == [1804289383, 846930886, 1681692777, 1714636915, 1957747793], "glibc seq wrong" assert d.values[:5] == [16807, 282475249, 1622650073, 984943658, 1144108930], "darwin seq wrong" print("PREDICTORS OK", flush=True)