UOJ Predictor

Plugin to calculate predicted rating changes of UOJ-like Online Judges.

目前为 2021-10-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name UOJ Predictor
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Plugin to calculate predicted rating changes of UOJ-like Online Judges.
  6. // @author tiger2005
  7. // @match *://zhengruioi.com/contest/*/standings
  8. // @match *://uoj.ac/contest/*/standings
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14. // transformed from https://github.com/vfleaking/uoj/blob/9f1302c774f2499af0dc52d3faa7dd7404d03b13/uoj/1/app/uoj-contest-lib.php
  15. function calcPredictedRatingChanges(K = 400){
  16. var delta = 500;
  17. var n = standings.length;
  18. var rating = [];
  19. var i, j;
  20. for(i = 0; i < n; i ++)
  21. rating.push(standings[i][2][1]);
  22. var rank = [];
  23. var foot = [];
  24. for(i = 0; i < n; ){
  25. j = i;
  26. while(j + 1 < n && standings[j+1][3] == standings[j][3])
  27. ++ j;
  28. var our_rk = 0.5 * ((i+1) + (j+1));
  29. while(i <= j){
  30. rank.push(our_rk);
  31. foot.push(n - rank[i]);
  32. i ++;
  33. }
  34. }
  35. var weight = [];
  36. for(i = 0; i < n; i ++)
  37. weight.push(Math.pow(7, rating[i] / delta));
  38. var exp = [];
  39. for(i = 0; i < n; i ++)
  40. exp.push(0);
  41. for(i = 0; i < n; i ++)
  42. for(j = 0; j < n; j ++)
  43. if(j != i)
  44. exp[i] += weight[i] / (weight[i] + weight[j]);
  45. var new_rating = [];
  46. for(i = 0; i < n; i ++)
  47. new_rating.push(rating[i] + Math.ceil(K * (foot[i] - exp[i]) / (n - 1)));
  48. for(i = n - 1; i >= 0; i --){
  49. if(i + 1 < n && standings[i][3] != standings[i+1][3])
  50. break;
  51. if(new_rating[i] > rating[i])
  52. new_rating[i] = rating[i];
  53. }
  54. for(i = 0; i < n; i ++)
  55. if(new_rating[i] < 0)
  56. new_rating[i] = 0;
  57. return new_rating;
  58. }
  59. function getUsernameTable(){
  60. var users = {};
  61. var n = standings.length;
  62. for(var i = 0; i < n; i ++)
  63. users[standings[i][2][0]] = [i, standings[i][2][1]];
  64. return users;
  65. }
  66. var predicted_rating_changes = calcPredictedRatingChanges();
  67. var username_table = getUsernameTable();
  68. var rating_changes_colors = ["rgb(0, 204, 0)", "rgb(102, 102, 102)", "rgb(204, 0, 0)"];
  69. function displayRatingChanges(){
  70. var st = $("#standings > .table-responsive > table");
  71. st.find("thead > tr").append(`<th style="width: 8em">Delta (max. = 400)</th>`)
  72. st.find("tbody > tr").each(function(){
  73. var username = $(this).children().eq(1).find("a").text();
  74. var rating_changes = predicted_rating_changes[username_table[username][0]] - username_table[username][1];
  75. var color = "", content = "";
  76. if(rating_changes > 0)
  77. color = rating_changes_colors[0], content = '+' + rating_changes;
  78. else if(rating_changes == 0)
  79. color = rating_changes_colors[1], content = '' + rating_changes;
  80. else
  81. color = rating_changes_colors[2], content = '' + rating_changes;
  82. $(this).append(`<td><div><span style='color: ${color}'>${content}</span></div></td>`)
  83. })
  84. }
  85. $.fn.long_table = function(data, cur_page, header_row, get_row_str, config) {
  86. return this.each(function() {
  87. var table_div = this;
  88.  
  89. $(table_div).html('');
  90.  
  91. var page_len = config.page_len != undefined ? config.page_len : 10;
  92.  
  93. if (!config.echo_full) {
  94. var n_rows = data.length;
  95. var n_pages = Math.max(Math.ceil(n_rows / page_len), 1);
  96. if (cur_page == undefined) {
  97. cur_page = 1;
  98. }
  99. if (cur_page < 1) {
  100. cur_page = 1;
  101. } else if (cur_page > n_pages) {
  102. cur_page = n_pages;
  103. }
  104. var cur_start = (cur_page - 1) * page_len;
  105. } else {
  106. var n_rows = data.length;
  107. var n_pages = 1;
  108. cur_page = 1;
  109. var cur_start = (cur_page - 1) * page_len;
  110. }
  111.  
  112. var div_classes = config.div_classes != undefined ? config.div_classes : ['table-responsive'];
  113. var table_classes = config.table_classes != undefined ? config.table_classes : ['table', 'table-bordered', 'table-hover', 'table-striped', 'table-text-center'];
  114.  
  115. var now_cnt = 0;
  116. var tbody = $('<tbody />')
  117. for (var i = 0; i < page_len && cur_start + i < n_rows; i++) {
  118. now_cnt++;
  119. if (config.get_row_index) {
  120. tbody.append(get_row_str(data[cur_start + i], cur_start + i));
  121. } else {
  122. tbody.append(get_row_str(data[cur_start + i]));
  123. }
  124. }
  125. if (now_cnt == 0) {
  126. tbody.append('<tr><td colspan="233">无</td></tr>');
  127. }
  128.  
  129. $(table_div).append(
  130. $('<div class="' + div_classes.join(' ') + '" />').append(
  131. $('<table class="' + table_classes.join(' ') + '" />').append(
  132. $('<thead>' + header_row + '</thead>')
  133. ).append(
  134. tbody
  135. )
  136. )
  137. );
  138.  
  139. if (config.print_after_table != undefined) {
  140. $(table_div).append(config.print_after_table());
  141. }
  142.  
  143. var get_page_li = function(p, h) {
  144. if (p == -1) {
  145. return $('<li></li>').addClass('disabled').append($('<a></a>').append(h));
  146. }
  147.  
  148. var li = $('<li></li>');
  149. if (p == cur_page) {
  150. li.addClass('active');
  151. }
  152. li.append(
  153. $('<a></a>').attr('href', '#' + table_div.id).append(h).click(function(e) {
  154. if (config.prevent_focus_on_click) {
  155. e.preventDefault();
  156. }
  157. $(table_div).long_table(data, p, header_row, get_row_str, config);
  158. })
  159. );
  160. return li;
  161. };
  162.  
  163. if (n_pages > 1) {
  164. var pagination = $('<ul class="pagination top-buffer-no bot-buffer-sm"></ul>');
  165. if (cur_page > 1) {
  166. pagination.append(get_page_li(cur_page - 1, '<span class="glyphicon glyphicon glyphicon-backward"></span>'));
  167. } else {
  168. pagination.append(get_page_li(-1, '<span class="glyphicon glyphicon glyphicon-backward"></span>'));
  169. }
  170. var max_extend = config.max_extend != undefined ? config.max_extend : 5;
  171. for (var i = Math.max(cur_page - max_extend, 1); i <= Math.min(cur_page + max_extend, n_pages); i++) {
  172. pagination.append(get_page_li(i, i.toString()));
  173. }
  174. if (cur_page < n_pages) {
  175. pagination.append(get_page_li(cur_page + 1, '<span class="glyphicon glyphicon glyphicon-forward"></span>'));
  176. } else {
  177. pagination.append(get_page_li(-1, '<span class="glyphicon glyphicon glyphicon-forward"></span>'));
  178. }
  179. $(table_div).append($('<div class="text-center"></div>').append(pagination));
  180. }
  181. displayRatingChanges();
  182. });
  183. };
  184. displayRatingChanges();
  185. })();