mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core&ui&judge: allow run multiple pretests in single request (#1098)
This commit is contained in:
1 parent
c45975ae33
commit
114be2848b
8 files changed
+51
-37
No files matched your search
@@ -107,7 +107,7 @@ export interface RecordPayload extends RecordJudgeInfo {
|
|||||||
source?: string;
|
source?: string;
|
||||||
progress?: number;
|
progress?: number;
|
||||||
/** pretest */
|
/** pretest */
|
||||||
input?: string;
|
input?: string | string[];
|
||||||
/** hack target rid */
|
/** hack target rid */
|
||||||
hackTarget?: string;
|
hackTarget?: string;
|
||||||
/** 0 if pretest&script */
|
/** 0 if pretest&script */
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const judge = async (ctx: JudgeTask) => {
|
|||||||
await using res = await runQueued(
|
await using res = await runQueued(
|
||||||
`${executeGenerator.execute} ${i}`,
|
`${executeGenerator.execute} ${i}`,
|
||||||
{
|
{
|
||||||
stdin: { content: ctx.input || '' },
|
stdin: { content: (ctx.input || [])[i - 1] || '' },
|
||||||
copyIn: executeGenerator.copyIn,
|
copyIn: executeGenerator.copyIn,
|
||||||
copyOut: ['stderr'],
|
copyOut: ['stderr'],
|
||||||
copyOutCached: ['stdout'],
|
copyOutCached: ['stdout'],
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
import { STATUS } from '@hydrooj/common';
|
import { NormalizedCase, STATUS } from '@hydrooj/common';
|
||||||
import { parseMemoryMB, parseTimeMS } from '@hydrooj/utils';
|
import { parseMemoryMB, parseTimeMS } from '@hydrooj/utils';
|
||||||
|
import { runFlow } from '../flow';
|
||||||
import { runQueued } from '../sandbox';
|
import { runQueued } from '../sandbox';
|
||||||
import signals from '../signals';
|
import signals from '../signals';
|
||||||
import { JudgeTask } from '../task';
|
import { Context } from './interface';
|
||||||
|
|
||||||
export const judge = async (ctx: JudgeTask) => {
|
const judgeCase = (c: NormalizedCase) => async (ctx: Context) => {
|
||||||
ctx.next({ status: STATUS.STATUS_COMPILING });
|
|
||||||
const execute = await ctx.compile(ctx.lang, ctx.code);
|
|
||||||
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
|
||||||
const { address_space_limit, process_limit } = ctx.session.getLang(ctx.lang);
|
const { address_space_limit, process_limit } = ctx.session.getLang(ctx.lang);
|
||||||
const res = await runQueued(
|
const res = await runQueued(
|
||||||
execute.execute,
|
ctx.execute.execute,
|
||||||
{
|
{
|
||||||
stdin: { content: ctx.input },
|
stdin: { content: c.input },
|
||||||
copyIn: execute.copyIn,
|
copyIn: ctx.execute.copyIn,
|
||||||
// Allow 2x limits for better debugging
|
// Allow 2x limits for better debugging
|
||||||
time: parseTimeMS(ctx.config.time || '1s') * 2,
|
time: parseTimeMS(ctx.config.time || '1s') * 2,
|
||||||
memory: parseMemoryMB(ctx.config.memory || '256m'),
|
memory: parseMemoryMB(ctx.config.memory || '256m'),
|
||||||
@@ -21,7 +19,7 @@ export const judge = async (ctx: JudgeTask) => {
|
|||||||
addressSpaceLimit: address_space_limit,
|
addressSpaceLimit: address_space_limit,
|
||||||
processLimit: process_limit,
|
processLimit: process_limit,
|
||||||
},
|
},
|
||||||
`judge[${ctx.rid}]`,
|
`pretest[${c.id}]<${ctx.rid}>`,
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
const {
|
const {
|
||||||
@@ -39,27 +37,42 @@ export const judge = async (ctx: JudgeTask) => {
|
|||||||
else message.push(`ExitCode: ${code}`);
|
else message.push(`ExitCode: ${code}`);
|
||||||
}
|
}
|
||||||
message.push(res.stdout, res.stderr);
|
message.push(res.stdout, res.stderr);
|
||||||
ctx.next({
|
if (!ctx.analysis && [STATUS.STATUS_WRONG_ANSWER, STATUS.STATUS_RUNTIME_ERROR].includes(status)) {
|
||||||
|
ctx.analysis = true;
|
||||||
|
await ctx.runAnalysis(ctx.execute, { content: c.input });
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: c.id,
|
||||||
|
subtaskId: 1,
|
||||||
status,
|
status,
|
||||||
time: Math.floor(time * 1000000) / 1000000,
|
score: 1,
|
||||||
memory,
|
|
||||||
score: status === STATUS.STATUS_ACCEPTED ? 100 : 0,
|
|
||||||
case: {
|
|
||||||
subtaskId: 0,
|
|
||||||
status,
|
|
||||||
score: 100,
|
|
||||||
time,
|
time,
|
||||||
memory,
|
memory,
|
||||||
message: message.join('\n').substring(0, 102400),
|
message: message.join('\n').substring(0, 102400),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const judge = async (ctx: Context) => {
|
||||||
|
ctx.config.subtasks = [{
|
||||||
|
id: 1,
|
||||||
|
type: 'sum',
|
||||||
|
score: 100,
|
||||||
|
time: ctx.config.time,
|
||||||
|
memory: ctx.config.memory,
|
||||||
|
if: [],
|
||||||
|
cases: ctx.input.map((i, idx) => ({
|
||||||
|
id: idx + 1,
|
||||||
|
time: ctx.config.time,
|
||||||
|
memory: ctx.config.memory,
|
||||||
|
input: i,
|
||||||
|
output: '',
|
||||||
|
score: 1,
|
||||||
|
})),
|
||||||
|
}];
|
||||||
|
await runFlow(ctx, {
|
||||||
|
compile: async () => {
|
||||||
|
ctx.execute = await ctx.compile(ctx.lang, ctx.code);
|
||||||
},
|
},
|
||||||
});
|
judgeCase,
|
||||||
if ([STATUS.STATUS_WRONG_ANSWER, STATUS.STATUS_RUNTIME_ERROR].includes(status)) {
|
|
||||||
await ctx.runAnalysis(execute, { content: ctx.input });
|
|
||||||
}
|
|
||||||
ctx.end({
|
|
||||||
status,
|
|
||||||
time: Math.floor(time * 1000000) / 1000000,
|
|
||||||
memory,
|
|
||||||
score: status === STATUS.STATUS_ACCEPTED ? 100 : 0,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -30,7 +30,7 @@ export class JudgeTask {
|
|||||||
rid: string;
|
rid: string;
|
||||||
lang: string;
|
lang: string;
|
||||||
code: CopyInFile;
|
code: CopyInFile;
|
||||||
input?: string;
|
input?: string[];
|
||||||
finished: boolean = false;
|
finished: boolean = false;
|
||||||
clean: (() => Promise<any>)[] = [];
|
clean: (() => Promise<any>)[] = [];
|
||||||
data: FileInfo[];
|
data: FileInfo[];
|
||||||
@@ -67,7 +67,7 @@ export class JudgeTask {
|
|||||||
this.source = this.request.source;
|
this.source = this.request.source;
|
||||||
this.meta = this.request.meta;
|
this.meta = this.request.meta;
|
||||||
this.files = this.request.files;
|
this.files = this.request.files;
|
||||||
this.input = this.request.input;
|
this.input = Array.isArray(this.request.input) ? this.request.input : [this.request.input || ''];
|
||||||
let tid = this.request.contest?.toString() || '';
|
let tid = this.request.contest?.toString() || '';
|
||||||
if (tid === '000000000000000000000000') tid = '';
|
if (tid === '000000000000000000000000') tid = '';
|
||||||
this.env = {
|
this.env = {
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class SystemScriptHandler extends SystemHandler {
|
|||||||
if (typeof global.Hydro.script[id].validate === 'function') {
|
if (typeof global.Hydro.script[id].validate === 'function') {
|
||||||
args = global.Hydro.script[id].validate(args);
|
args = global.Hydro.script[id].validate(args);
|
||||||
}
|
}
|
||||||
const rid = await record.add(domainId, -1, this.user._id, '-', id, false, { input: raw, type: 'pretest' });
|
const rid = await record.add(domainId, -1, this.user._id, '-', id, false, { input: [raw], type: 'pretest' });
|
||||||
const c = new JudgeResultCallbackContext(this.ctx, { type: 'judge', domainId, rid });
|
const c = new JudgeResultCallbackContext(this.ctx, { type: 'judge', domainId, rid });
|
||||||
c.next({ message: `Running script: ${id} `, status: STATUS.STATUS_JUDGING });
|
c.next({ message: `Running script: ${id} `, status: STATUS.STATUS_JUDGING });
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|||||||
@@ -483,9 +483,9 @@ export class ProblemSubmitHandler extends ProblemDetailHandler {
|
|||||||
@param('lang', Types.Name)
|
@param('lang', Types.Name)
|
||||||
@param('code', Types.String, true)
|
@param('code', Types.String, true)
|
||||||
@param('pretest', Types.Boolean)
|
@param('pretest', Types.Boolean)
|
||||||
@param('input', Types.String, true)
|
@param('input', Types.ArrayOf(Types.String), true)
|
||||||
@param('tid', Types.ObjectId, true)
|
@param('tid', Types.ObjectId, true)
|
||||||
async post(domainId: string, lang: string, code: string, pretest = false, input = '', tid?: ObjectId) {
|
async post(domainId: string, lang: string, code: string, pretest = false, input: string[] = [], tid?: ObjectId) {
|
||||||
const config = this.pdoc.config;
|
const config = this.pdoc.config;
|
||||||
if (typeof config === 'string' || config === null) throw new ProblemConfigError();
|
if (typeof config === 'string' || config === null) throw new ProblemConfigError();
|
||||||
if (['submit_answer', 'objective'].includes(config.type)) {
|
if (['submit_answer', 'objective'].includes(config.type)) {
|
||||||
@@ -498,6 +498,7 @@ export class ProblemSubmitHandler extends ProblemDetailHandler {
|
|||||||
if (!['default', 'remote_judge'].includes(this.response.body.pdoc.config?.type)) {
|
if (!['default', 'remote_judge'].includes(this.response.body.pdoc.config?.type)) {
|
||||||
throw new ProblemNotAllowPretestError('type');
|
throw new ProblemNotAllowPretestError('type');
|
||||||
}
|
}
|
||||||
|
if (!input.length) throw new ValidationError('input');
|
||||||
}
|
}
|
||||||
await this.limitRate('add_record', 60, system.get('limit.submission_user'), '{{user}}');
|
await this.limitRate('add_record', 60, system.get('limit.submission_user'), '{{user}}');
|
||||||
await this.limitRate('add_record', 60, pretest ? system.get('limit.pretest') : system.get('limit.submission'));
|
await this.limitRate('add_record', 60, pretest ? system.get('limit.pretest') : system.get('limit.submission'));
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export default class RecordModel {
|
|||||||
lang: string, code: string, addTask: boolean,
|
lang: string, code: string, addTask: boolean,
|
||||||
args: {
|
args: {
|
||||||
contest?: ObjectId;
|
contest?: ObjectId;
|
||||||
input?: string;
|
input?: string[];
|
||||||
files?: Record<string, string>;
|
files?: Record<string, string>;
|
||||||
hackTarget?: ObjectId;
|
hackTarget?: ObjectId;
|
||||||
type: 'judge' | 'rejudge' | 'pretest' | 'hack' | 'generate';
|
type: 'judge' | 'rejudge' | 'pretest' | 'hack' | 'generate';
|
||||||
@@ -165,7 +165,7 @@ export default class RecordModel {
|
|||||||
args.type = 'judge';
|
args.type = 'judge';
|
||||||
data.rejudged = true;
|
data.rejudged = true;
|
||||||
} else if (args.type === 'pretest') {
|
} else if (args.type === 'pretest') {
|
||||||
data.input = args.input || '';
|
data.input = args.input || [];
|
||||||
isContest = false;
|
isContest = false;
|
||||||
data.contest = RecordModel.RECORD_PRETEST;
|
data.contest = RecordModel.RECORD_PRETEST;
|
||||||
} else if (args.type === 'generate') {
|
} else if (args.type === 'generate') {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
const req = request.post(UiContext.postSubmitUrl, {
|
const req = request.post(UiContext.postSubmitUrl, {
|
||||||
lang: props.editorLang,
|
lang: props.editorLang,
|
||||||
code: props.editorCode,
|
code: props.editorCode,
|
||||||
input: props.pretestInput,
|
input: [props.pretestInput],
|
||||||
pretest: true,
|
pretest: true,
|
||||||
});
|
});
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|||||||
Reference in new issue
Block a user