Skip to content

Commit

Permalink
feat(Codeforces & AtCoder): 为 "样例测试结果检查器" 添加国际化支持并修复一些问题
Browse files Browse the repository at this point in the history
  • Loading branch information
beijixiaohu committed Jul 6, 2024
1 parent f388e18 commit abcaf23
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 53 deletions.
40 changes: 40 additions & 0 deletions resources/locales/atcoder-better/codeEditor.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,46 @@
"strict": "严格一致",
"wcmp": "wcmp - 匹配字符串"
}
},
"checkMessage": {
"ignoreWhitespace": {
"correct": "正确",
"mismatch": "不匹配"
},
"strict": {
"correct": "正确",
"mismatch": "不匹配"
},
"ncmp": {
"differ": "{{count}}个数字不同 - 预期: '{{expected}}', 实际: '{{actual}}'",
"longerExpected": "预期包含更长的序列 [长度 = {{expectedLength}}], 实际包含 {{actualLength}} 个元素",
"longerActual": "实际包含更长的序列 [长度 = {{actualLength}}], 预期包含 {{expectedLength}} 个元素",
"correctFew": "{{count}} 个数字: \"{{numbers}}\"",
"correctMany": "{{count}} 个数字"
},
"rcmp": {
"differ": "第{{n}}个数不同 - 预期: '{{expected}}', 实际: '{{actual}}', 误差: '{{error}}'",
"single": "找到 '{{actual}}', 预期 '{{expected}}', 误差 '{{error}}'",
"total": "{{count}} 个数字",
"invalidNumber": "第{{index}}个数无效 - 预期: '{{expected}}', 实际: '{{actual}}'",
"lengthMismatch": "预期和实际的数字数量不同 - 预期: {{expectedLength}} 个, 实际: {{actualLength}} 个"
},
"wcmp": {
"wordsDiffer": "{{count}}个单词不同 - 预期: '{{expected}}', 实际: '{{actual}}'",
"extraTokensInParticipant": "实际输出包含额外的单词",
"unexpectedEOF": "预期输出包含额外的单词",
"singleToken": "\"{{token}}\"",
"tokenCount": "{{count}}个单词"
},
"nyesno": {
"expectedInAnswer": "{{YES}} 或 {{NO}} 应该在预期输出中,但找到了 {{token}} [第 {{index}}{{ending}} token]",
"expectedInOutput": "{{YES}} 或 {{NO}} 应该在实际输出中,但找到了 {{token}} [第 {{index}}{{ending}} token]",
"mismatch": "不匹配\n预期: {{expected}}\n实际: {{actual}} [第 {{index}}{{ending}} token]",
"longerInAnswer": "预期输出包含更多的 token [长度 = {{expectedLength}}],但实际输出只包含 {{actualLength}} 个 token",
"longerInOutput": "实际输出包含更多的 token [长度 = {{actualLength}}],但预期输出只包含 {{expectedLength}} 个 token",
"emptyOutput": "空输出",
"summary": "{{index}} 个 token: yes 的数量是 {{yesCount}}, no 的数量是 {{noCount}}"
}
}
}
}
40 changes: 40 additions & 0 deletions resources/locales/codeforces-better/codeEditor.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,46 @@
"strict": "严格一致",
"wcmp": "wcmp - 匹配字符串"
}
},
"checkMessage": {
"ignoreWhitespace": {
"correct": "正确",
"mismatch": "不匹配"
},
"strict": {
"correct": "正确",
"mismatch": "不匹配"
},
"ncmp": {
"differ": "{{count}}个数字不同 - 预期: '{{expected}}', 实际: '{{actual}}'",
"longerExpected": "预期包含更长的序列 [长度 = {{expectedLength}}], 实际包含 {{actualLength}} 个元素",
"longerActual": "实际包含更长的序列 [长度 = {{actualLength}}], 预期包含 {{expectedLength}} 个元素",
"correctFew": "{{count}} 个数字: \"{{numbers}}\"",
"correctMany": "{{count}} 个数字"
},
"rcmp": {
"differ": "第{{n}}个数不同 - 预期: '{{expected}}', 实际: '{{actual}}', 误差: '{{error}}'",
"single": "找到 '{{actual}}', 预期 '{{expected}}', 误差 '{{error}}'",
"total": "{{count}} 个数字",
"invalidNumber": "第{{index}}个数无效 - 预期: '{{expected}}', 实际: '{{actual}}'",
"lengthMismatch": "预期和实际的数字数量不同 - 预期: {{expectedLength}} 个, 实际: {{actualLength}} 个"
},
"wcmp": {
"wordsDiffer": "{{count}}个单词不同 - 预期: '{{expected}}', 实际: '{{actual}}'",
"extraTokensInParticipant": "实际输出包含额外的单词",
"unexpectedEOF": "预期输出包含额外的单词",
"singleToken": "\"{{token}}\"",
"tokenCount": "{{count}}个单词"
},
"nyesno": {
"expectedInAnswer": "{{YES}} 或 {{NO}} 应该在预期输出中,但找到了 {{token}} [第 {{index}}{{ending}} token]",
"expectedInOutput": "{{YES}} 或 {{NO}} 应该在实际输出中,但找到了 {{token}} [第 {{index}}{{ending}} token]",
"mismatch": "不匹配\n预期: {{expected}}\n实际: {{actual}} [第 {{index}}{{ending}} token]",
"longerInAnswer": "预期输出包含更多的 token [长度 = {{expectedLength}}],但实际输出只包含 {{actualLength}} 个 token",
"longerInOutput": "实际输出包含更多的 token [长度 = {{actualLength}}],但预期输出只包含 {{expectedLength}} 个 token",
"emptyOutput": "空输出",
"summary": "{{index}} 个 token: yes 的数量是 {{yesCount}}, no 的数量是 {{noCount}}"
}
}
}
}
142 changes: 115 additions & 27 deletions script/dev/atcoder-better.user.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ==UserScript==
// @name Atcoder Better!
// @namespace https://greasyfork.org/users/747162
// @version 1.17.1
// @version 1.17.2
// @description 一个适用于 AtCoder 的 Tampermonkey 脚本,增强功能与界面。
// @author 北极小狐
// @match *://atcoder.jp/*
Expand Down Expand Up @@ -12479,19 +12479,19 @@ class TestCaseStatus {

// 设置checker的评测结果
setJudgeChecker(message) {
const createJudgeCheckerElement = (message) => {
function createJudgeCheckerElement(message) {
const judgeCheckerElement = OJB_safeCreateJQElement(`<div class="judge-checker">${i18next.t('moreSettings.validator.messagePrefix', { ns: 'codeEditor' })}${message}</div>`);
return judgeCheckerElement;
};
this.contentElement.append(createJudgeCheckerElement(message));
const judgeCheckerElement = createJudgeCheckerElement(message);
this.contentElement.before(judgeCheckerElement);
};

setJudge(judge) {
this.judgeElement.text(judge);
}
}


/**
* 评测结果检查器基类,所有检查器类应继承此类
*/
Expand All @@ -12518,7 +12518,7 @@ class IgnoreWhitespaceValidator extends judgeResultValidator {
const passed = expected === actual;
return {
passed: passed,
message: passed ? '正确' : '不匹配'
message: passed ? i18next.t('moreSettings.checkMessage.ignoreWhitespace.correct', { ns: 'codeEditor' }) : i18next.t('moreSettings.checkMessage.ignoreWhitespace.mismatch', { ns: 'codeEditor' })
};
}
}
Expand All @@ -12531,11 +12531,12 @@ class StrictValidator extends judgeResultValidator {
const passed = expected === actual;
return {
passed: passed,
message: passed ? '正确' : '不匹配'
message: passed ? i18next.t('moreSettings.checkMessage.strict.correct', { ns: 'codeEditor' }) : i18next.t('moreSettings.checkMessage.strict.mismatch', { ns: 'codeEditor' })
};
}
}


/**
* 整数检查器
*/
Expand All @@ -12551,7 +12552,7 @@ class NcmpValidator extends judgeResultValidator {
if (expectedInts[i] !== actualInts[i]) {
return {
passed: false,
message: `不匹配\n第${i + 1}个数字不同 - 预期: ${expectedInts[i]}, 实际: ${actualInts[i]}`
message: i18next.t('moreSettings.checkMessage.ncmp.differ', { ns: 'codeEditor', count: i + 1, expected: expectedInts[i], actual: actualInts[i] })
};
} else if (i < 5) {
firstElems.push(expectedInts[i]);
Expand All @@ -12561,20 +12562,20 @@ class NcmpValidator extends judgeResultValidator {
if (expectedInts.length > actualInts.length) {
return {
passed: false,
message: `不匹配\n预期包含更长的序列 [长度 = ${expectedInts.length}], 实际包含 ${actualInts.length} 个元素`
message: i18next.t('moreSettings.checkMessage.ncmp.longerExpected', { ns: 'codeEditor', expectedLength: expectedInts.length, actualLength: actualInts.length })
};
}

if (actualInts.length > expectedInts.length) {
return {
passed: false,
message: `不匹配\n实际包含更长的序列 [长度 = ${actualInts.length}], 预期包含 ${expectedInts.length} 个元素`
message: i18next.t('moreSettings.checkMessage.ncmp.longerActual', { ns: 'codeEditor', actualLength: actualInts.length, expectedLength: expectedInts.length })
};
}

return {
passed: true,
message: firstElems.length <= 5 ? `正确\n${firstElems.length} 个数字: "${firstElems.join(' ')}"` : `正确\n${expectedInts.length} 个数字`
message: firstElems.length <= 5 ? i18next.t('moreSettings.checkMessage.ncmp.correctFew', { ns: 'codeEditor', count: firstElems.length, numbers: firstElems.join(' ') }) : i18next.t('moreSettings.checkMessage.ncmp.correctMany', { ns: 'codeEditor', count: expectedInts.length })
};
}
}
Expand All @@ -12587,21 +12588,69 @@ class RcmpValidator extends judgeResultValidator {
super();
this.epsilon = epsilon;
}

validate(expected, actual) {
const expectedFloats = expected.split(/\s+/).map(Number);
const actualFloats = actual.split(/\s+/).map(Number);
const expectedFloats = expected.split(/\s+/).filter(Boolean).map(Number);
const actualFloats = actual.split(/\s+/).filter(Boolean).map(Number);

if (expectedFloats.length !== actualFloats.length) {
return {
passed: false,
message: i18next.t('moreSettings.checkMessage.rcmp.lengthMismatch', {
ns: 'codeEditor',
expectedLength: expectedFloats.length,
actualLength: actualFloats.length
})
};
}

for (let i = 0; i < expectedFloats.length; i++) {
if (isNaN(expectedFloats[i]) || isNaN(actualFloats[i])) {
return {
passed: false,
message: i18next.t('moreSettings.checkMessage.rcmp.invalidNumber', {
ns: 'codeEditor',
index: i + 1,
expected: expected.split(/\s+/)[i],
actual: actual.split(/\s+/)[i]
})
};
}

const error = Math.abs(expectedFloats[i] - actualFloats[i]);
if (error > this.epsilon) {
return {
passed: false,
message: `不匹配\n第${i + 1}个数\n预期: ${expectedFloats[i].toFixed(10)}\n实际: ${actualFloats[i].toFixed(10)}\n误差: ${error.toFixed(10)}`
message: i18next.t('moreSettings.checkMessage.rcmp.differ', {
ns: 'codeEditor',
n: i + 1,
expected: expectedFloats[i].toFixed(7),
actual: actualFloats[i].toFixed(7),
error: error.toFixed(7)
})
};
}
}

if (expectedFloats.length === 1) {
const error = Math.abs(expectedFloats[0] - actualFloats[0]);
return {
passed: true,
message: i18next.t('moreSettings.checkMessage.rcmp.single', {
ns: 'codeEditor',
expected: expectedFloats[0].toFixed(7),
actual: actualFloats[0].toFixed(7),
error: error.toFixed(7)
})
};
}

return {
passed: true,
message: '正确'
message: i18next.t('moreSettings.checkMessage.rcmp.total', {
ns: 'codeEditor',
count: expectedFloats.length
})
};
}
}
Expand All @@ -12620,21 +12669,30 @@ class WcmpValidator extends judgeResultValidator {
if (expectedWords[i] !== actualWords[i]) {
return {
passed: false,
message: `不匹配\n第${i + 1}个单词不同 - 预期: '${expectedWords[i]}', 实际: '${actualWords[i]}'`
message: i18next.t('moreSettings.checkMessage.wcmp.wordsDiffer', {
ns: 'codeEditor',
count: i + 1,
expected: expectedWords[i],
actual: actualWords[i]
})
};
}
}

if (expectedWords.length !== actualWords.length) {
return {
passed: false,
message: expectedWords.length > actualWords.length ? '实际输出包含额外的单词' : '预期输出包含额外的单词'
message: expectedWords.length > actualWords.length
? i18next.t('moreSettings.checkMessage.wcmp.extraTokensInParticipant', { ns: 'codeEditor' })
: i18next.t('moreSettings.checkMessage.wcmp.unexpectedEOF', { ns: 'codeEditor' })
};
}

return {
passed: true,
message: '正确'
message: minLength === 1
? i18next.t('moreSettings.checkMessage.wcmp.singleToken', { ns: 'codeEditor', token: expectedWords[0] })
: i18next.t('moreSettings.checkMessage.wcmp.tokenCount', { ns: 'codeEditor', count: minLength })
};
}
}
Expand Down Expand Up @@ -12662,7 +12720,7 @@ class NyesnoValidator extends judgeResultValidator {
if (expectedToken !== YES && expectedToken !== NO) {
return {
passed: false,
message: `${YES} 或 ${NO} 应该在预期输出中,但找到了 ${expectedToken} [第 ${index} 个 token]`
message: i18next.t('moreSettings.checkMessage.nyesno.expectedInAnswer', { ns: 'codeEditor', YES, NO, token: expectedToken, index, ending: this.englishEnding(index) })
};
}

Expand All @@ -12673,36 +12731,60 @@ class NyesnoValidator extends judgeResultValidator {
} else {
return {
passed: false,
message: `${YES} 或 ${NO} 应该在实际输出中,但找到了 ${actualToken} [第 ${index} 个 token]`
message: i18next.t('moreSettings.checkMessage.nyesno.expectedInOutput', { ns: 'codeEditor', YES, NO, token: actualToken, index, ending: this.englishEnding(index) })
};
}

if (expectedToken !== actualToken) {
return {
passed: false,
message: `不匹配\n预期: ${expectedToken}\n实际: ${actualToken} [第 ${index} 个 token]`
message: i18next.t('moreSettings.checkMessage.nyesno.mismatch', { ns: 'codeEditor', expected: expectedToken, actual: actualToken, index, ending: this.englishEnding(index) })
};
}
}

if (index < expectedTokens.length) {
return {
passed: false,
message: `预期输出包含更多的 token [长度 = ${expectedTokens.length}],但实际输出只包含 ${index} 个 token`
message: i18next.t('moreSettings.checkMessage.nyesno.longerInAnswer', { ns: 'codeEditor', expectedLength: expectedTokens.length, actualLength: index })
};
}

if (index < actualTokens.length) {
return {
passed: false,
message: `实际输出包含更多的 token [长度 = ${actualTokens.length}],但预期输出只包含 ${index} 个 token`
message: i18next.t('moreSettings.checkMessage.nyesno.longerInOutput', { ns: 'codeEditor', actualLength: actualTokens.length, expectedLength: index })
};
}

return {
passed: true,
message: index === 0 ? '空输出' : index === 1 ? `${actualTokens[0]}` : `${index} 个 token: yes 的数量是 ${yesCount}, no 的数量是 ${noCount}`
};
if (index === 0) {
return {
passed: true,
message: i18next.t('moreSettings.checkMessage.nyesno.emptyOutput', { ns: 'codeEditor' })
};
} else if (index === 1) {
return {
passed: true,
message: `${actualTokens[0]}`
};
} else {
return {
passed: true,
message: i18next.t('moreSettings.checkMessage.nyesno.summary', { ns: 'codeEditor', index, yesCount, noCount })
};
}
}

englishEnding(number) {
if (number % 10 === 1 && number % 100 !== 11) {
return 'st';
} else if (number % 10 === 2 && number % 100 !== 12) {
return 'nd';
} else if (number % 10 === 3 && number % 100 !== 13) {
return 'rd';
} else {
return 'th';
}
}
}

Expand Down Expand Up @@ -12786,7 +12868,13 @@ async function runCode(event, runButton, sourceDiv) {
passedTests++;
} else {
testCase.setStatus('Wrong Answer', 'error');
const diffContent = $('#DontShowDiff').prop('checked') ? result.Result.trim() : codeDiff(data.output.trim(), result.Result.trim());
const judgeResultCheckMode = OJB_getGMValue('judgeResultCheckMode', 'ignoreWhitespace');
// 如果检查模式为ignoreWhitespace,去掉字符串末尾的空格和换行符
if (judgeResultCheckMode === 'ignoreWhitespace') {
data.output = data.output.trim().replace(/\s+$/gm, '');
result.Result = result.Result.trim().replace(/\s+$/gm, '');
}
const diffContent = $('#DontShowDiff').prop('checked') ? result.Result : codeDiff(data.output, result.Result);
const contentType = $('#DontShowDiff').prop('checked') ? TestCaseContentType.NO_DIFF : TestCaseContentType.DIFF;
testCase.setContent(diffContent, contentType);
failedTests++;
Expand Down
Loading

0 comments on commit abcaf23

Please sign in to comment.