Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ resourceLimit:
problemTimeLimit: 2000
problemMemoryLimit: 512
submissionFileSize: 10485760
maxTotalScore: 1000000
queryLimit:
problemSet: 100
submissions: 10
Expand Down
4 changes: 4 additions & 0 deletions src/config/config.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,10 @@ class ResourceLimitConfig {
@IsInt()
@Min(0)
readonly submissionFileSize: number;

@IsInt()
@Min(1)
readonly maxTotalScore: number;
}

class QueryLimitConfig {
Expand Down
36 changes: 25 additions & 11 deletions src/problem-type/common/meta-and-subtasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { restrictProperties } from "./restrict-properties";
interface JudgeInfoWithMetaAndSubtasks {
timeLimit?: number;
memoryLimit?: number;
totalScore?: number;

fileIo?: {
inputFilename: string;
Expand Down Expand Up @@ -48,6 +49,8 @@ interface ValidateMetaAndSubtasksOptions {
enableInputFile: boolean | "optional";
enableOutputFile: boolean | "optional";
enableUserOutputFilename: boolean;

maxTotalScore?: number;
}

export function validateMetaAndSubtasks(
Expand Down Expand Up @@ -86,6 +89,13 @@ export function validateMetaAndSubtasks(
validateMemoryLimit(judgeInfo.memoryLimit, "TASK");
}

if (judgeInfo.totalScore != null) {
if (!Number.isSafeInteger(judgeInfo.totalScore) || judgeInfo.totalScore <= 0)
throw ["INVALID_TOTAL_SCORE", judgeInfo.totalScore];
if (options.maxTotalScore != null && judgeInfo.totalScore > options.maxTotalScore)
throw ["TOTAL_SCORE_TOO_LARGE", judgeInfo.totalScore, options.maxTotalScore];
}

if (options.enableFileIo && judgeInfo.fileIo) {
if (typeof judgeInfo.fileIo.inputFilename !== "string" || !isValidFilename(judgeInfo.fileIo.inputFilename))
throw ["INVALID_FILEIO_FILENAME", judgeInfo.fileIo.inputFilename];
Expand Down Expand Up @@ -114,7 +124,7 @@ export function validateMetaAndSubtasks(
throw ["INVALID_SCORING_TYPE", i + 1, scoringType];
}

if (points != null && (typeof points !== "number" || points < 0 || points > 100))
if (points != null && (typeof points !== "number" || points < 0))
throw ["INVALID_POINTS_SUBTASK", i + 1, points];

if (Array.isArray(dependencies)) {
Expand Down Expand Up @@ -185,7 +195,7 @@ export function validateMetaAndSubtasks(
delete testcase.memoryLimit;
}

if (points != null && (typeof points !== "number" || points < 0 || points > 100))
if (points != null && (typeof points !== "number" || points < 0))
throw ["INVALID_POINTS_TESTCASE", i + 1, j + 1, points];

restrictProperties(testcase, [
Expand All @@ -198,16 +208,7 @@ export function validateMetaAndSubtasks(
]);
});

// eslint-disable-next-line @typescript-eslint/no-shadow
const sum = testcases.reduce((s, { points }) => (points ? s + points : s), 0);
if (sum > 100) {
throw ["POINTS_SUM_UP_TO_LARGER_THAN_100_TESTCASES", i + 1, sum];
}
});
const sum = (judgeInfo.subtasks || []).reduce((s, { points }) => (points ? s + points : s), 0);
if (sum > 100) {
throw ["POINTS_SUM_UP_TO_LARGER_THAN_100_SUBTASKS", sum];
}

try {
toposort.array(
Expand All @@ -224,4 +225,17 @@ export function validateMetaAndSubtasks(
judgeInfo.subtasks.reduce((count, subtask) => count + subtask.testcases.length, 0) > options.testcaseLimit
)
throw ["TOO_MANY_TESTCASES"];

// Validate subtask points sum
const totalScore = judgeInfo.totalScore || 100;
const subtasksWithPoints = judgeInfo.subtasks?.filter(s => s.points != null) || [];
if (subtasksWithPoints.length > 0) {
const pointsSum = subtasksWithPoints.reduce((sum, s) => sum + (s.points || 0), 0);
if (pointsSum > totalScore) {
throw ["SUBTASK_POINTS_SUM_TOO_LARGE", pointsSum, totalScore];
}
if (judgeInfo.subtasks && subtasksWithPoints.length === judgeInfo.subtasks.length && pointsSum !== totalScore) {
throw ["SUBTASK_POINTS_SUM_NOT_EQUAL", pointsSum, totalScore];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export interface ProblemJudgeInfoInteraction extends ProblemJudgeInfo {
timeLimit: number;
memoryLimit: number;

/*
* Total score of the problem (default: 100)
*/
totalScore?: number;

/*
* If ture, samples in statement will be run before all subtasks
* If a submission failed on samples, all subtasks will be skipped
Expand Down
4 changes: 3 additions & 1 deletion src/problem-type/types/interaction/problem-type.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ export class ProblemTypeInteractionService
enableUserOutputFilename: false,
hardTimeLimit,
hardMemoryLimit,
testcaseLimit: ignoreLimits ? null : this.configService.config.resourceLimit.problemTestcases
testcaseLimit: ignoreLimits ? null : this.configService.config.resourceLimit.problemTestcases,
maxTotalScore: this.configService.config.resourceLimit.maxTotalScore
});

const { interactor } = judgeInfo;
Expand Down Expand Up @@ -125,6 +126,7 @@ export class ProblemTypeInteractionService
restrictProperties(judgeInfo, [
"timeLimit",
"memoryLimit",
"totalScore",
"runSamples",
"subtasks",
"interactor",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import { ProblemJudgeInfo } from "@/problem/problem-judge-info.interface";
import { Checker } from "@/problem-type/common/checker";

export interface ProblemJudgeInfoSubmitAnswer extends ProblemJudgeInfo {
/*
* Total score of the problem (default: 100)
*/
totalScore?: number;

/*
* There could be multiple subtasks in a problem
* Each subtask contains some testcases
Expand Down
5 changes: 3 additions & 2 deletions src/problem-type/types/submit-answer/problem-type.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ export class ProblemTypeSubmitAnswerService
enableFileIo: false,
enableInputFile: "optional",
enableOutputFile: true,
enableUserOutputFilename: true
enableUserOutputFilename: true,
maxTotalScore: this.configService.config.resourceLimit.maxTotalScore
});

validateChecker(judgeInfo, testData, {
Expand All @@ -79,7 +80,7 @@ export class ProblemTypeSubmitAnswerService
hardMemoryLimit: ignoreLimits ? null : this.configService.config.resourceLimit.problemMemoryLimit
});

restrictProperties(judgeInfo, ["subtasks", "checker"]);
restrictProperties(judgeInfo, ["totalScore", "subtasks", "checker"]);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export interface ProblemJudgeInfoTraditional extends ProblemJudgeInfo {
timeLimit: number;
memoryLimit: number;

/*
* Total score of the problem (default: 100)
*/
totalScore?: number;

/*
* Be null if not using file IO
*/
Expand Down
4 changes: 3 additions & 1 deletion src/problem-type/types/traditional/problem-type.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ export class ProblemTypeTraditionalService
enableUserOutputFilename: false,
hardTimeLimit: ignoreLimits ? null : this.configService.config.resourceLimit.problemTimeLimit,
hardMemoryLimit: ignoreLimits ? null : this.configService.config.resourceLimit.problemMemoryLimit,
testcaseLimit: ignoreLimits ? null : this.configService.config.resourceLimit.problemTestcases
testcaseLimit: ignoreLimits ? null : this.configService.config.resourceLimit.problemTestcases,
maxTotalScore: this.configService.config.resourceLimit.maxTotalScore
});

validateChecker(judgeInfo, testData, {
Expand All @@ -91,6 +92,7 @@ export class ProblemTypeTraditionalService
restrictProperties(judgeInfo, [
"timeLimit",
"memoryLimit",
"totalScore",
"fileIo",
"runSamples",
"subtasks",
Expand Down
4 changes: 3 additions & 1 deletion src/submission/submission.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ interface SubmissionTaskExtraInfo extends JudgeTaskExtraInfo {
uuid: string;
url: string;
};
problemTotalScore: number;
}

function makeSubmissionPriority(
Expand Down Expand Up @@ -770,7 +771,8 @@ export class SubmissionService implements JudgeTaskService<SubmissionProgress, S
})
: null
}
: null
: null,
problemTotalScore: (preprocessedJudgeInfo as any)?.totalScore || 100
}
);
} catch (e) {
Expand Down