AtCoderDifficultyDisplay

display a difficulty of AtCoder Problems.

Από την 07/06/2020. Δείτε την τελευταία έκδοση.

  1. // ==UserScript==
  2. // @name AtCoderDifficultyDisplay
  3. // @namespace https://github.com/hotarunx
  4. // @version 0.5
  5. // @description display a difficulty of AtCoder Problems.
  6. // @description:ja AtCoder Problemsの難易度を表示します。
  7. // @author hotarunx
  8. // @match https://atcoder.jp/contests/*/tasks/*
  9. // @grant none
  10. // @connect https://kenkoooo.com/atcoder/resources/*
  11. // @connect https://kenkoooo.com/atcoder/atcoder-api/*
  12. // @license MIT
  13. //
  14. // Copyright(c) 2020 hotarunx
  15. // This software is released under the MIT License, see LICENSE or https://github.com/hotarunx/AtCoderMyExtensions/blob/master/LICENSE.
  16. //
  17. // ==/UserScript==
  18.  
  19. (function () {
  20.  
  21. // -------------------------------------------------------------------------
  22. // 設定
  23. // 次の変数の値を書き換えることで各数値を表示するかどうかを変更できます
  24.  
  25. // 難易度を表示するかどうか
  26. const displayDifficulty = true;
  27.  
  28. // 提出状況を表示するかどうか
  29. const displaySubmissionStatus = true;
  30.  
  31. // true: 表示する
  32. // false: 表示しない
  33. // -------------------------------------------------------------------------
  34.  
  35. // URL of Estimated difficulties of the problems
  36. const SUBMISSION_API = "https://kenkoooo.com/atcoder/atcoder-api/results?user=" + userScreenName;
  37. const SUBMISSIONS_DATASET = "https://kenkoooo.com/atcoder/resources/problem-models.json";
  38.  
  39. if (displayDifficulty)
  40. fetch(SUBMISSIONS_DATASET)
  41. .then((response) => response.json())
  42. .then((jsonData) => {
  43. addDifficultyText(jsonData);
  44. });
  45.  
  46. if (displaySubmissionStatus && userScreenName != "")
  47. fetch(SUBMISSION_API)
  48. .then((response) => response.json())
  49. .then((submissionData) => {
  50. addSubmissionStatusText(submissionData);
  51. });
  52.  
  53. })();
  54.  
  55. // Webページの問題ステータス(実行時間制限とメモリ制限が書かれた部分)のHTMLオブジェクトを取得
  56. function getElementOfProblemStatus() {
  57. let element_status;
  58.  
  59. const main_container = document.getElementById('main-container');
  60. const elements_p = main_container.getElementsByTagName("p");
  61.  
  62. for (let i = 0; i < elements_p.length; i++) {
  63. const element = elements_p[i];
  64. if (element.textContent.match("メモリ制限:") || element.textContent.match("Memory Limit:")) {
  65. element_status = element;
  66. break
  67. }
  68. }
  69.  
  70. return element_status;
  71. }
  72.  
  73. // レーティングに対応する色のカラーコードを返す
  74. function colorRating(rating) {
  75. let color = '#FFFFFF'; // white
  76. if (rating < 400) color = '#808080'; // gray
  77. else if (rating < 800) color = '#804000'; // brown
  78. else if (rating < 1200) color = '#008000'; // green
  79. else if (rating < 1600) color = '#00C0C0'; // cyan
  80. else if (rating < 2000) color = '#0000FF'; // blue
  81. else if (rating < 2400) color = '#C0C000'; // yellow
  82. else if (rating < 2800) color = '#FF8000'; // orange
  83. else if (rating < 3200) color = '#FF0000'; // red
  84. else if (rating < 3600) color = '#E4E4E4'; // silver
  85. else color = '#FFD325'; // gold
  86.  
  87. return color;
  88. }
  89.  
  90. // レーティングを0以上に補正
  91. // 参考 https://qiita.com/anqooqie/items/92005e337a0d2569bdbd#%E6%80%A7%E8%B3%AA4-%E5%88%9D%E5%BF%83%E8%80%85%E3%81%B8%E3%81%AE%E6%85%88%E6%82%B2
  92. function correctLowerRating(rating) {
  93. if (rating >= 400) return rating;
  94. do {
  95. rating = 400 / Math.exp((400 - rating) / 400);
  96. } while (rating < 0);
  97. return rating;
  98. }
  99.  
  100. // 難易度を表示する文字列を生成
  101. function generateDifficultyText(difficulty, is_experimental) {
  102. // 難易度を0にして四捨五入
  103. difficulty = correctLowerRating(difficulty);
  104. difficulty = difficulty.toFixed();
  105.  
  106. // テキストを生成
  107. let difficultyText = "Difficulty: ";
  108. if (is_experimental) difficultyText += "🧪";
  109. difficultyText += difficulty;
  110.  
  111. // 色つけ
  112. const color = colorRating(difficulty);
  113. difficultyText = "<span style='color: " + color + ";'>" + difficultyText + "</span>";
  114.  
  115. // Problemsへのリンクを追加
  116. const atcoderProblemsUrl = "https://kenkoooo.com/atcoder/#/table/" + userScreenName;
  117. difficultyText = "<a href='" + atcoderProblemsUrl + "'>" + difficultyText + "</a>";
  118.  
  119. return " / " + difficultyText;
  120. }
  121.  
  122. function addDifficultyText(jsonData) {
  123. // URLから問題IDを取得
  124. const path = location.pathname.split("/");
  125. const id = path[path.length - 1];
  126. // 問題データを取得
  127. const problem = jsonData[id];
  128.  
  129. // 問題が存在しなければ終了
  130. if (problem == null || problem.difficulty == null) { return; }
  131.  
  132. // 難易度を表示する文字列を生成
  133. const text = generateDifficultyText(problem.difficulty, problem.is_experimental);
  134.  
  135. // 問題ステータスのHTMLオブジェクトを探してtextを追加
  136. let status = getElementOfProblemStatus();
  137. status.insertAdjacentHTML('beforeend', text);
  138. }
  139.  
  140. function addSubmissionStatusText(submissionData) {
  141. // URLから問題IDを取得
  142. const path = location.pathname.split("/");
  143. const id = path[path.length - 1];
  144.  
  145. // コンテスト時間を取得
  146. const start = Math.floor(Date.parse(startTime._i) / 1000);
  147. const end = Math.floor(Date.parse(endTime._i) / 1000);
  148.  
  149. // 4つの提出状況記録変数
  150. // コンテスト中にACした、コンテスト外にACした、コンテスト中に提出した、コンテスト外に提出した
  151. let contestAccepted = false, accepted = false, contestSubmitted = false, submitted = false;
  152.  
  153. let latestAcceptedSubmission, latestSubmission;
  154.  
  155. // この問題への提出をすべて探索して提出状況を更新する
  156. const submissions = submissionData.filter(function (item, index) { if (item.problem_id == id) return true; });
  157. submissions.sort((a, b) => a.epoch_second - b.epoch_second);
  158.  
  159. for (const item of submissions) {
  160. const time = item["epoch_second"];
  161. const isDuringContest = start <= time && time <= end;
  162. const isAccepted = item["result"] == "AC";
  163.  
  164. if (isDuringContest) {
  165. contestSubmitted = true;
  166. if (isAccepted) contestAccepted = true;
  167. } else {
  168. submitted = true;
  169. if (isAccepted) accepted = true;
  170. }
  171.  
  172. if (isAccepted) latestAcceptedSubmission = item;
  173. else latestSubmission = item;
  174. }
  175.  
  176. // 提出状況を表す文字列を生成
  177. let text;
  178. if (contestAccepted) text = "<span style='color: #5CB85C;'>★Accepted</span>";
  179. else if (accepted) text = "<span style='color: #5CB85C;'>Accepted</span>";
  180. else if (submitted) text = "<span style='color: #F0AD4E;'>Trying</span>";
  181. else if (contestSubmitted) text = "<span style='color: #F0AD4E;'>★Trying</span>";
  182. else text = "Trying";
  183.  
  184. // 最新のAC提出または提出へのリンクを追加
  185. if (submitted || contestSubmitted) {
  186. const submission = (latestAcceptedSubmission != null ? latestAcceptedSubmission : latestSubmission);
  187.  
  188. const url = "https://atcoder.jp/contests/" + submission.contest_id + "/submissions/" + submission.id;
  189.  
  190. text = "<a href='" + url + "'>" + text + "</a>";
  191. }
  192.  
  193. // 問題ステータスのHTMLオブジェクトを探してtextを追加
  194. let status = getElementOfProblemStatus();
  195. status.insertAdjacentHTML('beforeend', " / " + text);
  196. }