Intro
競技プログラミングコミュニティが年々大きくなっています。日本ではAOJやAtCoder、世界ではTopCoderやCodeforcesなどのオンラインジャッジが人気です。
オンラインジャッジはコンパイル、実行を行い、あらかじめ用意されているテストケースをパスするかどうか確認してくれます。
プログラミング初心者のプログラミング導入にも最適ですし、プログラマの力試し/筋力向上としても、とても良い教材です。
ただ、サンプルケースをダウンロードしたり、解答をいい感じに管理したりと定型作業も多いです。
ojとoj-templateというツールで、その定型作業を自動化できます。そのojをEmacsと連携させた oj.el というパッケージを紹介します。
なお、このojは私が作ったパッケージなので、技術的な話は「oj.el - Conao3 note」に書いてあります。
Install
(leaf oj
:doc "Competitive programming tools client for AtCoder, Codeforces"
:req "emacs-26.1" "quickrun-2.2"
:tag "convenience" "emacs>=26.1"
:url "https://github.com/conao3/oj.el"
:emacs>= 26.1
:ensure t
:custom ((oj-compiler-c . "clang")
(oj-compiler-python . "cpython")
(oj-default-online-judge . 'atcoder)))
Usage
oj-install-package
このパッケージはoj, oj-template, seleniumに依存しています。
これらのソフトは M-x oj-install-packages
でインストールできます。
(defun oj-install-packages ()
"Install `oj', `oj-template', `selenium' pip package via `pip3'."
(interactive)
(unless (yes-or-no-p "Install `oj', `oj-template', `selenium' via pip3?")
(error "Abort install"))
(dolist (elm '(("oj" . "online-judge-tools")
("oj-template" . "online-judge-template-generator")
("selenium" . "selenium")))
(unless (executable-find (car elm))
(unless (executable-find "python3")
(error "Missing `python3'. Please ensure Emacs's PATH and the installing"))
(unless (executable-find "pip3")
(error "Missing `pip3'. Please ensure Emacs's PATH and the installing"))
(oj--exec-script (format "pip3 install %s" (cdr elm))))))
もしこの関数によってインストールしたくない場合は、ターミナルで次のコマンドでインストールします。
pip3 install online-judge-tools
pip3 install online-judge-template-generator
pip3 install selenium
さらにEmacsの PATH
が適切に設定されていることを確認してください。M-! which oj
が oj
へのパスを返せば、Emacsからojを利用できます。出力がない場合は、Q&Aを参照するか、exec-path-from-shellを導入することを検討して下さい。
oj-prepare
M-x oj-prepare
はこのパッケージを使うためのファーストステップです。
このコマンドは次のようなフォーマットを受理します。
- Problem URL: "https://codeforces.com/contest/1349/problem/A"
- Contest URL: "https://codeforces.com/contest/1349/"
- Problem shortcode (for
oj-defualt-online-judge
): "1349/A" - Contest shortcode (for
oj-default-online-judge
): "1349"
Edit your code
oj-home-dir
にファイルが生成されるので、ファイルを開いて編集して下さい。
$ tree codeforces/
codeforces/
└── 1349
├── A
│ ├── a.out
│ ├── geckodriver.log
│ ├── generate.py
│ ├── main.cpp
│ ├── main.py
│ └── test
│ ├── sample-1.in
│ ├── sample-1.out
│ ├── sample-2.in
│ ├── sample-2.out
│ ├── sample-3.in
│ └── sample-3.out
...
└── F2
├── generate.py
├── main.cpp
├── main.py
└── test
├── sample-1.in
├── sample-1.out
├── sample-2.in
├── sample-2.out
├── sample-3.in
└── sample-3.out
$ cat codeforces/1349/A/main.cpp
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define v vector
#define rep(i, n) for (int i = 0; i < (int)(n); ++i)
#define rep3(i, m, n) for (int i = (m); i < (int)(n); ++i)
#define rrep(i, n) for (int i = (int)(n)-1; i >= 0; --i)
#define rrep3(i, m, n) for (int i = (int)(n)-1; i >= (m); --i)
#define all(x) x.begin(), x.end()
#define rall(x) x.end(x), x.begin()
#define endl '\n'
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll solve(int ebd, const vector<ll> & zdf) {
// TODO: edit here
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int ebd;
cin >> ebd;
vector<ll> zdf(ebd);
rep (i, ebd) {
cin >> zdf[i];
}
auto ans = solve(ebd, zdf);
cout << ans << endl;
return 0;
}
oj-test
M-x oj-test
は compile
と test
を行います。
もしテストケースをパスしたら、 *oj*
バッファに次のように出力されます。
[*] 3 cases found
[!] GNU time is not available: time
[*] sample-1
[x] time: 0.001666 sec
[+] AC
[*] sample-2
[x] time: 0.002213 sec
[+] AC
[*] sample-3
[x] time: 0.001923 sec
[+] AC
[x] slowest: 0.002213 sec (for sample-2)
[+] test success: 3 cases
コンパイルコマンドは自動的に quickrun
パッケージの定義から推測されます。quickrun-add-command
を使用することで、追加や上書きができます。
oj-submit
オンラインジャッジに提出するには M-x oj-submit
を実行します。(初回にはオンラインジャッジごとに M-x oj-login
が必要です)
Customize
Variables
-
oj-shell-program:
*oj*
バッファで使われるシェル (defaultshell-file-name
(bash
or some customized shell)) -
oj-home-dir: ファイルが生成されるパス (default
"~/.emacs.d/oj/"
) -
oj-default-online-judge: 規定のオンラインジャッジ (default
'codeforces
) -
oj-compiler-c: C/C++の提出で使われるコンパイラ (default
gcc
)Clangをローカルで使いたい場合は次のスニペットをinit.elに追加します。
(quickrun-set-default "c" "c/clang") (quickrun-set-default "c++" "c++/clang++")
-
oj-compiler-python: Pythonの提出で使われるコンパイラ (default
cpython
) -
oj-login-args:
oj login
のオプション引数 (defaultnil
)usage: oj login [-h] [-u USERNAME] [-p PASSWORD] [--check] [--use-browser {always,auto,never}] url positional arguments: url optional arguments: -h, --help show this help message and exit -u USERNAME, --username USERNAME -p PASSWORD, --password PASSWORD --check check whether you are logged in or not --use-browser {always,auto,never} specify whether it uses a GUI web browser to login or not (default: auto)
-
oj-prepare-args:
oj-prepare
のオプション引数 (defaultnil
)Args for `oj-prepare'. usage: oj-prepare [-h] [-v] [-c COOKIE] [--config-file CONFIG_FILE] url positional arguments: url optional arguments: -h, --help show this help message and exit -v, --verbose -c COOKIE, --cookie COOKIE --config-file CONFIG_FILE default: ~/.config/online-judge-tools/prepare.config.toml
-
oj-test-args:
oj test
のオプション引数 (defaultnil
)Args for `oj-test'. Note that the runtime command (`-c') is detected automatically. usage: oj test [-h] [-c COMMAND] [-f FORMAT] [-d DIRECTORY] [-m {simple,side-by-side}] [-S] [--no-rstrip] [--rstrip] [-s] [-e ERROR] [-t TLE] [--mle MLE] [-i] [-j N] [--print-memory] [--gnu-time GNU_TIME] [--no-ignore-backup] [--ignore-backup] [--json] [--judge-command JUDGE] [test [test ...]] positional arguments: test paths of test cases. (if empty: globbed from --format) optional arguments: -h, --help show this help message and exit -c COMMAND, --command COMMAND your solution to be tested. (default: "./a.out") -f FORMAT, --format FORMAT a format string to recognize the relationship of test cases. (default: "%s.%e") -d DIRECTORY, --directory DIRECTORY a directory name for test cases (default: test/) -m {simple,side-by-side}, --display-mode {simple,side-by-side} mode to display an output with the correct answer (default: simple) -S, --side-by-side display an output and the correct answer with side byside mode (equivalent to --display-mode side-by-side) --no-rstrip --rstrip rstrip output before compare (default) -s, --silent don't report output and correct answer even if not AC (for --mode all) -e ERROR, --error ERROR check as floating point number: correct if its absolute or relative error doesn't exceed it -t TLE, --tle TLE set the time limit (in second) (default: inf) --mle MLE set the memory limit (in megabyte) (default: inf) -i, --print-input print input cases if not AC -j N, --jobs N specifies the number of jobs to run simultaneously (default: no parallelization) --print-memory print the amount of memory which your program used, even if it is small enough --gnu-time GNU_TIME used to measure memory consumption (default: "time") --no-ignore-backup --ignore-backup ignore backup files and hidden files (i.e. files like "*~", "\#*\#" and ".*") (default) --json --judge-command JUDGE specify judge command instead of default diff judge. See https://online-judge-tools.readthedocs.io/en/ master/introduction.en.html #test-for-special-forms-of-problem for details format string for --format: %s name %e extension: "in" or "out" (both %s and %e are required.) tips: You can do similar things with shell: e.g. `for f in test/*.in ; do echo $f ; diff <(./a.out < $f) ${f/.in/.out} ; done`
-
oj-submit-args:
oj submit
のオプション引数 (default'("-y")
)Args for `oj-submit'. usage: oj submit [-h] [-l LANGUAGE] [--no-guess] [-g] [--no-guess-latest] [--guess-cxx-latest] [--guess-cxx-compiler {gcc,clang,all}] [--guess-python-version {2,3,auto,all}] [--guess-python-interpreter {cpython,pypy,all}] [--format-dos2unix] [--format-rstrip] [-G] [--no-open] [--open] [-w SECOND] [-y] [url] file positional arguments: url the URL of the problem to submit. if not given, guessed from history of download command. file optional arguments: -h, --help show this help message and exit -l LANGUAGE, --language LANGUAGE narrow down language choices if ambiguous --no-guess -g, --guess guess the language for your file (default) --no-guess-latest --guess-cxx-latest use the lasest version for C++ (default) --guess-cxx-compiler {gcc,clang,all} use the specified C++ compiler if both of GCC and Clang are available (default: gcc) --guess-python-version {2,3,auto,all} default: auto --guess-python-interpreter {cpython,pypy,all} use the specified Python interpreter if both of CPython and PyPy are available (default: cpython) --format-dos2unix replace CRLF with LF for given file --format-rstrip remove trailing newlines from given file -G, --golf now equivalent to --format-dos2unix --format-rstrip --no-open --open open the result page after submission (default) -w SECOND, --wait SECOND sleep before submitting -y, --yes don't confirm
Template file
ojにおいて、自動生成されるソースコードにはテンプレートファイルを使うことができます。
テンプレートをカスタマイズするには、下記の様なファイルを ~/.config/online-judge-tools/template/template-ext.cpp
として保存します。
<%!
import onlinejudge_template.generator.cplusplus as cplusplus
import onlinejudge_template.generator.about as about
%>\
<%
data['config']['rep_macro'] = 'rep'
data['config']['using_namespace_std'] = True
data['config']['long_long_int'] = 'll'
%>\
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
#include <tuple>
#include <cstdint>
#include <cstdio>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <deque>
#include <unordered_map>
#include <unordered_set>
#include <bitset>
#include <cctype>
using namespace std;
#define ll long long
#define ld long double
#define v vector
#define rep(i, n) for (int i = 0; i < (int)(n); ++i)
#define rep3(i, m, n) for (int i = (m); i < (int)(n); ++i)
#define rrep(i, n) for (int i = (int)(n)-1; i >= 0; --i)
#define rrep3(i, m, n) for (int i = (int)(n)-1; i >= (m); --i)
#define all(x) x.begin(), x.end()
#define rall(x) x.end(x), x.begin()
#define endl '\n'
${cplusplus.declare_constants(data)}
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
${cplusplus.return_type(data)} solve(${cplusplus.formal_arguments(data)}) {
// TODO: edit here
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
${cplusplus.read_input(data)}
auto ${cplusplus.return_value(data)} = solve(${cplusplus.actual_arguments(data)});
${cplusplus.write_output(data)}
return 0;
}
さらに下記の様なtomlを ~/.config/online-judge-tools/prepare.config.toml
として保存します。
[templates]
"main.py" = "main.py"
"main.cpp" = "template-ext.cpp"
"generate.py" = "generate.py"