using System;
using System.Linq;
namespace Decentralized.Domain
{
/// 卖家的说法:每个标签一项。型号是文字,其余五项取值与 的取值一致。
public struct Claim
{
public string Model;
public int Fan, Fingers, Bracket, Print, Sticker;
public int Get(int row) => row == CardLabels.RowFan ? Fan : row == CardLabels.RowFingers ? Fingers : row == CardLabels.RowBracket ? Bracket : row == CardLabels.RowPrint ? Print : Sticker;
public Claim With(int row, int value)
{
var c = this;
if (row == CardLabels.RowFan) c.Fan = value; else if (row == CardLabels.RowFingers) c.Fingers = value;
else if (row == CardLabels.RowBracket) c.Bracket = value; else if (row == CardLabels.RowPrint) c.Print = value; else c.Sticker = value;
return c;
}
public string Encode() => $"{Model}:{Fan}{Fingers}{Bracket}{Print}{Sticker}";
public static Claim Decode(string s)
{
var p = (s ?? "").Split(':');
if (p.Length != 2 || p[1].Length != 5 || !Hardware.Specs.ContainsKey(p[0])) return default;
int D(int i) => Math.Max(0, Math.Min(2, p[1][i] - '0'));
return new Claim { Model = p[0], Fan = D(0), Fingers = D(1), Bracket = D(2), Print = D(3), Sticker = D(4) };
}
/// 按真实情况写出的说法(没有隐瞒)。
public static Claim Truth(Unit u)
{
var l = u.Appearance;
return new Claim { Model = u.FakeOf ?? u.Model, Fan = l.Fan, Fingers = l.Fingers, Bracket = l.Bracket, Print = l.Print, Sticker = l.Sticker };
}
}
///
/// 标签栏的数据:六个标签(型号、风扇、金手指、挡板、丝印、贴纸),每个标签有卖家的说法和真实情况。
/// 一个标签被指出(拆穿)之后,说法改回真实情况;价格按「已指出的标签取真实情况,其余取说法」算,
/// 所以卖家隐瞒的问题要玩家一项项指出来才能把价格拉回真实价值。
///
public static class CardLabels
{
public const int RowSpec = 1, RowFan = 2, RowFingers = 4, RowBracket = 8, RowPrint = 16, RowSticker = 32, AllRows = 63;
public static readonly int[] Rows = { RowSpec, RowFan, RowFingers, RowBracket, RowPrint, RowSticker };
/// 各标签各取值对价格的影响(百分比,相加):风扇积灰 −4%、缺角 −12%;金手指发暗 −4%、氧化 −12%;挡板划痕 −2%;封条缺失 −6%。
/// 接口数不对、丝印、手写序列号本身不扣价,它们是假货的证据。
public static double Pct(int row, int value)
{
switch (row)
{
case RowFan: return value == 1 ? -4 : value == 2 ? -12 : 0;
case RowFingers: return value == 1 ? -4 : value == 2 ? -12 : 0;
case RowBracket: return value == 1 ? -2 : 0;
case RowSticker: return value == 2 ? -6 : 0;
}
return 0;
}
/// 这一项的真实取值是不是假货的证据。
public static bool IsFakeSign(int row, int realValue) =>
(row == RowBracket && realValue == 2) || (row == RowPrint && realValue != 0) || (row == RowSticker && realValue == 1);
///
/// 价值:claim 为空表示没有人说过什么,一律按真实情况;exposed 里的位表示已指出的标签(取真实情况)。
/// 型号取「说法里的型号」,型号标签被拆穿之后取真实芯片。价值 = 型号定价 ×(0.25 + 0.75 × 健康度)×(1 + 各标签折扣之和)。
///
public static long Value(Unit u, Claim? claim, int exposed)
{
var real = Claim.Truth(u);
Claim believed = claim == null ? real : claim.Value;
double sum = 0;
foreach (int r in Rows)
{
if (r == RowSpec) continue;
int v = claim == null || (exposed & r) != 0 ? real.Get(r) : believed.Get(r);
sum += Pct(r, v);
}
string model = claim == null || (exposed & RowSpec) != 0 ? real.Model : believed.Model;
double value = CardValue.Of(model, u.Health) * Math.Max(0, 1 + sum / 100.0);
return Math.Max(1, (long)Math.Round(value));
}
/// 指出一个标签:返回 0 没问题(说法属实)、1 隐瞒了对价格不利的情况、2 拆穿了假货。
/// 指出假货的证据(接口数、丝印、手写序列号)时,型号标签一并拆穿。
public static int Point(Unit u, Claim claim, int row, ref int exposed)
{
var real = Claim.Truth(u);
bool wasExposed = (exposed & row) != 0;
exposed |= row;
if (row == RowSpec) return claim.Model != real.Model ? 2 : 0;
if (IsFakeSign(row, real.Get(row)) && claim.Get(row) != real.Get(row))
{
exposed |= RowSpec;
return 2;
}
if (wasExposed) return 0;
return claim.Get(row) != real.Get(row) ? 1 : 0;
}
/// 说法里隐瞒了什么:说法比真实情况价值高的标签,以及型号不符。
public static int Lies(Unit u, Claim claim, int exposed)
{
var real = Claim.Truth(u);
int lies = 0;
foreach (int r in Rows)
{
if ((exposed & r) != 0) continue;
if (r == RowSpec) { if (claim.Model != real.Model) lies |= r; }
else if (claim.Get(r) != real.Get(r) && (Pct(r, claim.Get(r)) > Pct(r, real.Get(r)) || IsFakeSign(r, real.Get(r)))) lies |= r;
}
return lies;
}
/// 卖家怎么说:老实人如实说;中间商每项有三成概率隐瞒;骗子全部说成没问题。假卡的型号一律说成标称型号。
public static Claim DealerClaim(Unit u, DealerKind kind, Rng rng)
{
var c = Claim.Truth(u);
c.Model = u.Model;
if (kind == DealerKind.Honest) { c.Model = u.FakeOf ?? u.Model; return c; }
double hide = kind == DealerKind.Scammer ? 1.0 : 0.7;
foreach (int r in Rows)
{
if (r == RowSpec) continue;
if (rng.Chance(hide)) c = c.With(r, 0);
}
return c;
}
}
}