A Cargo subcommand for competitive programming.
Supports AtCoder, Codeforces, and yukicoder. Other websites are available via online-judge-tools/api-client.
- Log in a website,
- (Automatically) register in a contest,
- Retrieves sample/system test cases, and save them as YAML files,
- Test your code for the YAML files,
- Submit your code,
- Watch your submissions. (available for only AtCoder)
Registeration | Sample Test Cases | System Test Cases | Submiting | Watching Submissions | Submission Details | |
---|---|---|---|---|---|---|
AtCoder | ✔️ | ✔️ | ✔️ | ✔️ | ❔ | ❌ |
Codeforces | ❌ | ✔️ | N/A | ✔️ | ❌ | ❌ |
yukicoder | N/A | ✔️ | ✔️ | ✔️ | ❌ | ❌ |
Other websites | ❌ | Depends on online-judge-tools | Depends on online-judge-tools | Depends on online-judge-tools | ❌ | ❌ |
$ cargo install cargo-compete
If the build fails, adding --locked
may help.
$ cargo install --git https://github.com/qryxip/cargo-compete
We provide the binaries in GitHub Releases.
Generates some files for other commands.
Run this command first. It generates the following files.
-
Required for other commands.
-
Sets
build/target-dir
to share thetarget
directory. -
template-cargo-lock.toml
A template of
Cargo.lock
forcargo compete new
. Generated only if you answer2 Yes
toDo you use crates on AtCoder?
question. If this file is generated, file path to it is added tonew.template.lockfile
incompete.toml
.
See the section in the Japanese readme.
Logges in a website.
This is not a command for a package.
You don't have to run this command beforehand, because cargo-compete asks credentials if necessary.
Registeres in a contest.
This is not a command for a package.
You don't have to run this command beforehand, because cargo-compete registers in the contest if necessary.
Retrieves test cases and creates a package for the contest.
Requires compete.toml
.
Generate it with cargo compete init
first.
You can opens the pages in your browser with the --open
option.
And you can also open the source files and the test cases in your browser by testing open
in compete.toml
.
If you forget to add --open
, cd
to the generated package and run cargo compete open
.
Generates bin
targets and retrieves the test cases for them.
Requires compete.toml
.
Generate it with cargo compete init
first.
To use this function, configure add
in the compete.toml
like this.
# for yukicoder
[add]
url = '{% case args[0] %}{% when "contest" %}https://yukicoder.me/contests/{{ args[1] }}{% when "problem" %}https://yukicoder.me/problems/no/{{ args[1] }}{% endcase %}'
is-contest = ["bash", "-c", '[[ $(cut -d / -f 4) == "contests" ]]'] # optional
#target-kind = "bin" # ["bin", "example"]. default to "bin"
bin-name = '{% assign segments = url | split: "/" %}{{ segments[5] }}'
#bin-alias = '{% assign segments = url | split: "/" %}{{ segments[5] }}' # optional
#bin-src-path = './src/bin/{{ bin_alias }}.rs' # optional
❯ cargo compete a contest 296
Added `1358` (bin) for https://yukicoder.me/problems/no/1358
Added `1359` (bin) for https://yukicoder.me/problems/no/1359
Added `1360` (bin) for https://yukicoder.me/problems/no/1360
Added `1361` (bin) for https://yukicoder.me/problems/no/1361
Added `1362` (bin) for https://yukicoder.me/problems/no/1362
Added `1363` (bin) for https://yukicoder.me/problems/no/1363
Added `1364` (bin) for https://yukicoder.me/problems/no/1364
Added `1365` (bin) for https://yukicoder.me/problems/no/1365
Saved 1 test case to /home/ryo/src/competitive/yukicoder/testcases/1358.yml
Saved 3 test cases to /home/ryo/src/competitive/yukicoder/testcases/1359.yml
Saved 3 test cases to /home/ryo/src/competitive/yukicoder/testcases/1360.yml
Saved 3 test cases to /home/ryo/src/competitive/yukicoder/testcases/1361.yml
Saved 3 test cases to /home/ryo/src/competitive/yukicoder/testcases/1362.yml
Saved 1 test case to /home/ryo/src/competitive/yukicoder/testcases/1363.yml
Saved 3 test cases to /home/ryo/src/competitive/yukicoder/testcases/1364.yml
Saved 3 test cases to /home/ryo/src/competitive/yukicoder/testcases/1365.yml
❯ cargo compete a problem 9001
Added `9001` (bin) for https://yukicoder.me/problems/no/9001
Saved 1 test case to /home/ryo/src/competitive/yukicoder/testcases/9001.yml
Retrieves test cases for an existing package.
This is a command for a package.
cd
to the package generated with cargo compete new
.
With --open
option, you can download system test cases instead of sample ones.
For AtCoder, we have to use Dropbox API. Generate an access token with these two permissions in some way,
files.metadata.read
sharing.read
and save a JSON file in the following format to {local data directory}/cargo-compete/tokens/dropbox.json
.
(I'm thinking of better way)
{
"access_token": "<access token>"
}
Retrieves your submissions, and outputs as JSON.
This is a command for a package.
cd
to the package generated with cargo compete new
.
For example, you can get "the URL for the latest submission" by adding | jq -r '.summaries[0].detail
.
$ # for Linux
$ xdg-open "$(cargo compete r ss | jq -r '.summaries[0].detail')"
Opens pages in your browser, and opens source and test cases in your editor.
This is a command for a package.
cd
to the package generated with cargo compete new
.
Runs tests.
This is a command for a package.
cd
to the package generated with cargo compete new
.
You don't have to run this command beforehand, because the tests are run in the submit
command.
Submits your code.
This is a command for a package.
cd
to the package generated with cargo compete new
.
You can convert code with a tool such as cargo-equip and cargo-executable-payload by setting submit
in the compete.toml
.
[submit]
kind = "command"
args = ["cargo", "+1.70.0", "equip", "--exclude-atcoder-202301-crates", "--remove", "docs", "--minify", "libs", "--bin", "{{ bin_name }}"]
language_id = "5054"
[submit]
kind = "command"
args = ["cargo", "executable-payload", "--bin", "{{ bin_name }}"]
language_id = "5054"
Here is an example for compete.toml
.
# Path to the test file (Liquid template)
#
# Variables:
#
# - `manifest_dir`: Package directory
# - `contest`: Contest ID (e.g. "abc100")
# - `bin_name`: Name of a `bin` target (e.g. "abc100-a")
# - `bin_alias`: "Alias" for a `bin` target defined in `pacakge.metadata.cargo-compete` (e.g. "a")
# - `problem`: Alias for `bin_alias` (deprecated)
#
# Additional filters:
#
# - `kebabcase`: Convert to kebab case (by using the `heck` crate)
test-suite = "{{ manifest_dir }}/testcases/{{ bin_alias }}.yml"
# Open files with the command (`jq` command that outputs `string[] | string[][]`)
#
# VSCode:
#open = '[["code", "-a", .manifest_dir], ["code"] + (.paths | map([.src, .test_suite]) | flatten)]'
# Emacs:
#open = '["emacsclient", "-n"] + (.paths | map([.src, .test_suite]) | flatten)'
[template]
src = '''
fn main() {
todo!();
}
'''
[template.new]
# `edition` for `Cargo.toml`.
edition = "2018"
# `profile` for `Cargo.toml`.
#
# By setting this, you can run tests with `opt-level=3` while enabling `debug-assertions` and `overflow-checks`.
#profile = '''
#[dev]
#opt-level = 3
#'''
dependencies = '''
num = "=0.2.1"
num-bigint = "=0.2.6"
num-complex = "=0.2.4"
num-integer = "=0.1.42"
num-iter = "=0.1.40"
num-rational = "=0.2.4"
num-traits = "=0.2.11"
num-derive = "=0.3.0"
ndarray = "=0.13.0"
nalgebra = "=0.20.0"
alga = "=0.9.3"
libm = "=0.2.1"
rand = { version = "=0.7.3", features = ["small_rng"] }
getrandom = "=0.1.14"
rand_chacha = "=0.2.2"
rand_core = "=0.5.1"
rand_hc = "=0.2.0"
rand_pcg = "=0.2.1"
rand_distr = "=0.2.2"
petgraph = "=0.5.0"
indexmap = "=1.3.2"
regex = "=1.3.6"
lazy_static = "=1.4.0"
ordered-float = "=1.0.2"
ascii = "=1.0.0"
permutohedron = "=0.2.4"
superslice = "=1.0.0"
itertools = "=0.9.0"
itertools-num = "=0.1.3"
maplit = "=1.0.2"
either = "=1.5.3"
im-rc = "=14.3.0"
fixedbitset = "=0.2.0"
bitset-fixed = "=0.1.0"
proconio = { version = "=0.3.6", features = ["derive"] }
text_io = "=0.1.8"
whiteread = "=0.5.0"
rustc-hash = "=1.1.0"
smallvec = "=1.2.0"
'''
dev-dependencies = '''
#atcoder-202004-lock = { git = "https://github.com/qryxip/atcoder-202004-lock" }
'''
[template.new.copy-files]
"./template-cargo-lock.toml" = "Cargo.lock"
[new]
kind = "cargo-compete"
# Platform
#
# - atcoder
# - codeforces
# - yukicoder
platform = "atcoder"
# Path (Liquid template)
#
# Variables:
#
# - `contest`: Contest ID. **May be nil**
# - `package_name`: Package name
path = "./{{ contest }}"
#[new]
#kind = "oj-api"
#url = "https://atcoder.jp/contests/{{ id }}"
#path = "./{{ contest }}"
# for Library-Checker
#[add]
#url = "https://judge.yosupo.jp/problem/{{ args[0] }}"
##is-contest = ["false"] # optional
##target-kind = "bin" # ["bin", "example"]. default to "bin"
#bin-name = '{{ args[0] }}'
##bin-alias = '{{ args[0] }}' # optional
##bin-src-path = './src/bin/{{ bin_alias }}.rs' # optional
# for yukicoder
#[add]
#url = '{% case args[0] %}{% when "contest" %}https://yukicoder.me/contests/{{ args[1] }}{% when "problem" %}https://yukicoder.me/problems/no/{{ args[1] }}{% endcase %}'
#is-contest = ["bash", "-c", '[[ $(cut -d / -f 4) == "contests" ]]'] # optional
##target-kind = "bin" # ["bin", "example"]. default to "bin"
#bin-name = '{% assign segments = url | split: "/" %}{{ segments[5] }}'
##bin-alias = '{% assign segments = url | split: "/" %}{{ segments[5] }}' # optional
##bin-src-path = './src/bin/{{ bin_alias }}.rs' # optional
[test]
# Toolchain for the test. (optional)
toolchain = "1.42.0"
# Profile for `cargo build`. ("dev" | "release")
#
# Defaults to `"dev"`.
#profile = "dev"
[submit]
kind = "file"
path = "{{ src_path }}"
language_id = "5054"
#[submit]
#kind = "command"
#args = ["cargo", "+1.70.0", "equip", "--exclude-atcoder-202301-crates", "--remove", "docs", "--minify", "libs", "--bin", "{{ bin_name }}"]
#language_id = "5054"
And here is an example for package.metadata
in Cargo.toml
.
[package]
name = "practice"
version = "0.1.0"
authors = ["Ryo Yamashita <qryxip@gmail.com>"]
edition = "2018"
[package.metadata.cargo-compete.bin]
practice-a = { alias = "a", problem = "https://atcoder.jp/contests/practice/tasks/practice_1" }
practice-b = { alias = "b", problem = "https://atcoder.jp/contests/practice/tasks/practice_2" }
#[package.metadata.cargo-compete.example]
[[bin]]
name = "practice-a"
path = "src/bin/a.rs"
[[bin]]
name = "practice-b"
path = "src/bin/b.rs"
[dependencies]
num = "=0.2.1"
num-bigint = "=0.2.6"
num-complex = "=0.2.4"
num-integer = "=0.1.42"
num-iter = "=0.1.40"
num-rational = "=0.2.4"
num-traits = "=0.2.11"
num-derive = "=0.3.0"
ndarray = "=0.13.0"
nalgebra = "=0.20.0"
alga = "=0.9.3"
libm = "=0.2.1"
rand = { version = "=0.7.3", features = ["small_rng"] }
getrandom = "=0.1.14"
rand_chacha = "=0.2.2"
rand_core = "=0.5.1"
rand_hc = "=0.2.0"
rand_pcg = "=0.2.1"
rand_distr = "=0.2.2"
petgraph = "=0.5.0"
indexmap = "=1.3.2"
regex = "=1.3.6"
lazy_static = "=1.4.0"
ordered-float = "=1.0.2"
ascii = "=1.0.0"
permutohedron = "=0.2.4"
superslice = "=1.0.0"
itertools = "=0.9.0"
itertools-num = "=0.1.3"
maplit = "=1.0.2"
either = "=1.5.3"
im-rc = "=14.3.0"
fixedbitset = "=0.2.0"
bitset-fixed = "=0.1.0"
proconio = { version = "=0.3.6", features = ["derive"] }
text_io = "=0.1.8"
whiteread = "=0.5.0"
rustc-hash = "=1.1.0"
smallvec = "=1.2.0"
[dev-dependencies]
Test cases are saved as YAML files.
# https://atcoder.jp/contests/practice/tasks/practice_1
---
type: Batch
timelimit: 2s
match: Lines
cases:
- name: sample1
in: |
1
2 3
test
out: |
6 test
- name: sample2
in: |
72
128 256
myonmyon
out: |
456 myonmyon
extend:
- type: Text
path: "./a"
in: /in/*.txt
out: /out/*.txt
# https://atcoder.jp/contests/ddcc2019-final/tasks/ddcc2019_final_a
---
type: Batch
timelimit: 2s
match:
Float:
relative_error: 1e-8
absolute_error: 1e-8
cases:
- name: sample1
in: |
5
-->--
out: |
3.83333333333333
- name: sample2
in: |
7
-------
out: |
6.5
- name: sample3
in: |
10
-->>>-->--
out: |
6.78333333333333
extend:
- type: Text
path: "./a"
in: /in/*.txt
out: /out/*.txt
# https://judge.yosupo.jp/problem/sqrt_mod
---
type: Batch
timelimit: 10s
match:
Checker:
cmd: ~/.cache/online-judge-tools/library-checker-problems/math/sqrt_mod/checker "$INPUT" "$ACTUAL_OUTPUT" "$EXPECTED_OUTPUT"
shell: Bash
cases: []
extend:
- type: SystemTestCases
The format is TestSuite
in the following schemas.
A test suite for a normal problem.
Field | Type | Default | Description |
---|---|---|---|
timelimit |
Duration | null |
~ |
Time limit |
match |
Match |
Judging method | |
cases |
Case[] |
[] |
Sets of input and output |
extend |
Extend[] |
[] |
Additional sets of input and output |
A string that can parsed with humantime::format_duration
.
An untagged ADT.
Compares whole strings.
Compares words splitted by whitespace.
Compares lines.
Compares words splitted by whitespace.
absolute_error
and relative_error
are applied for pairs of words that can parsed as floating point numbers.
Field | Type | Default | Description |
---|---|---|---|
relative_error |
PositiveFiniteFloat64 | null |
~ |
Relative error |
absolute_error |
PositiveFiniteFloat64 | null |
~ |
Absolute error |
A 64-bit floating point number that is positive and is not inf
.
Checks with a shell script.
The following environment variables are given for the script.
INPUT
ACTUAL_OUTPUT
EXPECTED_OUTPUT
(only if theCase.out
is present)
Field | Type | Default | Description |
---|---|---|---|
cmd |
str |
Command | |
shell |
Shell |
Shell |
An untagged ADT.
Bash.
Field | Type | Default | Description |
---|---|---|---|
name |
str |
"" |
Name |
in |
str |
Input | |
out |
str | null |
~ |
Output |
timelimit |
Duration | null |
~ |
Overrides timelimit |
match |
Match | null |
~ |
Overrides match |
Field | Type | Default | Description |
---|---|---|---|
path |
str |
Directory | |
in |
Glob |
Text files for input | |
out |
Glob |
Text files for output | |
timelimit |
Duration | null |
~ |
Overrides timelimit |
match |
Match | null |
~ |
Overrides match |
A glob.
System test cases.
System test cases are stored under { cache directory }/cargo-compete/system-test-cases
.
They are automatically downloaded if missing when test
ing code.
Field | Type | Default | Description |
---|---|---|---|
problem |
Url | null |
~ | URL of the problem |
A URL.
A test suite for an interactive problem.
Field | Type | Default | Description |
---|---|---|---|
timelimit |
Duration | null |
~ |
Time limit |
A dummy test suite for dummy problems such as ones in APG4b.
Field | Type | Default | Description |
---|
The cookies and tokens are saved under { local data directory }/cargo-compete
.
.
├── cookies.jsonl
└── tokens
├── codeforces.json
├── dropbox.json
└── yukicoder.json
cargo-compete reads these environment variables if they exist, and use them.
$DROPBOX_ACCESS_TOKEN
$YUKICODER_API_KEY
$CODEFORCES_API_KEY
$CODEFORCES_API_SECRET
With online-judge-tools
For unsupported websites, oj-api(.exe)
in the $PATH
is used when download
ing and submit
ting.
[package]
name = "library-checker"
version = "0.0.0"
edition = "2018"
publish = false
[package.metadata.cargo-compete.bin]
aplusb = { problem = "https://judge.yosupo.jp/problem/aplusb" }
See the section in the Japanese readme.
Dual-licensed under MIT or Apache-2.0.