はじめに
F#でAtCoderに参加するための環境を、VS Code Remote Containers等を利用して自動化しました。
- Docker, dotnet templates, Powershell module等の開発
- Docker Hub, nuget, Powershell Gallery等へのパッケージ公開
- GitHub ACtionsを利用したコンテナイメージのビルド
等の勉強も兼ねてます。
コンテナイメージ
Language Test 202001とこちらの記事を参考にしました。
対応言語:F#(.NET Core3.1)
組み込みインストール
作業簡略化のため、.netテンプレートとPowershellモジュールを作成しました。
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/ubuntu/.devcontainer/base.Dockerfile
# [Choice] Ubuntu version (use hirsuite or bionic on local arm64/Apple Silicon): hirsute, focal, bionic
# ARG VARIANT="bionic"
FROM mcr.microsoft.com/vscode/devcontainers/base:0-bionic
# install software-properties-common(add-apt-repository)
RUN apt-get update \
&& export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends software-properties-common \
&& rm -rf /var/lib/apt/lists/* \
# for pypy3
&& add-apt-repository ppa:pypy/ppa -y \
# for nodejs
&& curl -sL https://deb.nodesource.com/setup_14.x | bash - \
# for .NET and powershell
&& wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \
&& dpkg -i packages-microsoft-prod.deb \
&& rm packages-microsoft-prod.deb \
&& apt-get update \
&& apt-get -y install --no-install-recommends \
# .NET
dotnet-sdk-3.1 \
dotnet-sdk-6.0 \
# Powershell
powershell \
# Python3, PyPy3の3つの環境想定
python3.8 \
python3-pip \
pypy3 \
# node
nodejs \
# online-judge-tools用ライブラリ
time \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
# update-alternatives
&& update-alternatives --install /usr/bin/python python /usr/bin/python3.8 30 \
&& update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 30 \
&& update-alternatives --install /usr/bin/pypy pypy /usr/bin/pypy3 30 \
&& update-alternatives --install /usr/bin/node node /usr/bin/nodejs 30 \
# コンテスト補助アプリケーションをインストール
&& pip install online-judge-tools \
&& rm -rf ~/.cache/pip \
&& npm install -g atcoder-cli \
&& npm cache clean --force
SHELL ["pwsh","-command"]
# powershell セットアップ
RUN Set-PSRepository -Name PSGallery -InstallationPolicy Trusted \
&& Install-Module Pester,AngleParse,InvokeBuild,AtcoderFs.Pwsh -Scope AllUsers -Confirm:$False -AcceptLicense -Force -Verbose \
&& Install-Script -Name Invoke-Build.ArgumentCompleters -Scope AllUsers -Confirm:$False -AcceptLicense -Force -Verbose \
&& 'Import-Module AtcoderFs.Pwsh' | Add-Content $PROFILE.AllUsersAllHosts -Force
使い方
セットアップ
vscodeにremote-containers拡張機能をインストールします。
ワークスペースフォルダーに./.devcontainer/devcontainer.json
を作ります。
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.202.5/containers/cpp
{
"name": "Atcoder helper for F#",
"image": "syamorock/atcoder-fs:main",
// Set *default* container specific settings.json values on container create.
"settings": {
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-vscode.cpptools",
"ms-dotnettools.csharp",
"Ionide.Ionide-fsharp",
"alfonsogarciacaro.vscode-template-fsharp-highlight",
"ms-vscode.powershell-preview"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "dotnet tool install paket -g",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
}
開発コンテナを起動します
コンテナの初回起動時
acc
とoj
にログインします。
※それぞれ、Enter-AtCoderCli
とEnter-AtCoderOnlineJudge
というラッパー関数を作成しています。
Update-FsTemplate
を実行してテンプレート関連の初期化を行います。
Initialize-PaketDepandencies
を実行してpaket
の初期化を行います。
これで環境構築、初期設定は完了です。
コンテスト参加時の流れ
参加するコンテストのコンテストIDを使用します。
https://atcoder.jp/contests/<contestId>
例:AtCoder Beginners Selection
URL…https://atcoder.jp/contests/abs
→コンテストIDはabs
コンテスト用のソリューションを作成する
開発コンテナを開いてNew-AtCoderContest
を実行します。
New-AtCoderContest -contestId abs
コンテストID名のフォルダが生成されます。
構成は以下の通りです。
abs
│ contest.acc.json
│ abs.sln
│
├─abc088b …… 各問題ごとに1フォルダ
│ │ abc088b.fsproj
│ │ Program.fs …… 提出用F#ファイル
│ │ abc088b.draft.fsx …… ラフスケッチ用のソリューションから独立したF#スクリプト
│ │
│ ├─test
│ ├─obj
│ └─bin
│
(略)
│
└─abs.Tests …… テスト用のプロジェクト
│ abs.Tests.fsproj
│ paket.references
│ Utils.fs
│ CustomTests.fs
│ Tests.fs
│ Program.fs
│
├─obj
└─bin
問題を解く
解きたい問題のフォルダのProgram.fs
を開いてプログラムを作成します。
問題のフォルダ名の関数に処理を記述する想定です。
テスト
Test-AtCoder
を使って2種類のテストが可能です。
自動生成されたテストプロジェクトによるテスト
Test-AtCoder -FolderPath <コンテストのフォルダ>
自動生成されたテストプロジェクトを利用してコンテストプロジェクト全体のテストを実行します。
初期状態ではすべてのテストがコメントアウトされています。
Test-AtCoder ./abs/
結果
[03:45:53 INF] EXPECTO? Running tests... <Expecto>
[03:45:53 INF] EXPECTO! 0 tests run in 00:00:00.0187893 for miscellaneous – 0 passed, 0 ignored, 0 failed, 0 errored. Success! <Expecto>
実施したいテストの行のコメントアウトを解除してください。
テストが実施されます。
[03:49:13 INF] EXPECTO? Running tests... <Expecto>
[03:49:13 ERR] Sample File Tests.Welcome to AtCoder sample-2 failed in 00:00:00.0450000.
WA:{ Parent = { Title = "Welcome to AtCoder"
FolderName = "practicea"
Memory = 256
Timeout = 00:00:02 }
InFile = /workspaces/test/abs/practicea/test/sample-2.in
OutFile = /workspaces/test/abs/practicea/test/sample-2.out }
. String actual was shorter than expected, at pos 0 for expected item '4'.
expected:
456 myonmyon
actual:
at Utils.generateSampleFileTestCase@68-2.Invoke(StringWriter _arg2) in /workspaces/test/abs/abs.Tests/Utils.fs:line 81
at Microsoft.FSharp.Core.Operators.Using[T,TResult](T resource, FSharpFunc`2 action) in D:\a\_work\1\s\src\fsharp\FSharp.Core\prim-types.fs:line 4806
at Utils.generateSampleFileTestCase@67-1.Invoke(StreamReader _arg1) in /workspaces/test/abs/abs.Tests/Utils.fs:line 67
at Microsoft.FSharp.Core.Operators.Using[T,TResult](T resource, FSharpFunc`2 action) in D:\a\_work\1\s\src\fsharp\FSharp.Core\prim-types.fs:line 4806
at Utils.generateSampleFileTestCase@64.Invoke(Unit unitVar) in /workspaces/test/abs/abs.Tests/Utils.fs:line 64 <Expecto>
[03:49:13 ERR] Sample File Tests.Welcome to AtCoder sample-1 failed in 00:00:00.
WA:{ Parent = { Title = "Welcome to AtCoder"
FolderName = "practicea"
Memory = 256
Timeout = 00:00:02 }
InFile = /workspaces/test/abs/practicea/test/sample-1.in
OutFile = /workspaces/test/abs/practicea/test/sample-1.out }
. String actual was shorter than expected, at pos 0 for expected item '6'.
expected:
6 test
actual:
at Utils.generateSampleFileTestCase@68-2.Invoke(StringWriter _arg2) in /workspaces/test/abs/abs.Tests/Utils.fs:line 81
at Microsoft.FSharp.Core.Operators.Using[T,TResult](T resource, FSharpFunc`2 action) in D:\a\_work\1\s\src\fsharp\FSharp.Core\prim-types.fs:line 4806
at Utils.generateSampleFileTestCase@67-1.Invoke(StreamReader _arg1) in /workspaces/test/abs/abs.Tests/Utils.fs:line 67
at Microsoft.FSharp.Core.Operators.Using[T,TResult](T resource, FSharpFunc`2 action) in D:\a\_work\1\s\src\fsharp\FSharp.Core\prim-types.fs:line 4806
at Utils.generateSampleFileTestCase@64.Invoke(Unit unitVar) in /workspaces/test/abs/abs.Tests/Utils.fs:line 64 <Expecto>
[03:49:13 INF] EXPECTO! 2 tests run in 00:00:00.1339686 for Sample File Tests – 0 passed, 0 ignored, 2 failed, 0 errored. <Expecto>
oj t
によるテスト
スイッチパラメータ-UseOJ
で、指定したフォルダにあるプロジェクトをコンパイルしてoj t
を実行します。
Test-AtCoder -FolderPath <各問題のフォルダ> -UseOJ
`posh
Test-AtCoder ./abs/practicea/ -UseOJ
Determining projects to restore...
Restored /workspaces/test/abs/practicea/practicea.fsproj (in 253 ms).
practicea -> /workspaces/test/abs/practicea/bin/Release/netcoreapp3.1/ubuntu.18.04-x64/practicea.dll
practicea -> /workspaces/test/abs/practicea/ojTest/
[INFO] online-judge-tools 11.5.1 (+ online-judge-api-client 10.10.0)
[INFO] 2 cases found
[INFO] sample-1
[INFO] time: 0.047700 sec
[FAILURE] WA
input:
1
2_3
test
output:
(empty)
expected:
6_test
[INFO] sample-2
[INFO] time: 0.056302 sec
[FAILURE] WA
input:
72
128_256
myonmyon
output:
(empty)
expected:
456_myonmyon
[INFO] slowest: 0.056302 sec (for sample-2)
[INFO] max memory: 23.488000 MB (for sample-2)
[FAILURE] test failed: 0 AC / 2 cases
問題を解いてもう一度テストしてみます。
let practicea argv =
let a = stdin.ReadLine() |> int
let bc =
stdin.ReadLine().Split ' ' |> Array.sumBy int
let s = stdin.ReadLine()
printfn "%i %s" (a + bc) s
[<EntryPoint>]
let main argv =
practicea argv
0
テスト合格です。
PS /workspaces/test> Test-AtCoder ./abs/
[04:02:22 INF] EXPECTO? Running tests... <Expecto>
[04:02:22 INF] EXPECTO! 2 tests run in 00:00:00.0614051 for Sample File Tests – 2 passed, 0 ignored, 0 failed, 0 errored. Success! <Expecto>
PS /workspaces/test> Test-AtCoder ./abs/practicea/ -UseOJ
Determining projects to restore...
Restored /workspaces/test/abs/practicea/practicea.fsproj (in 249 ms).
practicea -> /workspaces/test/abs/practicea/bin/Release/netcoreapp3.1/ubuntu.18.04-x64/practicea.dll
practicea -> /workspaces/test/abs/practicea/ojTest/
[INFO] online-judge-tools 11.5.1 (+ online-judge-api-client 10.10.0)
[INFO] 2 cases found
[INFO] sample-1
[INFO] time: 0.087652 sec
[SUCCESS] AC
[INFO] sample-2
[INFO] time: 0.080748 sec
[SUCCESS] AC
[INFO] slowest: 0.087652 sec (for sample-1)
[INFO] max memory: 32.928000 MB (for sample-1)
[SUCCESS] test success: 2 cases
解答提出
解答が出来上がったらSubmit-AtCoderTask
で提出します。
acc submit
のラッパーです。
Submit-AtCoderTask -FolderPath <各問題のフォルダ>
課題
- Powershellモジュールのテストコードを完成させる。
- GitHub Actionsの改修
終わりに
つい先日、.NET 6、およびF# 6 がリリースされました。
「.NET 6」の最大の魅力は、パフォーマンスの向上だ。プロファイルに基づく動的な最適化(Dynamic Profile-guided Optimization、Dynamic PGO)と呼ばれる技術が採用されており、たとえばTechEmpowerのMVCベンチマークではPGOにより1秒あたりに処理できるリクエスト数が26%(51万→64万)にまで改善されたという。
Atcoderでパフォーマンスの向上したF#6、およびC#10が使える日が早く来ることを願っています。
ということでリクエストしたいのですが......
このコンテストは、言語のアップデートテスト用コンテストです
こちらのスプレッドシートにて募集を行っていた各言語のバージョンアップならびに新規追加した言語のテストを行うためのコンテストです。
リクエストするにはシート1に行を追加すれば良いのでしょうか……?