AtCoder Easy Test を支える技術

# AtCoder Easy Test とは

日本最大の競技プログラミングサイト AtCoder を便利に使うためのユーザースクリプトです。
AtCoder のそれぞれの問題には、自分の回答が正しいかチェックするためのサンプルケースがついていることが多いです。サンプルケースを使って回答をチェックするのはかなり重要です。しかし、サンプルケースは複数あることが多く、これは多少面倒な作業です。 AtCoder Easy Test はこの作業を楽にします。

使用感は、こんなかんじです。（橙で囲んだ部分）
![提出](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638261/88e05ac3-f449-5cc4-b049-644ce5797185.png)

## インストール

AtCoder Easy Test を使うには、 Tampermonkey などのユーザースクリプトマネージャが必要です。 Tampermonkey は PC 版の Chrome や Firefox で使うことができます。スマートフォンでは、 Gear Browser を使うと可能です。

ユーザースクリプトマネージャをインストールした後、以下のページからインストールすることができます。
[https://greasyfork.org/ja/scripts/433152-atcoder-easy-test-v2](https://greasyfork.org/ja/scripts/433152-atcoder-easy-test-v2)

## 機能

AtCoder Easy Testの最も主要な機能は、コードのテストです。サンプルケースに対して実行結果が正しいか試したり、自分で作った入力に対して試すことができます。他にも、サンプルが全部合っていたら自動で提出する機能や、最後にテストしたコードを復元する機能（うっかりページを閉じたときのため）もあります。

AtCoder Easy Testは現在AtCoder, yukicoder, Codeforcesで使えます。他にも必要と思ったサイトがあれば使えるようにしていきたいです。

## 誕生の経緯

AtCoderのコンテストに参加している中で、サンプルを素早く試す要求が発生しました。
サンプルすら合わないのに焦って提出してWAを食らった悔しい経験は、もしかすると誰にもあるものかもしれません。

そのとき私はonline-judge-toolsのことは知っていましたが、導入の方法がよくわからなかったので、自作してしまうことを選びました。

## 基本的な使い方

提出ボタンの右側に表示される水色の「Test & Submit」ボタンを押すと、サンプルがすべて合っていれば提出します。勝手に提出されたくないときは「Test All Samples」を使います。

注意事項として、構築系の問題（条件を満たすものを一つ答えよ）では、必ずしもサンプルと合わないからといって間違っているとは限りません。
そのような場合では、「Test & Submit」ではなく自力で合っているかを確かめて提出する必要があります。

### 詳細な説明

AtCoderでの例を説明します。

#### 各サンプル

各サンプルに1つずつボタンが表示されます。
![サンプルテストボタン](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638261/b02f15ff-9923-fab8-ec51-95c6299cbb53.png)

これを押すと、現在書いているコードにこの入力を与えた時の結果が表示されます。
![結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638261/b2ee9098-ad2d-cdc5-ba69-9e96c5d783ad.png)

#### 全サンプルのテスト

提出ボタンの横に2つのボタン、「Test & Submit」と「Test All Samples」が表示されます。
![Two Buttons](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638261/b4275337-6c73-eb84-474e-675473fa1b63.png)

どちらのボタンも、押すことで全部のサンプルを試し、結果が表示されます。この結果の「AC」のようなアイコンはボタンになっていて、押すとケースごとの詳細な結果が表示されます。
![Result Row](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638261/bcec71b3-691d-647c-f90b-25e41b85523f.png)

2つのボタンの唯一の違いは、「Test & Submit」ボタンは、全部のサンプルが合っていれば自動的に提出することです。

#### テストの設定

画面下に表示されるこのツマミを押すか、`Alt+Esc`を押すとメニューが表示されます。
![handle](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638261/e87b676e-3d00-7071-9581-aa45cc30bc41.png)

メニューでは、テストする環境の変更や、許容する小数点誤差の変更ができます。また、右下の「Settings」ボタンを押すと詳細な設定画面が表示されます。
![メニュー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638261/d19d3c1f-9e78-e91e-357d-47c68e90385e.png)

この画面では、ページを跨いで保存される設定を変えることができます。値を変更すると自動的に保存されます。「Reset」ボタンを押すとすべて初期設定に戻ります。
![スクリーンショット 2021-11-26 13.00.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638261/599fc722-4120-79fb-52f5-4fe810f267a7.png)

#### コードの復元

「Restore Last Play」ボタンを押すと最後にテストしたコードが復元されます。

#### 提出フォームの表示

Codeforces では、問題画面に提出フォームがありません。それだと使い勝手が悪いので、テキストエリアを表示します。
また、 [cf-fast-submit](https://github.com/LumaKernel/cf-fast-submit) をインストールしていれば、連携して使うことができます。

## 現在に至るまで

- `2020/06/20`: **AtCoder Easy Test**の前身となる[Easy Test AtCoder](https://qiita.com/magurofly/items/18a2212106e5de07a7fa)が完成しました。このスクリプトは20人くらいの方に使ってもらっていました。サンプルのテストにはAtCoderのコードテストを使っていました。
- `2020/07/06`: **Easy Test AtCoder**をたくさんの人が使うと、ジャッジサーバーに負荷がかかるのでやめてほしいとなり、公開を中止しました。しばらくの間、どうすればいいか思いつかなかったのでほっておきました。
- `2020/11/09`: [paiza.io](https://paiza.io/)を知って、これは規約的にも使えそうだと判断したので、paiza.ioを使ってコードをテストすることに決めました。
- `2020/11/12`: [AtCoder Easy Test v0.1](https://greasyfork.org/ja/scripts/415946-atcoder-easy-test)が完成しました。このときは1ファイルにベタ書きで作っていました。
- `2020/11/13`: [Wandbox](https://wandbox.org/)も使えそうだったので使うことにしました。これによって、いくつかテストできる言語が増えました。また、 Wandbox の Java で（ Java 限定かはわからないですが）巨大なコードをテストするとエラーが出るので、 Java だけは AtCoder のコードテストを使うことにしました。 C++ や Python と比べると利用者も比較的少なく、負荷もかかりにくいと考えての判断です。
- `2020/11/14`:  ACL が使いたかったので、 C++ で ACL が使えるようにしました。`#include <atcoder/*>`が書いてある場合、グラフを構築して DFS 順で展開しています。
- `2020/11/21`: yukicoderでも使いたくなったので、 yukicoder 用に改造した[yukicoder Easy Test](https://greasyfork.org/ja/scripts/416485-yukicoder-easy-test)を作りました。のちに[AtCoder Easy Test v2](https://greasyfork.org/ja/scripts/433152-atcoder-easy-test-v2)に統合されます。
- `2021/06`: 利用者が増えてきて嬉しい反面、コンテスト開始直後から Wandbox などに大きい負荷がかかって重くなる問題が発生していました。
- `2021/08/17`: 利用者が一番多そうな Python だけでも、負荷を軽くできないかと考えた結果、ブラウザ上で動く Brython を導入することにしました。しかし、動作が非常に重く、計算量が大きい場合は実用に適しませんでした。
- `2021/09/29`: 作りたい機能はたくさんあるのに対して、1ファイルで開発しているため非常に面倒になりました。そこで、[ac-predictor](https://qiita.com/keymoon/items/e83259f882f26c8f10a1)を参考に複数ファイルに分割した[AtCoder Easy Test v2](https://greasyfork.org/ja/scripts/433152-atcoder-easy-test-v2)に作り直しました。
- `2021/09/30`: yukicoder でも使えるようになりました。
- `2021/10/23`: Codeforces でも使えるようになりました。
- `2021/10/23`: 同日、[ABC224](https://atcoder.jp/contests/abc224)がありました。この日、運が悪かったのかあまりに大量の人がテストしたのか、とうとうWandboxが落ちてしまいました。非常に申し訳ない気持ちになって、一刻も早くクライアントサイドの実行環境を用意しないといけないと考えました。
- `2021/10/26`: Brython と同じくブラウザ上で動く Python 実行環境であり、かつBrythonよりも軽快に動く Pyodide を導入しました。これは許容可能程度には高速でした。

## 開発に使っている環境

- git, GitHub, Tampermonkey: これらがないと始まりません。
- VSCode: かなり便利です。
- rollup.js: 開発するときは複数ファイルに分け、公開するときは1ファイルにまとめる、ということができます。 minify せずにくっつけられるので、 Greasyfork にコードを公開できます。
- TypeScript: 静的型付き言語は人類の叡智だと思います。

## コードの構成

AtCoder Easy Test v2 はいくつかのモジュールからなっている、ということになっています。実際にはUI関係とかごちゃごちゃしているんですが、そういうことにしてください。

- `bottomMenu`: 画面下部にメニューを表示する。メニューの内容は他のモジュールから決める。
- `codeRunner`: コードを実行して、結果を確認する。
- `codeSaver`: 過去に実行したコードを保存する。 localStorage を使って保存している。
- `config`: ページをまたいだ設定を保存する。設定画面を表示する。設定の内容は他のモジュールから決める。
- `editor`: 問題画面にエディタがないときに表示する、きわめて簡素なエディタ。
- `site`: それぞれのサイト（ AtCoder, yukicoder, Codeforces など）ごとに、UIの表示される場所を決めたり、さまざまなサイトごとに異なる操作をおこなう。
- `resultList`: 「Test All Samples」とかを押したときに表示されるやつ。
- `container`: 下メニューで表示される「Easy Test」タブの内容。
- 各種ボタン: はい。

### bottomMenu

画面下部にメニューを表示します。
メニューは最小化しているときでも画面下部 50px 程度を占拠していますが、邪魔にならないようにつまみのところ以外はポインタイベントを透過しています。
Bootstrap3 の tab を利用してタブを作ったり消したりする機能があります。
メニューを開いているとき、タブ一覧の背景をドラッグするとリサイズできたり。

### codeRunner

Wandbox, paiza.io, AtCoder などの環境を使ってコードを実行します。
言語IDが与えられると使える環境の一覧を返します。
指定された環境IDに対応する環境でコードを実行して、想定出力があれば比較してACかWAかを判定したりします。
内部エラーが発生したら何度か別の環境で再試行したりします。

### config

`GM_getValue, GM_setValue` を使って設定を JSON 形式で保存します。
他のモジュールから`get`メソッドや`set`メソッドを呼び出すと設定にアクセスできます。
また、 `register***` メソッドを使うと設定画面に項目を追加できます。

設定画面は、 `about:blank` を新しいウィンドウで開いてそこに項目を書き込んでいます。

### editor

ただのテキストエリアです。当初は Monaco とか Ace とか CodeMirror とか入れてみようと思ったんですが、かなり面倒だったのでやめました。 Codeforces でしか使わないし…。

### site

サイトごとの差異を吸収するためのものです。
`init_AtCoder` みたいな関数がたくさんあって、順番に実行します。それぞれの関数では URL をチェックして、そのサイトだったら fulfilled な Promise 、違ったら rejected なのを返します。
Promise で返される値はいろんな操作を定義したオブジェクトです。ユーザーが書いているソースコードを取得したり、テストケースの一覧を取得したりします。
このスクリプトは基本的に Bootstrap3 に依存しているので、 Bootstrap3 がない環境ではグローバルに追加したりしています。

### その他、UI

大体見たままなので詳細は省略します。

## おわり

以上、 AtCoder Easy Test の紹介でした。まだ使っていない人はよかったら使ってみてください。