mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
1 parent
d7ce25827e
commit
476b17153a
5 files changed
+74
-9
No files matched your search
@@ -79,7 +79,7 @@ export async function postJudge(rdoc: RecordDoc) {
|
|||||||
if (rdoc.contest) {
|
if (rdoc.contest) {
|
||||||
await contest.updateStatus(
|
await contest.updateStatus(
|
||||||
rdoc.domainId, rdoc.contest, rdoc.uid, rdoc._id,
|
rdoc.domainId, rdoc.contest, rdoc.uid, rdoc._id,
|
||||||
rdoc.pid, rdoc.status, rdoc.score,
|
rdoc.pid, rdoc.status, rdoc.score, rdoc.subtasks,
|
||||||
);
|
);
|
||||||
} else if (accept && updated) await domain.incUserInDomain(rdoc.domainId, rdoc.uid, 'nAccept', 1);
|
} else if (accept && updated) await domain.incUserInDomain(rdoc.domainId, rdoc.uid, 'nAccept', 1);
|
||||||
const isNormalSubmission = ![
|
const isNormalSubmission = ![
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import {
|
|||||||
} from '../error';
|
} from '../error';
|
||||||
import {
|
import {
|
||||||
BaseUserDict, ContestRule, ContestRules, ProblemDict,
|
BaseUserDict, ContestRule, ContestRules, ProblemDict,
|
||||||
ScoreboardNode, ScoreboardRow, Tdoc,
|
ScoreboardNode, ScoreboardRow, SubtaskResult, Tdoc,
|
||||||
} from '../interface';
|
} from '../interface';
|
||||||
import ranked from '../lib/rank';
|
import ranked from '../lib/rank';
|
||||||
import * as bus from '../service/bus';
|
import * as bus from '../service/bus';
|
||||||
import type { Handler } from '../service/server';
|
import type { Handler } from '../service/server';
|
||||||
import { buildProjection } from '../utils';
|
import { buildProjection } from '../utils';
|
||||||
import { PERM, STATUS } from './builtin';
|
import { PERM, STATUS, STATUS_SHORT_TEXTS } from './builtin';
|
||||||
import * as document from './document';
|
import * as document from './document';
|
||||||
import problem from './problem';
|
import problem from './problem';
|
||||||
import RecordModel from './record';
|
import RecordModel from './record';
|
||||||
@@ -356,6 +356,64 @@ const ioi = buildContestRule({
|
|||||||
showScoreboard: (tdoc, now) => now > tdoc.beginAt,
|
showScoreboard: (tdoc, now) => now > tdoc.beginAt,
|
||||||
}, oi);
|
}, oi);
|
||||||
|
|
||||||
|
const strictioi = buildContestRule({
|
||||||
|
TEXT: 'IOI(Strict)',
|
||||||
|
submitAfterAccept: false,
|
||||||
|
showRecord: (tdoc, now) => now > tdoc.endAt,
|
||||||
|
showSelfRecord: () => true,
|
||||||
|
showScoreboard: (tdoc, now) => now > tdoc.endAt,
|
||||||
|
stat(tdoc, journal) {
|
||||||
|
const detail = {};
|
||||||
|
let score = 0;
|
||||||
|
const subtasks: Record<number, SubtaskResult> = {};
|
||||||
|
for (const j of journal.filter((i) => tdoc.pids.includes(i.pid))) {
|
||||||
|
Object.keys(j.subtasks).forEach((i) => {
|
||||||
|
if (!subtasks[i] || subtasks[i].score < j.subtasks[i].score) {
|
||||||
|
subtasks[i] = j.subtasks[i];
|
||||||
|
subtasks[i].rid = j.rid;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
j.score = sumBy(Object.values(subtasks), 'score');
|
||||||
|
j.status = Math.max(...Object.values(subtasks).map((i) => i.status));
|
||||||
|
j.subtasks = subtasks;
|
||||||
|
if (!detail[j.pid] || detail[j.pid].score < j.score) detail[j.pid] = j;
|
||||||
|
}
|
||||||
|
for (const i in detail) score += detail[i].score;
|
||||||
|
return { score, detail };
|
||||||
|
},
|
||||||
|
async scoreboardRow(isExport, _, tdoc, pdict, udoc, rank, tsdoc, meta) {
|
||||||
|
const tsddict = tsdoc.detail || {};
|
||||||
|
const row: ScoreboardNode[] = [
|
||||||
|
{ type: 'rank', value: rank.toString() },
|
||||||
|
{ type: 'user', value: udoc.uname, raw: tsdoc.uid },
|
||||||
|
];
|
||||||
|
if (isExport) {
|
||||||
|
row.push({ type: 'email', value: udoc.mail });
|
||||||
|
row.push({ type: 'string', value: udoc.school || '' });
|
||||||
|
row.push({ type: 'string', value: udoc.displayName || '' });
|
||||||
|
row.push({ type: 'string', value: udoc.studentId || '' });
|
||||||
|
}
|
||||||
|
row.push({ type: 'total_score', value: tsdoc.score || 0 });
|
||||||
|
for (const s of tsdoc.journal || []) {
|
||||||
|
if (!pdict[s.pid]) continue;
|
||||||
|
pdict[s.pid].nSubmit++;
|
||||||
|
if (s.status === STATUS.STATUS_ACCEPTED) pdict[s.pid].nAccept++;
|
||||||
|
}
|
||||||
|
for (const pid of tdoc.pids) {
|
||||||
|
row.push({
|
||||||
|
type: 'record',
|
||||||
|
value: tsddict[pid]?.penaltyScore || '',
|
||||||
|
hover: Object.values(tsddict[pid]?.subtasks).map((i: SubtaskResult) => `${STATUS_SHORT_TEXTS[i.status]} ${i.score}`).join(','),
|
||||||
|
raw: tsddict[pid]?.rid,
|
||||||
|
style: tsddict[pid]?.status === STATUS.STATUS_ACCEPTED && tsddict[pid]?.rid.getTimestamp().getTime() === meta?.first?.[pid]
|
||||||
|
? 'background-color: rgb(217, 240, 199);'
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
},
|
||||||
|
}, ioi);
|
||||||
|
|
||||||
const ledo = buildContestRule({
|
const ledo = buildContestRule({
|
||||||
TEXT: 'Ledo',
|
TEXT: 'Ledo',
|
||||||
check: () => { },
|
check: () => { },
|
||||||
@@ -576,7 +634,7 @@ const homework = buildContestRule({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const RULES: ContestRules = {
|
export const RULES: ContestRules = {
|
||||||
acm, oi, homework, ioi, ledo,
|
acm, oi, homework, ioi, ledo, strictioi,
|
||||||
};
|
};
|
||||||
|
|
||||||
function _getStatusJournal(tsdoc) {
|
function _getStatusJournal(tsdoc) {
|
||||||
@@ -633,14 +691,18 @@ export async function getStatus(domainId: string, tid: ObjectId, uid: number) {
|
|||||||
return await document.getStatus(domainId, document.TYPE_CONTEST, tid, uid);
|
return await document.getStatus(domainId, document.TYPE_CONTEST, tid, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _updateStatus(tdoc: Tdoc<30>, uid: number, rid: ObjectId, pid: number, status: STATUS, score: number) {
|
async function _updateStatus(
|
||||||
|
tdoc: Tdoc<30>, uid: number, rid: ObjectId, pid: number, status: STATUS, score: number,
|
||||||
|
subtasks: Record<number, SubtaskResult>,
|
||||||
|
) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||||
if (isLocked(tdoc)) {
|
if (isLocked(tdoc)) {
|
||||||
status = STATUS.STATUS_WAITING;
|
status = STATUS.STATUS_WAITING;
|
||||||
score = 0;
|
score = 0;
|
||||||
|
subtasks = {};
|
||||||
}
|
}
|
||||||
const tsdoc = await document.revPushStatus(tdoc.domainId, document.TYPE_CONTEST, tdoc.docId, uid, 'journal', {
|
const tsdoc = await document.revPushStatus(tdoc.domainId, document.TYPE_CONTEST, tdoc.docId, uid, 'journal', {
|
||||||
rid, pid, status, score,
|
rid, pid, status, score, subtasks,
|
||||||
}, 'rid');
|
}, 'rid');
|
||||||
const journal = _getStatusJournal(tsdoc);
|
const journal = _getStatusJournal(tsdoc);
|
||||||
const stats = RULES[tdoc.rule].stat(tdoc, journal);
|
const stats = RULES[tdoc.rule].stat(tdoc, journal);
|
||||||
@@ -649,10 +711,10 @@ async function _updateStatus(tdoc: Tdoc<30>, uid: number, rid: ObjectId, pid: nu
|
|||||||
|
|
||||||
export async function updateStatus(
|
export async function updateStatus(
|
||||||
domainId: string, tid: ObjectId, uid: number, rid: ObjectId, pid: number,
|
domainId: string, tid: ObjectId, uid: number, rid: ObjectId, pid: number,
|
||||||
status = STATUS.STATUS_WRONG_ANSWER, score = 0,
|
status = STATUS.STATUS_WRONG_ANSWER, score = 0, subtasks: Record<number, SubtaskResult> = {},
|
||||||
) {
|
) {
|
||||||
const tdoc = await get(domainId, tid);
|
const tdoc = await get(domainId, tid);
|
||||||
return await _updateStatus(tdoc, uid, rid, pid, status, score);
|
return await _updateStatus(tdoc, uid, rid, pid, status, score, subtasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getListStatus(domainId: string, uid: number, tids: ObjectId[]) {
|
export async function getListStatus(domainId: string, uid: number, tids: ObjectId[]) {
|
||||||
|
|||||||
@@ -447,6 +447,7 @@ Invalid password for user {0}.: 用户 {0} 的密码错误。
|
|||||||
Invalid: 无效
|
Invalid: 无效
|
||||||
Invitation Code: 邀请码
|
Invitation Code: 邀请码
|
||||||
IO: 输入输出
|
IO: 输入输出
|
||||||
|
IOI(Strict): IOI(严格)
|
||||||
Is rated: 计入排名
|
Is rated: 计入排名
|
||||||
It works!: 它成了!
|
It works!: 它成了!
|
||||||
Italic: 斜体
|
Italic: 斜体
|
||||||
|
|||||||
@@ -158,7 +158,7 @@
|
|||||||
value:tdoc.allowViewCode|default(true),
|
value:tdoc.allowViewCode|default(true),
|
||||||
row:false
|
row:false
|
||||||
}) }}
|
}) }}
|
||||||
<div style="display: none" class="contest-rule-settings contest-rule--acm">
|
<div style="display: none" class="contest-rule-settings contest-rule--acm contest-rule--ioi contest-rule--strictioi">
|
||||||
{{ form.form_text({
|
{{ form.form_text({
|
||||||
columns:4,
|
columns:4,
|
||||||
label:'Lock (minutes)',
|
label:'Lock (minutes)',
|
||||||
|
|||||||
@@ -115,6 +115,8 @@
|
|||||||
<p>{{ _('Ledo') }} 赛制下,多次提交会导致选手的得分被扣除,排行榜将显示用户的最高得分。</p>
|
<p>{{ _('Ledo') }} 赛制下,多次提交会导致选手的得分被扣除,排行榜将显示用户的最高得分。</p>
|
||||||
<p>{{ _('Ledo') }} 赛制下,每道题的最终得分为: $s \times \max(0.95^{n}, 0.7)$ 。$s,n$ 分别代表本次得分和本次提交前的尝试次数。</p>
|
<p>{{ _('Ledo') }} 赛制下,每道题的最终得分为: $s \times \max(0.95^{n}, 0.7)$ 。$s,n$ 分别代表本次得分和本次提交前的尝试次数。</p>
|
||||||
<p>{{ _('Ledo') }} 排名规则为:按照如上规则折算后的分数从高到低排名。</p>
|
<p>{{ _('Ledo') }} 排名规则为:按照如上规则折算后的分数从高到低排名。</p>
|
||||||
|
<p>{{ _('IOI(Strict)') }} 赛制下,不同于IOI赛制,排行榜将被关闭至比赛结束。</p>
|
||||||
|
<p>{{ _('IOI(Strict)') }} 赛制下,每道题的排行榜得分将为用户每个子任务在所有提交中的最大得分的和。</p>
|
||||||
<p>时间与空间限制以题目说明为准,默认限制参见<a href="#limits">{{ _('Limitations') }}</a>。</p>
|
<p>时间与空间限制以题目说明为准,默认限制参见<a href="#limits">{{ _('Limitations') }}</a>。</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user