Skip to content

Commit

Permalink
feat: support subtask judge
Browse files Browse the repository at this point in the history
  • Loading branch information
seo-rii committed Jan 4, 2022
1 parent ddda02b commit 682e352
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 90 deletions.
38 changes: 24 additions & 14 deletions res/test.html
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,15 @@
setCard(
res.data.uid,
1,
`${res.data.reason}`,
res.data.resultCode,
`<div>
<h2>#${pmp[res.data.uid]}</h2>
<h4>UID</h4>
<p>${res.data.uid}</p>
<h4>Result</h4>
<p>${reasonString[res.data.reason]}</p>
<p>${res.data.reason}</p>
<h4>Scoring</h4>
<p>${res.data.result}</p>
<p>${JSON.stringify(res.data.result)}</p>
<h4>Message</h4>
<p>${res.data.message}</p>
<h4>Run Info</h4>
Expand All @@ -275,13 +275,13 @@ <h4>Run Info</h4>
setCard(
res.data.uid,
res.data.progress,
res.data.reason,
res.data.resultCode,
`<div>
<h2>#${pmp[res.data.uid]}</h2>
<h4>UID</h4>
<p>${res.data.uid}</p>
<h4>State</h4>
<p>${reasonString[res.data.reason]}</p>
<p>${res.data.reason}</p>
</div>`
)
if (res.type === 'JUDGE_STATUS') {
Expand All @@ -295,7 +295,7 @@ <h2>#${i}</h2>
<h4>UID</h4>
<p>${res.data.uid}</p>
<h4>State</h4>
<p>${reasonString[res.data.reason]}</p>
<p>${res.data.reason}</p>
</div>`
)
}
Expand Down Expand Up @@ -383,14 +383,24 @@ <h4>State</h4>
source: document.getElementById('source').value,
},
],
dataSet: {
data: [
{ input: '1 1', output: '2' },
{ input: '10 1', output: '11' },
{ input: '5 1', output: '6' },
{ input: '1 3', output: '4' },
],
},
dataSet: [
{
scoringType: 'QUANTIZED',
data: [
{ input: '1 1', output: '2' },
{ input: '10 1', output: '11' },
{ input: '5 1', output: '6' },
{ input: '1 3', output: '4' },
],
},
{
scoringType: 'QUANTIZED',
data: [
{ input: '15 1', output: '16' },
{ input: '17 21', output: '38' },
],
},
],
timeLimit: 2000,
memoryLimit: 1024,
}),
Expand Down
2 changes: 1 addition & 1 deletion src/judge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function requestJudge(problem: JudgeRequest) {
sendMessage(WebSocketResponseType.JUDGE_PROGRESS, {
uid: problem.uid,
progress: 0,
reason: 'PD',
resultCode: 'PD',
})
while (judgeList.length < MultiJudgeCount && waitList.length)
judge(waitList.shift() as string)
Expand Down
157 changes: 104 additions & 53 deletions src/runner/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { JudgeRequest } from '../types/request'
import { JudgeRequest, ScoringType } from '../types/request'
import { sendMessage } from '../socket'
import {
JudgeResult,
Expand All @@ -13,6 +13,7 @@ import {
clearTempEnv,
ExecuteResult,
getTmpPath,
representativeResult,
} from './util'

export default function commonJudge(
Expand All @@ -21,20 +22,19 @@ export default function commonJudge(
getExePath: (path: string) => string
) {
return new Promise<JudgeResult>(async (resolve) => {
let result = Array(data.dataSet.data.length).fill(0),
judgeResult = 'AC' as JudgeResultCode,
message = ''

sendMessage(WebSocketResponseType.JUDGE_PROGRESS, {
uid: data.uid,
progress: 0,
reason: 'CP',
resultCode: 'CP',
})

const tmpPath = initTempEnv(data.uid, data.source)

let maxMemoryUsage = 0,
maxTimeUsage = 0
let message = ''
const result: (number | number[])[] = [],
judgeResult: JudgeResultCode[] = [],
maxMemoryUsage: number[] = [],
maxTimeUsage: number[] = []

if (build) {
const buildResult = await build(tmpPath)
Expand All @@ -43,10 +43,18 @@ export default function commonJudge(
clearTempEnv(data.uid)
resolve({
uid: data.uid,
result,
reason: 'CE',
time: 0,
memory: 0,
result: data.dataSet.map((subtask) => {
switch (subtask.scoringType) {
case ScoringType.QUANTIZED:
return 0
case ScoringType.PROPORTIONAL:
return Array(subtask.data.length).fill(0)
}
}),
resultCode: 'CE',
reason: Array(data.dataSet.length).fill('CE'),
time: Array(data.dataSet.length).fill(0),
memory: Array(data.dataSet.length).fill(0),
message: buildResult.stderr
.replaceAll(getTmpPath(data.uid), '~')
.replaceAll(`/p-${data.uid}`, ''),
Expand All @@ -58,57 +66,100 @@ export default function commonJudge(
sendMessage(WebSocketResponseType.JUDGE_PROGRESS, {
uid: data.uid,
progress: 0,
reason: 'RUN',
resultCode: 'RUN',
})

for (const i in data.dataSet.data) {
const { code, stdout, stderr, resultType } = await executeJudge(
data,
getExePath(tmpPath),
data.dataSet.data[i].input
)
if (code) {
let errorMsg = ''
switch (resultType) {
case ResultType.normal:
errorMsg = stderr.split('\n').slice(0, -2).join('\n')
if (judgeResult === 'AC')
judgeResult = 'RE' as JudgeResultCode
break
case ResultType.timeLimitExceeded:
errorMsg = stderr
judgeResult = 'TLE' as JudgeResultCode
for (const subtaskI in data.dataSet) {
const subtask = data.dataSet[subtaskI]

let subtaskResult = Array(subtask.data.length).fill(0),
subtaskJudgeResult = [] as JudgeResultCode[],
subtaskMaxMemoryUsage = 0,
subtaskMaxTimeUsage = 0

for (const i in subtask.data) {
const { code, stdout, stderr, resultType } = await executeJudge(
data,
getExePath(tmpPath),
subtask.data[i].input
)
if (code) {
let errorMsg = ''
switch (resultType) {
case ResultType.normal:
errorMsg = stderr
.split('\n')
.slice(0, -2)
.join('\n')
subtaskJudgeResult.push('RE')
break
case ResultType.timeLimitExceeded:
errorMsg = stderr
subtaskJudgeResult.push('TLE')
}
if (!message)
message = errorMsg.replaceAll(getTmpPath(data.uid), '~')
if (subtask.scoringType === ScoringType.QUANTIZED) break
} else {
let info = '',
err = stderr.split('\n')
while (!info.includes('|') && err.length)
info = err.pop() || ''
const timeUsage =
parseFloat(info.split('m ')[1].split('s')[0]) * 1000
const memUsage = parseInt(info.split('|')[1])
subtaskMaxTimeUsage = Math.max(
subtaskMaxTimeUsage,
timeUsage
)
subtaskMaxMemoryUsage = Math.max(
subtaskMaxMemoryUsage,
memUsage
)
if (timeUsage > data.timeLimit) {
subtaskJudgeResult.push('TLE')
if (subtask.scoringType === ScoringType.QUANTIZED) break
} else if (isSame(stdout, subtask.data[i].output)) {
subtaskJudgeResult.push('AC')
subtaskResult[i as any] = 1
} else {
subtaskJudgeResult.push('WA')
if (subtask.scoringType === ScoringType.QUANTIZED) break
}
}
if (!message)
message = errorMsg.replaceAll(getTmpPath(data.uid), '~')
} else {
let info = '',
err = stderr.split('\n')
while (!info.includes('|') && err.length) info = err.pop() || ''
const timeUsage =
parseFloat(info.split('m ')[1].split('s')[0]) * 1000
const memUsage = parseInt(info.split('|')[1])
maxTimeUsage = Math.max(maxTimeUsage, timeUsage)
maxMemoryUsage = Math.max(maxMemoryUsage, memUsage)
if (timeUsage > data.timeLimit)
judgeResult = 'TLE' as JudgeResultCode
else if (isSame(stdout, data.dataSet.data[i].output))
result[i as any] = 1
else if (judgeResult === 'AC')
judgeResult = 'WA' as JudgeResultCode
sendMessage(WebSocketResponseType.JUDGE_PROGRESS, {
uid: data.uid,
progress:
(parseInt(i) + 1) / subtask.data.length / subtask.data +
parseInt(subtaskI) / data.dataSet.length,
resultCode: 'RUN',
})
}

subtaskMaxTimeUsage = Math.min(
Math.round(subtaskMaxTimeUsage),
data.timeLimit
)
subtaskMaxMemoryUsage = Math.min(
Math.round(subtaskMaxMemoryUsage),
data.memoryLimit
)

if (subtask.scoringType === ScoringType.QUANTIZED) {
subtaskResult = subtaskResult.slice(-1)[0]
}
sendMessage(WebSocketResponseType.JUDGE_PROGRESS, {
uid: data.uid,
progress: (parseInt(i) + 1) / data.dataSet.data.length,
reason: 'RUN',
})
result.push(subtaskResult)
judgeResult.push(representativeResult(subtaskJudgeResult))
maxTimeUsage.push(subtaskMaxTimeUsage)
maxMemoryUsage.push(subtaskMaxMemoryUsage)
}
clearTempEnv(data.uid)
resolve({
uid: data.uid,
result,
resultCode: representativeResult(judgeResult),
reason: judgeResult,
time: Math.min(Math.round(maxTimeUsage), data.timeLimit),
time: maxTimeUsage,
memory: maxMemoryUsage,
message,
})
Expand Down
18 changes: 13 additions & 5 deletions src/runner/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { JudgeRequest, JudgeType } from '../types/request'
import { JudgeRequest, JudgeType, ScoringType } from '../types/request'
import { JudgeResult } from '../types/response'
import * as fs from 'fs'
import * as path from 'path'
Expand Down Expand Up @@ -57,10 +57,18 @@ export default async function (data: JudgeRequest): Promise<JudgeResult> {
}
return {
uid: data.uid,
result: Array(data.dataSet.data.length).fill(0),
reason: 'CE',
result: data.dataSet.map((subtask) => {
switch (subtask.scoringType) {
case ScoringType.QUANTIZED:
return 0
case ScoringType.PROPORTIONAL:
return Array(subtask.data.length).fill(0)
}
}),
resultCode: 'CE',
reason: Array(data.dataSet.length).fill('CE'),
message: 'Unknown judge type',
time: 0,
memory: 0,
time: Array(data.dataSet.length).fill(0),
memory: Array(data.dataSet.length).fill(0),
}
}
19 changes: 11 additions & 8 deletions src/runner/languages/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,31 @@ export function judge(
data: JudgeRequest<JudgeType.OutputOnly, JudgeSourceType.TEXT, OutputOnly>
) {
return new Promise<JudgeResult>((resolve) => {
let match = Array(data.dataSet.data.length).fill(false)
let match = Array(data.dataSet.length).fill(0)
for (const s in data.source) {
for (const i in data.dataSet.data) {
for (const i in data.dataSet) {
if (
isSame(data.source[s].source, data.dataSet.data[i].output)
isSame(
data.source[s].source,
data.dataSet[i].data[0].output
)
) {
match[i] = true
}
}
sendMessage(WebSocketResponseType.JUDGE_PROGRESS, {
uid: data.uid,
progress: (s as unknown as number) / data.source.length,
progress: (parseInt(s) + 1) / data.source.length,
reason: 'RUN',
})
}
resolve({
uid: data.uid,
result: match,
reason:
match.reduce((a, b) => a + b, 0) === match.length ? 'AC' : 'WA',
time: 0,
memory: 0,
resultCode: 'WA',
reason: match.map((m) => (m ? 'AC' : 'WA')),
time: Array(data.dataSet.length).fill(0),
memory: Array(data.dataSet.length).fill(0),
})
})
}
Expand Down
10 changes: 10 additions & 0 deletions src/runner/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { execSync, spawn } from 'child_process'
import fs from 'fs'
import { JudgeRequest, SourceFile } from '../types/request'
import { JudgeResultCode } from '../types/response'

export const enum ResultType {
normal,
Expand Down Expand Up @@ -145,3 +146,12 @@ export function executeJudge(
{ input, timeout: data.timeLimit || 0, cwd: getTmpPath(data.uid) }
)
}

export function representativeResult(results: JudgeResultCode[]) {
if (results.includes('CE')) return 'CE'
if (results.includes('RE')) return 'RE'
if (results.includes('TLE')) return 'TLE'
if (results.includes('MLE')) return 'MLE'
if (results.includes('WA')) return 'WA'
return 'AC'
}
Loading

0 comments on commit 682e352

Please sign in to comment.