judge: 将testlib移入vendor

This commit is contained in:
undefined committed 2021-04-07 18:19:51 +08:00
1 parent ec4971d213
commit a0a6f5107f
10 files changed
+29 -2897

No files matched your search

+3 -1
View File
@@ -15,7 +15,9 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
steps: steps:
- name: Check out - name: Check out
uses: actions/checkout@v1 uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up Node - name: Set up Node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
+4
View File
@@ -1,3 +1,7 @@
[submodule "customize"] [submodule "customize"]
path = packages/customize path = packages/customize
url = https://github.com/hydro-dev/customize.git url = https://github.com/hydro-dev/customize.git
[submodule "testlib"]
path = packages/hydrojudge/vendor/testlib
url = https://github.com/MikeMirzayanov/testlib.git
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@hydrooj/hydrojudge", "name": "@hydrooj/hydrojudge",
"bin": "bin/hydrojudge.js", "bin": "bin/hydrojudge.js",
"version": "2.4.18", "version": "2.4.19",
"os": [ "os": [
"linux" "linux"
], ],
+7 -1
View File
@@ -224,7 +224,9 @@ export async function readYamlCases(folder: string, cfg: Dictionary<any> = {}, a
} }
} else throw new FormatError('Invalid user_extra_files config.'); } else throw new FormatError('Invalid user_extra_files config.');
} }
if (cfg.cases) { if (cfg.outputs) {
config.type = 'submit_answer';
} else if (cfg.cases) {
config.subtasks = [{ config.subtasks = [{
score: parseInt(cfg.score, 10) || Math.floor(100 / cfg.cases.length), score: parseInt(cfg.score, 10) || Math.floor(100 / cfg.cases.length),
time_limit_ms: parseTimeMS(cfg.time || '1s'), time_limit_ms: parseTimeMS(cfg.time || '1s'),
@@ -264,6 +266,7 @@ export async function readYamlCases(folder: string, cfg: Dictionary<any> = {}, a
config.subtasks = c.subtasks; config.subtasks = c.subtasks;
config.count = c.count; config.count = c.count;
} }
if (config.type === 'submit_answer' && !cfg.outputs) throw new FormatError('outputs config not found');
return Object.assign(cfg, config); return Object.assign(cfg, config);
} }
@@ -299,9 +302,12 @@ function isValidConfig(config) {
export default async function readCases(folder: string, cfg: Record<string, any> = {}, args) { export default async function readCases(folder: string, cfg: Record<string, any> = {}, args) {
const iniConfig = path.resolve(folder, 'config.ini'); const iniConfig = path.resolve(folder, 'config.ini');
const yamlConfig = path.resolve(folder, 'config.yaml'); const yamlConfig = path.resolve(folder, 'config.yaml');
const ymlConfig = path.resolve(folder, 'config.yml');
let config; let config;
if (fs.existsSync(yamlConfig)) { if (fs.existsSync(yamlConfig)) {
config = { ...yaml.load(fs.readFileSync(yamlConfig).toString()) as object, ...cfg }; config = { ...yaml.load(fs.readFileSync(yamlConfig).toString()) as object, ...cfg };
} else if (fs.existsSync(ymlConfig)) {
config = { ...yaml.load(fs.readFileSync(ymlConfig).toString()) as object, ...cfg };
} else if (fs.existsSync(iniConfig)) { } else if (fs.existsSync(iniConfig)) {
config = { ...convertIniConfig(fs.readFileSync(iniConfig).toString()), ...cfg }; config = { ...convertIniConfig(fs.readFileSync(iniConfig).toString()), ...cfg };
} else config = cfg; } else config = cfg;
+1 -1
View File
@@ -24,7 +24,7 @@ export async function check(config): Promise<[number, number, string]> {
export async function compileChecker(checkerType: string, checker: string, copyIn: any) { export async function compileChecker(checkerType: string, checker: string, copyIn: any) {
if (!checkers[checkerType]) throw new SystemError('Unknown checker type {0}.', [checkerType]); if (!checkers[checkerType]) throw new SystemError('Unknown checker type {0}.', [checkerType]);
if (checkerType === 'testlib') copyIn['testlib.h'] = { src: resolve(__dirname, '../files/testlib.h') }; if (checkerType === 'testlib') copyIn['testlib.h'] = { src: resolve(__dirname, '../vendor/testlib/testlib.h') };
const file = await fs.readFile(checker); const file = await fs.readFile(checker);
// TODO cache compiled checker // TODO cache compiled checker
return await compile(parseFilename(checker).split('.')[1], file.toString(), 'checker', copyIn); return await compile(parseFilename(checker).split('.')[1], file.toString(), 'checker', copyIn);
+1 -1
View File
@@ -19,7 +19,7 @@ function judgeCase(c) {
return async (ctx, ctxSubtask) => { return async (ctx, ctxSubtask) => {
ctx.executeInteractor.copyIn.in = c.input ? { src: c.input } : { content: '' }; ctx.executeInteractor.copyIn.in = c.input ? { src: c.input } : { content: '' };
ctx.executeInteractor.copyIn.out = c.output ? { src: c.output } : { content: '' }; ctx.executeInteractor.copyIn.out = c.output ? { src: c.output } : { content: '' };
ctx.executeInteractor.copyIn['testlib.h'] = { src: resolve(__dirname, '../../files/testlib.h') }; ctx.executeInteractor.copyIn['testlib.h'] = { src: resolve(__dirname, '../../vendor/testlib/testlib.h') };
const [{ code, time_usage_ms, memory_usage_kb }, resInteractor] = await run([ const [{ code, time_usage_ms, memory_usage_kb }, resInteractor] = await run([
{ {
execute: ctx.executeUser.execute.replace(/\$\{name\}/g, 'code'), execute: ctx.executeUser.execute.replace(/\$\{name\}/g, 'code'),
+10 -25
View File
@@ -1,4 +1,3 @@
import { readFile } from 'fs-extra';
import * as STATUS from '../status'; import * as STATUS from '../status';
export async function judge({ export async function judge({
@@ -6,42 +5,28 @@ export async function judge({
}) { }) {
next({ status: STATUS.STATUS_JUDGING, progress: 0 }); next({ status: STATUS.STATUS_JUDGING, progress: 0 });
code = code.replace(/\r/g, ''); code = code.replace(/\r/g, '');
const expected = (await readFile(config.subtasks[0].cases[0].output)).toString().replace(/\r/g, '');
if (config.subtasks.length === 1) {
const status = code.trim() === expected.trim() ? STATUS.STATUS_ACCEPTED : STATUS.STATUS_WRONG_ANSWER;
const score = status === STATUS.STATUS_ACCEPTED ? config.subtasks[0].score : 0;
next({
status,
progress: 100,
case: {
status, score, time_ms: 0, memory_kb: 0, message: '',
},
});
return end({
status, score, time_ms: 0, memory_kb: 0,
});
}
const outputs = code.split('\n'); const outputs = code.split('\n');
let score = 0; let totalScore = 0;
let totalStatus = 0; let totalStatus = 0;
const lines = expected.split('\n'); for (const i in config.outputs) {
for (const i in config.subtasks) { const c = config.outputs[i];
const subtask = config.subtasks[i];
let status = STATUS.STATUS_WRONG_ANSWER; let status = STATUS.STATUS_WRONG_ANSWER;
if (lines[i].trim() === outputs[i].trim()) { let score = 0;
score += subtask.score; if (outputs[i].trim() === (c.output || c[0]).trim()) {
score = c.score || c[1];
status = STATUS.STATUS_ACCEPTED; status = STATUS.STATUS_ACCEPTED;
} }
totalScore += score;
totalStatus = Math.max(status, totalStatus); totalStatus = Math.max(status, totalStatus);
next({ next({
status: totalStatus, status: totalStatus,
process: (100 * config.subtasks.length) / (+i + 1), progress: (100 * (+i + 1)) / config.outputs.length,
case: { case: {
status, score, time_ms: 0, memory_kb: 0, message: '', status, score, time_ms: 0, memory_kb: 0, message: '',
}, },
}, i + 1); }, +i + 1);
} }
return end({ return end({
status: totalStatus, score, time_ms: 0, memory_kb: 0, status: totalStatus, score: totalScore, time_ms: 0, memory_kb: 0,
}); });
} }
@@ -31,8 +31,7 @@ export default class MessagePadContainer extends React.PureComponent {
{this.props.panel.map((i, name) => ( {this.props.panel.map((i, name) => (
<div className="row"> <div className="row">
<div className="medium-7 columns form__item end"> <div className="medium-7 columns form__item end">
{i.type === 'textbox' && this.Textbox(i, name)} {i.choices ? this.Radio(i, name) : this.Textbox(i, name)}
{i.type === 'radio' && this.Radio(i, name)}
</div> </div>
</div> </div>
))} ))}