実現したいこと
Visual Studio Codeにおいてショートカット一つで、テストまたは提出を行いたいです。
また、言語に依らずに提出できるようにしたいです。
環境
OS : Windows 11
コマンドライン : PowerShell
IDE : Visual Studio Code
目次
online-judge-toolsインストールする
PowerShellのスクリプトを作成する
task.jsonの設定をする
ショートカットの設定をする
方法
online-judge-toolsインストールする
pip install online-judge-tools
これでインストールできます
PowerShellのスクリプトを作成する
以下のようなスクリプトを作成しました。
function Test {
param (
[Parameter(Mandatory=$true)]
[string]$test_directory,
[Parameter(Mandatory=$true)]
[string]$atcoder_url,
[Parameter(Mandatory=$true)]
[string]$solved_file_extension
)
# Check if the test directory already exists
if (Test-Path $test_directory -PathType Container) {
Write-Host "Test directory already exists. Skipping download."
} else {
# Download the test cases for the specific AtCoder problem
oj dl -d $test_directory $atcoder_url
}
if ($solved_file_extension -eq "cpp") {
# Compile the C++ code using g++ with specified options and output directory
$compiler_options = "-std=c++20 -g -Wall -Wextra -Wshadow -Wfloat-equal -Wno-char-subscripts -ftrapv -fno-omit-frame-pointer -fno-sanitize-recover -I C:\ac-library"
$compiled_output = Join-Path (Get-Item $solved_file_path).Directory "$solved_file_name.out"
Write-Host "Output executable path: $compiled_output"
$command = "g++ $compiler_options -o $compiled_output $solved_file_path"
# Execute the compilation command
Invoke-Expression $command
# Check if the compilation was successful
if($LASTEXITCODE -ne 0) {
Write-Host "Compilation failed."
exit
}
# Run tests using the compiled C++ executable
oj t -c $compiled_output -d $test_directory --ignore-spaces
# Remove the compiled executable after testing
Remove-Item $compiled_output -Force
} elseif ($solved_file_extension -eq "py") {
# Run tests using the provided Python script
oj test -c "python $solved_file_path" -d $test_directory --ignore-spaces
} else {
Write-Host "Unsupported file extension: $solved_file_extension"
}
}
function Submmit{
param (
[Parameter(Mandatory=$true)]
[string]$atcoder_url,
[Parameter(Mandatory=$true)]
[string]$solved_file_path,
[Parameter(Mandatory=$true)]
[string]$solved_file_extension
)
if ($LASTEXITCODE -eq 0) {
$result = "OK"
} else {
$result = "NG"
}
Write-Host "Result: $result"
if($result -ne "OK") {
exit
}
if($solved_file_extension -eq "cpp"){
$language_code = 5001 # 5001 (C++ 20 (gcc 12.2))
}elseif($solved_file_extension -eq "py"){
$language_code = 5078 # 5078 (Python (PyPy 3.10-v7.3.12))
}else{
Write-Host "Unsupported file extension: $solved_file_extension"
exit
}
oj s $atcoder_url $solved_file_path -y -l $language_code
}
#####################################################################
# Get the file path from the command line arguments
$solved_file_path = $Args[0] # Example: .\158\a_re.py
Write-Host "slolved_file_path : $solved_file_path"
$is_submit = [System.Convert]::ToBoolean($Args[1])
Write-Host "is_submit : $is_submit"
# Check if no arguments were provided
if ($Args.Count -eq 0) {
Write-Host "No arguments provided. Please specify a file path."
exit
}
$file_path_components = $Args[0] -split "\\"
$problem_number = $file_path_components[-2]
$solved_file = $file_path_components[-1]
Write-Host "solved_file : $solved_file"
Write-Host "problem_number : $problem_number"
$solved_file_components = $solved_file -split "\."
$solved_file_name = $solved_file_components[0] # Example: a_re.py
$solved_file_extension = $solved_file_components[1] # Example: py
Write-Host "solved_file_name : $solved_file_name"
Write-Host "solved_file_extension : $solved_file_extension"
$problem_alphabet = $solved_file.Substring(0, 1) # Example: a
Write-Host "problem_alphabet : $problem_alphabet"
$test_directory = (($file_path_components[0..($file_path_components.Length - 2)]) -join "/") + "/test/$problem_alphabet/"
Write-Host "test_directory : $test_directory"
$atcoder_url = "https://atcoder.jp/contests/abc$problem_number/tasks/abc${problem_number}_$problem_alphabet"
Write-Host "atcoder_url : $atcoder_url"
if ($is_submit) {
Test $test_directory $atcoder_url $solved_file_extension
Submmit $atcoder_url $solved_file_path $solved_file_extension
} else {
Test $test_directory $atcoder_url $solved_file_extension
}
1つ目の引数で、テストしたいスクリプトのパスを受け取り、2つ目の引数で提出するかどうかを決めます。
提出する場合であっても、すべてのテストケースでACしないと提出されないようにしてあります。
実行結果の例です
S = input()
S = sorted(S)
if S[0] != S[2]:
print("Yes")
else:
print("Noooooooooo")
* Executing task: D:\AtCoder\ABC\powershell\oj_test.ps1 D:\AtCoder\ABC\158\a_wa.py false
slolved_file_path : D:\AtCoder\ABC\158\a_wa.py
is_submit : False
sloved_file : a_wa.py
problem_number : 158
sloved_file_name : a_wa
solved_file_extension : py
problem_alphabet : a
test_directory : D:/AtCoder/ABC/158/test/a/
atcoder_url : https://atcoder.jp/contests/abc158/tasks/abc158_a
Test directory already exists. Skipping download.
[INFO] online-judge-tools 11.5.1 (+ online-judge-api-client 10.10.1)
[INFO] 3 cases found
[WARNING] GNU time is not available: time
[INFO] sample-1
[INFO] time: 0.022923 sec
[SUCCESS] AC
[INFO] sample-2
[INFO] time: 0.022173 sec
[SUCCESS] AC
[INFO] sample-3
[INFO] time: 0.020233 sec
[FAILURE] WA
input:
BBB
output:
Noooooooooo\r
expected:
No
[INFO] slowest: 0.022923 sec (for sample-1)
[FAILURE] test failed: 2 AC / 3 cases
ご自身の環境に合わせて以下の部分を修正すれば同じように動作すると思います。
$file_path_components = $Args[0] -split "\\"
$problem_number = $file_path_components[-2]
$solved_file = $file_path_components[-1]
Write-Host "solved_file : $solved_file"
Write-Host "problem_number : $problem_number"
$solved_file_components = $solved_file -split "\."
$solved_file_name = $solved_file_components[0] # Example: a_re.py
$solved_file_extension = $solved_file_components[1] # Example: py
Write-Host "solved_file_name : $solved_file_name"
Write-Host "solved_file_extension : $solved_file_extension"
$problem_alphabet = $solved_file.Substring(0, 1) # Example: a
Write-Host "problem_alphabet : $problem_alphabet"
$test_directory = (($file_path_components[0..($file_path_components.Length - 2)]) -join "/") + "/test/$problem_alphabet/"
Write-Host "test_directory : $test_directory"
$atcoder_url = "https://atcoder.jp/contests/abc$problem_number/tasks/abc${problem_number}_$problem_alphabet"
Write-Host "atcoder_url : $atcoder_url"
参考までに私は以下のようなフォルダー構造になっています。
AtCoder
├── ABC
├── 158
├── test
├── a
├── sample-1.in
├── sample-1.out
...
├── b
...
├── a.cpp
├── b.cpp
├── c.py
├── c_ans.py
├── 159
...
また、提出時の言語のコードの対応は以下です。
[INFO] supported languages are:
[INFO] supported languages are:
5001 (C++ 20 (gcc 12.2))
5002 (Go (go 1.20.6))
5003 (C# 11.0 (.NET 7.0.7))
5004 (Kotlin (Kotlin/JVM 1.8.20))
5005 (Java (OpenJDK 17))
5006 (Nim (Nim 1.6.14))
5007 (V (V 0.4))
5008 (Zig (Zig 0.10.1))
5009 (JavaScript (Node.js 18.16.1))
5010 (JavaScript (Deno 1.35.1))
5011 (R (GNU R 4.2.1))
5012 (D (DMD 2.104.0))
5013 (D (LDC 1.32.2))
5014 (Swift (swift 5.8.1))
5015 (Dart (Dart 3.0.5))
5016 (PHP (php 8.2.8))
5017 (C (gcc 12.2.0))
5018 (Ruby (ruby 3.2.2))
5019 (Crystal (Crystal 1.9.1))
5020 (Brainfuck (bf 20041219))
5021 (F# 7.0 (.NET 7.0.7))
5022 (Julia (Julia 1.9.2))
5023 (Bash (bash 5.2.2))
5024 (Text (cat 8.32))
5025 (Haskell (GHC 9.4.5))
5026 (Fortran (gfortran 12.2))
5027 (Lua (LuaJIT 2.1.0-beta3))
5028 (C++ 23 (gcc 12.2))
5029 (Common Lisp (SBCL 2.3.6))
5030 (COBOL (Free) (GnuCOBOL 3.1.2))
5031 (C++ 23 (Clang 16.0.6))
5032 (Zsh (Zsh 5.9))
5033 (SageMath (SageMath 9.5))
5034 (Sed (GNU sed 4.8))
5035 (bc (bc 1.07.1))
5036 (dc (dc 1.07.1))
5037 (Perl (perl 5.34))
5038 (AWK (GNU Awk 5.0.1))
5039 (なでしこ (cnako3 3.4.20))
5040 (Assembly x64 (NASM 2.15.05))
5041 (Pascal (fpc 3.2.2))
5042 (C# 11.0 AOT (.NET 7.0.7))
5043 (Lua (Lua 5.4.6))
5044 (Prolog (SWI-Prolog 9.0.4))
5045 (PowerShell (PowerShell 7.3.1))
5046 (Scheme (Gauche 0.9.12))
5047 (Scala 3.3.0 (Scala Native 0.4.14))
5048 (Visual Basic 16.9 (.NET 7.0.7))
5049 (Forth (gforth 0.7.3))
5050 (Clojure (babashka 1.3.181))
5051 (Erlang (Erlang 26.0.2))
5052 (TypeScript 5.1 (Deno 1.35.1))
5053 (C++ 17 (gcc 12.2))
5054 (Rust (rustc 1.70.0))
5055 (Python (CPython 3.11.4))
5056 (Scala (Dotty 3.3.0))
5057 (Koka (koka 2.4.0))
5058 (TypeScript 5.1 (Node.js 18.16.1))
5059 (OCaml (ocamlopt 5.0.0))
5060 (Raku (Rakudo 2023.06))
5061 (Vim (vim 9.0.0242))
5062 (Emacs Lisp (Native Compile) (GNU Emacs 28.2))
5063 (Python (Mambaforge / CPython 3.10.10))
5064 (Clojure (clojure 1.11.1))
5065 (プロデル (mono版プロデル 1.9.1182))
5066 (ECLiPSe (ECLiPSe 7.1_13))
5067 (Nibbles (literate form) (nibbles 1.01))
5068 (Ada (GNAT 12.2))
5069 (jq (jq 1.6))
5070 (Cyber (Cyber v0.2-Latest))
5071 (Carp (Carp 0.5.5))
5072 (C++ 17 (Clang 16.0.6))
5073 (C++ 20 (Clang 16.0.6))
5074 (LLVM IR (Clang 16.0.6))
5075 (Emacs Lisp (Byte Compile) (GNU Emacs 28.2))
5076 (Factor (Factor 0.98))
5077 (D (GDC 12.2))
5078 (Python (PyPy 3.10-v7.3.12))
5079 (Whitespace (whitespacers 1.0.0))
5080 (><> (fishr 0.1.0))
5081 (ReasonML (reason 3.9.0))
5082 (Python (Cython 0.29.34))
5083 (Octave (GNU Octave 8.2.0))
5084 (Haxe (JVM) (Haxe 4.3.1))
5085 (Elixir (Elixir 1.15.2))
5086 (Mercury (Mercury 22.01.6))
5087 (Seed7 (Seed7 3.2.1))
5088 (Emacs Lisp (No Compile) (GNU Emacs 28.2))
5089 (Unison (Unison M5b))
5090 (COBOL (GnuCOBOL(Fixed) 3.1.2))
task.jsonの設定をする
{
"version": "2.0.0",
"tasks": [
{
"label": "Test scripts for Atcoder",
"type": "shell",
"command": "${workspaceFolder}\\ABC\\powershell\\oj_test.ps1",
"args": [
"${fileDirname}\\${fileBasename}",
"false"
],
"problemMatcher": []
},
{
"label": "Submit scripts for Atcoder",
"type": "shell",
"command": "${workspaceFolder}\\ABC\\powershell\\oj_test.ps1",
"args": [
"${fileDirname}\\${fileBasename}",
"true"
],
"problemMatcher": []
}
]
}
ショートカットの設定をする
以下に、"Hello"というメッセージを出力する簡単なタスクを設定する手順を示します。
これを参考にしてショートカットの設定を行ってください。
プロジェクトフォルダーで、.vscodeという名前のフォルダーがない場合は、新しく作成します。このフォルダーはVSCodeの設定ファイルを保存するためのものです。
.vscodeフォルダー内に、tasks.jsonという名前のファイルを作成または編集します。
以下のようにtasks.jsonファイルを設定します:
{
"version": "2.0.0",
"tasks": [
{
"label": "Hello World",
"type": "shell",
"command": "echo",
"args": [
"Hello"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
この設定は、"Hello World"というラベルのタスクを定義し、それが実行されたときにシェルコマンドecho "Hello"を実行するものです。
メニューの「表示 (View)」→「コマンド パレット (Command Palette)」を開き、コマンドパレットにTasks: Run Taskと入力します。
プロンプトに表示されるタスクリストから "Hello World" を選択して実行します。
これで、タスクが実行され、"Hello"というメッセージが出力されるはずです。
また、このタスクを特定のショートカットキーに関連付けることもできます。メニューの「ファイル (File)」→「基本設定 (Preferences)」→「キーボード ショートカット (Keyboard Shortcuts)」を開き、検索ボックスに"workbench.action.tasks.runTask"と入力して、タスクの実行コマンドにショートカットキーを関連付けることができます。
[追記]
上記の方法では、ショートカットを実行した後にどのタスクを実行するかを決める必要がありましたが、それぞれのタスクにショートカットを割り当てる方法が以下の記事で解説されていました。
参考記事