AtCoderACPercentage

自分と同じくらいのレートの人々の何%がその問題を解けているかを表示する。

  1. // ==UserScript==
  2. // @name AtCoderACPercentage
  3. // @namespace https://github.com/null-null-programming
  4. // @version 0.4
  5. // @description 自分と同じくらいのレートの人々の何%がその問題を解けているかを表示する。
  6. // @author null_null
  7. // @license MIT
  8. // @include https://atcoder.jp/contests/*/standings*
  9. // @exclude https://atcoder.jp/contests/*/standings/json
  10. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
  11. // ==/UserScript==
  12.  
  13. (async function () {
  14. //参加者自身のRating
  15. const userRating = await getUserRating(userScreenName);
  16.  
  17. //問題名のリスト
  18. const problemNames = standings.TaskInfo.map(task => task.TaskScreenName);
  19. //問題名の記号(AとかBとか)配列 <-これを作っておかないとF1 F2 などが来たときにバグる
  20. const Assignment = standings.TaskInfo.map(task => task.Assignment);
  21.  
  22. let solvedPercentage;
  23.  
  24. initView();
  25. updateData();
  26. updateView();
  27.  
  28. //ビューのupdate監視
  29. new MutationObserver(updateView).observe(
  30. document.getElementById("standings-tbody"),
  31. { childList: true }
  32. );
  33.  
  34. //リフレッシュボタンの監視(押されたとき/自動更新時に一瞬だけ無効化される)
  35. new MutationObserver(async mutationRecord => {
  36. const isDisabled = mutationRecord[0].target.classList.contains(
  37. "disabled"
  38. );
  39. if (isDisabled) {
  40. updateData();
  41. updateView();
  42. }
  43. })
  44. .observe(document.getElementById("refresh"), {
  45. attributes: true,
  46. attributeFilter: ["class"]
  47. });
  48.  
  49. function updateData(){
  50. //コンテスト情報を辞書型に直す userScreenName->Data
  51. const contestResultData = {};
  52. //参加回数が15回以上のコンテスト参加者のuserScreenNameリスト
  53. let contestUserName = [];
  54.  
  55. standings.StandingsData.forEach(res => {
  56. //辞書型に変換
  57. contestResultData[res.UserScreenName] = res;
  58.  
  59. //コンテスト参加回数10回未満、自分自身、未提出者は除いてリストに入れる
  60. if (res.Competitions >= 10 && res.UserScreenName !== userScreenName && res.TotalResult.Count > 0)
  61. contestUserName.push(res.UserScreenName);
  62. });
  63.  
  64. //TODO:評価関数の洗練
  65. //自身のレートとの絶対値の差が小さい順に並び替え。
  66. contestUserName.sort(function (x, y) {
  67. return Math.abs(userRating - contestResultData[x].Rating) - Math.abs(userRating - contestResultData[y].Rating);
  68. });
  69.  
  70. //TODO:選抜者人数の見直し
  71. //選抜者人数の10パーセントを選抜者人数とする。
  72. const USER_NUM = contestUserName.length * 0.1;
  73.  
  74. //自身のレートに近いUSER_NUM人の参加者を選抜
  75. contestUserName = contestUserName.slice(0, USER_NUM);
  76.  
  77. //何人が解けたかを問題ごとに集計
  78. solvedPercentage = problemNames.map(problemName => {
  79. let sum = 0;
  80.  
  81. //各ユーザーごとに集計
  82. contestUserName.forEach(userName => {
  83. if ((contestResultData[userName].TaskResults[problemName] || -1).Score > 0) sum++;
  84. });
  85.  
  86. //小数第1位までパーセントを表示
  87. return Math.round(sum * 10 * 100 / USER_NUM) / 10;
  88. });
  89. }
  90.  
  91. function initView(){
  92. //結果を表示するテーブルを作成する。
  93. //行を追加
  94. let table = document.getElementById('standings-tbody');
  95. let row = table.insertRow(-1);
  96.  
  97. row.id = 'ac-percentage-row';
  98.  
  99. //列を追加
  100. let cells = [];
  101.  
  102. for (let i = 0; i < problemNames.length + 1; i++) {
  103. cells[i] = row.insertCell(i);
  104.  
  105. if (i === 0) {
  106. //行の左端 題名を書き込む
  107. cells[i].innerText = 'AC Percentage';
  108. cells[i].style.color = '#00AA3E';
  109. cells[i].colSpan = '3';
  110. } else {
  111. cells[i].style.color = '#888888';
  112. }
  113. cells[i].style.fontSize = '80%';
  114. }
  115. }
  116.  
  117. function updateView(){
  118. //結果を表示するテーブルを作成する。
  119. //行を取得
  120. let row = document.getElementById('ac-percentage-row');
  121. if (!row) initView();
  122.  
  123. for (let i = 1; i < problemNames.length + 1; i++) {
  124. let cell = row.children[i];
  125. cell.innerText = Assignment[i - 1] + ':' + solvedPercentage[i - 1] + '%';
  126. }
  127. }
  128. })();
  129.  
  130. //参加しているコンテスト名を取得する。
  131. function getContestName() {
  132. let contestURL = location.href;
  133. let contestArray = contestURL.split('/');
  134. return contestArray[contestArray.length - 2];
  135. }
  136.  
  137. async function getUserRating(userScreenName) {
  138. let parser = new DOMParser();
  139. let archiveDom = parser.parseFromString((await $.get('https://atcoder.jp/users/' + userScreenName)), "text/html");
  140. let userRating = archiveDom.querySelector("#main-container > div.row > div.col-sm-9 > table > tbody > tr:nth-child(2) > td > span");
  141. return Number(userRating.innerText);
  142. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址