第十一阶段 stage 1:污点代替没收、模块不设上限、出块 ×10

- 预测市场内幕单改为确定性记污点:结算或卖出时照常兑付,内幕份额对应的兑付记收款钱包污点;删除 PredictInsiderShare、PredictInsiderCatch
- 操纵检出被查后照常兑付,预测兑付记钱包污点、永续盈利记 Poinbase 账户污点;删除 ManipFreezeDays
- Wallet.AddTaintCapped:污点不超过钱包当时的价值,超出部分按 PredictFraudShare / PredictFraudCap 记诈骗罪证(TaintCrime.Commit)
- 模块效果不设上限,只留算力、耗电、损耗乘数的下限(ModuleHashFloor / ModuleKjFloor / ModuleWearFloor)
- BlockReward 100 → 1000,NpcHash0 360 → 3600
- 修正内幕卖出日志"只退本金"的错误说法;8 种语言文字与手册更新

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Opus 5.5 committed 2026-11-08 14:17:41 +08:00
1 parent 45a6ec2f77
commit bc23ae056b
18 files changed
+248 -205

No files matched your search

+42 -34
View File
@@ -113,12 +113,13 @@ namespace Decentralized.Domain
public bool Held;
}
/// <summary>夜间结算发生的事。Kind:win / lose / void / caught。</summary>
/// <summary>夜间结算发生的事。Kind:win / lose / void / insider(内幕份额记了污点)/ manip(被判操纵,整笔兑付记了污点)。
/// Amount 是兑付或本金;Taint 是实际加上的污点,Over 是超出钱包价值、折成罪证的部分,Fraud 是罪证。</summary>
public sealed class PredictEvent
{
public string Kind, Title;
public PredictMarket Market;
public double Amount, Fraud;
public double Amount, Taint, Over, Fraud;
}
/// <summary>候选盘口(接入层按游戏当前状态给出):先验概率按窗口算。</summary>
@@ -181,29 +182,24 @@ namespace Decentralized.Domain
}
/// <summary>全部卖出这个钱包在盘口里的持仓(盘口关闭前):收回 LMSR 的卖价减手续费,污点按比例退回,赚的部分是干净的。返回收回的 CRC。</summary>
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, out double got) => SellAll(acct, m, day, null, out got, out _);
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, Func<string, double> price, out double got) => SellAll(acct, m, day, price, out got, out _, out _);
/// <summary>
/// 全部卖出;内幕持仓(这个盘口合计)达到查处线、而且卖出有赚,按查处概率被查:本金和收益全部没收,收益的一半记罪证(fraud &gt; 0)。
/// 先买后卖、赶在结算前离场,同样会被查。
/// 全部卖出;持仓里的内幕份额(下单时已经知道答案)对应的那部分卖出所得记污点(带上限,见 <see cref="Wallet.AddTaintCapped"/>),
/// taint 是实际加上的污点,over 是超出钱包价值、没加进去的部分(由调用方按 <see cref="Wallet.OverflowFraud"/> 折成罪证)。先买后卖、赶在结算前离场也一样。
/// </summary>
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, Rng rng, out double got, out double fraud)
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, Func<string, double> price, out double got, out double taint, out double over)
{
got = 0; fraud = 0;
got = 0; taint = 0; over = 0;
var pos = PosOf(m.Id, acct.Address);
if (pos == null) return PredictResult.NoPosition;
if (!m.IsOpen(day)) return PredictResult.Closed;
double value = m.SellValue(pos.Yes, pos.Shares);
got = value * (1 - Tuning.PredictFee);
bool insider = PosAll(m.Id).Sum(x => x.Insider) >= Tuning.PredictInsiderShare * m.ShareCap;
if (pos.Yes) m.QYes -= pos.Shares; else m.QNo -= pos.Shares;
Positions.Remove(pos);
if (rng != null && insider && pos.Insider > 0 && got > pos.Cost && rng.Chance(Math.Min(1, Tuning.PredictInsiderCatch * StoryMods.Current.Enforce(day))))
{
fraud = Math.Min(Tuning.PredictFraudCap, (got - pos.Cost) * Tuning.PredictFraudShare);
got = 0;
}
Pay(acct, pos, got);
over = MarkInsider(acct, pos, got, price, out taint);
return PredictResult.Ok;
}
@@ -213,11 +209,22 @@ namespace Decentralized.Domain
acct.Add(Assets.CRC, payout); acct.Taint += pos.Taint * keep; acct.Basis += pos.Basis * keep;
}
/// <summary>内幕份额对应的兑付记污点(带上限)。taint 是实际加上的污点,返回超出钱包价值、没加进去的部分。</summary>
private static double MarkInsider(Wallet acct, PredictPos pos, double payout, Func<string, double> price, out double taint)
{
taint = 0;
if (pos.Insider <= 0 || pos.Shares <= 0 || payout <= 0) return 0;
double amount = payout * Math.Min(1, pos.Insider / pos.Shares);
double over = acct.AddTaintCapped(amount, price);
taint = amount - over;
return over;
}
/// <summary>
/// 夜间:先登记今晚的观测,再结算 ToDay 到了的盘口(赢的钱回到下注的钱包,wallet 按地址找)。outcome 由接入层给出(事件 / 派系读观测,纯度和行情读游戏数值),null 表示拿不到数据。
/// 赢家每份兑付 1 信用点;内幕持仓达到份额上限的 <see cref="Tuning.PredictInsiderShare"/> 后,有概率被查:本金和收益全部没收,收益的一半记罪证。
/// 赢家每份兑付 1 信用点,照常兑付;内幕份额对应的兑付记污点,行情盘被判操纵时整笔兑付记污点(都带上限,超出钱包价值的部分折成罪证)。
/// </summary>
public List<PredictEvent> Night(int day, IEnumerable<string> activeNow, Func<PredictMarket, bool?> outcome, Func<string, Wallet> wallet, Rng rng, double push = 0)
public List<PredictEvent> Night(int day, IEnumerable<string> activeNow, Func<PredictMarket, bool?> outcome, Func<string, Wallet> wallet, Func<string, double> price, Rng rng, double push = 0)
{
var now = activeNow.Distinct().ToList();
if (Prev != null) Started[day] = now.Where(x => !Prev.Contains(x)).ToList();
@@ -243,14 +250,6 @@ namespace Decentralized.Domain
if (!Stats.TryGetValue(sk, out var st)) Stats[sk] = st = new PredictStat();
st.Count++; st.SumP0 += m.P0; if (res.Value) st.Hits++;
var winners = all.Where(x => x.Yes == res.Value).ToList();
// 操纵:行情盘上,赢家的方向和玩家推价的方向一致时按推价幅度被查——本金和收益都没收,收益的一半记罪证
if (m.Kind == PredictKind.Price && winners.Count > 0 && winners.Any(x => x.Yes ? push > 0 : push < 0) && rng.Chance(Math.Min(1, Market.ManipChance(push) * StoryMods.Current.Enforce(day))))
{
double profitM = winners.Sum(x => x.Shares) - cost;
evs.Add(new PredictEvent { Kind = "manip", Title = m.Title, Market = m, Amount = cost, Fraud = Math.Min(Tuning.PredictFraudCap, Math.Max(0, profitM) * Tuning.PredictFraudShare) });
Record(day, m, m.Outcome, -cost, true);
continue;
}
if (all.Count > 0 && winners.Count == 0)
{
evs.Add(new PredictEvent { Kind = "lose", Title = m.Title, Market = m, Amount = cost });
@@ -259,19 +258,28 @@ namespace Decentralized.Domain
else if (winners.Count > 0)
{
double payout = winners.Sum(x => x.Shares), profit = payout - cost;
// 内幕持仓按盘口合计(拆到几个钱包里也一样)
if (all.Sum(x => x.Insider) >= Tuning.PredictInsiderShare * m.ShareCap && rng.Chance(Math.Min(1, Tuning.PredictInsiderCatch * StoryMods.Current.Enforce(day))))
// 操纵:行情盘上,赢家的方向和玩家推价的方向一致时按推价幅度被查——照常兑付,整笔兑付记污点
bool manip = m.Kind == PredictKind.Price && winners.Any(x => x.Yes ? push > 0 : push < 0) && rng.Chance(Math.Min(1, Market.ManipChance(push) * StoryMods.Current.Enforce(day)));
double taint = 0, over = 0;
foreach (var pos in winners)
{
// 查实:本金和收益全部没收
evs.Add(new PredictEvent { Kind = "caught", Title = m.Title, Market = m, Amount = payout, Fraud = Math.Min(Tuning.PredictFraudCap, Math.Max(0, profit) * Tuning.PredictFraudShare) });
Record(day, m, m.Outcome, -cost, true);
}
else
{
foreach (var pos in winners) Pay(wallet(pos.Wallet), pos, pos.Shares);
evs.Add(new PredictEvent { Kind = "win", Title = m.Title, Market = m, Amount = payout });
Record(day, m, m.Outcome, profit, true);
var w = wallet(pos.Wallet);
Pay(w, pos, pos.Shares);
if (manip)
{
double o = w.AddTaintCapped(pos.Shares, price);
taint += pos.Shares - o; over += o;
}
else
{
// 内幕份额(下单时已经知道答案)对应的兑付记污点,每个钱包各算各的
over += MarkInsider(w, pos, pos.Shares, price, out double t);
taint += t;
}
}
string kind = manip ? "manip" : taint + over > 1e-9 ? "insider" : "win";
evs.Add(new PredictEvent { Kind = kind, Title = m.Title, Market = m, Amount = payout, Taint = taint, Over = over, Fraud = Wallet.OverflowFraud(over) });
Record(day, m, m.Outcome, profit, true);
}
else Record(day, m, m.Outcome, 0, false);
}