#P10004. 番外教程(2)IOI赛制优化

番外教程(2)IOI赛制优化

1.效果

需求:Hydrooj本身自带的IOI赛制在相同分数下排名是相同的。为了确保能区分不同排名。CodeFun2000在此基础上做更改,使得分数相同的情况下,按到达此分数的时刻升序排序.

2.思路

查询本比赛每道题目的最大得分的提交时刻的最小值。从中获取JudgeAt的最大值。一些必要细节请阅读源码

3.源码

contest_addon/index.ts

import {
    BadRequestError,
    Context, db, DocumentModel, ForbiddenError, Handler , PRIV,
    RecordModel, SettingModel, SystemModel, Time , UserModel, DomainModel,
    MessageModel, Types , param , ProblemModel , StorageModel , ContestModel ,
    STATUS 
} from 'hydrooj';


export async function apply(ctx: Context) {

    // ---------------contest-ioi-addon------------------------
    /**
     * 1.分数越高排越前
     * 2.分数相同比较<达到这个分数的时刻的时间戳>的大小,按升序排序
     */
    const vaildStatus = [
        STATUS.STATUS_ACCEPTED ,
        STATUS.STATUS_WRONG_ANSWER ,
        STATUS.STATUS_TIME_LIMIT_EXCEEDED, 
        STATUS.STATUS_MEMORY_LIMIT_EXCEEDED,
        STATUS.STATUS_RUNTIME_ERROR
    ];
    ctx.on('handler/after/ContestScoreboard', async (that) => {
        var tdoc = that.response.body.tdoc;
        if (tdoc.rule != 'ioi') return ;
        var rows = that.response.body.rows;
        var n = rows.length;
        var j = 1;
        //console.log(that.response.body);
        for (var i = 1 ; i < n ; i = j){
            // deal with the same score
            while (j < n && rows[i][2].value === rows[j][2].value){
                j += 1;
            }
            console.log(i , j - 1 , rows[i][0].value , rows[i][2].value);
            
            for (var k = i ; k < j ; k++){
                var lvtArr : any = [];
                // get last valid timestamp for every problem
                for (var pid of tdoc.pids){
                    var uid = Number(rows[k][1].raw);
                    var arr = await RecordModel.getMulti('system' , {
                        uid : uid , 
                        pid : pid , 
                        contest : {$exists : true , $eq : tdoc.docId} , 
                        status : { $in : vaildStatus}
                    }).toArray();

                    if (arr.length === 0) continue;

                    var maxScore = Math.max(...(arr.map(a => {return a['score']})) );
                    var ms_arr = arr.filter(a => {return a['score'] === maxScore});
                    var lvt = Math.min(...(ms_arr.map(a => {return new Date(a['judgeAt']).getTime()})) );
                    lvtArr.push(lvt);

                    console.log(k , pid , arr.map(a => {return a['judgeAt']}));
                }
                rows[k]['lastValidTimestamp'] = Math.max(...lvtArr);
                console.log(k , rows[k]['lastValidTimestamp']);
            }
            var tmp = rows.slice(i , j).sort((r1 , r2) => {
                return r1['lastValidTimestamp'] - r2['lastValidTimestamp'];
            });
            for (var k = i ; k < j ; k++)
                rows[k] = tmp[k - i];
            // reset the rank
            for (var k = i ; k < j ; k++){
                if (rows[k][2].value === 0){
                    rows[k][0].value = i;
                }else{
                    rows[k][0].value = k;
                }
            }
        }
        that.response.body.rows = rows;
    });

}