🔧

VSCodeでの競プロ環境構築 (Julia)

2023/08/09に公開

この記事では、Julia言語でVSCode上に競技プログラミング環境を構築し、AtCoderに簡単に提出できる状態を作ります。
AtCoder以外のコンテストサイト等についてはあまり説明しません。

環境としてはWSLを想定していますが、そうでなくても殆ど問題ないと思います。
また、この記事ではWSLのインストールやVSCodeのインストール等については説明しません。

競プロ用ディレクトリの作成

適当な競プロ用のディレクトリ(contests とか)を作成します。
特に言及しない限り、この記事内では競プロ用ディレクトリをカレントディレクトリとします。

Juliaup のインストール

Juliaupはバージョン管理ツールで、Juliaのバージョン切り替えを簡単にできるものです。
Pythonでいうところのpyenvのようなものです。
インストールはこれに書いてある通りに、

curl -fsSL https://install.julialang.org | sh

を実行します。
出てくる文言を読んで、特に問題が無ければ、 Proceed with installation を選択して、インストールをします。
少し待つと、インストールが終わり、環境変数 PATH を再読み込みしてください、と出ると思うので、 .bashrc なり .profile なりを再読み込みします。

Juliaupがきちんとインストール出来ているかを確認するために以下のコマンドを実行します。

juliaup status

うまくいっていれば、下記のような表示が出て、最新のJuliaがインストールされていることがわかるはずです。

 Default  Channel  Version                Update
-------------------------------------------------
       *  release  1.9.2+0.x64.linux.gnu

念のため、Juliaの方もきちんとPATHが通っているか確認します。

julia --version

これで、上記のバージョンと同じもの(この記事の場合であれば1.9.2)が表示されていれば、問題ありません。

AtCoderでのJuliaのバージョンや使用可能なパッケージはここに書かれているので、これと同じ環境にすると、バージョン違いやパッケージの有無で WA になりにくくすることができると思います。

VSCodeにおけるJuliaの設定

拡張機能 Julia のインストール

拡張機能 Juliaをインストールをして、適当なJuliaのファイル(拡張子が .jl)を開き、下の青いステータスバーに Julia env: v1.9 などと表示されていれば大丈夫です。

確認のために適当なコードをファイルに保存して実行します。
とりあえず、Juliaのコード例に載っている、マンデルブロ集合の描画プログラムを試します。

main.jl
function mandelbrot(a)
    z = 0
    for i=1:50
        z = z^2 + a
    end
    return z
end

for y=1.0:-0.05:-1.0
    for x=-2.0:0.0315:0.5
        abs(mandelbrot(complex(x, y))) < 2 ? print("*") : print(" ")
    end
    println()
end

# Taken from: https://rosettacode.org/wiki/Mandelbrot_set#Julia

Alt+Enter で実行することができるので、実行すると以下のようにマンデルブロ集合が出力されます。

出力



                                                           **
                                                         ******
                                                       ********
                                                         ******
                                                      ******** **   *
                                              ***   *****************
                                              ************************  ***
                                              ****************************
                                           ******************************
                                            ******************************
                                         ************************************
                                *         **********************************
                           ** ***** *     **********************************
                           ***********   ************************************
                         ************** ************************************
                         ***************************************************
                     *****************************************************
 ***********************************************************************
                     *****************************************************
                         ***************************************************
                         ************** ************************************
                           ***********   ************************************
                           ** ***** *     **********************************
                                *         **********************************
                                         ************************************
                                            ******************************
                                           ******************************
                                              ****************************
                                              ************************  ***
                                              ***   *****************
                                                      ******** **   *
                                                         ******
                                                       ********
                                                         ******
                                                           **




フォーマッタの設定

Shift+Alt+F で整形できるようになっていますが、保存時に整形されるようにすると便利なので、設定をしていきます。
.vscode ディレクトリ内の .settings.json"editor.formatOnSave": true を追記します。
これにより Ctrl+S で整形されるようになります。

フォーマッタのルールを調整したい場合は .JuliaFormatter.toml を作成し、これに従って調整をします。
例としては以下のようにすればよいです。

.JuliaFormatter.toml
always_for_in = true
remove_extra_newlines = true

online-judge-tools の設定

ここでは online-judge-tools/ojonline-judge-tools/template-generator の2つを導入します。
これらについての説明は、以下のURLを見てください。

導入方法についてもそれぞれの GitHub に書いてありますが、この記事でも一応記述しておきます。
これらのツールの導入のために pippipenv などを使えるようにしてください。
インストールについての説明は省略します。

また、この記事では pipenv を使ってインストールするので、それ以外の場合は適宜読み替えてください。

oj のインストール

下記のコマンドでインストールします。

pipenv install online-judge-tools

oj でのログイン

ログインをしなければ提出などができないので、設定していきます。

pipenv run oj login https://atcoder.jp

上記のコマンドを入力すると、AtCoderでのユーザー名とパスワードを求められるので入力します。

template-generator のインストール

基本的にはここに従います。
下記のコマンドでインストールします。

pipenv install online-judge-template-generator

このツールを使うことで、問題に合わせた入出力が書かれたファイルを自動生成できるのですが、そのような設定を Julia でするには、かなりカスタマイズが必要なため、これは行いません。どの問題でも同じファイルを用意することで対応します。

VSCodeにおけるonline-judge-toolsの設定

AtCoder以外の方法も示した方が良いと思い、Codeforcesの設定もしていますが、CodeforcesではJuliaが使えなさそうです。
後からこのことに気がつきましたが、一応残しておきます。

シェルスクリプトの作成

まず scripts ディレクトリを作成し、その中に submit.sh という名前でファイルを作成します。
そのファイルに以下のように書きます。

submit.sh
#!/bin/bash

submit_file=$1 # relative file path (${service_name}/${contest_id}/${problem_id}/main.jl)

service_name=${submit_file%%/*}
contest_id=$(basename ${submit_file%/*/*})
problem_id=$(basename ${submit_file%/*})

case "$service_name" in
  "AtCoder" )
    problem_url="https://atcoder.jp/contests/${contest_id}/tasks/${problem_id}"
    ;;
  "Codeforces" )
    problem_url="https://codeforces.com/contest/${contest_id}/problem/${problem_id}"
    ;;
esac

pipenv run oj s -y ${problem_url} ${submit_file}

その後 chmod 755 scripts/submit.sh を実行してパーミッションを変更します。

同じように scripts ディレクトリの中に dltest.sh という名前でファイルを作成し、以下のように記述して、パーミッションを変更します。

dltest.sh
#!/bin/bash

problem_dir=$1 # relative directory path (${service_name}/${contest_id}/${problem_id})

service_name=${problem_dir%%/*}
contest_id=$(basename ${problem_dir%/*})
problem_id=${problem_dir##*/}

test_dir=${problem_dir}/test

case "$service_name" in
  "AtCoder" )
    problem_url="https://atcoder.jp/contests/${contest_id}/tasks/${problem_id}"
    ;;
  "Codeforces" )
    problem_url="https://codeforces.com/contest/${contest_id}/problem/${problem_id}"
    ;;
esac

if [ ! -e ${test_dir} ]; then
    pipenv run oj d -d ${test_dir} ${problem_url}
fi

テンプレート等の作成

~/.config/online-judge-tools/ ディレクトリに prepare.config.toml を以下の内容で作成します。

prepare.config.toml
contest_directory = "{service_name}/{contest_id}"
problem_directory = "{problem_id}"

[templates]
"main.jl" = "main.jl"

そして ~/.config/online-judge-tools/template/ ディレクトリに main.jl を作成します。
これがテンプレートファイルになるので、使いやすいように変更してください。

main.jl
function main()
    n = parse(Int, readline())
end

main()

タスクの登録

.vscode ディレクトリに tasks.json を作成し、以下のように記述して保存します。

tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      "type": "shell",
      "label": "download test cases",
      "command": "${workspaceFolder}/scripts/dltest.sh",
      "args": [
        "${relativeFileDirname}"
      ]
    },
    {
      "type": "shell",
      "label": "do oj-prepare",
      "command": "pipenv",
      "args": [
        "run",
        "oj-prepare",
        "${input:service_url}/${input:contest_id}"
      ]
    },
    {
      "type": "shell",
      "label": "do oj test",
      "command": "pipenv",
      "args": [
        "run",
        "oj",
        "t",
        "-c",
        "julia ${file}",
        "-d",
        "${fileDirname}/test"
      ],
      "dependsOn": [
        "download test cases"
      ],
      "group": {
        "kind": "test",
        "isDefault": true
      },
      "presentation": {
        "focus": true
      }
    },
    {
      "type": "shell",
      "label": "submit",
      "command": "${workspaceFolder}/scripts/submit.sh",
      "args": [
        "${relativeFile}"
      ]
    }
  ],
  "inputs": [
    {
      "id": "service_url",
      "type": "pickString",
      "description": "Contest site URL",
      "options": [
        "https://atcoder.jp/contests",
        "https://codeforces.com/contest"
      ],
      "default": "https://atcoder.jp/contests/"
    },
    {
      "id": "contest_id",
      "type": "promptString",
      "description": "Contest ID"
    }
  ]
}

Ctrl+Shift+P を押して Tasks: Run Task を選択すると、登録したタスクが出てきて、選択するだけで実行できるようになっています。
一応、使用するタスクを説明しておくと、

  • do oj-prepare: コンテストのディレクトリ作成とテストケースのダウンロード
  • do oj test: 入力例などによるテスト
  • submit: コードの提出

となっています。
他のタスクは直接使用することはないと思います。
始まっていないコンテストで do oj-prepare はできないので注意してください。

タスクをショートカット一発で実行できるようにする場合は keybindings.json に適当に追記すればよいです。

あとは実際に問題を解いて提出してみてください。

うまくいかないとき

この設定をしてしばらく遊んでいると、何個か問題が出てきたので追記しておきます。
随時更新する予定です。

REPLの起動時にpython環境を読み込んでしまう

venv環境等を使っていると、ターミナルの起動時に pyenv shell x.y.z みたいなのが出てきて、これが標準入力を邪魔するときがあります。
この読み込みを無くすために settings.json"python.terminal.activateEnvironment": false を追記します。

REPL起動時の最初の一行目が無視される

Inline evaluation: readline() is broken で議論されています。
どうにかするのは難しいそうなので Alt+Enter の代わりに Ctrl+F5 を使うといいかもしれません。
REPLを使う理由が強くなければ基本的にこの対策でよさそうです。

Discussion