using System;
namespace Decentralized.Domain
{
/// 夜间发生的事。Kind:collapse(付不出利息,挤兑)/ reported(被举报);Amount 是被没收的池子。
public sealed class PonziEvent
{
public string Kind;
public double Amount;
}
///
/// 高息理财(庞氏):玩家定一个日息,下层的人按日息和下层声望往池子里存 CRC。每晚先拿新存入的钱付利息,不够的从发行人钱包的 CRC 补,
/// 补不上就挤兑崩盘;池子越大、日息越高越容易被举报,被举报同样崩盘。崩盘时池子被没收;玩家也可以随时卷走池子(污点全额)。
/// 每个存档只能开一次。
///
public sealed class Ponzi
{
public bool Open, Done;
public double Rate, Pool, OwnPaid, AdCost, Inflow;
/// 发行人的钱包地址:宣传费、垫付的利息从它出,卷走的池子进它。
public string Wallet;
public int OpenDay = -1;
public static double Cap(int day) => Tuning.PonziCap0 + Tuning.PonziCapPerDay * day;
/// 每晚新存入(均值):基数随天数,日息高于 4% 时按指数放大,下层声望越高越多。
public static double InflowMean(int day, double rate, int lowerRep) =>
(Tuning.PonziBase0 + Tuning.PonziBasePerDay * day) * Math.Pow(rate / 0.04, Tuning.PonziRateExp) * Math.Max(0.3, Math.Min(1.5, 0.5 + lowerRep / 100.0));
public static double ReportChance(double rate, double pool, int day) =>
Math.Min(0.9, Tuning.PonziReport0 + Tuning.PonziReportRate * rate + Tuning.PonziReportSize * Math.Min(1, pool / Cap(day)));
public bool CanOpen(int day) => !Open && !Done && day >= Tuning.PonziMinDay;
/// 开张:宣传费从发行人钱包的 CRC 里扣。
public bool Start(Wallet acct, double rate, int day)
{
if (!CanOpen(day) || acct.Avail(Assets.CRC) + 1e-9 < Tuning.PonziAdCost) return false;
acct.Add(Assets.CRC, -Tuning.PonziAdCost);
Open = true; Rate = rate; Pool = 0; OwnPaid = 0; AdCost = Tuning.PonziAdCost; OpenDay = day; Wallet = acct.Address;
return true;
}
/// 夜间结算。返回崩盘 / 被举报事件(没有事就返回 null)。
public PonziEvent Night(int day, int lowerRep, Wallet acct, Rng rng)
{
if (!Open) return null;
double mean = InflowMean(day, Rate, lowerRep);
double inflow = Math.Min(Cap(day) - Pool, mean * rng.Range(0.6, 1.4));
inflow = Math.Max(0, inflow); Inflow = inflow;
double interest = Pool * Rate;
double fromInflow = Math.Min(interest, inflow), rest = interest - fromInflow;
if (rest > acct.Avail(Assets.CRC) + 1e-9)
{
var ev = new PonziEvent { Kind = "collapse", Amount = Pool + inflow - fromInflow };
Open = false; Done = true; Pool = 0;
return ev;
}
if (rest > 0) { acct.Add(Assets.CRC, -rest); OwnPaid += rest; }
Pool += inflow - fromInflow;
Pool -= Pool * Tuning.PonziWithdraw;
if (rng.Chance(ReportChance(Rate, Pool, day)))
{
var ev = new PonziEvent { Kind = "reported", Amount = Pool };
Open = false; Done = true; Pool = 0;
return ev;
}
return null;
}
/// 卷走:池子全部进账户,污点全额(骗局所得)。返回卷走的数额。
/// 卷走:池子全部进发行人钱包,污点全额(骗局所得)。
public double Rug(Wallet acct)
{
if (!Open) return 0;
double take = Pool;
acct.Add(Assets.CRC, take); acct.Taint += take;
Open = false; Done = true; Pool = 0;
return take;
}
}
}