競プロコンテスト環境などを自動化しました

Mar 4, 2020 18:08 compro Development

お久しぶりです。ますぐれです。卒論が終わり、現在は一人暮らしの準備中です。 本日は競技プログラミングのあれこれを色々自動化したので、その環境構築の内容について書いていきたいと思います。

自動化したこと

  • #includeした自作ライブラリを提出用コードに展開する
  • ライブラリの自動テスト環境
  • コンテスト中のサンプルテスト/提出

手元のOSはwindows10ですが、基本的にはWSLを使っています。競プロ環境もWSLに載せています。 では、早速順番に自動化の中身について見に行きましょう。

#includeした自作ライブラリを提出用コードに展開する

そもそも、自動化を始めたきっかけはこれです。従来の競プロ環境では、ライブラリとは別にVSCodeのコードスニペット用のファイルも管理していたのですが、これが気持ち悪いのでやめたいなあというところがスタート地点でした。

そんなことをツイッターでつぶやいたところ、熨斗袋さんがonline-judge-verify-helperの存在を教えてくれました。

これは本当はライブラリのCIを回すためのツールで、この中にoj-bundleというプログラムが入っています。これがまさにほしいものだったので導入しました。コマンド一発で簡単です。

これを入れてoj-bundle hoge.cpp > fuga.cppとすると、hoge側で#includeされた自作ライブラリがfugaにババーンと展開されるというわけです。最高です。やっとスニペットとダブルで管理する苦しみから解放されました。

……と思ったのですが、これだけでは不十分です。というのも、自分はライブラリを書いているファイルと同じところにmainを作ってverifyしていたので、これも付いてきてしまうわけですね。そのためライブラリ自体に手を入れる必要が出てきました。

ライブラリの自動テスト環境

その問題を解決するため、このままテスト環境もいい感じにすることを決意しました。幸いさっきインストールしてきたonline-judge-verify-helperはまさしくそれのためのツールなのでこのまま利用します。

CIの環境としてはGithub Actionsを選択しました。今まで使ったことがなかったのでテストも兼ねています。 詳しい使い方は公式サイトを見るとわかると思います。

特にハマるところはないんですが、手元で普通にテストを回すとclang++とg++両方が必要というところでぼくはハマりました。clang++使ってないのにclang++入れるんかみたいな気持ちになっていたのですが、環境変数CXXに自分の使うコンパイラを指定してあげることで解消できることを開発者の@kmykさんに教えていただきました。

Skip execution with clang++ if clang++ doesn’t exist · Issue #173 · kmyk/online-judge-verify-helper

これぼくがツイッターでつぶやいた数時間後にはissueできててめっちゃ感動しました。(2020/05/03追記: 現在はツールの方にアプデが入ったので指定するやり方はdeprecatedになりました)

閑話休題。ということで、手元で実行するときはCXX=g++ oj-verify runみたいにしてあげると良さそうです。使い勝手もよくて楽しいです。

oj-verifyの大まかな使い方は、***.test.cppみたいなファイルを用意して、そこに#define PROBLEM ${問題のURL}と書いておくと、そのファイルのプログラムがその問題に対して実行されるという感じです。C++以外にも対応しています。詳しいことは公式レポジトリへ。

GitHub Actionsについても公式に書いてありますが、ぼくはこんな感じにして運用しています。こっちもかなり使いやすくてよいですね。

name: verify

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Set up Python
      uses: actions/setup-python@v1
      with:
        python-version: 3.8

    - name: Install dependencies
      run: pip3 install -U git+https://github.com/kmyk/online-judge-verify-helper.git@master

    - name: Run tests
      env:
        CXX: g++
      run: oj-verify run --tle 10

コンテスト中のサンプルテスト/提出

人間だんだんと欲が出てくるもので、ここまできたらコンテスト中にコマンドラインから提出できたらいいなあとなりました。bundleとか忘れそうですし、自動化したいわけです。

ここら辺についても調べていると、online-judge-toolsというものとatcoder-cliというツールを発見します。前者はonline-judge-verify-helperと同じ方が開発しているものです。後者はそれをAtCoder用に使いやすくしたWrapperです。(でもnodeで動いているのでnodeの環境が別途必要です)

これ使えば一瞬で出来るなと思ってやってみたんですが、WSLだと提出後にテキストブラウザが起動したり、提出の度に確認を求められたりしてかなり面倒でした。なので、atcoder-cliはコンテストフォルダ作成用と割り切って、提出/テスト部分は自前で作ることにしました。

とはいっても、雑にやればかなり簡単なシェルスクリプトを書くだけでサクッと自動化できます。atcoder-cliが作成するフォルダはいい感じにカスタマイズできるので、作成するたびにそれ用のシェルスクリプトとテンプレートファイルを置くように設定しました。

シェルスクリプトはこんな感じです。コンテストフォルダはabc***/a/template.cppみたいな感じで1問題1フォルダみたいな構成になっています。abc***の中に入ってそれぞれ提出したい問題番号(aとかbとか)を引数に渡して実行します。

#!/bin/bash

basename `pwd`
cd $1
g++ template.cpp && oj test
#!/bin/bash

contest=$(basename `pwd`)
cd $1
oj-bundle template.cpp > submit.cpp
oj s "https://atcoder.jp/contests/${contest}/tasks/${contest}_$1" submit.cpp --no-open -y

こうすることでコンテスト中はもうコマンドラインだけですべてが完結するようになりました。--no-openを付けることでブラウザが立ち上がるのを拒否し、-yで毎回提出の際に間違っていないか確認を求められることがなくなります。

終わりに

実は、ここで書いたシェルスクリプトはatcoder-cliが内部的に呼ぶojにオプションを渡せるようにしてくれると不要になります。ちょっとコードリーディングしてきたのですが、渡せなさそうだったので仕方がなくこういうスクリプトを書いているという感じです。アプデを期待しています。

上のシェルスクリプトなんですが、フォルダの名前から提出先URLを推測しているので、ARCに併設されていたABCではうまく動きません。最近はこういうコンテストもないし大丈夫かなあと思ってこんな感じで妥協しています。

こういう自動化とか楽しいので、まだやったことないという方がいらっしゃれば是非取り組んでみましょう! 身近な作業の自動化は労力の割にめっちゃ幸せになれることが多いのでぜひに。