judge: provide score & subtaskId

This commit is contained in:
undefined committed 2022-05-13 00:00:55 +08:00
1 parent 60aa0e1611
commit a585c13a72
19 files changed
+118 -186

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@hydrooj/hydrojudge",
"bin": "bin/hydrojudge.js",
"version": "2.19.4",
"version": "2.20.0",
"main": "package.json",
"author": "undefined <i@undefined.moe>",
"repository": "https://github.com/hydro-dev/Hydro.git",
+1 -1
View File
@@ -25,7 +25,7 @@ export = async function compile(
);
if (status !== STATUS.STATUS_ACCEPTED) throw new CompileError({ status, stdout, stderr });
if (!fileIds[target]) throw new CompileError({ stderr: 'Executable file not found.' });
if (next) next({ compiler_text: compilerText(stdout, stderr) });
if (next) next({ compilerText: compilerText(stdout, stderr) });
return {
execute,
copyIn: { ...copyIn, [target]: { fileId: fileIds[target] } },
+14 -30
View File
@@ -9,12 +9,13 @@ import PQueue from 'p-queue';
import WebSocket from 'ws';
import { LangConfig } from '@hydrooj/utils/lib/lang';
import { STATUS } from '@hydrooj/utils/lib/status';
import type { JudgeResultBody } from 'hydrooj';
import readCases from '../cases';
import { getConfig } from '../config';
import { CompileError, FormatError, SystemError } from '../error';
import judge from '../judge';
import log from '../log';
import { CopyInFile } from '../sandbox/interface';
import type { CopyInFile } from '../sandbox/interface';
import * as sysinfo from '../sysinfo';
import * as tmpfs from '../tmpfs';
import {
@@ -82,21 +83,21 @@ class JudgeTask {
await this.doSubmission(startPromise);
} catch (e) {
if (e instanceof CompileError) {
this.next({ compiler_text: compilerText(e.stdout, e.stderr) });
this.next({ compilerText: compilerText(e.stdout, e.stderr) });
this.end({
status: STATUS.STATUS_COMPILE_ERROR, score: 0, time_ms: 0, memory_kb: 0,
status: STATUS.STATUS_COMPILE_ERROR, score: 0, time: 0, memory: 0,
});
} else if (e instanceof FormatError) {
this.next({ judge_text: 'Testdata configuration incorrect.' });
this.next({ judge_text: { message: e.message, params: e.params } });
this.next({ message: 'Testdata configuration incorrect.' });
this.next({ message: { message: e.message, params: e.params } });
this.end({
status: STATUS.STATUS_FORMAT_ERROR, score: 0, time_ms: 0, memory_kb: 0,
status: STATUS.STATUS_FORMAT_ERROR, score: 0, time: 0, memory: 0,
});
} else {
log.error(e);
this.next({ message: { message: e.message, params: e.params || [], ...argv.options.debug ? { stack: e.stack } : {} } });
this.end({
status: STATUS.STATUS_SYSTEM_ERROR, score: 0, time_ms: 0, memory_kb: 0,
status: STATUS.STATUS_SYSTEM_ERROR, score: 0, time: 0, memory: 0,
});
}
} finally {
@@ -132,38 +133,21 @@ class JudgeTask {
await judge[type].judge(this, startPromise);
}
next(data, id?: number) {
next(data: Partial<JudgeResultBody>, id?: number) {
log.debug('Next: %d %o', id, data);
data.key = 'next';
data.rid = new ObjectID(this.rid);
if (data.time_ms) data.time = data.time_ms;
if (data.memory_kb) data.memory = data.memory_kb;
if (data.compiler_text) data.compilerText = data.compiler_text;
if (data.judge_text) data.message = data.judge_text;
delete data.time_ms;
delete data.memory_kb;
delete data.compiler_text;
delete data.judge_text;
if (data.case) {
data.case = {
id,
status: data.case.status,
time: data.case.time_ms || data.case.time,
memory: data.case.memory_kb || data.case.memory,
message: data.case.message || data.case.judgeText || data.case.judge_text || '',
};
data.case.id ||= id;
data.case.message ||= '';
}
this.ws.send(JSON.stringify(data));
}
end(data) {
end(data: Partial<JudgeResultBody>) {
log.info('End: %o', data);
data.key = 'end';
data.rid = new ObjectID(this.request.rid);
data.time = data.time_ms ?? data.time;
delete data.time_ms;
data.memory = data.memory_kb ?? data.memory;
delete data.memory_kb;
data.rid = this.request.rid;
this.ws.send(JSON.stringify(data));
}
}
@@ -221,7 +205,7 @@ export default class Hydro {
}
if (filenames.length) {
log.info(`Getting problem data: ${this.config.host}/${source}`);
if (next) next({ judge_text: 'Syncing testdata, please wait...' });
if (next) next({ message: 'Syncing testdata, please wait...' });
await this.ensureLogin();
const res = await this.axios.post(`/d/${domainId}/judge/files`, {
pid: +pid,
+14 -15
View File
@@ -8,10 +8,8 @@ import { Logger } from '../log';
import { del, run } from '../sandbox';
import { CmdFile } from '../sandbox/interface';
import signals from '../signals';
import { parseFilename } from '../utils';
import {
Case, Context, ContextSubTask, SubTask,
} from './interface';
import { NormalizedCase, NormalizedSubtask, parseFilename } from '../utils';
import { Context, ContextSubTask } from './interface';
const Score = {
sum: (a: number, b: number) => (a + b),
@@ -21,18 +19,18 @@ const Score = {
const logger = new Logger('judge/default');
function judgeCase(c: Case, sid: string) {
function judgeCase(c: NormalizedCase, sid: string) {
return async (ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => {
if (ctx.errored || (ctx.failed[sid] && ctxSubtask.subtask.type === 'min')
|| (ctxSubtask.subtask.type === 'max' && ctxSubtask.score === ctxSubtask.subtask.score)
|| ((ctxSubtask.subtask.if || []).filter((i: string) => ctx.failed[i]).length)
|| ((ctxSubtask.subtask.if || []).filter((i) => ctx.failed[i]).length)
) {
ctx.next({
case: {
status: STATUS.STATUS_CANCELED,
score: 0,
time_ms: 0,
memory_kb: 0,
time: 0,
memory: 0,
message: '',
},
addProgress: 100 / ctx.config.count,
@@ -100,10 +98,11 @@ function judgeCase(c: Case, sid: string) {
ctx.total_memory_usage_kb = Math.max(ctx.total_memory_usage_kb, memory_usage_kb);
ctx.next({
case: {
subtaskId: ctxSubtask.subtask.id,
status,
score: 0,
time_ms: time_usage_ms,
memory_kb: memory_usage_kb,
score,
time: time_usage_ms,
memory: memory_usage_kb,
message,
},
addProgress: 100 / ctx.config.count,
@@ -126,7 +125,7 @@ function judgeCase(c: Case, sid: string) {
env: ctx.env,
});
const out = r.stdout.toString();
if (out.length) ctx.next({ compiler_text: out.substring(0, 1024) });
if (out.length) ctx.next({ compilerText: out.substring(0, 1024) });
if (process.env.DEV) console.log(r);
} catch (e) {
logger.info('Failed to run analysis');
@@ -137,7 +136,7 @@ function judgeCase(c: Case, sid: string) {
};
}
function judgeSubtask(subtask: SubTask, sid: string) {
function judgeSubtask(subtask: NormalizedSubtask, sid: string) {
return async (ctx: Context) => {
subtask.type = subtask.type || 'min';
const ctxSubtask = {
@@ -219,7 +218,7 @@ export const judge = async (ctx: Context, startPromise = Promise.resolve()) => {
ctx.end({
status: ctx.total_status,
score: ctx.total_score,
time_ms: Math.floor(ctx.total_time_usage_ms * 1000000) / 1000000,
memory_kb: ctx.total_memory_usage_kb,
time: Math.floor(ctx.total_time_usage_ms * 1000000) / 1000000,
memory: ctx.total_memory_usage_kb,
});
};
+10 -9
View File
@@ -5,10 +5,10 @@ import { getConfig } from '../config';
import { runPiped } from '../sandbox';
import signals from '../signals';
import { parse } from '../testlib';
import { findFileSync, parseFilename } from '../utils';
import {
Case, Context, ContextSubTask, SubTask,
} from './interface';
findFileSync, NormalizedCase, NormalizedSubtask, parseFilename,
} from '../utils';
import { Context, ContextSubTask } from './interface';
const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h');
const Score = {
@@ -17,7 +17,7 @@ const Score = {
min: Math.min,
};
function judgeCase(c: Case) {
function judgeCase(c: NormalizedCase) {
return async (ctx: Context, ctxSubtask: ContextSubTask) => {
ctx.executeInteractor.copyIn.in = c.input ? { src: c.input } : { content: '' };
ctx.executeInteractor.copyIn.out = c.output ? { src: c.output } : { content: '' };
@@ -63,10 +63,11 @@ function judgeCase(c: Case) {
ctx.next({
status: STATUS.STATUS_JUDGING,
case: {
subtaskId: ctxSubtask.subtask.id,
status,
score,
time_ms: time_usage_ms,
memory_kb: memory_usage_kb,
time: time_usage_ms,
memory: memory_usage_kb,
message,
},
progress: Math.floor((c.id * 100) / ctx.config.count),
@@ -74,7 +75,7 @@ function judgeCase(c: Case) {
};
}
function judgeSubtask(subtask: SubTask) {
function judgeSubtask(subtask: NormalizedSubtask) {
return async (ctx: Context) => {
subtask.type = subtask.type || 'min';
const ctxSubtask = {
@@ -134,7 +135,7 @@ export const judge = async (ctx: Context, startPromise = Promise.resolve()) => {
ctx.end({
status: ctx.total_status,
score: ctx.total_score,
time_ms: ctx.total_time_usage_ms,
memory_kb: ctx.total_memory_usage_kb,
time: ctx.total_time_usage_ms,
memory: ctx.total_memory_usage_kb,
});
};
+9 -43
View File
@@ -1,31 +1,15 @@
import PQueue from 'p-queue';
import { LangConfig } from '@hydrooj/utils/lib/lang';
import type { NormalizedSubtask } from '@hydrooj/utils/lib/common';
import type { LangConfig } from '@hydrooj/utils/lib/lang';
import type { JudgeResultBody } from 'hydrooj';
import { Execute } from '../interface';
import { CopyInFile } from '../sandbox/interface';
export type Context = JudgeTaskInterface & RuntimeContext;
export interface JudgeTaskInterface {
next(arg0: {
compiler_text?: string;
status?: number;
message?: string;
progress?: number;
addProgress?: number;
case?: {
status: number;
score?: number;
time_ms: number;
memory_kb: number;
message: string;
};
}, arg1?: number): void;
end(arg0: {
status: number;
score: number;
time_ms: number;
memory_kb: number;
}): void;
next(data: Partial<JudgeResultBody>, id?: number): void;
end(data: Partial<JudgeResultBody>): void;
getLang: (name: string) => LangConfig;
stat: Record<string, Date>;
@@ -56,22 +40,20 @@ export interface RuntimeContext {
executeUser?: Execute;
}
type ExtraFile = string[];
export interface Config {
type: string;
subType?: string;
time: string;
memory: string;
subtasks?: SubTask[];
subtasks?: NormalizedSubtask[];
count?: number;
checker_type?: string;
detail?: boolean;
filename?: string;
judge_extra_files?: ExtraFile;
user_extra_files?: ExtraFile;
judge_extra_files?: string[];
user_extra_files?: string[];
template?: Record<string, [string, string]>;
checker?: string;
@@ -86,24 +68,8 @@ export interface Config {
}[];
}
export interface SubTask {
time: number;
memory: number;
type: 'sum' | 'max' | 'min';
score: number;
cases: Record<string, Case>;
if: string[];
}
export interface ContextSubTask {
subtask: SubTask;
subtask: NormalizedSubtask;
score: number;
status: number;
}
export interface Case {
id: number;
input: string;
output: string;
score: number;
}
+2 -2
View File
@@ -29,11 +29,11 @@ export async function judge({
status: totalStatus,
progress: (100 * (+i + 1)) / config.outputs.length,
case: {
status, score, time_ms: 0, memory_kb: 0, message: '',
status, score, time: 0, memory: 0, message: '',
},
}, +i + 1);
}
return end({
status: totalStatus, score: totalScore, time_ms: 0, memory_kb: 0,
status: totalStatus, score: totalScore, time: 0, memory: 0,
});
}
+9 -7
View File
@@ -12,8 +12,8 @@ import { Context } from './interface';
const failure = (status: number, message?: string) => ({
status,
score: 0,
time_ms: 0,
memory_kb: 0,
time: 0,
memory: 0,
message,
});
@@ -88,9 +88,11 @@ export const judge = async (ctx: Context) => {
ctx.next({
status,
case: {
subtaskId: 0,
status,
time_ms: time_usage_ms,
memory_kb: memory_usage_kb,
score: 100,
time: time_usage_ms,
memory: memory_usage_kb,
message: message.join('\n').substring(0, 102400),
},
});
@@ -115,7 +117,7 @@ export const judge = async (ctx: Context) => {
memory: 256,
});
const out = r.stdout.toString();
if (out.length) ctx.next({ compiler_text: out.substring(0, 1024) });
if (out.length) ctx.next({ compilerText: out.substring(0, 1024) });
if (process.env.DEV) console.log(r);
} catch (e) {
logger.info('Failed to run analysis');
@@ -128,7 +130,7 @@ export const judge = async (ctx: Context) => {
ctx.end({
status,
score: status === STATUS.STATUS_ACCEPTED ? 100 : 0,
time_ms: Math.floor(time_usage_ms * 1000000) / 1000000,
memory_kb: memory_usage_kb,
time: Math.floor(time_usage_ms * 1000000) / 1000000,
memory: memory_usage_kb,
});
};
+11 -13
View File
@@ -5,10 +5,8 @@ import { check, compileChecker } from '../check';
import { getConfig } from '../config';
import { FormatError } from '../error';
import { del, run } from '../sandbox';
import { parseFilename } from '../utils';
import {
Case, Context, ContextSubTask, SubTask,
} from './interface';
import { NormalizedCase, NormalizedSubtask, parseFilename } from '../utils';
import { Context, ContextSubTask } from './interface';
const Score = {
sum: (a: number, b: number) => (a + b),
@@ -16,18 +14,18 @@ const Score = {
min: Math.min,
};
function judgeCase(c: Case, sid: string) {
function judgeCase(c: NormalizedCase, sid: string) {
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: string) => ctx.failed[i]).length)
|| ((ctxSubtask.subtask.if || []).filter((i) => ctx.failed[i]).length)
) {
ctx.next({
case: {
status: STATUS.STATUS_CANCELED,
score: 0,
time_ms: 0,
memory_kb: 0,
time: 0,
memory: 0,
message: '',
},
addProgress: 100 / ctx.config.count,
@@ -87,8 +85,8 @@ function judgeCase(c: Case, sid: string) {
case: {
status,
score: 0,
time_ms: 0,
memory_kb: 0,
time: 0,
memory: 0,
message,
},
addProgress: 100 / ctx.config.count,
@@ -96,7 +94,7 @@ function judgeCase(c: Case, sid: string) {
};
}
function judgeSubtask(subtask: SubTask, sid: string) {
function judgeSubtask(subtask: NormalizedSubtask, sid: string) {
return async (ctx: Context) => {
subtask.type = subtask.type || 'min';
const ctxSubtask = {
@@ -160,7 +158,7 @@ export const judge = async (ctx: Context, startPromise = Promise.resolve()) => {
ctx.end({
status: ctx.total_status,
score: ctx.total_score,
time_ms: 0,
memory_kb: 0,
time: 0,
memory: 0,
});
};
+10 -25
View File
@@ -7,6 +7,7 @@ import fs from 'fs-extra';
import { noop } from 'lodash';
import { ObjectID } from 'mongodb';
import { STATUS } from '@hydrooj/utils/lib/status';
import type { JudgeResultBody } from 'hydrooj';
import { Logger } from 'hydrooj/src/logger';
import * as monitor from 'hydrooj/src/service/monitor';
import readCases from './cases';
@@ -108,34 +109,18 @@ async function postInit() {
}
function getNext(that) {
return (data, id = 0) => {
return (data: Partial<JudgeResultBody>, id = 0) => {
logger.debug('Next: %d %o', id, data);
data.rid = new ObjectID(that.rid);
if (data.time_ms) data.time = data.time_ms;
if (data.memory_kb) data.memory = data.memory_kb;
if (data.compiler_text) data.compilerText = data.compiler_text;
delete data.time_ms;
delete data.memory_kb;
delete data.compiler_text;
if (data.case) {
data.case = {
id,
status: data.case.status,
time: data.case.time_ms || data.case.time,
memory: data.case.memory_kb || data.case.memory,
message: data.case.message || data.case.judgeText || '',
};
}
if (data.case) data.case.id ||= id;
_judge.next(data);
};
}
function getEnd(rid: string) {
return (data) => {
return (data: Partial<JudgeResultBody>) => {
data.key = 'end';
data.rid = new ObjectID(rid);
data.time = data.time_ms ?? data.time;
data.memory = data.memory_kb ?? data.memory;
logger.info('End: status=%d score=%d time=%dms memory=%dkb', data.status, data.score, data.time, data.memory);
_judge.end(data);
};
@@ -160,8 +145,8 @@ async function postInit() {
config: any;
env: any;
input?: string;
next: (data: any, id?: number) => void;
end: (data: any) => void;
next: ReturnType<typeof getNext>;
end: ReturnType<typeof getEnd>;
tmpdir: string;
clean: (() => Promise<any>)[];
folder: string;
@@ -204,20 +189,20 @@ async function postInit() {
await this.submission();
} catch (e) {
if (e instanceof CompileError) {
this.next({ compiler_text: compilerText(e.stdout, e.stderr) });
this.next({ compilerText: compilerText(e.stdout, e.stderr) });
this.end({
status: STATUS.STATUS_COMPILE_ERROR, score: 0, time_ms: 0, memory_kb: 0,
status: STATUS.STATUS_COMPILE_ERROR, score: 0, time: 0, memory: 0,
});
} else if (e instanceof FormatError) {
this.next({ message: { message: e.message, params: e.params } });
this.end({
status: STATUS.STATUS_FORMAT_ERROR, score: 0, time_ms: 0, memory_kb: 0,
status: STATUS.STATUS_FORMAT_ERROR, score: 0, time: 0, memory: 0,
});
} else {
logger.error(e);
this.next({ message: { message: e.message, params: e.params, ...process.env.DEV ? { stack: e.stack } : {} } });
this.end({
status: STATUS.STATUS_SYSTEM_ERROR, score: 0, time_ms: 0, memory_kb: 0,
status: STATUS.STATUS_SYSTEM_ERROR, score: 0, time: 0, memory: 0,
});
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hydrooj",
"version": "3.13.4",
"version": "3.13.5",
"bin": "bin/hydrooj.js",
"main": "src/loader",
"module": "src/loader",
+6 -6
View File
@@ -41,19 +41,19 @@ export async function postJudge(rdoc: RecordDoc) {
await bus.serial('record/judge', rdoc, updated);
}
export async function next(body: JudgeResultBody) {
export async function next(body: Partial<JudgeResultBody>) {
body.rid = new ObjectID(body.rid);
let rdoc = await record.get(body.rid);
if (!rdoc) return;
const $set: Partial<RecordDoc> = {};
const $push: any = {};
if (body.case) {
const c: TestCase = {
const c: Required<TestCase> = {
...body.case,
id: body.case.id || 0,
memory: body.case.memory,
time: body.case.time,
subtaskId: body.case.subtaskId || 0,
score: body.case.score || 0,
message: body.case.message || '',
status: body.case.status,
};
rdoc.testCases.push(c);
$push.testCases = c;
@@ -75,7 +75,7 @@ export async function next(body: JudgeResultBody) {
bus.broadcast('record/change', rdoc!, $set, $push);
}
export async function end(body: JudgeResultBody) {
export async function end(body: Partial<JudgeResultBody>) {
if (body.rid) body.rid = new ObjectID(body.rid);
let rdoc = await record.get(body.rid);
if (!rdoc) return;
-1
View File
@@ -289,7 +289,6 @@ export class ProblemRandomHandler extends ProblemHandler {
const category = flattenDeep(decodeURIComponent(qs).split(' ')
.filter((i) => i.startsWith('category:'))
.map((i) => i.split('category:')[1]?.split(',')));
console.log(qs, category);
const q = buildQuery(this.user);
if (category.length) q.$and = category.map((tag) => ({ tag }));
await bus.serial('problem/list', q, this);
+1 -1
View File
@@ -183,7 +183,7 @@ class RecordDetailHandler extends Handler {
time: 0,
memory: 0,
testCases: [{
status: 9, score: 0, time: 0, memory: 0, message: 'score canceled',
id: 0, subtaskId: 0, status: 9, score: 0, time: 0, memory: 0, message: 'score canceled',
}],
};
const [latest] = await Promise.all([
+14 -25
View File
@@ -2,7 +2,6 @@ import type fs from 'fs';
import type { Dictionary, NumericDictionary } from 'lodash';
import type { ItemBucketMetadata } from 'minio';
import type { Cursor, ObjectID } from 'mongodb';
import type { Readable, Writable } from 'stream';
import type { ProblemDoc } from './model/problem';
type document = typeof import('./model/document');
@@ -208,6 +207,7 @@ export interface SampleContentNode {
sectionTitle: string,
payload: [string, string],
}
// TODO drop contentNode support
export type ContentNode = PlainContentNode | TextContentNode | SampleContentNode;
export type Content = string | ContentNode[] | Record<string, ContentNode[]>;
@@ -272,6 +272,8 @@ export interface ProblemStatusDoc extends StatusDoc {
export interface TestCase {
id?: number;
subtaskId?: number;
score?: number;
time: number;
memory: number;
status: number;
@@ -288,9 +290,9 @@ export interface RecordDoc {
score: number;
memory: number;
time: number;
judgeTexts: string[];
judgeTexts: (string | JudgeMessage)[];
compilerTexts: string[];
testCases: TestCase[];
testCases: Required<TestCase>[];
rejudged: boolean;
source?: string;
/** judge uid */
@@ -497,24 +499,25 @@ export interface Script {
validate: any,
}
export interface JudgeMessage {
message: string;
params?: string[];
stack?: string;
}
export interface JudgeResultBody {
key: string;
domainId: string;
rid: ObjectID;
judger?: number;
progress?: number;
addProgress?: number;
case?: {
id?: number;
status: number;
time: number;
memory: number;
message?: string;
},
case?: TestCase,
status?: number;
score?: number;
time?: number;
memory?: number;
message?: string;
message?: string | JudgeMessage;
compilerText?: string,
}
@@ -527,19 +530,6 @@ export interface Task {
[key: string]: any;
}
export interface UploadStream extends Writable {
id: ObjectID;
}
export interface HydroFileSystem {
openUploadStream: (filename: string) => UploadStream
openDownloadStream: (_id: ObjectID) => Readable
// TODO
find: (...args: any[]) => any
}
export type PathComponent = [string, string, Dictionary<any>?, boolean?];
export interface BaseService {
started: boolean;
error?: Error | string;
@@ -637,7 +627,6 @@ export interface Model {
export interface Service {
bus: typeof import('./service/bus'),
db: typeof import('./service/db'),
fs: HydroFileSystem,
monitor: typeof import('./service/monitor'),
server: typeof import('./service/server'),
storage: typeof import('./service/storage'),
+1 -1
View File
@@ -95,7 +95,7 @@ app.on('error', (error) => {
}
});
wsServer.on('error', (error) => {
console.log(123, error);
console.log('Websocket server error:', error);
});
const ignoredLimit = `,${argv.options.ignoredLimit},`;
+5 -1
View File
@@ -200,9 +200,13 @@ const tasks = {
},
record: async (doc) => {
if (doc.hidden) return null;
const testCases: TestCase[] = [];
const testCases: Required<TestCase>[] = [];
let i = 1;
for (const c of doc.cases || []) {
testCases.push({
id: i++,
subtaskId: 0,
score: 0,
status: c.status,
time: c.time_ms || c.time,
memory: c.memory_kb || c.memory,
+8 -3
View File
@@ -173,7 +173,7 @@ interface MatchRule {
const SubtaskMatcher: MatchRule[] = [
{
regex: /^([^\d]*)(\d+).(in|txt)$/,
regex: /^([^\d]*(?:\d+[a-zA-Z]+)*)(\d+).(in|txt)$/,
output: [
(a) => `${a[1] + a[2]}.out`,
(a) => `${a[1] + a[2]}.ans`,
@@ -260,9 +260,14 @@ export function readSubtasksFromFiles(files: string[], config) {
return Object.values(subtask);
}
type NormalizedCase = Required<ParsedCase>;
interface NormalizedSubtask extends Required<ParsedSubtask> {
export interface NormalizedCase extends Required<ParsedCase> {
time: number;
memory: number;
}
export interface NormalizedSubtask extends Required<ParsedSubtask> {
cases: NormalizedCase[];
time: number;
memory: number;
}
export function normalizeSubtasks(
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/utils",
"version": "1.2.3",
"version": "1.2.4",
"description": "hydrooj utils",
"main": "package.json",
"repository": "https://github.com/hydro-dev/Hydro.git",