mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
judge: use common workflow
This commit is contained in:
1 parent
1394af0591
commit
7bf2e73561
4 files changed
+171
-251
No files matched your search
@@ -0,0 +1,94 @@
|
|||||||
|
import Queue from 'p-queue';
|
||||||
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
|
import { getConfig } from './config';
|
||||||
|
import { CompileError, FormatError } from './error';
|
||||||
|
import { Context, ContextSubTask } from './judge/interface';
|
||||||
|
import { NormalizedCase, NormalizedSubtask } from './utils';
|
||||||
|
|
||||||
|
interface Task {
|
||||||
|
compile: () => Promise<void>;
|
||||||
|
judgeCase: (c: NormalizedCase, sid: string) => (
|
||||||
|
(ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => Promise<any>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function judgeSubtask(subtask: NormalizedSubtask, sid: string, judgeCase: Task['judgeCase']) {
|
||||||
|
return async (ctx: Context) => {
|
||||||
|
subtask.type = subtask.type || 'min';
|
||||||
|
const ctxSubtask = {
|
||||||
|
subtask,
|
||||||
|
status: 0,
|
||||||
|
score: subtask.type === 'min'
|
||||||
|
? subtask.score
|
||||||
|
: 0,
|
||||||
|
};
|
||||||
|
const cases = [];
|
||||||
|
for (const cid in subtask.cases) {
|
||||||
|
const runner = judgeCase(subtask.cases[cid], subtask.id.toString() ?? sid);
|
||||||
|
cases.push(ctx.queue.add(async () => {
|
||||||
|
if (ctx.errored
|
||||||
|
|| (ctxSubtask.subtask.type === 'min' && ctxSubtask.score === 0)
|
||||||
|
|| (ctxSubtask.subtask.type === 'max' && ctxSubtask.score === ctxSubtask.subtask.score)
|
||||||
|
|| ((ctxSubtask.subtask.if || []).filter((i) => ctx.failed[i]).length)
|
||||||
|
) {
|
||||||
|
ctx.next({
|
||||||
|
case: {
|
||||||
|
id: subtask.cases[cid].id,
|
||||||
|
status: STATUS.STATUS_CANCELED,
|
||||||
|
subtaskId: subtask.id,
|
||||||
|
score: 0,
|
||||||
|
time: 0,
|
||||||
|
memory: 0,
|
||||||
|
message: '',
|
||||||
|
},
|
||||||
|
addProgress: 100 / ctx.config.count,
|
||||||
|
});
|
||||||
|
} else await runner(ctx, ctxSubtask, runner);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
await Promise.all(cases).catch((e) => {
|
||||||
|
ctx.errored = true;
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
ctx.total_status = Math.max(ctx.total_status, ctxSubtask.status);
|
||||||
|
return ctxSubtask.score;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const runFlow = async (ctx: Context, task: Task) => {
|
||||||
|
if (!ctx.config.subtasks.length) throw new FormatError('Problem data not found.');
|
||||||
|
ctx.next({ status: STATUS.STATUS_COMPILING });
|
||||||
|
if (ctx.config.template && 'content' in ctx.code) {
|
||||||
|
if (ctx.config.template[ctx.lang]) {
|
||||||
|
const tpl = ctx.config.template[ctx.lang];
|
||||||
|
ctx.code.content = tpl[0] + ctx.code.content + tpl[1];
|
||||||
|
} else throw new CompileError('Language not supported by provided templates');
|
||||||
|
}
|
||||||
|
await task.compile();
|
||||||
|
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
||||||
|
const tasks = [];
|
||||||
|
ctx.total_status = 0;
|
||||||
|
ctx.total_score = 0;
|
||||||
|
ctx.total_memory_usage_kb = 0;
|
||||||
|
ctx.total_time_usage_ms = 0;
|
||||||
|
ctx.rerun = getConfig('rerun') || 0;
|
||||||
|
ctx.queue = new Queue({ concurrency: getConfig('singleTaskParallelism') });
|
||||||
|
ctx.failed = {};
|
||||||
|
for (const sid in ctx.config.subtasks) tasks.push(judgeSubtask(ctx.config.subtasks[sid], sid, task.judgeCase)(ctx));
|
||||||
|
const scores = await Promise.all(tasks);
|
||||||
|
for (const sid in ctx.config.subtasks) {
|
||||||
|
let effective = true;
|
||||||
|
for (const required of ctx.config.subtasks[sid].if || []) {
|
||||||
|
if (ctx.failed[required.toString()]) effective = false;
|
||||||
|
}
|
||||||
|
if (effective) ctx.total_score += scores[sid];
|
||||||
|
}
|
||||||
|
ctx.stat.done = new Date();
|
||||||
|
if (process.env.DEV) ctx.next({ message: JSON.stringify(ctx.stat) });
|
||||||
|
ctx.end({
|
||||||
|
status: ctx.total_status,
|
||||||
|
score: ctx.total_score,
|
||||||
|
time: Math.floor(ctx.total_time_usage_ms * 1000000) / 1000000,
|
||||||
|
memory: ctx.total_memory_usage_kb,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
import Queue from 'p-queue';
|
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import { check, compileChecker } from '../check';
|
import { check, compileChecker } from '../check';
|
||||||
import compile from '../compile';
|
import compile from '../compile';
|
||||||
import { getConfig } from '../config';
|
import { runFlow } from '../flow';
|
||||||
import { CompileError, FormatError } from '../error';
|
|
||||||
import { Logger } from '../log';
|
import { Logger } from '../log';
|
||||||
import { CmdFile, del, run } from '../sandbox';
|
import { CmdFile, del, run } from '../sandbox';
|
||||||
import signals from '../signals';
|
import signals from '../signals';
|
||||||
import { NormalizedCase, NormalizedSubtask, parseFilename } from '../utils';
|
import { NormalizedCase, parseFilename } from '../utils';
|
||||||
import { Context, ContextSubTask } from './interface';
|
import { Context, ContextSubTask } from './interface';
|
||||||
|
|
||||||
const Score = {
|
const Score = {
|
||||||
@@ -20,25 +18,6 @@ const logger = new Logger('judge/default');
|
|||||||
|
|
||||||
function judgeCase(c: NormalizedCase, sid: string) {
|
function judgeCase(c: NormalizedCase, sid: string) {
|
||||||
return async (ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => {
|
return async (ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => {
|
||||||
if (ctx.errored
|
|
||||||
|| (ctxSubtask.subtask.type === 'min' && ctxSubtask.score === 0)
|
|
||||||
|| (ctxSubtask.subtask.type === 'max' && ctxSubtask.score === ctxSubtask.subtask.score)
|
|
||||||
|| ((ctxSubtask.subtask.if || []).filter((i) => ctx.failed[i]).length)
|
|
||||||
) {
|
|
||||||
ctx.next({
|
|
||||||
case: {
|
|
||||||
id: c.id,
|
|
||||||
status: STATUS.STATUS_CANCELED,
|
|
||||||
subtaskId: ctxSubtask.subtask.id,
|
|
||||||
score: 0,
|
|
||||||
time: 0,
|
|
||||||
memory: 0,
|
|
||||||
message: '',
|
|
||||||
},
|
|
||||||
addProgress: 100 / ctx.config.count,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { filename } = ctx.config;
|
const { filename } = ctx.config;
|
||||||
const copyIn = { ...ctx.execute.copyIn };
|
const copyIn = { ...ctx.execute.copyIn };
|
||||||
if (filename) copyIn[`${filename}.in`] = c.input ? { src: c.input } : { content: '' };
|
if (filename) copyIn[`${filename}.in`] = c.input ? { src: c.input } : { content: '' };
|
||||||
@@ -140,84 +119,29 @@ function judgeCase(c: NormalizedCase, sid: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function judgeSubtask(subtask: NormalizedSubtask, sid: string) {
|
export const judge = async (ctx: Context) => await runFlow(ctx, {
|
||||||
return async (ctx: Context) => {
|
compile: async () => {
|
||||||
subtask.type = subtask.type || 'min';
|
[ctx.execute, ctx.checker] = await Promise.all([
|
||||||
const ctxSubtask = {
|
compile(
|
||||||
subtask,
|
ctx.session.getLang(ctx.lang), ctx.code,
|
||||||
status: 0,
|
Object.fromEntries(
|
||||||
score: subtask.type === 'min'
|
(ctx.config.user_extra_files || []).map((i) => [i.split('/').pop(), { src: i }]),
|
||||||
? subtask.score
|
|
||||||
: 0,
|
|
||||||
};
|
|
||||||
const cases = [];
|
|
||||||
for (const cid in subtask.cases) {
|
|
||||||
const runner = judgeCase(subtask.cases[cid], subtask.id.toString() ?? sid);
|
|
||||||
cases.push(ctx.queue.add(() => runner(ctx, ctxSubtask, runner)));
|
|
||||||
}
|
|
||||||
await Promise.all(cases).catch((e) => {
|
|
||||||
ctx.errored = true;
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
ctx.total_status = Math.max(ctx.total_status, ctxSubtask.status);
|
|
||||||
return ctxSubtask.score;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const judge = async (ctx: Context) => {
|
|
||||||
if (!ctx.config.subtasks.length) throw new FormatError('Problem data not found.');
|
|
||||||
ctx.next({ status: STATUS.STATUS_COMPILING });
|
|
||||||
if (ctx.config.template && 'content' in ctx.code) {
|
|
||||||
if (ctx.config.template[ctx.lang]) {
|
|
||||||
const tpl = ctx.config.template[ctx.lang];
|
|
||||||
ctx.code.content = tpl[0] + ctx.code.content + tpl[1];
|
|
||||||
} else throw new CompileError('Language not supported by provided templates');
|
|
||||||
}
|
|
||||||
[ctx.execute, ctx.checker] = await Promise.all([
|
|
||||||
compile(
|
|
||||||
ctx.session.getLang(ctx.lang), ctx.code,
|
|
||||||
Object.fromEntries(
|
|
||||||
(ctx.config.user_extra_files || []).map((i) => [i.split('/').pop(), { src: i }]),
|
|
||||||
),
|
|
||||||
ctx.next,
|
|
||||||
),
|
|
||||||
compileChecker(
|
|
||||||
ctx.session.getLang,
|
|
||||||
ctx.config.checker_type,
|
|
||||||
ctx.config.checker,
|
|
||||||
{
|
|
||||||
user_code: ctx.code,
|
|
||||||
...Object.fromEntries(
|
|
||||||
(ctx.config.judge_extra_files || []).map((i) => [parseFilename(i), { src: i }]),
|
|
||||||
),
|
),
|
||||||
},
|
ctx.next,
|
||||||
),
|
),
|
||||||
]);
|
compileChecker(
|
||||||
ctx.clean.push(ctx.execute.clean, ctx.checker.clean);
|
ctx.session.getLang,
|
||||||
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
ctx.config.checker_type,
|
||||||
const tasks = [];
|
ctx.config.checker,
|
||||||
ctx.total_status = 0;
|
{
|
||||||
ctx.total_score = 0;
|
user_code: ctx.code,
|
||||||
ctx.total_memory_usage_kb = 0;
|
...Object.fromEntries(
|
||||||
ctx.total_time_usage_ms = 0;
|
(ctx.config.judge_extra_files || []).map((i) => [parseFilename(i), { src: i }]),
|
||||||
ctx.rerun = getConfig('rerun') || 0;
|
),
|
||||||
ctx.queue = new Queue({ concurrency: getConfig('singleTaskParallelism') });
|
},
|
||||||
ctx.failed = {};
|
),
|
||||||
for (const sid in ctx.config.subtasks) tasks.push(judgeSubtask(ctx.config.subtasks[sid], sid)(ctx));
|
]);
|
||||||
const scores = await Promise.all(tasks);
|
ctx.clean.push(ctx.execute.clean, ctx.checker.clean);
|
||||||
for (const sid in ctx.config.subtasks) {
|
},
|
||||||
let effective = true;
|
judgeCase,
|
||||||
for (const required of ctx.config.subtasks[sid].if || []) {
|
});
|
||||||
if (ctx.failed[required.toString()]) effective = false;
|
|
||||||
}
|
|
||||||
if (effective) ctx.total_score += scores[sid];
|
|
||||||
}
|
|
||||||
ctx.stat.done = new Date();
|
|
||||||
if (process.env.DEV) ctx.next({ message: JSON.stringify(ctx.stat) });
|
|
||||||
ctx.end({
|
|
||||||
status: ctx.total_status,
|
|
||||||
score: ctx.total_score,
|
|
||||||
time: Math.floor(ctx.total_time_usage_ms * 1000000) / 1000000,
|
|
||||||
memory: ctx.total_memory_usage_kb,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import Queue from 'p-queue';
|
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import compile from '../compile';
|
import compile from '../compile';
|
||||||
import { getConfig } from '../config';
|
import { runFlow } from '../flow';
|
||||||
import { runPiped } from '../sandbox';
|
import { runPiped } from '../sandbox';
|
||||||
import signals from '../signals';
|
import signals from '../signals';
|
||||||
import { parse } from '../testlib';
|
import { parse } from '../testlib';
|
||||||
import {
|
import {
|
||||||
findFileSync, NormalizedCase, NormalizedSubtask, parseFilename,
|
findFileSync, NormalizedCase, parseFilename,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { Context, ContextSubTask } from './interface';
|
import { Context, ContextSubTask } from './interface';
|
||||||
|
|
||||||
@@ -76,66 +75,32 @@ function judgeCase(c: NormalizedCase) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function judgeSubtask(subtask: NormalizedSubtask) {
|
export const judge = async (ctx: Context) => await runFlow(ctx, {
|
||||||
return async (ctx: Context) => {
|
compile: async () => {
|
||||||
subtask.type = subtask.type || 'min';
|
[ctx.executeUser, ctx.executeInteractor] = await Promise.all([
|
||||||
const ctxSubtask = {
|
(() => {
|
||||||
subtask,
|
const copyIn = {};
|
||||||
status: 0,
|
for (const file of ctx.config.user_extra_files) {
|
||||||
score: subtask.type === 'min'
|
copyIn[parseFilename(file)] = { src: file };
|
||||||
? subtask.score
|
}
|
||||||
: 0,
|
return compile(ctx.session.getLang(ctx.lang), ctx.code, copyIn, ctx.next);
|
||||||
};
|
})(),
|
||||||
const cases = [];
|
(() => {
|
||||||
for (const cid in subtask.cases) {
|
const copyIn = {
|
||||||
cases.push(ctx.queue.add(() => judgeCase(subtask.cases[cid])(ctx, ctxSubtask)));
|
'testlib.h': { src: testlibSrc },
|
||||||
}
|
user_code: ctx.code,
|
||||||
await Promise.all(cases);
|
};
|
||||||
ctx.total_status = Math.max(ctx.total_status, ctxSubtask.status);
|
for (const file of ctx.config.judge_extra_files) {
|
||||||
ctx.total_score += ctxSubtask.score;
|
copyIn[parseFilename(file)] = { src: file };
|
||||||
};
|
}
|
||||||
}
|
return compile(
|
||||||
|
ctx.session.getLang(parseFilename(ctx.config.interactor).split('.')[1].replace('@', '.')),
|
||||||
export const judge = async (ctx: Context) => {
|
{ src: ctx.config.interactor },
|
||||||
ctx.next({ status: STATUS.STATUS_COMPILING });
|
copyIn,
|
||||||
[ctx.executeUser, ctx.executeInteractor] = await Promise.all([
|
);
|
||||||
(() => {
|
})(),
|
||||||
const copyIn = {};
|
]);
|
||||||
for (const file of ctx.config.user_extra_files) {
|
ctx.clean.push(ctx.executeUser.clean, ctx.executeInteractor.clean);
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
},
|
||||||
}
|
judgeCase,
|
||||||
return compile(ctx.session.getLang(ctx.lang), ctx.code, copyIn, ctx.next);
|
});
|
||||||
})(),
|
|
||||||
(() => {
|
|
||||||
const copyIn = {
|
|
||||||
'testlib.h': { src: testlibSrc },
|
|
||||||
user_code: ctx.code,
|
|
||||||
};
|
|
||||||
for (const file of ctx.config.judge_extra_files) {
|
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
|
||||||
}
|
|
||||||
return compile(
|
|
||||||
ctx.session.getLang(parseFilename(ctx.config.interactor).split('.')[1].replace('@', '.')),
|
|
||||||
{ src: ctx.config.interactor },
|
|
||||||
copyIn,
|
|
||||||
);
|
|
||||||
})(),
|
|
||||||
]);
|
|
||||||
ctx.clean.push(ctx.executeUser.clean, ctx.executeInteractor.clean);
|
|
||||||
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
|
||||||
const tasks = [];
|
|
||||||
ctx.total_status = ctx.total_score = ctx.total_memory_usage_kb = ctx.total_time_usage_ms = 0;
|
|
||||||
ctx.queue = new Queue({ concurrency: getConfig('parallelism') });
|
|
||||||
for (const sid in ctx.config.subtasks) {
|
|
||||||
tasks.push(judgeSubtask(ctx.config.subtasks[sid])(ctx));
|
|
||||||
}
|
|
||||||
await Promise.all(tasks);
|
|
||||||
ctx.stat.done = new Date();
|
|
||||||
if (process.env.DEV) ctx.next({ message: JSON.stringify(ctx.stat) });
|
|
||||||
ctx.end({
|
|
||||||
status: ctx.total_status,
|
|
||||||
score: ctx.total_score,
|
|
||||||
time: ctx.total_time_usage_ms,
|
|
||||||
memory: ctx.total_memory_usage_kb,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import { readFile } from 'fs-extra';
|
import { readFile } from 'fs-extra';
|
||||||
import Queue from 'p-queue';
|
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import { check, compileChecker } from '../check';
|
import { check, compileChecker } from '../check';
|
||||||
import { getConfig } from '../config';
|
import { runFlow } from '../flow';
|
||||||
import { FormatError } from '../error';
|
|
||||||
import { del, run } from '../sandbox';
|
import { del, run } from '../sandbox';
|
||||||
import { NormalizedCase, NormalizedSubtask, parseFilename } from '../utils';
|
import { NormalizedCase, parseFilename } from '../utils';
|
||||||
import { Context, ContextSubTask } from './interface';
|
import { Context, ContextSubTask } from './interface';
|
||||||
|
|
||||||
const Score = {
|
const Score = {
|
||||||
@@ -16,23 +14,6 @@ const Score = {
|
|||||||
|
|
||||||
function judgeCase(c: NormalizedCase, sid: string) {
|
function judgeCase(c: NormalizedCase, sid: string) {
|
||||||
return async (ctx: Context, ctxSubtask: ContextSubTask) => {
|
return async (ctx: Context, ctxSubtask: ContextSubTask) => {
|
||||||
if (ctx.errored || (ctx.failed[sid] && ctxSubtask.subtask.type === 'min')
|
|
||||||
|| (ctxSubtask.subtask.type === 'max' && ctxSubtask.score === ctxSubtask.subtask.score)
|
|
||||||
|| ((ctxSubtask.subtask.if || []).filter((i) => ctx.failed[i]).length)
|
|
||||||
) {
|
|
||||||
ctx.next({
|
|
||||||
case: {
|
|
||||||
id: c.id,
|
|
||||||
status: STATUS.STATUS_CANCELED,
|
|
||||||
score: 0,
|
|
||||||
time: 0,
|
|
||||||
memory: 0,
|
|
||||||
message: '',
|
|
||||||
},
|
|
||||||
addProgress: 100 / ctx.config.count,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const chars = /[a-zA-Z0-9_.-]/;
|
const chars = /[a-zA-Z0-9_.-]/;
|
||||||
const name = (ctx.config.filename && /^[a-zA-Z0-9-_#.]+$/.test(ctx.config.filename))
|
const name = (ctx.config.filename && /^[a-zA-Z0-9-_#.]+$/.test(ctx.config.filename))
|
||||||
? ctx.config.filename.replace('#', c.id.toString())
|
? ctx.config.filename.replace('#', c.id.toString())
|
||||||
@@ -96,67 +77,23 @@ function judgeCase(c: NormalizedCase, sid: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function judgeSubtask(subtask: NormalizedSubtask, sid: string) {
|
|
||||||
return async (ctx: Context) => {
|
|
||||||
subtask.type = subtask.type || 'min';
|
|
||||||
const ctxSubtask = {
|
|
||||||
subtask,
|
|
||||||
status: 0,
|
|
||||||
score: subtask.type === 'min'
|
|
||||||
? subtask.score
|
|
||||||
: 0,
|
|
||||||
};
|
|
||||||
const cases = [];
|
|
||||||
for (const cid in subtask.cases) {
|
|
||||||
const runner = judgeCase(subtask.cases[cid], sid);
|
|
||||||
cases.push(ctx.queue.add(() => runner(ctx, ctxSubtask)));
|
|
||||||
}
|
|
||||||
await Promise.all(cases).catch((e) => {
|
|
||||||
ctx.errored = true;
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
ctx.total_status = Math.max(ctx.total_status, ctxSubtask.status);
|
|
||||||
return ctxSubtask.score;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const judge = async (ctx: Context) => {
|
export const judge = async (ctx: Context) => {
|
||||||
if (!ctx.config.subtasks.length) throw new FormatError('Problem data not found.');
|
await runFlow(ctx, {
|
||||||
ctx.next({ status: STATUS.STATUS_COMPILING });
|
compile: async () => {
|
||||||
ctx.checker = await (() => {
|
ctx.checker = await (() => {
|
||||||
const copyIn = { user_code: ctx.code };
|
const copyIn = { user_code: ctx.code };
|
||||||
for (const file of ctx.config.judge_extra_files) {
|
for (const file of ctx.config.judge_extra_files) {
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
copyIn[parseFilename(file)] = { src: file };
|
||||||
}
|
}
|
||||||
return compileChecker(
|
return compileChecker(
|
||||||
ctx.session.getLang,
|
ctx.session.getLang,
|
||||||
ctx.config.checker_type,
|
ctx.config.checker_type,
|
||||||
ctx.config.checker,
|
ctx.config.checker,
|
||||||
copyIn,
|
copyIn,
|
||||||
);
|
);
|
||||||
})();
|
})();
|
||||||
ctx.clean.push(ctx.checker.clean);
|
ctx.clean.push(ctx.checker.clean);
|
||||||
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
},
|
||||||
const tasks = [];
|
judgeCase,
|
||||||
ctx.total_status = 0;
|
|
||||||
ctx.total_score = 0;
|
|
||||||
ctx.queue = new Queue({ concurrency: getConfig('parallelism') });
|
|
||||||
ctx.failed = {};
|
|
||||||
for (const sid in ctx.config.subtasks) tasks.push(judgeSubtask(ctx.config.subtasks[sid], sid)(ctx));
|
|
||||||
const scores = await Promise.all(tasks);
|
|
||||||
for (const sid in ctx.config.subtasks) {
|
|
||||||
let effective = true;
|
|
||||||
for (const required of ctx.config.subtasks[sid].if || []) {
|
|
||||||
if (ctx.failed[required.toString()]) effective = false;
|
|
||||||
}
|
|
||||||
if (effective) ctx.total_score += scores[sid];
|
|
||||||
}
|
|
||||||
ctx.stat.done = new Date();
|
|
||||||
if (process.env.DEV) ctx.next({ message: JSON.stringify(ctx.stat) });
|
|
||||||
ctx.end({
|
|
||||||
status: ctx.total_status,
|
|
||||||
score: ctx.total_score,
|
|
||||||
time: 0,
|
|
||||||
memory: 0,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Reference in new issue
Block a user