表演赛,难度裁判和表现裁判分计分错误
This commit is contained in:
parent
51ed62dfe5
commit
18b1f36de5
@ -1,3 +1,5 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'judge_account.dart';
|
||||
|
||||
abstract final class EliteScoringRuleCatalog {
|
||||
@ -48,6 +50,18 @@ abstract final class EliteScoringRuleCatalog {
|
||||
'8': 2.56,
|
||||
};
|
||||
|
||||
/// 表演赛中,个人绳、车轮跳、交互绳沿用个人花样换算表;
|
||||
/// 长绳、旅行跳按脑图规定使用 L(x)=1.5^x。
|
||||
static double showDifficultyWeight(String category, String level) {
|
||||
if (category.contains('长绳') || category.contains('旅行跳')) {
|
||||
final value = math.pow(1.5, double.parse(level)).toDouble();
|
||||
// 服务端 ScoreRuleItem.UnitValue 为 DECIMAL(18,4),离线回退使用相同精度,
|
||||
// 避免平板预览与正式提交后的服务端结果出现浮点尾差。
|
||||
return double.parse(value.toStringAsFixed(4));
|
||||
}
|
||||
return standardDifficultyWeights[level] ?? 0;
|
||||
}
|
||||
|
||||
static const individualSolo = 'individualSolo';
|
||||
static const individualDuo = 'individualDuo';
|
||||
static const individualTeam4 = 'individualTeam4';
|
||||
@ -83,20 +97,22 @@ abstract final class EliteScoringRuleCatalog {
|
||||
: 'DT摇绳难度:L(x)=1.5^x,取最高20个难度动作的加权总和并除以20。',
|
||||
CompetitionKind.show =>
|
||||
profile.roleCode == 'A'
|
||||
? 'A组裁判记录个人绳力量体操、多摇、车轮跳、交互绳和旅行跳难度;三名A组裁判取平均。'
|
||||
: 'B组裁判记录个人绳绳子运用、车轮跳、交互绳和长绳难度;三名B组裁判取平均。',
|
||||
? 'A组裁判记录个人绳力量体操、多摇、车轮跳、交互绳和旅行跳难度;旅行跳按L(x)=1.5^x换算,其余沿用个人花样换算表;1至3名A组裁判按实际人数取平均。'
|
||||
: 'B组裁判记录个人绳绳子运用、车轮跳、交互绳和长绳难度;长绳按L(x)=1.5^x换算,其余沿用个人花样换算表;1至3名B组裁判按实际人数取平均。',
|
||||
CompetitionKind.prescribed => '',
|
||||
};
|
||||
}
|
||||
|
||||
static String performanceGuidance(JudgeAccount profile) {
|
||||
final judgeCount = profile.competition == CompetitionKind.show ? 3 : 2;
|
||||
final judgeDescription = profile.competition == CompetitionKind.show
|
||||
? '1至3名表现裁判按实际人数取平均'
|
||||
: '2名裁判平均';
|
||||
final multiplier = profile.competition == CompetitionKind.doubleDutch
|
||||
? '0.033'
|
||||
: '0.05';
|
||||
return '音乐20%、完成质量25%、娱乐25%、创意15%、多样性15%;'
|
||||
'每记录1次失误,五项评分同步减1。本页仅显示当前裁判原始评分;'
|
||||
'$judgeCount名裁判平均及×$multiplier由平台汇总。';
|
||||
'减数量仅记录裁判手工减号,每1次失误再从五项评分各减1。本页仅显示当前裁判原始评分;'
|
||||
'$judgeDescription,再×$multiplier由平台汇总。';
|
||||
}
|
||||
|
||||
static String chiefGuidance(JudgeAccount profile) =>
|
||||
|
||||
@ -30,7 +30,7 @@ class CompetitionRuleCalculator {
|
||||
/// 当前单名表现裁判给出的加权原始分。
|
||||
///
|
||||
/// [minusCounts]只包含裁判手动减号;失误自动生成的减号由 [errors]
|
||||
/// 按规则公式扣除。页面可以把两者相加展示,但写入平台时必须分开。
|
||||
/// 按规则公式扣除。页面展示与写入平台时都必须把两者分开。
|
||||
///
|
||||
/// 裁判端只展示该分数;裁判间平均以及项目系数由平台汇总时处理。
|
||||
static double performanceRaw(
|
||||
|
||||
@ -404,7 +404,9 @@ class ScoringController extends GetxController {
|
||||
} else if (key != null && item.adjustSign < 0) {
|
||||
performanceMinusCounts[key] = count;
|
||||
_itemUiKeys[item.code] = key;
|
||||
} else if (key != null && item.inputMode == 'score') {
|
||||
} else if (key != null &&
|
||||
!item.isAutoGenerated &&
|
||||
item.inputMode == 'score') {
|
||||
final rounded = score.round().clamp(0, 24);
|
||||
performancePlusCounts[key] = (rounded - 12).clamp(0, 12);
|
||||
performanceMinusCounts[key] = (12 - rounded).clamp(0, 12);
|
||||
@ -708,9 +710,22 @@ class ScoringController extends GetxController {
|
||||
double showDifficultyCategorySubtotal(String category) =>
|
||||
showDifficultyLevels.fold<double>(0, (sum, level) {
|
||||
final count = showDifficultyCount(category, level);
|
||||
return sum + (rule.difficultyWeights[level] ?? 0) * count;
|
||||
return sum + _showDifficultyUnit(category, level) * count;
|
||||
});
|
||||
|
||||
double _showDifficultyUnit(String category, String level) {
|
||||
if (apiScoreSheet != null) {
|
||||
for (final item in apiScoreSheet!.items) {
|
||||
if (_exactDifficultyLevelOf(item) != level) continue;
|
||||
final matched = _matchingKey([
|
||||
category,
|
||||
], '${item.subjectName ?? ''}${item.name}${item.code}');
|
||||
if (matched != null) return item.unitValue;
|
||||
}
|
||||
}
|
||||
return EliteScoringRuleCatalog.showDifficultyWeight(category, level);
|
||||
}
|
||||
|
||||
String _showDifficultyKey(String category, String level) =>
|
||||
'$category::$level';
|
||||
|
||||
@ -748,9 +763,9 @@ class ScoringController extends GetxController {
|
||||
|
||||
int performanceMinusCount(String item) => performanceMinusCounts[item] ?? 0;
|
||||
|
||||
/// 页面展示的减数量包含裁判手动减号和失误自动产生的减号。
|
||||
/// 页面减数量只展示裁判手工减号;失误在公式中另行扣减。
|
||||
int performanceEffectiveMinusCount(String item) =>
|
||||
performanceMinusCount(item) + performanceErrorCount.value;
|
||||
performanceMinusCount(item);
|
||||
|
||||
Map<String, int> get performanceEffectiveMinusCounts => {
|
||||
for (final item in performanceItems.keys)
|
||||
@ -783,10 +798,7 @@ class ScoringController extends GetxController {
|
||||
}
|
||||
|
||||
void setPerformanceEffectiveMinusCount(String item, int count) {
|
||||
final manualCount = (count - performanceErrorCount.value)
|
||||
.clamp(0, 99)
|
||||
.toInt();
|
||||
performanceMinusCounts[item] = manualCount;
|
||||
performanceMinusCounts[item] = count.clamp(0, 99).toInt();
|
||||
_syncPerformanceItemScore(item);
|
||||
recalculate();
|
||||
}
|
||||
@ -821,7 +833,7 @@ class ScoringController extends GetxController {
|
||||
})
|
||||
.join('+');
|
||||
return '单项=加数量+12-人工减数量-失误数量(限0~24整数)\n'
|
||||
'失误${performanceErrorCount.value}次 → 页面五项减数量各自动+'
|
||||
'失误${performanceErrorCount.value}次 → 公式中五项各自额外减'
|
||||
'${performanceErrorCount.value}\n'
|
||||
'当前减/加:$markDetails\n'
|
||||
'单项分:$itemScores\n'
|
||||
@ -951,6 +963,12 @@ class ScoringController extends GetxController {
|
||||
double _difficultyScore() {
|
||||
if (profile.role != ScoreRole.difficulty) return 0;
|
||||
if (apiScoreSheet != null) return _apiDifficultyScore();
|
||||
if (profile.competition == CompetitionKind.show) {
|
||||
return showDifficultyCategories.fold<double>(
|
||||
0,
|
||||
(sum, category) => sum + showDifficultyCategorySubtotal(category),
|
||||
);
|
||||
}
|
||||
if (profile.competition == CompetitionKind.doubleDutch) {
|
||||
return profile.roleCode == 'DT'
|
||||
? CompetitionRuleCalculator.doubleDutchTurner(difficultyCounts)
|
||||
|
||||
@ -1169,7 +1169,7 @@ class _PerformancePanel extends StatelessWidget {
|
||||
children: [
|
||||
_PerformanceMarkCount(
|
||||
value: minusCount,
|
||||
semanticLabel: '${entry.key}减号数量,包含失误自动减号',
|
||||
semanticLabel: '${entry.key}裁判手工减号数量',
|
||||
color: hasErrors ? AppColors.danger : AppColors.primary,
|
||||
onTap: controller.isReadOnly
|
||||
? null
|
||||
@ -2475,7 +2475,7 @@ List<SubmissionScoreDetail> _submissionScoreDetails(
|
||||
SubmissionScoreDetail(
|
||||
label: '失误',
|
||||
value: '${controller.performanceErrorCount.value} 次',
|
||||
description: '已自动计入各表现类别减数量',
|
||||
description: '计分时从五个表现类别各减去该次数',
|
||||
),
|
||||
];
|
||||
case ScoreRole.creative:
|
||||
|
||||
@ -22,6 +22,17 @@ void main() {
|
||||
expect(score, closeTo(14.4, .0001));
|
||||
});
|
||||
|
||||
test('表演赛表现裁判的手工减数量与失误次数分别扣减', () {
|
||||
final score = CompetitionRuleCalculator.performanceRaw(
|
||||
const {'音乐': 0, '完成质量': 0, '娱乐': 0, '创意': 0, '多样性': 0},
|
||||
// 脑图公式顺序对应单项分:音乐9、创意10、多样性8、娱乐8、完成质量8。
|
||||
minusCounts: const {'音乐': 1, '完成质量': 2, '娱乐': 2, '创意': 0, '多样性': 2},
|
||||
errors: 2,
|
||||
);
|
||||
|
||||
expect(score, closeTo(8.5, .000001));
|
||||
});
|
||||
|
||||
test('交互绳表现使用0.033系数', () {
|
||||
final result = CompetitionRuleCalculator.performance(
|
||||
const {'音乐': 0, '完成质量': 0, '娱乐': 0, '创意': 0, '多样性': 0},
|
||||
@ -59,7 +70,7 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
test('失误自动生成减数量后仍按公式单独减去失误数量', () {
|
||||
test('已有手工减数量时仍按公式单独减去失误数量', () {
|
||||
final withoutError = CompetitionRuleCalculator.performanceRaw(
|
||||
const {'音乐': 3, '完成质量': 3, '娱乐': 3, '创意': 3, '多样性': 3},
|
||||
minusCounts: const {'音乐': 2, '完成质量': 2, '娱乐': 2, '创意': 2, '多样性': 2},
|
||||
|
||||
@ -82,6 +82,18 @@ void main() {
|
||||
]);
|
||||
});
|
||||
|
||||
test('表演赛长绳和旅行跳使用1.5指数换算,其余类别沿用个人花样表', () {
|
||||
expect(
|
||||
EliteScoringRuleCatalog.showDifficultyWeight('DP+DM(个人绳)', '3'),
|
||||
.34,
|
||||
);
|
||||
expect(EliteScoringRuleCatalog.showDifficultyWeight('DT(旅行跳)', '3'), 3.375);
|
||||
expect(
|
||||
EliteScoringRuleCatalog.showDifficultyWeight('DL(长绳)', '0.5'),
|
||||
1.2247,
|
||||
);
|
||||
});
|
||||
|
||||
test('各精英级裁判具有对应规则提示', () {
|
||||
for (final account in ['IF-DP1', 'WH-DA1', 'DD-DJ1', 'SH-A1']) {
|
||||
expect(
|
||||
@ -100,4 +112,16 @@ void main() {
|
||||
contains('空间违例'),
|
||||
);
|
||||
});
|
||||
|
||||
test('表演赛规则提示按实际1至3名裁判取平均', () {
|
||||
final difficulty = JudgeAccounts.find('SH-A1')!;
|
||||
expect(
|
||||
EliteScoringRuleCatalog.difficultyGuidance(difficulty),
|
||||
contains('1至3名A组裁判按实际人数取平均'),
|
||||
);
|
||||
expect(
|
||||
EliteScoringRuleCatalog.performanceGuidance(JudgeAccounts.find('SH-P1')!),
|
||||
contains('1至3名表现裁判按实际人数取平均'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user