mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
1 parent
e0db6c6d47
commit
b7dc35d795
6 files changed
+177
-3
No files matched your search
@@ -81,8 +81,10 @@ Hydro 同时支持 VJudge,这意味着你可以直接在系统内导入其他
|
||||
- [UOJ](https://uoj.ac):国内知名 OJ,国家集训队常用;
|
||||
- [SPOJ](https://www.spoj.com):国内连接很不稳定,不推荐;
|
||||
- [洛谷](https://www.luogu.com.cn):使用此功能需要向洛谷购买授权;
|
||||
- [HDUOJ](https://acm.hdu.edu.cn):杭州电子科技大学在线评测系统,其中包含多校训练题;
|
||||
- [CSGOJ](https://cpc.csgrandeur.cn):广东省赛与湖南省赛赛题评测平台;
|
||||
- [POJ](https://poj.org):较为古董,服务器稳定性差;
|
||||
- [YACS](https://iai.sh.cn)上海市计算机学会竞赛平台;
|
||||
- HUSTOJ:理论上支持所有 HUSTOJ 驱动的系统,但由于各个系统中 UI 有差异,通常需要手动适配。
|
||||
|
||||
### 多赛制支持
|
||||
|
||||
@@ -73,7 +73,7 @@ export async function next(body: Partial<JudgeResultBody> & { rdoc: RecordDoc })
|
||||
if (!rdoc) return null;
|
||||
}
|
||||
const { $set, $push } = processPayload(rdoc, body);
|
||||
rdoc = await record.update(body.rdoc.domainId, body.rid, $set, $push, {}, body.addProgress ? { progress: body.addProgress } : {});
|
||||
rdoc = await record.update(rdoc.domainId, body.rid, $set, $push, {}, body.addProgress ? { progress: body.addProgress } : {});
|
||||
bus.broadcast('record/change', rdoc, $set, $push, body);
|
||||
return rdoc;
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ class AccountService {
|
||||
async judge(task) {
|
||||
const rdoc = await RecordModel.get(task.domainId, task.rid);
|
||||
task = Object.assign(rdoc, task);
|
||||
const next = (payload) => JudgeHandler.next({ ...payload, rid: task.rid });
|
||||
const end = (payload) => JudgeHandler.end({ ...payload, rid: task.rid });
|
||||
const next = (payload) => JudgeHandler.next({ ...payload, rid: task.rid, rdoc });
|
||||
const end = (payload) => JudgeHandler.end({ ...payload, rid: task.rid, rdoc });
|
||||
await next({ status: STATUS.STATUS_FETCHED });
|
||||
try {
|
||||
const langConfig = SettingModel.langs[task.lang];
|
||||
|
||||
@@ -9,6 +9,7 @@ import luogu from './luogu';
|
||||
import poj from './poj';
|
||||
import spoj from './spoj';
|
||||
import uoj from './uoj';
|
||||
import yacs from './yacs';
|
||||
|
||||
const vjudge: Record<string, any> = {
|
||||
codeforces,
|
||||
@@ -23,5 +24,6 @@ const vjudge: Record<string, any> = {
|
||||
ybt,
|
||||
ybtbas,
|
||||
hduoj,
|
||||
yacs,
|
||||
};
|
||||
export default vjudge;
|
||||
@@ -0,0 +1,160 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import { JSDOM } from 'jsdom';
|
||||
import {
|
||||
Logger, md5, sleep, STATUS, yaml,
|
||||
} from 'hydrooj';
|
||||
import { BasicFetcher } from '../fetch';
|
||||
import { IBasicProvider, RemoteAccount } from '../interface';
|
||||
import { VERDICT } from '../verdict';
|
||||
|
||||
const logger = new Logger('remote/yacs');
|
||||
|
||||
export default class YACSProvider extends BasicFetcher implements IBasicProvider {
|
||||
constructor(public account: RemoteAccount, private save: (data: any) => Promise<void>) {
|
||||
super(account, 'https://api.iai.sh.cn', 'form', logger);
|
||||
}
|
||||
|
||||
async getData(url: string) {
|
||||
const { text } = await this.get(new URL(url, 'https://iai.sh.cn').toString());
|
||||
const { window: { document } } = new JSDOM(text);
|
||||
return JSON.parse(document.querySelector('script#__NEXT_DATA__').innerHTML).props.pageProps;
|
||||
}
|
||||
|
||||
token: string;
|
||||
|
||||
get loggedIn(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
this.get('/user/fetchByToken').query({ token: this.token })
|
||||
.end((err, { status }) => resolve(status === 200));
|
||||
});
|
||||
}
|
||||
|
||||
async ensureLogin() {
|
||||
if (await this.loggedIn) return true;
|
||||
logger.info('retry login');
|
||||
try {
|
||||
const { body: { token } } = await this.post('/user/login')
|
||||
.send({
|
||||
username: this.account.handle,
|
||||
password: /^[a-f0-9]{32}$/.test(this.account.password)
|
||||
? this.account.password
|
||||
: md5(`${this.account.password}yacs`),
|
||||
});
|
||||
this.token = token;
|
||||
} catch (e) { }
|
||||
return await this.loggedIn;
|
||||
}
|
||||
|
||||
async getProblem(id: string) {
|
||||
logger.info(id);
|
||||
const { problem } = await this.getData(`/problem/${id.split('P')[1]}`);
|
||||
const tag = [];
|
||||
if (problem.level) tag.push(problem.level);
|
||||
if (problem.contest) tag.push(problem.contest.name);
|
||||
let content = '';
|
||||
if (problem.background.trim()) content += `## 题目背景\n\n${problem.background.trim()}\n\n`;
|
||||
content += `## 题目描述\n\n${problem.description.trim()}\n\n`;
|
||||
content += `## 输入格式\n\n${problem.inputFormat.trim()}\n\n`;
|
||||
content += `## 输出格式\n\n${problem.outputFormat.trim()}\n\n`;
|
||||
content += problem.exampleList.map((sample, index) => {
|
||||
const sampleId = index + 1;
|
||||
let ret = '';
|
||||
ret += `\`\`\`input${sampleId}\n${sample.input.trim()}\n\`\`\`\n\n`;
|
||||
ret += `\`\`\`output${sampleId}\n${sample.output.trim()}\n\`\`\`\n\n`;
|
||||
if (sample.note && sample.note.trim()) ret += `## 样例解释 ${sampleId}\n\n${sample.note.trim()}\n\n`;
|
||||
return ret;
|
||||
}).join('');
|
||||
content += `## 数据范围\n\n${problem.dataRange.trim()}\n\n`;
|
||||
if (problem.source.trim()) content += `## 来源\n\n${problem.source.trim()}\n\n`;
|
||||
return {
|
||||
title: problem.title,
|
||||
data: {
|
||||
'config.yaml': Buffer.from(yaml.dump({
|
||||
time: `${problem.limitTime}ms`,
|
||||
memory: `${problem.limitMemory}m`,
|
||||
type: 'remote_judge',
|
||||
subType: 'yacs',
|
||||
target: id,
|
||||
})),
|
||||
},
|
||||
files: {},
|
||||
tag,
|
||||
content,
|
||||
};
|
||||
}
|
||||
|
||||
async listProblem(page: number, resync = false) {
|
||||
if (resync && page > 1) return [];
|
||||
const data = await this.getData(`/problem?pi=${page}`);
|
||||
return data.problemList
|
||||
.filter((problem) => problem.contest.status === '榜单已公布')
|
||||
.map((problem) => `P${problem.id}`);
|
||||
}
|
||||
|
||||
async submitProblem(id: string, lang: string, code: string, info, next, end) {
|
||||
if (!['C++', 'Python 3.6'].includes(lang)) {
|
||||
end({ status: STATUS.STATUS_COMPILE_ERROR, message: `Language not supported: ${lang}` });
|
||||
return null;
|
||||
}
|
||||
const { body } = await this.post('/submission/submit')
|
||||
.set('Yacs-Token', this.token)
|
||||
.send({
|
||||
code,
|
||||
language: lang,
|
||||
problemId: +id.split('P')[1],
|
||||
});
|
||||
return `${body.id}`;
|
||||
}
|
||||
|
||||
async waitForSubmission(id: string, next, end) {
|
||||
const done = {};
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
await sleep(3000);
|
||||
const { submission } = await this.getData(`/submission/${id}`);
|
||||
const status = VERDICT[submission.finalStatus];
|
||||
if (status === STATUS.STATUS_COMPILE_ERROR) {
|
||||
return await end({
|
||||
status,
|
||||
compilerText: submission.finalErrorInfo,
|
||||
score: 0,
|
||||
time: 0,
|
||||
memory: 0,
|
||||
});
|
||||
}
|
||||
const cases = [];
|
||||
let taskId = 0;
|
||||
for (const task of submission.finalTaskList) {
|
||||
taskId++;
|
||||
let pointId = 0;
|
||||
for (const point of (task as any).dataPointList) {
|
||||
pointId++;
|
||||
if (done[`${taskId}.${pointId}`]) continue;
|
||||
if (VERDICT[point.status] === STATUS.STATUS_JUDGING) continue;
|
||||
done[`${taskId}.${pointId}`] = true;
|
||||
cases.push({
|
||||
id: pointId,
|
||||
subtaskId: taskId,
|
||||
score: point.scoreGet,
|
||||
time: 0,
|
||||
memory: 0,
|
||||
status: VERDICT[point.status],
|
||||
});
|
||||
}
|
||||
}
|
||||
await next({
|
||||
status,
|
||||
score: submission.finalScoreGet,
|
||||
cases,
|
||||
});
|
||||
if (status !== STATUS.STATUS_JUDGING) {
|
||||
return await end({
|
||||
status,
|
||||
score: submission.finalScoreGet,
|
||||
time: submission.finalTimeUsage,
|
||||
memory: submission.finalMemoryUsage / 1024,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,16 @@ export const VERDICT = new Proxy<Record<string, STATUS>>({
|
||||
SUBMIT_FAILED_PERM: STATUS.STATUS_SYSTEM_ERROR,
|
||||
SUBMIT_FAILED_TEMP: STATUS.STATUS_SYSTEM_ERROR,
|
||||
|
||||
// YACS
|
||||
正在评测: STATUS.STATUS_JUDGING,
|
||||
答案正确: STATUS.STATUS_ACCEPTED,
|
||||
编译失败: STATUS.STATUS_COMPILE_ERROR,
|
||||
部分正确: STATUS.STATUS_WRONG_ANSWER,
|
||||
运行超时: STATUS.STATUS_TIME_LIMIT_EXCEEDED,
|
||||
内存超出: STATUS.STATUS_MEMORY_LIMIT_EXCEEDED,
|
||||
暂未公布: STATUS.STATUS_SYSTEM_ERROR,
|
||||
评测机故障: STATUS.STATUS_SYSTEM_ERROR,
|
||||
|
||||
// HustOJ
|
||||
WAITING: STATUS.STATUS_WAITING,
|
||||
等待: STATUS.STATUS_WAITING,
|
||||
|
||||
Reference in new issue
Block a user