8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Emacsで競技プログラミング | ojのラッパーパッケージを書いた話

Last updated at Posted at 2020-05-23

Intro

競技プログラミングコミュニティが年々大きくなっています。日本ではAOJAtCoder、世界ではTopCoderCodeforcesなどのオンラインジャッジが人気です。

オンラインジャッジはコンパイル、実行を行い、あらかじめ用意されているテストケースをパスするかどうか確認してくれます。

プログラミング初心者のプログラミング導入にも最適ですし、プログラマの力試し/筋力向上としても、とても良い教材です。

ただ、サンプルケースをダウンロードしたり、解答をいい感じに管理したりと定型作業も多いです。

ojoj-templateというツールで、その定型作業を自動化できます。そのojをEmacsと連携させた oj.el というパッケージを紹介します。

0211d0a3-1e4b-431a-a898-00571c8a16e4.gif

なお、この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 ojoj へのパスを返せば、Emacsからojを利用できます。出力がない場合は、Q&Aを参照するか、exec-path-from-shellを導入することを検討して下さい。

oj-prepare

M-x oj-prepare はこのパッケージを使うためのファーストステップです。

このコマンドは次のようなフォーマットを受理します。

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-testcompiletest を行います。

もしテストケースをパスしたら、 *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* バッファで使われるシェル (default shell-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 のオプション引数 (default nil)

    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 のオプション引数 (default nil)

    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 のオプション引数 (default nil)

    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"
8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?