using System;
using System.Collections.Generic;
namespace SLSUtilities.General
{
///
/// 可复现的种子随机数管理器。
/// 从一个主种子 (master seed) 派生出各子系统独立的 ,
/// 确保同一种子下所有随机结果完全可复现,且各子系统之间互不干扰。
///
/// 核心概念:
///
/// -
/// Channel
///
/// 通过字符串 domain 派生的固定 RNG,适合生命周期与 Randomizer 相同的子系统。
/// 同一 domain 始终返回同一个 实例。
///
///
/// -
/// Fork
///
/// 通过任意 key 派生的一次性 RNG,每次调用都返回新实例。
/// 适合与特定数据绑定(坐标、ID 等),同一 key 始终产生相同序列。
///
///
/// -
/// Next
///
/// 自增计数器派生的 RNG,每次调用都返回不同的实例。
/// 适合无特定绑定数据的通用场景。
///
///
///
///
/// 使用示例:
///
///
/// // ═══════════════════════════════════════════════════════════
/// // 1. 创建 Randomizer(通常在一局 Run 开始时)
/// // ═══════════════════════════════════════════════════════════
/// //
/// // 传入种子字符串,为空则自动生成
/// var randomizer = new Randomizer("my-seed-123");
/// // randomizer.Seed → "my-seed-123"
///
/// // ═══════════════════════════════════════════════════════════
/// // 2. Channel — 固定子系统 RNG(地图生成、战利品表等)
/// // ═══════════════════════════════════════════════════════════
/// //
/// // 首次调用创建,后续调用返回同一实例
/// Random mapRng = randomizer.Channel("Map");
/// Random lootRng = randomizer.Channel("Loot");
/// // randomizer.Channel("Map") == mapRng → true(同一引用)
///
/// // ═══════════════════════════════════════════════════════════
/// // 3. Fork — 按 key 派生一次性 RNG(节点坐标、房间 ID 等)
/// // ═══════════════════════════════════════════════════════════
/// //
/// // 机械台/商店:同一坐标 → 同一随机序列
/// Random nodeRng = randomizer.Fork(nodePosition.x, nodePosition.y);
/// mechanicalTable.Setup(nodeRng);
///
/// // 也可以用字符串 key
/// Random bossRng = randomizer.Fork("BossRoom", floorIndex);
///
/// // ═══════════════════════════════════════════════════════════
/// // 4. Next — 自增通用 RNG(无特定绑定的临时随机)
/// // ═══════════════════════════════════════════════════════════
/// //
/// // 每次调用返回不同实例,但同一 seed 下第 N 次调用结果一致
/// Random rng1 = randomizer.Next(); // 第 1 次
/// Random rng2 = randomizer.Next(); // 第 2 次,与 rng1 不同
///
/// // ═══════════════════════════════════════════════════════════
/// // 5. 实际游戏场景举例
/// // ═══════════════════════════════════════════════════════════
/// //
/// // Roguelite Run 开始:
/// // var runRng = new Randomizer(MainGameManager.Seed);
/// // RunMapData map = MapGenerator.Generate(config, runRng.Channel("Map"));
/// //
/// // 进入机械台节点 (3, 5):
/// // mechanicalTable.Setup(runRng.Fork(3, 5));
/// // → 稀有度 Roll + 装备 Roll 都由该 Fork 驱动
/// //
/// // 进入商店节点 (2, 4):
/// // logisticsCenter.Setup(runRng.Fork(2, 4));
/// // → 商品列表 + 价格 Roll 都由该 Fork 驱动
/// //
/// // 战斗中敌人掉落:
/// // Random dropRng = runRng.Channel("EnemyDrop");
/// // → 所有敌人掉落共享一个 Channel,保持掉落顺序一致
/// //
/// // 临时需要一个 RNG(不关心绑定):
/// // Random tempRng = runRng.Next();
///
///
public sealed class Randomizer
{
/// 本实例使用的种子字符串。
public string Seed { get; }
private readonly int _masterSeed;
private int _counter;
private readonly Dictionary _channels = new Dictionary();
///
/// 使用指定种子创建。传入 null 或空字符串则自动生成 8 位随机种子。
///
public Randomizer(string seed = null)
{
if (string.IsNullOrEmpty(seed))
{
seed = Guid.NewGuid().ToString("N")[..8];
}
Seed = seed;
_masterSeed = seed.GetHashCode();
}
// ─────────────────── Channel ───────────────────
///
/// 获取或创建指定 domain 的固定 Channel RNG。
/// 同一 domain 始终返回同一个 实例。
///
/// 子系统标识,如 "Map"、"Loot"、"EnemyDrop"。
public Random Channel(string domain)
{
if (_channels.TryGetValue(domain, out Random existing))
{
return existing;
}
Random rng = new Random(HashCode.Combine(_masterSeed, domain));
_channels[domain] = rng;
return rng;
}
// ─────────────────── Fork ───────────────────
///
/// 通过任意数量的 key 派生一次性 RNG。
/// 同一组 key 始终产生相同的随机序列。
///
/// 用于派生种子的 key(坐标、ID、字符串等)。
public Random Fork(params object[] keys)
{
int derived = _masterSeed;
foreach (object key in keys)
{
derived = HashCode.Combine(derived, key);
}
return new Random(derived);
}
// ─────────────────── Next ───────────────────
///
/// 创建一个自增计数器派生的通用 RNG。
/// 每次调用返回不同实例,适用于无特定绑定的通用场景。
///
public Random Next()
{
_counter++;
return new Random(HashCode.Combine(_masterSeed, _counter));
}
}
}