UOJ Predictor

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

目前为 2021-10-31 提交的版本。查看 最新版本

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