はじめに
AtCoderに先ずは慣れようとおもって、Pythonで何度か参加してみました。
調べてみると、コンテストの参加を手助けしてくれるソフトがいくつかあるんですね。
そのなかで、atcoder-toolsを使って便利だったので、これをElixirでも使えるようにしてみました。
atcoder-toolsの紹介
どんな機能があるかというと
- AtCoderへのログイン,入出力例データなどの抽出
- 枝刈り探索による高精度・高速な入力フォーマット解析 (ARC、ABC、AGCについては約9割ほど)
- 問題文中に含まれるMOD値、YES/NO文字列、誤差ジャッジのための誤差値等の定数値抽出
- サンプルのローカルテスト機能
- 誤差ジャッジに対応 by @chaemon
- コード提出機能
- 入力フォーマット解析結果や抽出した定数値を用いたテンプレートからのコード自動生成(以下の表に記載されている言語をサポートしています)
(サイトからの転記)
と、とても便利です。
問題画面からコピペしなくてもテストできるのはとても便利です。
インストール方法
Elixir対応版は、本家にはまだ含まれていません。
Forkしたものを公開したので、
pip installでGitのURL指定でインストールすれば使えると思います。
Python 3.6以上が動作する必要があります。
本家の資料を基にインストールしてください。
pip install atcoder-toolsの部分は、以下のようにGitのURL指定にします。
pip install git+https://github.com/masahiro-999/atcoder-tools.git@elixir
atcoder-toolsコマンドを実行したときに、markupsafe
に関するエラーが発生した場合は、pip install MarkupSafe==2.0.1
でバージョン指定すると解決します。
こんなエラー
```bash (atcoder) masa@WINPC1:~$ atcoder-tools Traceback (most recent call last): File "/home/masa/.venv/atcoder/bin/atcoder-tools", line 5, in from atcodertools.atcoder_tools import main File "/home/masa/.venv/atcoder/lib/python3.10/site-packages/atcodertools/atcoder_tools.py", line 8, in from atcodertools.tools.envgen import main as envgen_main File "/home/masa/.venv/atcoder/lib/python3.10/site-packages/atcodertools/tools/envgen.py", line 14, in from atcodertools.client.atcoder import AtCoderClient, Contest, LoginError, PageNotFoundError File "/home/masa/.venv/atcoder/lib/python3.10/site-packages/atcodertools/client/atcoder.py", line 15, in from atcodertools.common.language import Language File "/home/masa/.venv/atcoder/lib/python3.10/site-packages/atcodertools/common/language.py", line 4, in from atcodertools.codegen.code_generators import cpp, elixir, java, rust, python, nim, d, cs, swift, go, julia File "/home/masa/.venv/atcoder/lib/python3.10/site-packages/atcodertools/codegen/code_generators/cpp.py", line 2, in from atcodertools.codegen.template_engine import render File "/home/masa/.venv/atcoder/lib/python3.10/site-packages/atcodertools/codegen/template_engine.py", line 5, in from jinja2 import Environment File "/home/masa/.venv/atcoder/lib/python3.10/site-packages/jinja2/__init__.py", line 12, in from .environment import Environment File "/home/masa/.venv/atcoder/lib/python3.10/site-packages/jinja2/environment.py", line 25, in from .defaults import BLOCK_END_STRING File "/home/masa/.venv/atcoder/lib/python3.10/site-packages/jinja2/defaults.py", line 3, in from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401 File "/home/masa/.venv/atcoder/lib/python3.10/site-packages/jinja2/filters.py", line 13, in from markupsafe import soft_unicode ImportError: cannot import name 'soft_unicode' from 'markupsafe' (/home/masa/.venv/atcoder/lib/python3.10/site-packages/markupsafe/__init__.py) ```私の使用している、テンプレートファイルと、.atcodertools.tomlを参考に載せておきます。
defmodule Main do
import Bitwise
def next_token(acc \\ "") do
case IO.getn(:stdio, "", 1) do
" " -> acc
"\n" -> acc
x -> next_token(acc <> x)
end
end
def input(), do: IO.read(:line) |> String.trim()
def ii(), do: next_token() |> String.to_integer()
def li(), do: input() |> String.split(" ") |> Enum.map(&String.to_integer/1)
{% if mod %}
MOD = {{ mod }}
{% endif %}
{% if yes_str %}
YES = "{{ yes_str }}"
{% endif %}
{% if no_str %}
NO = "{{ no_str }}"
{% endif %}
{% if prediction_success %}
def solve({{ actual_arguments }}) do
end
{% else %}
def solve() do
end
{% endif %}
def main() do
{% if prediction_success %}
{{input_part}}
solve({{ actual_arguments }})
{% else %}
# Failed to predict input format
{% endif %}
end
end
[codestyle]
indent_type='space' # 'tab' or 'space'
indent_width=4
template_file='~/atcoder-workspace-ex/my_template.ex'
workspace_dir='~/atcoder-workspace-ex/'
lang='elixir' # Check README.md for the supported languages.
#code_generator_file="~/custom_code_generator.py"
#code_generator_toml="~/atcoder-workspace/code_generator-ex.toml"
[postprocess]
#exec_on_each_problem_dir='clang-format -i ./*.cpp'
#exec_on_contest_dir='touch CMakeLists.txt'
[compiler]
#compile_command='g++ main.cpp -o main -std=c++17'
#compile_only_when_diff_detected=true
[tester]
compile_before_testing=true
compile_only_when_diff_detected=true
timeout_adjustment=1.2
[etc]
download_without_login=false
parallel_download=false
save_no_session_cache=false
skip_existing_problems=false
in_example_format="in_{}.txt"
out_example_format="out_{}.txt"
使い方(コード生成)
abc139コンテストの場合
atcoder-tools gen abc139
main.exのひな型が生成されます。
defmodule Main do
def input(), do: IO.read(:line) |> String.trim()
def ii(), do: input() |> String.to_integer()
def li(), do: input() |> String.split(" ") |> Enum.map(&String.to_integer/1)
def solve(a, b) do
end
def main() do
a = ii()
b = ii()
solve(a, b)
end
end
この問題は、二つの整数a,bが与えられるので、solveに与える引数もa,bとなっています。
オリジナルのatcoder-toolsでは、問題文の表記と同じA,Bと大文字の変数名が使われますが、Elixirでは大文字の変数名が使えないのですべて小文字に変換されます。
入力の値のプログラムの自動生成は、Elixirの対応はまだうまく行かない場合もあるので、あてにせず確認して下さい。
この課題のA Bが同じ行になっている場合、私の設定ではうまくいかず、
[a,b] = li()
とする必要があります。
テスト
solve関数を実装してテストしてみます。
defmodule Main do
def input(), do: IO.read(:line) |> String.trim()
def ii(), do: input() |> String.to_integer()
def li(), do: input() |> String.split(" ") |> Enum.map(&String.to_integer/1)
def get_ans(num_tap, socket, a, b) do
cond do
socket >= b -> num_tap
num_tap == 0 -> get_ans(1, a, a, b)
:true -> get_ans(num_tap+1, socket+a-1, a, b)
end
end
def solve(a,b) do
ans = get_ans(0, 1, a, b)
IO.puts(ans)
end
def main() do
[a, b] = li()
solve(a, b)
end
end
main.exのあるディレクトリで次のコマンドを実行すると、テストが実行されます。
$ atcoder-tools test
次のような感じで結果が表示されます。
問題のページにある例以外の値をテストした場合は、in_*.txt
とout_*.txt
のファイルを追加すれば任意の値でテストできます。
提出
提出もでます。
$ atcoder-tools submit
提出前に、テストを実行してOKの場合のみ提出する設定がデフォルトになっています。
この例では、すでに提出済みなので、エラーになっています。二度目の提出の場合は-u
を付けます
まとめ
これで問題の回答に専念できる環境ができました。
ElixirでAtCoderに参加してみましょう!
2023/9/23
pip installのパスを変更しました。