2026-08-23 16:21:49 +08:00
|
|
|
|
namespace YD_Event.Application.JudgeScore;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 表演赛(个人花样基础套路赛)内置计分规则定义。
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 只在初始化/同步规则库时使用;运行期的评分项一律从 ScoreRule / ScoreRuleItem 读取,
|
|
|
|
|
|
/// 后台调整评分项、难度分值、席位配置后无需改代码。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 计分口径(裁判端原型 + 花样跳绳裁判端逻辑与测试验收说明书 6.7):<br/>
|
|
|
|
|
|
/// 席位:难度A组×3、难度B组×3、表现P×3、主裁T×1。<br/>
|
|
|
|
|
|
/// A 组裁判一人评 个人绳(DP+DM)、车轮跳(DA)、交互绳(DT_INTERACTION)、旅行跳(DT_TRAVEL) 四个类别,A=average(A1,A2,A3);<br/>
|
|
|
|
|
|
/// B 组裁判一人评 个人绳(DR)、车轮跳(DB)、交互绳(DJ)、长绳(DL) 四个类别,B=average(B1,B2,B3);<br/>
|
|
|
|
|
|
/// 总难度 D=(A+B)/2;表现 P=average(p1,p2,p3)×0.05;<br/>
|
|
|
|
|
|
/// 规定元素 Q=max(0,1-0.05×缺失类别数),5 类为 个人绳、车轮跳、交互绳、长绳(6米以上)、旅行跳;<br/>
|
|
|
|
|
|
/// 扣分 M=max(0,1-(失误+违例)),表演赛不记间歇与空间违例;<br/>
|
|
|
|
|
|
/// 总分 R=max(0, D×(1+P)×Q×M)。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static class ShowcaseRuleDefinition
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 表演赛规则编码
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public const string RuleCode = "ROPE_SHOWCASE";
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 表演赛规则名称
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public const string RuleName = "表演赛计分规则";
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// A 组难度裁判角色类别编码。一人评个人绳、车轮跳、交互绳、旅行跳四个类别。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public const string RoleDifficultyGroupA = "DGA";
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// B 组难度裁判角色类别编码。一人评个人绳、车轮跳、交互绳、长绳四个类别。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public const string RoleDifficultyGroupB = "DGB";
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 表现裁判角色类别编码
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public const string RolePerformance = FreestyleRuleDefinition.RolePerformance;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 主裁判角色类别编码
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public const string RoleChief = FreestyleRuleDefinition.RoleChief;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 规定元素目标类别数:个人绳、车轮跳、交互绳、长绳(6米以上)、旅行跳
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public const decimal RequiredTarget = 5m;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// A 组难度裁判要评的类别。
|
|
|
|
|
|
/// 逻辑说明书要求把脑图里两个同名 DT 拆成 DT_INTERACTION(交互绳)与 DT_TRAVEL(旅行跳),避免字段互相覆盖。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static IReadOnlyList<ShowcaseDifficultyCategory> GroupACategories { get; } = new List<ShowcaseDifficultyCategory>
|
|
|
|
|
|
{
|
|
|
|
|
|
new("DP_DM", "个人绳", "DP+DM(个人绳)"),
|
|
|
|
|
|
new("DA", "车轮跳", "DA(车轮跳)"),
|
|
|
|
|
|
new("DT_INTERACTION", "交互绳", "DT(交互绳)"),
|
|
|
|
|
|
new("DT_TRAVEL", "旅行跳", "DT(旅行跳)")
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// B 组难度裁判要评的类别
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static IReadOnlyList<ShowcaseDifficultyCategory> GroupBCategories { get; } = new List<ShowcaseDifficultyCategory>
|
|
|
|
|
|
{
|
|
|
|
|
|
new("DR", "个人绳", "DR(个人绳)"),
|
|
|
|
|
|
new("DB", "车轮跳", "DB(车轮跳)"),
|
|
|
|
|
|
new("DJ", "交互绳", "DJ(交互绳)"),
|
|
|
|
|
|
new("DL", "长绳", "DL(长绳)")
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 规定元素类别。成套动作中必须出现这 5 类,每缺一类参与 Q 扣减。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static IReadOnlyList<RequiredElementDefinition> RequiredElements { get; } = new List<RequiredElementDefinition>
|
|
|
|
|
|
{
|
|
|
|
|
|
new("REQ_SINGLE_ROPE", "个人绳"),
|
|
|
|
|
|
new("REQ_WHEEL", "车轮跳"),
|
|
|
|
|
|
new("REQ_WIRE", "交互绳"),
|
|
|
|
|
|
new("REQ_LONG_ROPE", "长绳"),
|
|
|
|
|
|
new("REQ_TRAVEL", "旅行跳")
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-09-21 11:14:47 +08:00
|
|
|
|
/// 表演赛裁判席位配置:A/B 组难度裁判各 1~3 人、表现裁判 1~3 人、主裁 1 人
|
2026-08-23 16:21:49 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public const string JudgeRoleSchemaJson = """
|
2026-09-21 11:14:47 +08:00
|
|
|
|
[{"title":"主裁判","slots":[{"code":"T","name":"主裁判-T","min":1,"max":1}]},{"title":"难度裁判","slots":[{"code":"DGA","name":"难度裁判-A组裁判","min":1,"max":3,"categories":["DP+DM (个人绳)","DA (车轮跳)","DT (交互绳)","DT (旅行跳)"]},{"code":"DGB","name":"难度裁判-B组裁判","min":1,"max":3,"categories":["DR (个人绳)","DB (车轮跳)","DJ (交互绳)","DL (旅行跳)"]}]},{"title":"表现裁判","slots":[{"code":"P","name":"表现裁判-P","min":1,"max":3}]}]
|
2026-08-23 16:21:49 +08:00
|
|
|
|
""";
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 表演赛计分参数配置。
|
|
|
|
|
|
/// totalMode=factor 表示按 R = D × (1 + P) × Q × M 相乘;A 组、B 组各自取平均后 difficultyDivisor=2 对应 D=(A+B)/2;
|
|
|
|
|
|
/// 主裁同时承担 Q(规定元素缺失数)与 M(扣分合计)两个因子,因此配置了两条 T。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public const string RuleConfigJson = """
|
|
|
|
|
|
{"ruleType":"SHOWCASE","totalMode":"factor","baseScore":0,"scoreDecimals":2,"difficultyDivisor":2,"correctionNeedApproval":false,"roleAggregations":[{"roleType":"DGA","symbol":"A","label":"A组难度","sourceField":"DifficultyScore","targetField":"DifficultyScore","aggregate":"avg","sign":1,"factor":"D","factorWeight":1},{"roleType":"DGB","symbol":"B","label":"B组难度","sourceField":"DifficultyScore","targetField":"DifficultyScore","aggregate":"avg","sign":1,"factor":"D","factorWeight":1},{"roleType":"P","symbol":"P","label":"表现","sourceField":"PerformanceScore","targetField":"PerformanceScore","aggregate":"avg","sign":1,"factor":"P","factorWeight":0.05},{"roleType":"T","symbol":"Q","label":"规定元素缺失","sourceField":"RequiredMissingCount","targetField":"RequiredScore","aggregate":"max","sign":1,"factor":"Q","factorWeight":0.05},{"roleType":"T","symbol":"M","label":"扣分","sourceField":"DeductionScore","targetField":"DeductionScore","aggregate":"sum","sign":-1,"factor":"M","factorWeight":1}],"tieBreakers":["TotalScore","-DeductionScore","RequiredScore","PerformanceScore","DifficultyScore"]}
|
|
|
|
|
|
""";
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 表演赛全部评分项
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static IReadOnlyList<PrescribedRuleItemDefinition> Items { get; } = BuildItems();
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 构造表演赛评分项:A/B 组各 4 个类别的难度等级、表现五维与失误、主裁扣分与 5 类规定元素、取消资格原因
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static List<PrescribedRuleItemDefinition> BuildItems()
|
|
|
|
|
|
{
|
|
|
|
|
|
var items = new List<PrescribedRuleItemDefinition>();
|
|
|
|
|
|
var sort = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 一名难度裁判要按类别分块评分,因此每个类别的难度等级项都用类别编码作前缀独立成项。
|
|
|
|
|
|
foreach (var category in GroupACategories)
|
|
|
|
|
|
{
|
|
|
|
|
|
items.AddRange(FreestyleRuleDefinition.BuildDifficultyItems(RoleDifficultyGroupA,
|
|
|
|
|
|
category.Code, category.DisplayName, null, null, category.Code, ref sort));
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach (var category in GroupBCategories)
|
|
|
|
|
|
{
|
|
|
|
|
|
items.AddRange(FreestyleRuleDefinition.BuildDifficultyItems(RoleDifficultyGroupB,
|
|
|
|
|
|
category.Code, category.DisplayName, null, null, category.Code, ref sort));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
items.AddRange(FreestyleRuleDefinition.BuildPerformanceItems(ref sort));
|
|
|
|
|
|
// 表演赛主裁只记失误与违例,不记间歇、空间违例。
|
|
|
|
|
|
items.AddRange(FreestyleRuleDefinition.BuildChiefDeductionItems(null, null, ref sort));
|
|
|
|
|
|
|
|
|
|
|
|
// 规定元素:5 类中每类只要有动作即计一类完成,缺失类别数由服务端算出后参与 Q。
|
|
|
|
|
|
foreach (var element in RequiredElements)
|
|
|
|
|
|
{
|
|
|
|
|
|
items.Add(new PrescribedRuleItemDefinition
|
|
|
|
|
|
{
|
|
|
|
|
|
JudgeRoleType = RoleChief, ItemCode = element.ItemCode, ItemName = element.ItemName,
|
|
|
|
|
|
ItemType = ScoreItemType.RequiredElement, ScoreDimension = ScoreDimensionType.Required,
|
|
|
|
|
|
CalcMode = ScoreCalcMode.CountByUnit,
|
|
|
|
|
|
UnitValue = 1m, MinValue = 0m, MaxValue = 50m, StepValue = 1m, DefaultValue = 0m,
|
|
|
|
|
|
IsRequired = false, Sort = ++sort,
|
|
|
|
|
|
ExtensionJson = FreestyleItemExtension.RequiredElement()
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
items.Add(new PrescribedRuleItemDefinition
|
|
|
|
|
|
{
|
|
|
|
|
|
JudgeRoleType = RoleChief, ItemCode = "REQUIRED_MISSING", ItemName = "规定元素缺失(个)",
|
|
|
|
|
|
ItemType = ScoreItemType.RequiredElement, ScoreDimension = ScoreDimensionType.None,
|
|
|
|
|
|
CalcMode = ScoreCalcMode.DirectValue,
|
|
|
|
|
|
MinValue = 0m, MaxValue = RequiredTarget, StepValue = 1m, DefaultValue = 0m,
|
|
|
|
|
|
IsRequired = false, Sort = ++sort,
|
|
|
|
|
|
ExtensionJson = FreestyleItemExtension.RequiredMissing(RequiredTarget)
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
items.AddRange(FreestyleRuleDefinition.BuildDisqualifyItems(ref sort));
|
|
|
|
|
|
return items;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 表演赛一个难度类别的定义
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="Code">类别编码,同时作为评分项编码前缀与评分对象编码</param>
|
|
|
|
|
|
/// <param name="Name">类别名称</param>
|
|
|
|
|
|
/// <param name="DisplayName">页面分块标题,与裁判端原型逐字一致</param>
|
|
|
|
|
|
public record ShowcaseDifficultyCategory(string Code, string Name, string DisplayName);
|