atcoder-toolsとVSCodeでPythonのAtCoder環境を整えた話

# はじめに
競技プログラミングを始め前々から気になっていた[atcoder-tools](https://github.com/kyuridenamida/atcoder-tools/tree/stable?tab=readme-ov-file#atcoder-tools)を導入しました。革命です。より使いやすくやるようVSCodeによるショートカット作成までの備忘録です。atcoder-toolsには様々なコマンドがありますが、最低限の内容でまとめています。
また、pythonの言語仕様に合わせて多少の改造をしました。

# 導入〜基本的な理解
[こちらの記事](https://qiita.com/noraworld/items/17cd1960b0cd3648f599)を参考にしました。このセクションではその際につまづいた点をまとめているので、本記事に軽く目を通した後にあちらの記事を見ていただけるとわかりやすいかと思います。
### インストールについて
[公式のREADME](https://github.com/kyuridenamida/atcoder-tools/tree/stable?tab=readme-ov-file#how-to-install)にもありますが、atcoder-toolsはpython3系で動作するため`pip3`(python3系用のpip)によるインストールが望ましいです。
```shell
$ pip3 install atcoder-tools
```
### 使用する際の流れについて
atcoder-toolsは以下の流れで使用します。意識しておくと理解しやすいはずです。
1. コンテストのディレクトリを作成する
1. 解く問題のフォルダに移動する
1. コードテストや提出を行う
1. 次の問題へ進む(2に戻る)
 ### 設定ファイル`.atcodertools.toml`について
 `~/.atcodertools.toml`(=Macなら `/Users/username/.atcodertools.toml`)を作成します。最初から存在してるわけではありません。

# より使いやすくする
私的感覚でより使いやすするために行ったことを残します。

## atcoder-tools genのオプション省略
先ほどの記事と内容が被りますが、`~/.atcodertools.toml`を編集し、デフォルトのオプションを変更しました。
```.atcodertools.toml
[codestyle]
workspace_dir = '~/Your/atcoder/dir/'
lang = 'python' 
```
これで以下のコマンドでディレクトリを作成できます。
```shell
$ atcoder-tools gen abc000
```

## templateを自作
今まで使っていた自作のテンプレートと、atcoder-toolsのテンプレートを合わせました。テンプレートの詳しい仕様は[README](https://github.com/kyuridenamida/atcoder-tools/tree/stable?tab=readme-ov-file#%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%81%AE%E4%BE%8B)を読むとわかりやすいです。

`~/Your/atcoder/dir/template.py`を作成しました。


<details><summary>template.py</summary>

```python
#!/usr/bin/env python3
{% if prediction_success %}
from sys import stdin
{% else %}
Integer = lambda:int(input())
Ints = lambda :map(int,input().split())
IntsList=lambda :list(map(int,input().split()))
String = lambda :input()
Strings = lambda :input().split()
StringsList = lambda : list(input().split())
{% endif %}
{% if yes_str %}
yes = lambda : print("{{ yes_str }}")
{% endif %}
{% if no_str %}
no = lambda : print("{{ no_str }}")
{% endif %}
{% if yes_str and no_str %}
yesno = lambda ans:print("{{ yes_str }}" if ans else "{{ no_str }}")
{% endif %}
{% if mod %}
MOD = {{ mod }}  # type: int
{% endif %}
from sys import setrecursionlimit
from collections import defaultdict
setrecursionlimit(10**6)
{% if prediction_success %}

def solve({{ formal_arguments }}):
    
{% endif %}


def main():
    {% if prediction_success %}
    def iterate_tokens():
        for line in stdin:
            for word in line.split():
                yield word
    tokens = iterate_tokens()
    {{ input_part }}
    solve({{ actual_arguments }})
    {% else %}
    # Failed to predict input format
    pass
    {% endif %}

if __name__ == '__main__':
    main()

```

</details>

注意点として、ファイルの先頭に
```
#!/usr/bin/env python3
```
を記述しないと、atcoder-toolsが実行可能ファイルとして判定できずエラーが起きます。

これをテンプレートとして使用するので`~/.atcodertools.toml`に追記します。
```.atcodertools.toml
[codestyle]
workspace_dir = '~/Your/atcoder/dir/'
lang = 'python' 

#追記部分
template_file='~/Your/atcoder/dir/template.py'
```

## ショートカットの作成
現在使うコマンドを確かめてみます。
```shell
# どうにもならなそう
$ atcoder-tools gen abc000

#ショートカットがあると便利そう
$ cd problem/dir
$ atcoder-tools test
$ atcoder-tools submit
```
ショートカットの機能は

1. 編集中のファイルを保存
1. problemのディレクトリに移動
1. コマンドを入力・実行する

というものにします。ファイルの保存を忘れてエラーが出るのはなんとも無駄です。
### 1. multi-commandのインストール
VSCodeの標準機能では一つのショートカットで複数のコマンドを連続で実行できないため、[multi-command](https://marketplace.visualstudio.com/items?itemName=ryuta46.multi-command)という拡張機能をインストールします。

### 2. コマンドの設定
`cmd+shift+p`でコマンドパレットを開きます。`Preferences: Open User Settings(JSON)`と入力して出てきたファイルを選択し、以下の内容を追記します。
```json-doc
{
    //追記ここから
    "multiCommand.commands": [
        {
            "command": "multiCommand.saveAndRun",
            "sequence": [
                "workbench.action.files.save",
                {
                    "command": "workbench.action.terminal.sendSequence",
                    "args": {
                        "text": "cd ${fileDirname}\u000Datcoder-tools test\u000D"
                    }
                }
            ]
        },
        {
            "command": "multiCommand.saveAndSubmit",
            "sequence": [
                "workbench.action.files.save",
                {
                    "command": "workbench.action.terminal.sendSequence",
                    "args": {
                        "text": "cd ${fileDirname}\u000Datcoder-tools submit -u\u000D"
                    }
                }
            ]
        }
    ],
    //追記ここまで
}
```

### 3. ショートカットの設定
押しやすいという安直な理由で`cmd+d`,`cmd+shift+d`にショートカットを追加します。
コマンドパレットから`Preferences: Open Keyboard Shortcuts(JSON)`を開き、リストに以下の内容を追加します
```json-doc
[
    //追記ここから
    {
        "key": "cmd+d",
        "command":"multiCommand.saveAndRun"
    },
    {
        "key": "cmd+shift+d",
        "command": "multiCommand.saveAndSubmit"
    },
    //追記ここまで
]
```

これでショートカットの作成が終わりました。
`atcoder-tools submit`はコードテストを行い`AC`ならば提出してくれます。逆に言えば`WA`や`TLE`があれば提出しないので、天邪鬼にこちらを使う気がします。
ちなみにA問題を解いた後は次のコマンドでB問題のソースを開けます。有用な気もしますが、VSCode使ってる時点である程度マウス操作前提でもあるので使うかどうかわかっていません。
```shell
$ cd ../b
$ open main.py
```

:::note warn
ここからの変更はファイルを直接編集するため、次の可能性があります
1. atcoder-toolsの更新時に元に戻る（きっとCPythonで提出することになる）
1. プルダウンの言語名変更時に提出できなくなる
:::
## 自動inputの型推定を修正
`list[int]`のような型が、 `"List[int]"`のように表記されています。なんとも不思議です。
PRしようか迷いましたが、恐らくpythonのより多くのバージョンに対応させるためこの状態なのだろう（調べていない）と考え自分の環境でのみ修正します。
1. atcoder-toolsがインストールされているディレクトリを開きます(これはpyenvなどの環境によって違うのでご自身で調べてください)。`atcodertools`というフォルダ名のはずです
1. 以下のファイルを開きます
    ```
    /atcodertools/codegen/code_generators/universal_generator/python.toml
    ```

1. `\"List[{type}]\"`を`list[{type}]`に置換します。VSCodeの置換機能を使うと楽です
1. `\"List[List[{type}]]\"`を`list[list[{type}]]`に置換します

これで型の修正ができました。コーディング時に型をしっかり定義してあると楽な場合も多いので、よりストレスフリーになりました。

## 提出の際の言語をPyPyに変更
[こちらのサイト](https://github.com/otokichi3/atcoder)を参考に変更しました。しかしサイト通りだとCpythonが先にマッチしうまくいきません。

1. 以下のファイルを開きます
    ```
    atcodertools/common/language.py
    ```
1. 次のように編集します
```diff_python
PYTHON = Language(
    name="python",
    display_name="Python",
    extension="py",
-   submission_lang_pattern=re.compile(".*Python \\(3.*|.*Python \\(CPython 3.*"),
+   submission_lang_pattern=re.compile(".*Python \\(PyPy.*"),
    default_code_generator=python.main,
    default_template_path=get_default_template_path('py'),
    compile_command="python3 -mpy_compile {filename}.py",
    test_command="python3 {filename}.py",
    exec_fi
```




# 最後に
コードテストと提出の自動化が最大のメリットのように感じます。自動inputは今後使い続けるかわかりませんが、とりあえず使ってみようと思います。



#### 参考

https://github.com/kyuridenamida/atcoder-tools

https://qiita.com/noraworld/items/17cd1960b0cd3648f599