// ==UserScript==
// @name UOJ Predictor
// @namespace http://tampermonkey.net/
// @version 0.3
// @description Plugin to calculate predicted rating changes of UOJ-like Online Judges.
// @author tiger2005
// @match *://zhengruioi.com/contest/*/standings*
// @match *://uoj.ac/contest/*/standings*
// @match *://www.zhengruioi.com/contest/*/standings*
// @match *://www.uoj.ac/contest/*/standings*
// @grant none
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
// transformed from https://github.com/vfleaking/uoj/blob/9f1302c774f2499af0dc52d3faa7dd7404d03b13/uoj/1/app/uoj-contest-lib.php
function calcPredictedRatingChanges(K = 400){
var delta = 500;
var n = standings.length;
var rating = [];
var i, j;
for(i = 0; i < n; i ++)
rating.push(standings[i][2][1]);
var rank = [];
var foot = [];
for(i = 0; i < n; ){
j = i;
while(j + 1 < n && standings[j+1][3] == standings[j][3])
++ j;
var our_rk = 0.5 * ((i+1) + (j+1));
while(i <= j){
rank.push(our_rk);
foot.push(n - rank[i]);
i ++;
}
}
var weight = [];
for(i = 0; i < n; i ++)
weight.push(Math.pow(7, rating[i] / delta));
var exp = [];
for(i = 0; i < n; i ++)
exp.push(0);
for(i = 0; i < n; i ++)
for(j = 0; j < n; j ++)
if(j != i)
exp[i] += weight[i] / (weight[i] + weight[j]);
var new_rating = [];
for(i = 0; i < n; i ++)
new_rating.push(rating[i] + Math.ceil(K * (foot[i] - exp[i]) / (n - 1)));
for(i = n - 1; i >= 0; i --){
if(i + 1 < n && standings[i][3] != standings[i+1][3])
break;
if(new_rating[i] > rating[i])
new_rating[i] = rating[i];
}
for(i = 0; i < n; i ++)
if(new_rating[i] < 0)
new_rating[i] = 0;
return new_rating;
}
var max_rating_changes = localStorage.getItem("_MAX_RATING_CHANGES");
if(max_rating_changes == undefined){
localStorage.setItem("_MAX_RATING_CHANGES", 400);
max_rating_changes = 400;
}
else
max_rating_changes = Number(max_rating_changes);
function getUsernameTable(){
var users = {};
var n = standings.length;
for(var i = 0; i < n; i ++)
users[standings[i][2][0]] = [i, standings[i][2][1]];
return users;
}
var predicted_rating_changes = calcPredictedRatingChanges(max_rating_changes);
var username_table = getUsernameTable();
var rating_changes_colors = ["rgb(0, 204, 0)", "rgb(102, 102, 102)", "rgb(204, 0, 0)"];
function changeMaxRatingChanges(){
var str = prompt("Insert max rating changes, ranged [0, 1000]");
var result = Number(str);
if(typeof(result) != "number" || isNaN(result) || result < 0 || result > 1000)
alert("Invalid input!");
else{
var st = $("#standings > .table-responsive > table");
st.find("thead > tr > th:last-child").remove();
st.find("tbody > tr > td:last-child").remove();
max_rating_changes = Math.floor(result);
localStorage.setItem("_MAX_RATING_CHANGES", max_rating_changes);
predicted_rating_changes = calcPredictedRatingChanges(max_rating_changes);
displayRatingChanges();
}
}
function displayRatingChanges(){
var st = $("#standings > .table-responsive > table");
if(max_rating_changes == 0)
st.find("thead > tr").append(`
Unrated | `)
else
st.find("thead > tr").append(`Delta (max. = ${max_rating_changes}) | `)
st.find("tbody > tr").each(function(){
var username = $(this).children().eq(1).find("a").text();
var rating_changes = predicted_rating_changes[username_table[username][0]] - username_table[username][1];
var color = "", content = "";
if(rating_changes > 0)
color = rating_changes_colors[0], content = '+' + rating_changes;
else if(rating_changes == 0)
color = rating_changes_colors[1], content = '' + rating_changes;
else
color = rating_changes_colors[2], content = '' + rating_changes;
$(this).append(`${content} | `)
})
$(".changeMaxRatingChanges").click(function(){
changeMaxRatingChanges();
})
}
$.fn.long_table = function(data, cur_page, header_row, get_row_str, config) {
return this.each(function() {
var table_div = this;
$(table_div).html('');
var page_len = config.page_len != undefined ? config.page_len : 10;
if (!config.echo_full) {
var n_rows = data.length;
var n_pages = Math.max(Math.ceil(n_rows / page_len), 1);
if (cur_page == undefined) {
cur_page = 1;
}
if (cur_page < 1) {
cur_page = 1;
} else if (cur_page > n_pages) {
cur_page = n_pages;
}
var cur_start = (cur_page - 1) * page_len;
} else {
var n_rows = data.length;
var n_pages = 1;
cur_page = 1;
var cur_start = (cur_page - 1) * page_len;
}
var div_classes = config.div_classes != undefined ? config.div_classes : ['table-responsive'];
var table_classes = config.table_classes != undefined ? config.table_classes : ['table', 'table-bordered', 'table-hover', 'table-striped', 'table-text-center'];
var now_cnt = 0;
var tbody = $('')
for (var i = 0; i < page_len && cur_start + i < n_rows; i++) {
now_cnt++;
if (config.get_row_index) {
tbody.append(get_row_str(data[cur_start + i], cur_start + i));
} else {
tbody.append(get_row_str(data[cur_start + i]));
}
}
if (now_cnt == 0) {
tbody.append('无 |
');
}
$(table_div).append(
$('').append(
$('').append(
$('' + header_row + '')
).append(
tbody
)
)
);
if (config.print_after_table != undefined) {
$(table_div).append(config.print_after_table());
}
var get_page_li = function(p, h) {
if (p == -1) {
return $('').addClass('disabled').append($('').append(h));
}
var li = $('');
if (p == cur_page) {
li.addClass('active');
}
li.append(
$('').attr('href', '#' + table_div.id).append(h).click(function(e) {
if (config.prevent_focus_on_click) {
e.preventDefault();
}
$(table_div).long_table(data, p, header_row, get_row_str, config);
})
);
return li;
};
if (n_pages > 1) {
var pagination = $('');
if (cur_page > 1) {
pagination.append(get_page_li(cur_page - 1, ''));
} else {
pagination.append(get_page_li(-1, ''));
}
var max_extend = config.max_extend != undefined ? config.max_extend : 5;
for (var i = Math.max(cur_page - max_extend, 1); i <= Math.min(cur_page + max_extend, n_pages); i++) {
pagination.append(get_page_li(i, i.toString()));
}
if (cur_page < n_pages) {
pagination.append(get_page_li(cur_page + 1, ''));
} else {
pagination.append(get_page_li(-1, ''));
}
$(table_div).append($('').append(pagination));
}
displayRatingChanges();
});
};
displayRatingChanges();
})();