// ==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(); })();